SuperAPI AI Directory

solo-pdf-print

Office & productivity

View repository

Updated 10-15 21:46

About this skill

HTML → print PDF

The tool — WeasyPrint. It is one of three candidates that can do what printing is actually for: headers and footers with page numbers, predictable breaks, and control via @page.

0. First, what costs an hour

On macOS WeasyPrint crashes on import, although it is installed:

OSError: cannot load library 'libgobject-2.0-0'

The libraries are installed in homebrew but not in the loader path. Cured with one variable:

export DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib
uv run --with weasyprint python build.py

⚠️ The variable is needed both in the script and in CI and in the hook — the hook's environment differs from the shell environment. If there is nowhere to set it, at the beginning of the script:

import os; os.environ.setdefault("DYLD_FALLBACK_LIBRARY_PATH", "/opt/homebrew/lib")

1. What NOT to print with

Tool What's wrong
headless Chrome --print-to-pdf It prints, but does not exit: the process hangs until timeout. Verified 2026-09-13 — the PDF was written, but subprocess crashed due to TimeoutExpired after 180 s. And it cannot @page margin-boxes, that is, there will be no page numbers at all
wkhtmltopdf Qt WebKit engine: neither flexbox, nor columns, nor modern break-*
pandoc → LaTeX Good for a book, overkill for a one-pager; Cyrillic requires font configuration

Chrome remains appropriate in exactly one case: when the page is drawn by JavaScript. WeasyPrint does not execute scripts — the data must be provided already in HTML.

2. A skeleton that works

# /// script
# dependencies = ["weasyprint"]
# ///
from weasyprint import HTML
HTML(filename="out.html").write_pdf("out.pdf")
@page {
  size: A4 portrait;               /* landscape — для лент и таблиц */
  margin: 15mm 14mm 16mm 14mm;
  @bottom-right { content: counter(page) " / " counter(pages);
                  font: 8pt Georgia, serif; color: #888; }
}
@page :first { @bottom-right { content: ""; } }   /* на титуле номер не нужен */

section { break-before: page; }     /* не page-break-before: он устарел */
.card    { break-inside: avoid; }   /* карточку не рвать между страницами */
h3       { break-after: avoid; }    /* заголовок не бросать внизу листа */

Units — mm and pt, not px: on paper a pixel means nothing. Set the card width in mm so that after cutting they are a predictable size.

3. Cyrillic and Bashkir letters

System Georgia / Times New Roman cover Cyrillic completely, including ә ҙ ҡ ң ө ҫ ү һ. Web fonts are not needed for printing and only slow down the build. If a font is nevertheless included — check exactly these letters: substitution of a glyph from another font is visible as a jump in style in the middle of a word.

4. Check with your eyes, not pdftotext

pdftotext pulls text in flow order and will show neither overlaps, nor clipped blocks, nor empty pages. You need to look at the picture:

pdftoppm -f 1 -l 2 -r 76 -png out.pdf /tmp/page   # первые две страницы в PNG

and open the PNG. What is caught only this way:

  • text that has run outside the frame of a fixed-height card;
  • half a page of emptiness because of break-before on an empty section;
  • duplication: a brief summary and the full text, where the summary is a quote from it;
  • emoji, which on screen are markers, but on paper are motley pictures. In print they are usually cut out; structure is carried by frames and captions.

5. Markdown → PDF

A ready example with Russian typography — 6-crm/bin/md_to_pdf.py: markdown + weasyprint, tables, @bottom-right, a hard break between files.

DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib \
  uv run python bin/md_to_pdf.py --out OUT.pdf FILE1.md FILE2.md

6. When printing for people, not for an archive

  • Font size — from 10pt. 8pt is readable on screen and not readable on paper.
  • One thought — one block. A callout with a question is more noticeable than a paragraph with a question.
  • Page number is mandatory, if there are more than three sheets: otherwise a scattered stack cannot be reassembled.
  • Cards for cutting — frame .8pt solid, margins inside at least 4pt, and a 2-3 mm allowance for cutter inaccuracy.
  • Printing can be black-and-white: hierarchy should hold up on boldness and frames, color — only a hint.

Connections

Text style — /solo:humanize. A ready generator for a multi-page document with a tree and cards — ~/personal/family/bin/shezhere-print.py.