pdfgenrs is a Rust application for generating PDFs through an API.
- Quick start
- Technologies and tools
- Folder structure
- API
- Applications that use pdfgenrs
- Developing pdfgenrs
- Release
- Contact
- Contributing
Most teams use pdfgenrs as a base image together with their own templates. The base image already includes default fonts.
- Create a Dockerfile in your own repository:
FROM ghcr.io/navikt/pdfgenrs:<release>
COPY templates /app/templatesFind the latest <release> in GitHub releases.
- Create the basic folder structure:
mkdir -p templates/your_appname- Add a Typst template (e.g.,
templates/your_appname/your_template.typ), then run a request:
curl -s -X POST http://localhost:8080/api/v1/genpdf/your_appname/your_template \
-H "Content-Type: application/json" \
-d '{"key":"value"}' \
--output output.pdf- (Optional) Add custom fonts or resources:
If your templates use custom fonts or reference resources (e.g., logos), add them to your Dockerfile:
FROM ghcr.io/navikt/pdfgenrs:<release>
COPY templates /app/templates
COPY fonts /app/fonts
COPY resources /app/resourcesCreate the corresponding directories locally:
mkdir -p fonts resourcestemplates/your_appname/- Add
.typTypst templates. - Template file names are part of API paths.
- Templates can read JSON with
#let data = json("/data.json").
- Add
data/your_appname/- Add JSON files matching template names for local testing.
fonts/- Add
.ttf,.otf, or.ttcfonts used by templates.
- Add
resources/- Add other assets your templates need.
For template examples, see templates.
Base URL (local): http://localhost:8080
<your_appname> maps to a folder under templates/, and <template> maps to a .typ file in that folder.
Example:
- Template file:
templates/pale-2/pale-2.typ - Endpoint path:
/api/v1/genpdf/pale-2/pale-2
| Endpoint | Method | Request Content-Type | Response Content-Type | Notes |
|---|---|---|---|---|
/api/v1/genpdf/{your_appname}/{template} |
POST |
application/json |
application/pdf |
Typst + JSON to PDF |
/api/v1/genpdf/html/{your_appname} |
POST |
text/html |
application/pdf |
HTML to PDF |
/api/v1/genpdf/image/{your_appname} |
POST |
image/png or image/jpeg |
application/pdf |
Image to PDF |
/api/v1/genhtml/{your_appname}/{template} |
POST |
application/json |
text/html; charset=utf-8 |
Typst + JSON to HTML |
/internal/is_alive |
GET |
- | - | Liveness |
/internal/is_ready |
GET |
- | - | Readiness |
/internal/metrics |
GET |
- | text/plain |
Prometheus metrics |
All POST endpoints enforce a request body limit of 2097152 bytes (2 MiB), including:
POST /api/v1/genpdf/html/{your_appname}POST /api/v1/genpdf/image/{your_appname}POST /api/v1/genpdf/{your_appname}/{template}POST /api/v1/genhtml/{your_appname}/{template}
Set environment variable REQUEST_BODY_LIMIT_BYTES to tune this limit. Example in Dockerfile for 3 MiB:
FROM ghcr.io/navikt/pdfgenrs:<release>
COPY templates /app/templates
ENV REQUEST_BODY_LIMIT_BYTES=3145728
Compiles a Typst template using JSON request data and returns a PDF.
- Request Content-Type:
application/json - Response Content-Type:
application/pdf - Success:
200 OK - Common errors:
404 Not Found(template/app not found)500 Internal Server Error(rendering failed)
curl -s -X POST http://localhost:8080/api/v1/genpdf/<your_appname>/<template> \
-H "Content-Type: application/json" \
-d '{"key":"value"}' \
--output output.pdfConverts HTML in the request body to a PDF.
- Request Content-Type: typically
text/html - Response Content-Type:
application/pdf - Success:
200 OK - Common errors:
500 Internal Server Error
curl -s -X POST http://localhost:8080/api/v1/genpdf/html/<your_appname> \
-H "Content-Type: text/html" \
--data-binary '<html><body><h1>Hello</h1></body></html>' \
--output output.pdfConverts an image to PDF.
- Supported Request Content-Type:
image/pngimage/jpeg
- Response Content-Type:
application/pdf - Success:
200 OK - Common errors:
415 Unsupported Media Type(if not PNG/JPEG)500 Internal Server Error
curl -s -X POST http://localhost:8080/api/v1/genpdf/image/<your_appname> \
-H "Content-Type: image/png" \
--data-binary @image.png \
--output output.pdfCompiles a Typst template using JSON request data and returns HTML.
- Request Content-Type:
application/json - Response Content-Type:
text/html; charset=utf-8 - Success:
200 OK - Common errors:
404 Not Found(template/app not found)500 Internal Server Error(rendering failed)
curl -s -X POST http://localhost:8080/api/v1/genhtml/<your_appname>/<template> \
-H "Content-Type: application/json" \
-d '{"key":"value"}'When DEV_MODE=true, test data from data/{your_appname}/{template}.json is loaded and GET endpoints are enabled:
GET /api/v1/genpdf/{your_appname}/{template}→ returnsapplication/pdfGET /api/v1/genhtml/{your_appname}/{template}→ returnstext/html; charset=utf-8
These endpoints return:
200 OKon success404 Not Foundif template or test data is missing
When DEV_MODE=false, these GET endpoints are not available (405 Method Not Allowed).
200 OKwhen alive500 Internal Server Errorotherwise
200 OKwhen ready500 Internal Server Errorotherwise
Exposes Prometheus metrics for operational monitoring.
- Response Content-Type:
text/plain - Success:
200 OK
Metrics exposed:
| Metric | Type | Labels | Description |
|---|---|---|---|
http_requests_total |
Counter | method, path, status | Total number of HTTP requests |
http_request_duration_seconds |
Histogram | method, path, status | Request latency distribution |
By default, pdfgenrs loads all assets (templates, data) into memory on startup. Changes to files in these folders require an application restart.
Font files are loaded from FONTS_DIR (default: fonts) on startup.
Make sure Rust and Cargo are installed:
rustc --version
cargo --versioncargo fmt
cargo clippy --all-targets -- -D warnings
cargo build
cargo test
cargo bench --bench performance
cargo runWe use default GitHub releases.
This project follows semantic versioning and does not prefix tags or release titles with v (use 1.2.3, not v1.2.3).
For release steps, see Creating a release on GitHub.
This project is currently maintained by @navikt.
If you have questions, please create an issue and tag it with the appropriate label.
For contact requests within the @navikt org, use the Slack channel #pdfgen.
To get started, fork the repository and create a new branch.
See more info in CONTRIBUTING.md.