FAQ & troubleshooting
Common questions and gotchas: Linux fonts, small-font layouts, thread-safety, and known limits.
This page answers the questions that come up most often when adopting Rivoli PDF, and the few sharp edges worth knowing before you hit them. If your question is about how to do something, the how-to guides are the better starting point.
Licensing & dependencies
Section titled “Licensing & dependencies”What license is Rivoli PDF under?
Section titled “What license is Rivoli PDF under?”The Rivoli PDF License: source-available; free for individuals and for organizations with annual gross revenue under USD 1,000,000. The project is published by Rivoli AI; the repository is github.com/rivoli-ai/rivoli-pdf.
Does it need Ghostscript, a browser, or any external executable?
Section titled “Does it need Ghostscript, a browser, or any external executable?”No. Rivoli PDF runs in-process (it generates and reads PDFs with no shell-out to Ghostscript, wkhtmltopdf, a headless browser, or any other executable. Layout and PDF writing are pure managed code; raster image work uses SkiaSharp, whose native assets NuGet restores with the package. So there are no system packages to install and no external process to manage, and behaviour is reproducible across machines), but the library is not 100% managed code end to end. See the dependency table below for what that means when you publish.
What third-party packages does it pull in?
Section titled “What third-party packages does it pull in?”The libraries depend on a small set of managed NuGet packages, surfaced transitively when you install:
| Package | Used for |
|---|---|
SixLabors.ImageSharp | Decoding and processing raster images |
SkiaSharp | Image/graphics operations during rendering |
CSJ2K | JPEG 2000 (JPX) image decoding |
System.Text.Encoding.CodePages | Legacy text encodings |
System.Security.Cryptography.Pkcs | Cryptographic primitives (encryption support) |
These are restored by NuGet; you do not install anything outside dotnet. SkiaSharp carries a
native runtime component, resolved through its own NuGet runtime assets, so there is nothing to
install at the OS level, but the runtime identifier you publish for has to be one SkiaSharp ships
assets for.
| Target | Status |
|---|---|
| Windows x64 / arm64 | Supported; natives ship in the SkiaSharp package |
| macOS x64 / arm64 | Supported; natives ship in the SkiaSharp package |
| Linux glibc x64 / arm64 | Supported via SkiaSharp.NativeAssets.Linux, referenced for you |
| Linux musl (Alpine) | Needs an extra reference. Add SkiaSharp.NativeAssets.Linux.NoDependencies plus the libfontconfig1 package, or use a glibc-based image |
| Browser / wasm | Not supported |
If you are containerising on alpine and see a DllNotFoundException for libSkiaSharp, that
table is the reason.
Platforms & runtime
Section titled “Platforms & runtime”Which .NET version and platforms are supported?
Section titled “Which .NET version and platforms are supported?”The packages target .NET 8 and .NET 10 and run on Windows, Linux, and macOS. Install them with:
dotnet add package Rivoli.Pdf.Coredotnet add package Rivoli.PdfMy text renders with wrong or missing glyphs on Linux, why?
Section titled “My text renders with wrong or missing glyphs on Linux, why?”This is almost always a missing font on the host, not a library bug. Headless Linux images (containers, CI runners) often ship without the fonts your document references. Two fixes:
- Install the fonts you use (e.g.
apt-get install fonts-dejavu fonts-liberation), or bundle the font files with your app. - Better for slim containers: register the font from bytes or a stream so you don’t depend on
the host’s font directories at all:
document.RegisterFontFamily("Source Serif", fontBytes, FontWeight.Normal)(there is also aStreamoverload). Ship the font as an embedded resource or blob and register it at startup. See How do I register a custom font. - For CJK content, point the renderer at a font that has the glyphs via
PdfSaveOptions.CjkFontPath, or supply the bytes withPdfSaveOptions.CjkFontData(no filesystem dependency). See How do I handle CJK text.
Behavior & guarantees
Section titled “Behavior & guarantees”Is the output deterministic / reproducible?
Section titled “Is the output deterministic / reproducible?”Yes. That is a core design goal. Given the same input model and options, Rivoli PDF produces
byte-stable output: rendering is deterministic, with no reliance on wall-clock time,
locale-dependent rendering, or external processes. This is what
lets the project run a pixel-fidelity harness in CI and lets you check generated PDFs into golden
tests. (If you set DocumentMetadata fields like creation dates yourself, those values flow through
as you provide them.)
Is Rivoli PDF thread-safe?
Section titled “Is Rivoli PDF thread-safe?”Generate documents on separate instances, one document per thread. Building and saving an
independent Document on each thread is the supported model and works well.
Why can’t I encrypt a PDF/A file?
Section titled “Why can’t I encrypt a PDF/A file?”Because PDF/A forbids encryption: it is part of the standard, not a library limitation. If you set
both SecuritySettings and a PdfAMode on PdfSaveOptions, validation rejects the combination
(PdfSaveOptions.Validate() throws a PdfValidationException). Choose one per output: an archival
PDF/A file, or an encrypted file.
My layout looks wrong with very small fonts, what’s happening?
Section titled “My layout looks wrong with very small fonts, what’s happening?”At small font sizes, rounding in line metrics and tight LineHeight/SpaceBefore/SpaceAfter
values can compound and visibly shift text. If a dense, small-font block looks off, give it a
little breathing room: set an explicit LineHeight (e.g. 1.2–1.4) on the Style rather than
relying on the minimum, and avoid stacking very tight paragraph spacing. The layout is
deterministic, so once a block looks right it stays right across renders.
How do I produce accessible (tagged) PDFs?
Section titled “How do I produce accessible (tagged) PDFs?”Set EnableTaggedPDF = true and a document Language on PdfSaveOptions (or flip the global
default PdfGlobalOptions.DefaultEnableTaggedPDF). A language tag is also required for PDF/A-1a
conformance.
using Rivoli.Pdf;
var renderer = new PdfDocumentRenderer("1.7");renderer.RenderToFile(doc, "accessible.pdf", new PdfSaveOptions{ EnableTaggedPDF = true, Language = "en-US",});Capabilities & maturity
Section titled “Capabilities & maturity”How mature is PDF import and rendering?
Section titled “How mature is PDF import and rendering?”Building documents and writing PDFs is the primary path. Reading/import and rasterization are also supported, with some caveats:
LoadPdfparses existing PDFs back into the model. Expect excellent round-trips for documents the library itself produced and best-effort results for arbitrary third-party PDFs.- Document-level features survive the round-trip: bookmarks, comments/markup annotations, page
labels, /Info metadata and the XMP packet, optional-content layers (hidden ones stay toggleable,
and OCMD //VE visibility expressions survive), print page boxes, color-space identity
(
/Separation//DeviceNspot colors, CMYK ink splits, ICC,/Indexed//Lab//Cal*), and prepress graphics-state parameters such as overprint. - The rasterizer that backs the fidelity harness has known gaps around certain shadings and image codecs.
When in doubt, treat extraction/reading as “supported with caveats” and validate against your own inputs.
What is the fidelity harness?
Section titled “What is the fidelity harness?”It is the project’s deterministic round-trip test rig. It takes a corpus of PDFs, runs them through parse → rebuild → re-export, and compares the result pixel-for-pixel against a PDFium oracle render. Because rendering is deterministic and needs no external executable, the harness runs in CI without extra tooling. It is how the project measures import and rendering accuracy objectively across a large document corpus.
Are digital signatures supported?
Section titled “Are digital signatures supported?”Yes. Encryption (passwords and permissions via PdfSecuritySettings) and cryptographic
signing (via PdfSigner) are both supported. See the
security tutorial for a worked example.
How do I report a bug or request a feature?
Section titled “How do I report a bug or request a feature?”Open an issue on the repository: github.com/rivoli-ai/rivoli-pdf. Reproducible cases (ideally the model or input PDF plus the options you used) are the most useful.
See also
Section titled “See also”- How-to guides: task-oriented recipes.
- API reference: the namespace and type map.
- The document model: the tree you build and
LoadPdfparses.