Skip to content

MkDocs to PDF

Manuscrio reads a finished MkDocs build directory and exports it to publication-ready PDF manuals:

Terminal window
manuscrio-pdf inspect ./site # what manuals does this site contain?
manuscrio-pdf export ./site # one manual for the documentation edition

Both commands run end to end in a public example repository: Working MkDocs Material integration on GitHub — manuscrio/mkdocs-pdf-export.

The framework is detected from the built pages; --framework mkdocs skips detection when you are diagnosing an unusual build.

Three steps after the step that already builds your site. MkDocs writes ./site, and --logo supplies the mark the adapter cannot yet read from a Material build:

- run: pip install -r requirements.txt && mkdocs build
- name: Export the docs to PDF
run: |
npx --yes [email protected] export site \
--logo path/to/your-logo.svg \
--output-dir manuscrio-output
- uses: actions/upload-artifact@v7
with:
name: manual
path: manuscrio-output/*.pdf

The build needs Python and the wrapper needs Node, but a GitHub-hosted runner already has Node, so a MkDocs workflow sets up only Python. Pin the version, as above: one manuscrio version resolves to exactly one image, so re-running an old pipeline renders what it rendered the first time.

The complete workflow is in manuscrio/mkdocs-pdf-export, and Run in CI has the docker run form for a runner without Node.

MkDocs itself prescribes no markup. The theme supplies all of it, so an exporter reads a theme rather than MkDocs — and Manuscrio reads Material. The built-in theme emits unrelated markup and is not supported.

A build using another theme fails detection, because the marker Manuscrio looks for is Material’s. Forcing the adapter then refuses cleanly rather than exporting something partial:

No rendered MkDocs documentation pages found

Both refusals are clean, and neither produces an incomplete manual. That matters more than it sounds: an exporter that half-reads an unfamiliar theme produces a plausible PDF missing content nobody notices until a customer does.

An edition is one plugin instance, one locale, and one version. MkDocs has neither plugin instances nor versions, so those two thirds of the edition ID are the constants default and current. The locale still separates editions — the adapter groups pages by the rendered page’s lang attribute — so a multilingual site produces one edition per language, and a single-language site has exactly one, usually default:en:current.

Material’s navigation has no section level, so --scope section does not apply:

ScopeProduces
--scope editionone manual, sidebar roots as its top-level chapters
--scope sidebar-rootone manual per sidebar root
--scope sectionrefused — there is nothing to enumerate
A chapter opening in a manual exported from a MkDocs Material build
MkDocsMaterial docs, Setup · lapis · 121 pages

Logo discovery is not yet implemented for MkDocs: inspect reports an empty logo, and the manual carries the Manuscrio wordmark unless you supply your own.

Terminal window
manuscrio-pdf export ./site --logo brand/acme.svg

This is a gap in the adapter rather than a property of MkDocs. See branding.

Material’s navigation offers no stable identifier for a root, so Manuscrio numbers them by position — set-0-1, root-2. Those IDs shift when an entry is inserted above them, so read them from inspect at run time rather than pinning them in a pipeline. See manual IDs and output file names.

For MkDocs, every sidebar appears under unassignedDocumentationSets and assignedPages reads 0. Those fields describe attribution to a navbar section, and Material has none — so nothing is wrong and nothing is excluded. The edition exports in full. The wording is Docusaurus-shaped and will improve.

Run the two commands at the top of this page against your own ./site. Nothing is uploaded and there is no account: without a licence the export is still the complete manual at production rendering quality, with no page limit, carrying an evaluation watermark until a licence removes it.