A dockerized service providing a REST API interface to leverage WeasyPrint's functionality for generating PDF documents from HTML and CSS.
- Simple REST API to access WeasyPrint
- Real-time monitoring dashboard with metrics visualization
- Prometheus metrics endpoint on dedicated port (9180) for security and Grafana integration
- Compatible with amd64 and arm64 architectures
- Easily deployable via Docker
- Security-hardened container: Runs as non-root user with OCI security labels
To install the latest version of the WeasyPrint Service, run the following command:
docker pull ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestTo start the WeasyPrint service container, execute:
docker run --detach \
--init \
--publish 9080:9080 \
--publish 9180:9180 \
--name weasyprint-service \
ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestThe service will be accessible on port 9080, and Prometheus metrics on port 9180.
Important: The
--initflag enables Docker's built-in init process which handles signal forwarding and zombie process reaping. This is required for proper operation of the service.
The service supports generating PDFs in various archival and accessibility formats via the pdf_variant query parameter.
Supported PDF/A variants (archival):
pdf/a-1b,pdf/a-2b,pdf/a-3b- Basic conformance levelspdf/a-2u,pdf/a-3u,pdf/a-4u- Unicode conformance levelspdf/a-1a,pdf/a-2a,pdf/a-3a- Accessible conformance levelspdf/a-4e,pdf/a-4f- PDF/A-4 variants
Supported PDF/UA variants (universal accessibility):
pdf/ua-1,pdf/ua-2
Example usage:
curl -X POST "http://localhost:9080/convert/html?pdf_variant=pdf/a-2b" \
-H "Content-Type: text/html" \
-d "<html><body>Hello World</body></html>" \
--output document.pdfBreaking Change (WeasyPrint 67.0): The
pdf/a-4bvariant is no longer supported. Usepdf/a-4forpdf/a-4einstead.
By default, WeasyPrint subsets fonts to include only the glyphs used in the document, reducing PDF size. However, some fonts have invalid OS/2 Unicode range bits that cause subsetting to fail with:
ValueError: expected 0 <= int <= 122, found: 123
Use the full_fonts=true query parameter to disable font subsetting and embed complete fonts:
curl -X POST "http://localhost:9080/convert/html?full_fonts=true" \
-H "Content-Type: text/html" \
-d "<html><body>Hello World</body></html>" \
--output document.pdfNote: This increases PDF file size but avoids font subsetting errors.
Device Scaling can be configured via the DEVICE_SCALE_FACTOR environment variable. This allows you to adjust the scaling factor for the SVG to PNG conversion.
Valid range: 1.0 - 10.0 (default: 1.0)
To customize the device scaling when running the container:
docker run --detach \
--init \
--publish 9080:9080 \
--name weasyprint-service \
--env DEVICE_SCALE_FACTOR=2.0 \
ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestNote: Invalid values will fall back to default (1.0) with a warning logged.
The service limits concurrent SVG to PNG conversions to prevent memory leaks and resource exhaustion.
Valid range: 1 - 100 (default: 10)
To customize the concurrency limit when running the container:
docker run --detach \
--init \
--publish 9080:9080 \
--name weasyprint-service \
--env MAX_CONCURRENT_CONVERSIONS=20 \
ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestNote: Invalid values will fall back to default (10) with a warning logged.
The service can automatically restart the Chromium browser after a specified number of conversions to prevent memory accumulation and ensure long-term stability.
Valid range: 0 - 10000 (default: 0 = disabled)
To enable automatic restart after every 1000 conversions:
docker run --detach \
--init \
--publish 9080:9080 \
--name weasyprint-service \
--env CHROMIUM_RESTART_AFTER_N_CONVERSIONS=1000 \
ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestHow it works:
- When enabled (value > 0), Chromium will automatically restart after reaching the specified conversion count
- The restart happens transparently before the next conversion begins
- Conversion counter resets to 0 after each restart
- Set to 0 (default) to disable automatic restarts
- Useful for long-running services with high conversion volumes
Note: Invalid values will fall back to default (0) with a warning logged.
The service requires a persistent Chromium browser instance for SVG to PNG conversion.
Startup Behavior (Fail-Fast):
- The service will terminate if Chromium cannot be initialized during startup
- Common causes: Missing dependencies, insufficient memory, or missing Chromium binaries
- Docker requirements:
--shm-sizeshould be configured if running many concurrent conversions - Health check: The
/healthendpoint verifies Chromium is running and healthy at runtime
Automatic Recovery:
- If a conversion fails due to Chromium crash or error, the service automatically restarts Chromium and retries
- Valid range: 1 - 10 (default: 2)
- This provides resilience against transient Chromium failures during operation
- If restart fails or all retry attempts are exhausted, the conversion request will fail with an error
- Recovery attempts are logged for monitoring and troubleshooting
To customize the number of retry attempts:
docker run --detach \
--init \
--publish 9080:9080 \
--name weasyprint-service \
--env CHROMIUM_MAX_CONVERSION_RETRIES=3 \
ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestNote: Invalid values will fall back to default (2) with a warning logged.
Monitoring:
- Use Docker healthcheck or the
/healthendpoint to monitor service availability - Check service logs for automatic recovery events and conversion failures
- Failed conversions are logged with WARNING level, recovery attempts with INFO level
To diagnose Chromium startup issues, check the service logs for error messages during initialization. The container will exit if Chromium fails to start.
The service includes an interactive web-based monitoring dashboard accessible at /dashboard:
Dashboard Features:
Key Performance Indicators:
- Service health status with real-time indicator
- Total conversions (HTML→PDF and SVG→PNG)
- Error rate percentage
- Current queue size and active conversions
- Average response time
- System uptime and browser restarts
Interactive Charts:
- Queue & Active Conversions - Real-time visualization of request queue and concurrent processing
- CPU Usage (%) - CPU consumption tracking with percentage scale
- Memory Usage (MB) - Memory tracking showing Chromium memory, total system memory, and available memory
Technical Details:
- Auto-refresh: Updates every 5 seconds
- Data retention: Last 20 data points on charts
- Technology: Chart.js 4.4.0 (bundled locally) for visualizations
- Design: Light or dark theme support via environment variable
- API endpoint: Fetches data from
/health?detailed=true - Version information: Service, WeasyPrint, and Chromium versions displayed in the header
Theme Configuration:
The dashboard theme can be configured via the DASHBOARD_THEME environment variable:
Valid values: light, dark (case-insensitive, default: light)
To use dark theme:
docker run --detach \
--init \
--publish 9080:9080 \
--name weasyprint-service \
--env DASHBOARD_THEME=dark \
ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestNote: Invalid values will fall back to light theme with a warning logged.
Production Considerations:
- Consider restricting dashboard access via reverse proxy (nginx, Traefik)
- Use authentication middleware for sensitive environments
- Monitor dashboard endpoint metrics separately
The service exposes Prometheus-compatible metrics for comprehensive monitoring and observability through Grafana dashboards.
Metrics Endpoint: /metrics on port 9180 (dedicated metrics port)
Security: The metrics endpoint is served on a separate port (9180) from the main API (9080). This allows network-level isolation using security groups or firewall rules to restrict metrics access to your Prometheus server only.
Available Metrics:
Conversion Metrics:
pdf_generations_total- Total successful PDF generationspdf_generation_failures_total- Total failed PDF conversionspdf_generation_error_rate_percent- PDF generation error ratesvg_conversions_total- Total successful SVG conversionssvg_conversion_failures_total- Total failed SVG conversionssvg_conversion_error_rate_percent- SVG conversion error rate
Performance Metrics:
pdf_generation_duration_seconds- PDF generation time histogramsvg_conversion_duration_seconds- SVG conversion time histogramqueue_time_seconds- Request queue wait time histogramhttp_request_duration_seconds- HTTP request duration histogram
Resource Metrics:
cpu_percent- Current CPU usagesystem_memory_total_bytes- Total system memorysystem_memory_available_bytes- Available system memorychromium_memory_bytes- Current Chromium memory usagequeue_size- Current requests in queueactive_pdf_generations- Active PDF generations processes
Health Metrics:
uptime_seconds- Service uptimechromium_restarts_total- Chromium restart countchromium_consecutive_failures- Health check failure streak
Prometheus Configuration Example:
scrape_configs:
- job_name: 'weasyprint-service'
static_configs:
- targets: ['weasyprint-service:9180'] # Metrics on dedicated port
metrics_path: '/metrics'
scrape_interval: 15s
scrape_timeout: 10sGrafana Dashboard Queries:
# PDF generation rate (requests per second)
rate(pdf_generations_total[5m])
# Error rate percentage
(rate(pdf_generation_failures_total[5m]) + rate(svg_conversion_failures_total[5m]))
/ (rate(pdf_generations_total[5m]) + rate(svg_conversions_total[5m])) * 100
# 95th percentile conversion duration
histogram_quantile(0.95, rate(pdf_generation_duration_seconds_bucket[5m]))
# Memory usage (MB)
chromium_memory_bytes / 1024 / 1024
Docker Compose Example with Prometheus & Grafana:
version: '3.8'
services:
weasyprint-service:
image: ghcr.io/schweizerischebundesbahnen/weasyprint-service:latest
init: true
ports:
- "9080:9080" # Main API
- "9180:9180" # Metrics endpoint
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
prometheus-data:
grafana-data:Pre-configured Monitoring Stack:
For a complete, production-ready monitoring setup with pre-configured Prometheus, Grafana, and dashboards:
cd monitoring
./start-monitoring.shThis will start the WeasyPrint service, Prometheus, and Grafana with a pre-built dashboard. Access Grafana at http://localhost:3000 (admin/admin) and view the dashboard at http://localhost:3000/d/weasyprint-service.
For detailed setup instructions and configuration options, see monitoring/README.md.
For complete metric descriptions and alerting examples, see CLAUDE.md.
The service includes a robust logging system with the following features:
- Log files are stored in
/opt/weasyprint/logsdirectory - Log level can be configured via
LOG_LEVELenvironment variable (default: INFO) - Log format:
timestamp - logger name - log level - message - Each service start creates a new timestamped log file
To customize logging when running the container:
docker run --detach \
--init \
--publish 9080:9080 \
--name weasyprint-service \
--env LOG_LEVEL=DEBUG \
--volume /path/to/local/logs:/opt/weasyprint/logs \
ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestAvailable log levels:
- DEBUG: Detailed information for debugging
- INFO: General operational information (default)
- WARNING: Warning messages for potential issues
- ERROR: Error messages for failed operations
- CRITICAL: Critical issues that require immediate attention
The container is built with security best practices:
Non-root User:
- Container runs as
appuser(uid 1000) instead of root - Reduces attack surface and follows principle of least privilege
- Compatible with Kubernetes pod security policies requiring non-root containers
OCI Security Labels: The image includes security metadata labels for container scanning and policy enforcement:
org.opencontainers.image.security.caps.drop="ALL"- Indicates all capabilities should be droppedorg.opencontainers.image.security.no-new-privileges="true"- Prevents privilege escalation
Recommended Runtime Security:
docker run --detach \
--init \
--publish 9080:9080 \
--name weasyprint-service \
--security-opt no-new-privileges:true \
--cap-drop ALL \
--read-only \
--tmpfs /tmp \
ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestNote: When using --read-only, ensure log volume is mounted if persistent logging is required:
--volume /path/to/logs:/opt/weasyprint/logsA document names its own resources: an image, a font, a stylesheet. WeasyPrint loads what it names, so a document reaching this service can make it read an address of the network it sits in, and <link rel="attachment"> returns the answer inside the produced PDF.
Every load therefore goes through a policy. By default the service refuses every address which is not public: loopback, private ranges, link local including the cloud metadata address 169.254.169.254, and their IPv6 equivalents.
| Variable | Default | Purpose |
|---|---|---|
EXTERNAL_RESOURCES_POLICY |
BLOCK_INTERNAL |
BLOCK_INTERNAL, ALLOWLIST_ONLY or ALLOW_ALL |
EXTERNAL_RESOURCES_ALLOWED_ORIGINS |
empty | Comma separated origins which are allowed whatever they resolve to |
EXTERNAL_RESOURCES_MAX_SIZE_MB |
16 |
Size a single resource may reach |
EXTERNAL_RESOURCES_TIMEOUT_SECONDS |
10 |
Time the whole load of one resource may take, its redirects and its address attempts included. Resolving a name is bounded by the resolver of the host rather than by this value, so one lookup may outlast it. No further lookup starts once it has passed |
To load resources from an internal host, list its origin:
EXTERNAL_RESOURCES_ALLOWED_ORIGINS=cdn.intranet,https://images.intranet:8443An entry is written [scheme://]host[:port], and what it leaves out is not compared:
| Entry | What it allows |
|---|---|
cdn.intranet |
that host under either scheme, on any port |
cdn.intranet:8443 |
that host on port 8443, under either scheme |
https://cdn.intranet |
that host under https, on port 443 |
https://cdn.intranet:8443 |
that host under https, on port 8443 |
The three policies:
# BLOCK_INTERNAL (default) - public addresses and the allowed origins
# ALLOWLIST_ONLY - only the allowed origins
# ALLOW_ALL - no restriction, this exposes the network of the service to whoever writes a document
-e EXTERNAL_RESOURCES_POLICY=ALLOWLIST_ONLYHow a request is made. The name is resolved, every address it answers with is checked, and the request is bound to what was checked, so a second lookup cannot answer differently. Every redirect hop is checked again, at most five of them. A loaded resource has to be an image, a font or a stylesheet, by the declared type and, where a server declares none, by the content itself. That last rule is what closes <link rel="attachment"> as a way to read a body back, and it costs one thing: a stylesheet served without a content type is refused, because a stylesheet cannot be told from any other text by its first bytes, and it is text bodies which the rule is there to keep out. A server which declares text/css is loaded as before.
Schemes. data: passes, it carries its own content. file: is refused, except for the files uploaded with a request to /convert/html-with-attachments, which the service itself put in a temporary directory. ftp: and everything else are refused.
Behind a proxy. A proxy resolves the name itself, so such a request cannot be bound to a checked address. It is made only for a host listed in EXTERNAL_RESOURCES_ALLOWED_ORIGINS. Where a proxy is configured for every destination, BLOCK_INTERNAL therefore behaves as ALLOWLIST_ONLY.
Blocked resources are reported in the log and the document is rendered without them, which is how WeasyPrint treats a resource it cannot load.
Both servers speak plain HTTP by default, which is what a deployment behind a reverse proxy or an ingress expects: TLS terminates there and nothing has to be configured here. That remains the recommended setup where such a component is already in place.
Where the service is reached directly across a network, each server can serve TLS itself.
The API server:
| Variable | Purpose |
|---|---|
TLS_CERT_FILE |
Certificate chain in PEM format |
TLS_KEY_FILE |
Private key in PEM format |
TLS_KEY_PASSWORD |
Password of the key, where it has one |
TLS_CLIENT_CA_FILE |
CA that client certificates are verified against |
TLS_CLIENT_AUTH |
none (default), optional or required |
The metrics server, configured on its own:
| Variable | Purpose |
|---|---|
METRICS_TLS_CERT_FILE |
Certificate chain in PEM format |
METRICS_TLS_KEY_FILE |
Private key in PEM format |
METRICS_TLS_KEY_PASSWORD |
Password of the key, where it has one |
METRICS_TLS_CLIENT_CA_FILE |
CA that client certificates are verified against |
METRICS_TLS_CLIENT_AUTH |
none (default), optional or required |
The two sets are independent and neither inherits from the other. The metrics port can stay plain behind a network rule while the API serves TLS, or the other way round.
docker run --detach \
--init \
--publish 9080:9080 \
--name weasyprint-service \
--volume /path/to/tls:/opt/weasyprint/tls:ro \
-e TLS_CERT_FILE=/opt/weasyprint/tls/server.pem \
-e TLS_KEY_FILE=/opt/weasyprint/tls/server.key \
ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestAn incomplete configuration stops the start with a message naming the variable. The service never falls back to plain HTTP, because serving in the clear while an operator believes otherwise is worse than not starting.
The material is loaded before either server listens, so a key which does not match its certificate, a wrong TLS_KEY_PASSWORD or an unusable CA also stop the start, rather than surfacing at the first connection.
Mutual TLS. With TLS_CLIENT_AUTH=required the service accepts only clients presenting a certificate signed by TLS_CLIENT_CA_FILE. That authenticates the caller, which the API key below does not: the certificate says who connected, the key says who may convert. The two combine.
The healthcheck of the container follows the configured scheme. It talks to its own process over loopback, so it does not verify the certificate. Where client certificates are required, give the probe one with TLS_HEALTHCHECK_CERT_FILE and TLS_HEALTHCHECK_KEY_FILE, otherwise the container reports itself unhealthy. The two go together: one without the other fails the probe with a message naming them, rather than an opaque handshake error.
Certificate renewal. The certificate is read once, at startup. A renewed certificate takes effect when the container restarts.
Prometheus. Once the metrics port serves TLS, its scrape configuration needs scheme: https next to the target.
The conversion endpoints can be protected with an API key. The feature is optional and disabled by default.
Configuration:
API_KEY: One or more API keys. Authentication is disabled when the variable is unset or empty.- Several keys are configured as a comma-separated list, for example
API_KEY=key-a,key-b. This allows key rotation without downtime.
Protected endpoints:
POST /convert/htmlPOST /convert/html-with-attachments
/health, /version, /dashboard, /static and /api/docs stay open. This keeps the Docker healthcheck and the monitoring dashboard working. The Prometheus endpoint on port 9180 is not affected; isolate it at the network level as described in Prometheus & Grafana Integration.
Start the service with authentication:
docker run --detach \
--init \
--publish 9080:9080 \
--name weasyprint-service \
-e API_KEY=your-secret-key \
ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestSend the key in the X-API-Key header:
export API_KEY=your-secret-key
curl -X POST http://localhost:9080/convert/html \
-H "X-API-Key: ${API_KEY}" \
-H "Content-Type: text/html" \
--data-binary @index.html \
--output document.pdfOr as a bearer token:
curl -X POST http://localhost:9080/convert/html \
-H "Authorization: Bearer ${API_KEY}" \
-H "Content-Type: text/html" \
--data-binary @index.html \
--output document.pdfRequests with a missing or invalid key return 401 Unauthorized with the header WWW-Authenticate: Bearer. The key value is never written to the log.
To extend or customize the service, use it as a base image in the Dockerfile:
FROM ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestTo run the service using Docker Compose:
docker-compose up -dThe Docker Compose configuration includes the init: true parameter which enables proper process management for the container.
The endpoint /convert/html-with-attachments parses multipart/form-data and supports configuring Starlette's form parsing limits via environment variables:
- FORM_MAX_FIELDS: Maximum number of non-file form fields to accept. Default: 1000.
- FORM_MAX_FILES: Maximum number of file parts to accept. Default: 1000.
- FORM_MAX_PART_SIZE: Maximum allowed size in bytes for any single part (file or field). Default: 10485760 (10 MiB).
Notes:
- Values are parsed as integers. Invalid or negative values fall back to the defaults (negative values are clamped to 0 internally).
- These limits only affect the /convert/html-with-attachments endpoint. The endpoint requires Content-Type: multipart/form-data and will return 400 Bad Request otherwise.
Examples:
Docker run:
docker run --detach \
--init \
--publish 9080:9080 \
--name weasyprint-service \
-e FORM_MAX_FIELDS=2000 \
-e FORM_MAX_FILES=2000 \
-e FORM_MAX_PART_SIZE=20971520 \
ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestThe following entry may be added to the run command:
docker run --init -v /path/to/host/fonts:/usr/share/fonts/custom ...Replace /path/to/host/fonts with the folder containing custom fonts
You can insert native PDF sticky note annotations at specific positions in the resulting document by using the following HTML structure (nested notes are supported for replies):
<span class="sticky-note">
<span class="sticky-note-time">2025-04-30T07:24:55.000+02:00</span>
<span class="sticky-note-username">Test User 1</span>
<span class="sticky-note-title">Test Title</span>
<span class="sticky-note-text">Test sticky note text</span>
<span class="sticky-note">
<span class="sticky-note-time">2020-05-12T08:17:02.000+02:00</span>
<span class="sticky-note-username">Test User 2</span>
<span class="sticky-note-title">Reply Title</span>
<span class="sticky-note-text">Reply text</span>
</span>
</span>The minimum recommended memory for the weasyprint-service container is 2 GB.
Memory consumption depends on the size and complexity of documents being converted. For large documents (1000+ pages with images), peak memory usage can reach 3 GB or more. When running multiple concurrent conversions, memory requirements increase proportionally — plan for approximately 1 GB of additional memory per concurrent conversion of a large document.
To limit memory usage in Docker:
docker run --detach \
--init \
--publish 9080:9080 \
--memory=2g \
--name weasyprint-service \
ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestNote: After a conversion completes, the container's reported memory (RSS) may not decrease immediately. This is normal behavior — Python's memory allocator and glibc malloc retain freed memory as a cache for future allocations rather than returning it to the OS. This memory will be reused by subsequent conversions and does not indicate a memory leak.
By default, Python and glibc do not return freed memory to the OS after a conversion completes. This can be problematic when occasional traffic spikes (e.g. 10 parallel conversions) cause high memory consumption that persists long after the spike ends.
To enable automatic memory reclamation after each conversion, set the RECLAIM_MEMORY_AFTER_CONVERSION environment variable:
docker run --detach \
--init \
--publish 9080:9080 \
--name weasyprint-service \
--env RECLAIM_MEMORY_AFTER_CONVERSION=true \
ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestWhen enabled, the service runs gc.collect() followed by glibc's malloc_trim(0) after each PDF generation, which forces Python to collect unreachable objects and asks the C allocator to return free memory pages to the OS.
For even better memory reclamation, combine with MALLOC_ARENA_MAX=2 to reduce glibc arena fragmentation:
docker run --detach \
--init \
--publish 9080:9080 \
--name weasyprint-service \
--env RECLAIM_MEMORY_AFTER_CONVERSION=true \
--env MALLOC_ARENA_MAX=2 \
ghcr.io/schweizerischebundesbahnen/weasyprint-service:latestTrade-offs:
| Enabled | Disabled (default) | |
|---|---|---|
| Memory after spike | Returns to near-baseline | Stays at peak level |
| Performance | Small overhead per conversion (~1–5 ms) | No overhead |
| Subsequent conversions | May be slightly slower (re-allocation) | Faster (memory reused from cache) |
Recommendation: Enable this option in memory-constrained environments or when traffic is bursty. Leave disabled if conversions are frequent and steady, as cached memory improves performance.
The Docker image uses a multi-stage build with the following components:
- Base image:
debian:trixie-slim(same base aspython:3.14-slim) - Python: Installed via uv from
.tool-versionsfile - Package manager: uv for fast, reproducible dependency management
- Runtime user: Non-root
appuser(uid 1000)
Key benefits:
- Single source of truth for Python version (
.tool-versions) - Faster builds with uv cache mounts
- Smaller attack surface with non-root execution
- Consistent with local development environment
To build the Docker image from the source with a custom version, use:
docker build \
--build-arg APP_IMAGE_VERSION=0.0.0 \
--file Dockerfile \
--tag weasyprint-service:0.0.0 .Replace 0.0.0 with the desired version number.
To enable VSDX (Microsoft Visio) file conversion support, add the ENABLE_VSDX_SUPPORT=true build argument:
docker build \
--build-arg APP_IMAGE_VERSION=0.0.0 \
--build-arg ENABLE_VSDX_SUPPORT=true \
--file Dockerfile \
--tag weasyprint-service:0.0.0 .This installs LibreOffice (~200MB additional size) for VSDX to PNG conversion. Without this flag, VSDX images will remain unchanged in the output.
To start the Docker container with your custom-built image:
docker run --detach \
--init \
--publish 9080:9080 \
--publish 9180:9180 \
--name weasyprint-service \
weasyprint-service:0.0.0To stop the running container, execute:
docker container stop weasyprint-serviceThe container-structure-test tool is used to verify that the Docker image meets expected standards and specifications. It validates the container structure, ensuring proper file paths, permissions, and commands are available, which helps maintain consistency and reliability of the containerized application.
Before running the following command, ensure that the container-structure-test tool is installed. You can find installation instructions in the official documentation.
container-structure-test test --image weasyprint-service:0.0.0 --config ./tests/container/container-structure-test.yamlGrype is used for vulnerability scanning of the Docker image. This tool helps identify known security vulnerabilities in the dependencies and packages included in the container, ensuring the deployed application meets security standards and doesn't contain known exploitable components.
To scan the Docker image for vulnerabilities, you can use Grype. First, ensure that Grype is installed by following the installation instructions.
Then run the vulnerability scan on your image:
grype weasyprint-service:0.0.0Tox automates testing in different Python environments, ensuring that the application works correctly across various Python versions and configurations. It helps maintain compatibility and provides a standardized way to run test suites, formatting checks, and other quality assurance processes.
uv run toxPytest is used for unit and integration testing of the application code. These tests verify that individual components and the entire application function correctly according to specifications. Running pytest during development helps catch bugs early and ensures code quality.
# all tests
uv run pytest# a specific test
uv run pytest tests/test_svg_processor.py -vPre-commit hooks run automated checks on code before it's committed to the repository. This ensures consistent code style, formatting, and quality across the project. It helps catch common issues early in the development process, maintaining high code standards and reducing the need for style-related revisions during code reviews.
uv run pre-commit run --allThis service provides REST API. OpenAPI Specification can be obtained here.