Skip to content

add print-on-demand edition of the PDF - #272

Draft
rpietzsch wants to merge 16 commits into
mainfrom
feature/print-on-demand
Draft

rpietzsch wants to merge 16 commits into
mainfrom
feature/print-on-demand

Conversation

@rpietzsch

Copy link
Copy Markdown
Contributor

add a print edition of the PDF: the book block of a printed book, built next to the screen PDF

💪 What

  • Adds task pdf:print (dec-tool build-pdf --edition print). It uses the same merge, pandoc run and fonts as task pdf, typeset as the interior of a perfect-bound book (BoD, A4, black and white). style.typ branches on --input edition=print; without it, the screen PDF builds as before.
  • Page furniture of a bound book:
    • mirrored margins, page numbers on the outer edge, and running titles
    • parts that start on right-hand pages, with blank pages left without header or footer
    • an even page count
  • Adds a title page as page 1 and an imprint with the publisher from tools/pdf/print.yml. Its author list lives in tools/pdf/authors.yml, refreshed from the GitHub contributors by task pdf:authors.
  • Paper cannot follow links: links within the book print the page they lead to, and links out of it print their address as a footnote.
  • Makes images and colours printable: copies at 300 ppi without transparency, and a black-and-white palette with no grey lighter than BoD's 20 % black.
  • Adds section modes in tools/pdf/print.yml (full, list, omit):
    • The Task and Operator Reference and the Release Notes print as list, their overview pages plus short tables.
    • The Transformers categories print as one Category | Transformers table instead of empty headings.
  • Wraps long code lines inside their frame in print.
  • In both editions:
    • A figure taller than the page scales down to fit, instead of running into the footer.
    • Image figures no longer carry a Typst number.
  • Fixes the Transformers reference table, which ended at the Excel map row on the website too. The reference generator now turns a fenced block in a plugin description into a code span, and the two affected lines of generated Markdown are regenerated.

🤔 Why

  • BoD needs a book block, not the screen PDF: 300 ppi images without transparency, printable greys, an even page count, and at most 1,200 A4 pages on 80 g paper. The screen PDF has 1683 pages; the print edition has 966.
  • Review of the first print build found these defects, all fixed here:
    • Broken Transformers table: it fell apart after Excel map. stripped_single_line squeezed the description's fenced block onto one line, and rumdl --fix (MD038) then inserted a blank line that ended the table.
    • Empty headings: list mode left the 24 operator categories as empty numbered headings, because the Transformers overview already lists their pages.
    • Long code lines: Typst does not break a long token such as a URL, so it ran past the code frame.
    • Tall figures: three IDS/OSINT tutorial screenshots print 26.8–28 cm tall at column width, taller than the 24.3 cm page body.
    • Doubled figure numbers: captions printed as Figure 15: Figure 2:. That tutorial numbers its figures in the caption text and refers to those numbers, and the website adds no numbering of its own.

👩‍🔬 How to validate

Needs pandoc 3.11 and typst 0.15.x.

task pdf:print    # writes dist/documentation-eccenca-com-<version>-print.pdf
task pdf          # the screen PDF
task pdf:authors  # refreshes tools/pdf/authors.yml; set GITHUB_TOKEN to avoid the rate limit

- Last run:
  - The print edition has 966 pages, about 165 MB.
  - The screen PDF has 1683 pages.
  - No page has content running into the footer.
  - No line runs past the text column.
- Screen PDF: each print-only change was compared against a baseline build and left the textes and document IDs. The screen PDF changes only in:
  - the Transformers table
  - the three tall figures
  - the figure captions
- Pages to look at in the print edition:
  - the A.3 reference ending in the Category | Transformers table
  - the wrapped code line in the IDS/OSINT tutorial (EventRecordID)
  - the three tall screenshots in the same tutorial
