The render_pdf binary
The gem does no PDF work itself. It runs render_pdf, a standalone renderer written in Rust that is built and published with every Soli release. This page explains how the gem finds it, how to supply your own, and how to use it without Ruby.
#How the gem finds it
Soli::PDF::Binary.path resolves the executable on every render, in this order:
Soli::PDF.binary_path = '...', if set.ENV['SOLI_PDF_BIN'], if set.- The cached download:
<cache>/soli-pdf/<BINARY_VERSION>/render-pdf-<os>-<arch>/render_pdf, where<cache>is$XDG_CACHE_HOMEor~/.cache. If it isn't there yet, the gem downloads it first.
For 1 and 2, the file must exist and be executable. If it doesn't, the gem raises Soli::PDF::BinaryNotFound and never falls back to a download. A typo in a production path fails loudly instead of quietly pulling a binary from the internet.
#The download
The first render on a machine without the cached copy:
- builds the asset name from the host, for example
render-pdf-linux-amd64. The OS islinux,darwinorwindows, and the CPU isamd64orarm64; - fetches
https://github.com/solisoft/soli_lang/releases/download/v2.9.0/<asset>.tar.gz, following up to five redirects; - fetches
<asset>.tar.gz.sha256and compares it with the SHA-256 of the archive. On a mismatch it raisesDownloadErrorand keeps nothing; - unpacks the archive with
tarinto a temporary directory next to the install directory, then moves it into place in one step. A crash halfway never leaves a broken install.
The archive contains the executable and a fonts/ directory with Titillium Web and JetBrains Mono.
~/.cache/soli-pdf/2.9.0/render-pdf-linux-amd64/
├── render_pdf
└── fonts/
├── TitilliumWeb-Regular.ttf …Bold, Italic, BoldItalic
└── JetBrainsMono-Regular.ttf …Bold, Italic
#Fonts next to the binary
On every render, the gem appends --font-dir <dir>/fonts when a fonts/ directory sits next to the executable. This is how the bundled fonts reach the renderer, and it also applies to a binary you supply. Copy the archive as a whole, or pass fonts:. Without any font, a render fails with no usable fonts found.
#Using your own build
Build the renderer from the Soli repository:
git clone https://github.com/solisoft/soli_lang
cd soli_lang/pdf
cargo build --release --bin render_pdf
mkdir -p /opt/render-pdf && cp target/release/render_pdf /opt/render-pdf/ && cp -R fonts /opt/render-pdf/fonts
Then point the gem at it:
export SOLI_PDF_BIN=/opt/render-pdf/render_pdf
#Running it without Ruby
render_pdf is a regular command-line tool. The gem runs it like this:
render_pdf --template /tmp/soli-pdf123.json --data - -o - --title "Invoice F-42" \
--font-dir ~/.cache/soli-pdf/2.9.0/render-pdf-linux-amd64/fonts < data.json > invoice.pdf
| Flag | Meaning |
|---|---|
--template <path> | The layout template. Required. |
--data <path> | The data document. Required unless --invoice is given. |
--invoice <path> | A typed invoice, used instead of --data. Cannot be combined with --data or --xml. |
--xml <path> | Factur-X CII XML to embed. The output becomes PDF/A-3b. |
--profile <name> | Factur-X profile: minimum, basicwl, basic, en16931 (default) or extended. |
-o, --out <path> | Output file. Required. - writes the PDF to standard output. |
--no-images | Do not fetch http(s) images. |
--title, --author, --subject | Document metadata. |
--stationery <path> | Letterhead PDF drawn beneath every page. |
--attach <path> | Embed a file. Repeatable. |
--password, --owner-password | AES-128 encryption. |
--font-dir <path> | A font directory. Repeatable. Defaults to ./fonts and ./font. |
- means standard input for one of --template, --data and --invoice. On success the exit status is 0, and each warning is printed to standard error as a warning: … line. On failure, the binary prints error: … and exits with a non-zero status.
That makes it usable from any language, or from a shell script:
render_pdf --template invoice.template.json --data invoice.data.json -o invoice.pdf
#Performance and concurrency
Each render starts one short-lived process, so renders don't share state. It is safe to render from many threads or processes at once. The only shared state in the gem is Soli::PDF.last_warnings. See Soli::PDF.
Process start-up and font loading happen on every call. For bulk jobs, such as a month of statements, render in parallel background jobs rather than in a web request.