← All docs

CLI

dyndyn-pdf is a single self-contained executable (or a global .NET tool) that converts PDF documents to pixel-faithful, live-text HTML from the command line — no server, no browser, no external services.

dyndyn-pdf convert report.pdf -o out/

That produces out/report.html: one file, fonts/images/SVG inlined as data URIs, text you can select and search. See library.md for the same conversion driven from code, and server.md for the hosted HTTP alternative.


Install

As a .NET global tool (needs the .NET 10 runtime):

dotnet tool install -g DynDyn.Pdf.Cli
dyndyn-pdf convert report.pdf -o out/

The tool package bundles the managed engine plus the native pdfium binaries for every supported platform under runtimes/<rid>/native/, so one install works everywhere the .NET 10 runtime does.

As a self-contained, single-file binary (no .NET runtime needed on the host):

dotnet publish src/DynDyn.Pdf.Cli -c Release -r linux-x64 --self-contained \
  -p:PublishSingleFile=true -o ./publish/linux-x64

Supported RIDs (the native rendering library is auto-restored from NuGet for each; no manual install step): win-x64, win-arm64, win-x86, linux-x64, linux-arm64 (incl. musl/Alpine), osx-x64, osx-arm64.

A build published with -p:NativeAssets=false drops the native payload (and pdfium/SkiaSharp) entirely — smaller binary, fully managed, --engine native and --background raster then report exit code 4 instead of being available. See managed-vs-native.md for when that trade-off makes sense.


Commands

convert — convert one or more PDFs

dyndyn-pdf convert <inputs...> [options]

<inputs...> accepts, in any combination, one or more of:

There is no stdin/stdout mode: every input is a real path on disk, and every output is written to disk (never printed to stdout). Matches across all inputs are de-duplicated and sorted (ordinal, case-insensitive) for a deterministic batch order. Anything that resolves to zero PDFs (a typo, a missing file) is reported as warning: no PDF matched '<pattern>' on stderr and does not fail the run by itself — the run only fails with exit code 3 if no input resolved to anything.

Batch mode engages automatically whenever more than one PDF resolves — no separate flag needed. Each document gets its own converter, reader, and font registry (full isolation); one document failing does not stop the rest, and their outputs are still written.

# One document
dyndyn-pdf convert report.pdf -o out/

# A whole folder, 8 documents at a time, per-page output
dyndyn-pdf convert reports/ -o out/ --format pages --jobs 8

# A recursive glob, selected pages, viewer app packaging
dyndyn-pdf convert "reports/**/*.pdf" -o out/ --format viewer --pages 1-20

Options

Flag ConversionOptions Default Values / range Notes
-o, --output <dir> OutputDirectory input's directory any writable dir
-f, --format <v> (writer choice) single single, pages, viewer single=one file; pages=folder+assets; viewer=single-file app w/ TOC + zoom
-e, --engine <v> (converter choice) managed managed, native managed=clip-aware, real Bézier fonts; native=pdfium
--raster-fallback <v> RasterFallback auto never, auto, always Substitute a native raster background where the vector one can't composite; text stays live. Never trips exit 4 — it degrades to the vector background with a warning instead
--semantic-tags SemanticStructure off flag Emit <h1-6>/<p>/<ul>/<li>/<table>… from a tagged PDF via display:contents. No effect on untagged PDFs
--detect-decorative DetectDecorative off flag Detect running headers/footers and page numbers as non-selectable decorative text
--extract-styles ExtractStyles off flag Dedup repeated style declarations into short CSS classes; off = byte-identical output
--dpi <n> Dpi 120 18–600 CSS pixel scale = dpi/72
-p, --pages <range> Pages all 1-5,8,10- 1-based; applies to every document in a batch
--line-grouping <v> LineGrouping always always, auto, never always=merge into justified lines; auto=merge only where pixel-accurate; never=every word at its exact position
--no-line-grouping LineGrouping = Never off flag Alias for --line-grouping never
--background <v> Background svg svg, raster, none raster needs the native rendering library and SkiaSharp
--no-background Background = None off flag Alias for --background none
--jpeg-quality <n> JpegQuality 85 1–100 Opaque background images only (alpha images use PNG)
--parallelism <n> MaxDegreeOfParallelism all cores ≥1 Managed post-processing threads within one document; in batch mode, derived from --jobs when unset
-j, --jobs <n> BatchOptions.MaxConcurrentDocuments derived from cores + available memory ≥1 PDFs converted concurrently; ignored for a single input
--raw-ttf FontEmbedding = RawTtf off (Woff1) flag Raw TrueType instead of WOFF 1.0; larger output, a font-debugging aid
--password <string> Password none string User password for an encrypted PDF. Managed engine only — passing it with --engine native prints a warning and is ignored
-v, --verbosity <level> (log level) normal quiet, minimal, normal, detailed, diagnostic See table below
--report <path> (CLI-only) none file path Writes a machine-readable JSON report: per-document input hash/timing/exit status, per-page counts (spans, paths, images, fonts, bytes), and every warning as {page, code, severity, message}. Written regardless of the exit code, including on failure/cancellation
--fail-on <v> (CLI-only exit-code gate) none none, loss, any Exit code 5 when the conversion completes but degrades fidelity. loss=any Loss-severity warning; any=any warning at all. Per document in batch mode
--timeout <seconds> (CLI-only) 0 (disabled) ≥0 Hard per-document timeout. On expiry the document is abandoned via cancellation and reported with exit code 7; a batch document timing out does not abort the rest

-v/--verbosity maps to a log level:

Level Log level
quiet none
minimal warning
normal information
detailed debug
diagnostic trace

A live progress line is shown when verbosity ≤ normal and stdout is a TTY (not redirected).

The full option reference — including library-only settings with no CLI flag — lives in configuration.md.

info — inspect a document without converting

dyndyn-pdf info report.pdf

Prints file size, page count, and page geometry (grouping consecutive pages that share the same size/rotation). Reads only the page-tree dictionaries — it never decodes content streams or loads fonts, so it stays fast even on content-heavy documents. Exit 3 if the file doesn't exist, 1 on a parse failure.

render — native-only rendering oracle (build-conditional)

Present only in builds that include the native payload (i.e. not -p:NativeAssets=false publishes). It calls pdfium directly rather than through the managed pipeline and exists as internal fidelity-benchmark tooling, not part of the conversion story described here.


Exit codes

Script against these — they are a stable contract:

Code Meaning
0 Success
1 Conversion failed (or, in batch, at least one document failed — successes are still written)
2 Invalid arguments (bad --dpi, --jpeg-quality, --pages range, --timeout, or an output-path collision in a batch)
3 Input not found (no PDF matched any input)
4 A native-only feature (--engine native or --background raster) was requested but the native rendering library (or, for raster, SkiaSharp) is unavailable for this RID. --raster-fallback never triggers this — it degrades to the vector background with a warning instead
5 The conversion completed and produced output, but --fail-on (default none, so this never fires unless requested) was tripped. In batch mode, a hard document failure (1) takes priority over a 5 when both occur across the batch
6 The PDF is password-protected and no password (or the wrong one) was supplied via --password
7 The conversion was aborted by --timeout
8 The PDF is encrypted with a security handler this engine does not implement (e.g. /Adobe.PubSec public-key/certificate encryption) — no password can ever unlock it

Full defaults and the library/REST equivalents for every flag are in configuration.md. For containerizing the CLI as a batch job, see deployment.md.