- Website: Build › Task and Operator Reference › Transformers is one table of 237 operators
- Tests: poetry run pytest -m "not integration" passes 100 tests.
- task check: rumdl, the navigation check and the output check pass. Locally, yamllint failatch/ folder. The link check logs an external apify.com error and does not fail on it.
- Open: backlog items P13–P17 (normalization pass, preflight report, low-resolution originals, CI, cover) are not part of this PR.

`task pdf:print` builds the book block for a printed book (BoD, A4,
black and white) next to the screen PDF, as planned in tasks/spec.md.

- mirrored page furniture, right-hand part starts, title page and imprint with an author list from `task pdf:authors`
- links printed as page references and footnotes, images at 300 ppi without transparency, a black-and-white palette
- tools/pdf/print.yml shortens the operator reference and the release notes to overview tables; reference categories print as one table
- long code lines wrap inside their frame
- both editions: tall figures scale to the page, image figures carry no Typst number
- fix the transformer reference table, which ended at the Excel map row: the generator turns a fenced block in a description into a code span
@github-actions

github-actions Bot commented Sep 15, 2026

Copy link
Copy Markdown

Test Results

0 tests   0 ✅  0s ⏱️
0 suites  0 💤
1 files    0 ❌

Results for commit 3c205fa.

♻️ This comment has been updated with latest results.

@rpietzsch
rpietzsch marked this pull request as draft September 15, 2026 09:10
The imprint listed contributors by their GitHub IDs.

- task pdf:authors stores each profile name in tools/pdf/authors.yml and adds new authors to authors.names in tools/pdf/print.yml
- print.yml names authors (with titles) and excludes IDs; excluded names are never looked up
- the build prints the given name, else the profile name, else the ID, and warns about each ID it prints
- use the GitHub CLI token when no token is set, and end a rate limit, a rejected token or a network error with a message
The print edition had no way to leave out single pages or parts of a page, and omit left a title and a note behind.

- print.yml keys may name a page; omit drops pages and subtrees without a trace
- the class print-exclude replaces a part of a page with a note pointing to the page online; the site and screen PDF are unchanged
- omit the cmem-client API and the OSINT tutorial, and the INSERT listing of the Snowflake tutorial: 966 to 666 pages
- spec §10 and backlog P18, authoring note in repo-conventions.md
@seebi

seebi commented Sep 15, 2026

Copy link
Copy Markdown
Member

open issues

  • footer: "A Build" -> "Part A: Build"
  • footnote references at the end of the document
  • regenerate statistic based on ignored content
  • material grid box height can maybe be aligned
  • build operators: chapter with descriptions and parmaters (but no examples) instead of overview tables
  • PDF/X-4, CMYK
  • image scaling (maybe directly in the sources) - calculate with DPI?

- left-hand footers name the part as "Part A: Build"
- web addresses become per-part endnote lists instead of footnotes
- the author order counts only commits to printed pages and images
- facing cards in a two-column grid share one height
- print the operator reference as compact entries from
  data/plugins.json: fields, description without examples, one
  parameter table and related operators, replacing the overview tables

The print edition grows from 666 to 870 pages; the screen PDF is
unchanged.
Ghostscript 10.08 crashes on the colour emoji Typst writes as a Type 3
font, and silently drops transparent SVG content when it converts to
PDF/X-4, so the print edition renders both to images first.

- dec-tool pdf-normalize writes the book block as PDF/X-4 in CMYK, or a
  greyscale preview with --gray; the ISO Coated v2 profile is fetched
  into dist/icc/ rather than vendored, as its licence forbids that
- dec-tool pdf-preflight checks page size and count, fonts, image
  resolution, transparency, annotations, page numbers and grey areas;
  the build runs it before and after the conversion
- dec-tool image-widths writes a screenshot's missing width into the
  Markdown, from its pixels and capture density: 24 widths in 14 pages
- the print build lists images below 150 ppi in low-resolution.tsv,
  minus the ones accepted in print.yml
- a short lead line under a heading stays with it, so no heading ends a
  page alone
The widths came from the density a screenshot was captured at, which left
42 of them printing between 85 and 149 ppi: an image fills the text column
unless a width says otherwise, however few pixels it carries.

- narrow every raster image below 150 ppi: width = pixels / (column in inches * 150), floored to a whole percent, one step further down where the rounding of the density falls short
- read an existing width whether it is quoted or not
- write 47 widths into 21 pages; nothing prints below 150 ppi any more, and the book loses eight pages
- report the width a page declares next to the density in low-resolution.tsv, so an entry says whether the image is stretched or simply too small
- close P15: no screenshot had to be replaced
@rpietzsch

Copy link
Copy Markdown
Contributor Author

all points are addressed. I am checking if any housekeeping is pending and change to ready for review afterwards.

rpietzsch and others added 8 commits September 16, 2026 14:29
The text column and the two densities were defined again in each tool, so the report's threshold and the width tool's target could drift apart, and two functions named printed_density measured different things.

- move the 16 cm column and the 300/150 ppi densities to tools/print_geometry.py, and import them in the builder, the width tool and the preflight
- rename build_pdf.printed_density to typst_density: it mirrors Typst's sizing, while the width tool measures a share of the column
- take FOGRA39 from pdf_normalize instead of repeating it in the preflight
- run image-widths in `task check` as check:images
- correct the status lines, spec §8 and the print.yml comment, which still said a coarse image is replaced rather than narrowed
The first print edition serves users, so what an administrator or a developer needs stays in the online edition.

- omit part E (deploy-and-configure/, 34 pages) and part G (develop/, 89 pages) in print.yml; develop/cmem-client-api/ goes with it, since section keys do not nest
- the book drops from 864 to 668 pages, and the parts re-letter from E Automate onwards
- a reference into an omitted part becomes a web address in its part's endnote list, so none is lost
- record the scope in spec §10 and backlog P26
The user guide does not carry a release history; the online edition has it. The two-column list configuration stays as a comment, so switching back is possible.
CI installs from the lock file and had no pypdf, so every test module
failed to import: tools/__init__.py loads build_pdf, which loads
pdf_normalize. Locally it worked because pypdf sat in the virtualenv from
an install outside poetry.

- declare pypdf for the PDF reader of pdf_normalize and pdf_preflight
- declare click, markdown and requests too: the tools import them
  directly, but they only arrived as dependencies of other packages
The PDF workflow built only the screen edition, on main, and its artifact had to be dug out of the run page.

- build the book block, its PDF/X-4 copy and the greyscale preview too, on main and on the print-on-demand branch, or on demand
- install ghostscript and poppler-utils: the preflight report reads the PDFs with pdffonts, pdfimages and pdftotext
- upload one artifact per edition and list them in the run summary with page count, size and download link
- cache the output intent profile, and pass ghostscript's grey profile as PDF_GRAY_PROFILE, which ensure_gray_profile now honours
- give each ref its own concurrency group, so main and a branch no longer cancel each other
Ubuntu ships Ghostscript 10.02, where pdfwrite declares PDFX a boolean,
so -dPDFX=4 raises /typecheck in --pdfmark-- and PDF/X-4 cannot be
written at all. The workflow installed that one from apt.

- build an alpine image with ghostscript and call it through a shim that
  mounts the workspace and /tmp at the paths the build uses
- take the grey output intent profile out of the same image
- let build-pdf name the binary with --ghostscript or GHOSTSCRIPT: it
  ran the conversions with a hardcoded gs, which the runner no longer has
- refuse a ghostscript older than 10.03 with the reason, and note one
  older than the tested 10.08, instead of failing deep inside pdfmark
The step passed but the summary stayed empty: its rows went through a
shell function whose redirect never reached GITHUB_STEP_SUMMARY, and a
silent non-write survives bash -e.

- build the table in one block and tee it, so the editions and their
  links show up in the step log as well
- print the size of the summary file afterwards
- fall back to a "missing" row instead of a blank one
- pick the screen edition by excluding -print: the glob for it also
  matched the greyscale copy, whose name ends in a digit too
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants