Communitygithub.com

swiss-pdf

Convert md / docx / txt / html into a Swiss International Typographic Style A4 PDF with a single click.

What is swiss-pdf?

This skill converts Markdown, DOCX, TXT, and HTML files into elegantly typeset A4 PDFs following Swiss International Typographic principles. It produces clean, white-background documents with black text, ultra-thin divider lines, large chapter numbers, monospaced numerals, a single accent color (red #E5484D), automatic cover pages, table of contents, and page headers/footers. The skill supports six styled blocks (note, quote, inverse, warn, stat, check, kv) for structured content. It works via drag-and-drop on Windows (bat file) or command-line interface, using Pandoc for document conversion and Playwright/Chromium for PDF rendering. All typographic assets (fonts, styles) are self-contained, ensuring consistent output across machines without requiring manual font installation. It handles YAML front-matter for metadata, automatic chapter numbering, and smart table formatting. The skill is ideal for developers, technical writers, and professionals who need polished, corporate-ready PDFs from raw text files.

Works with✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/echoBai83/swiss-pdf/tree/HEAD

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

What does swiss-pdf do?

Convert md / docx / txt / html into an A4 PDF in Swiss International Typographic Style with one click. White background, black text, ultra-thin dividing lines, large chapter numbers, monospaced numerals, single accent color red #E5484D.

Design tokens are drawn from a Swiss International Style hardcover book (large chapter numbers, ultra-thin separators, single accent color), adapted into a single-flow auto-pagination system, so documents of any length can be processed directly.

How to Use

Method 1: Drag and Drop (Recommended)

Drag a file onto swiss-pdf.bat → an identically named .pdf is generated in the same directory, along with a xxx.pdf-src.html (reviewable HTML source).

Method 2: Command Line

python make.py <input_file>                    # Generate PDF
python make.py <input_file> --html             # Generate HTML only (saved as xxx.html, open in browser to print manually)
python make.py <input_file> -o <output.pdf>
python make.py a.md b.docx c.txt           # Batch processing
python make.py <input_file> --no-cover --no-toc
python make.py <input_file> --title "Title" --kick "Kicker" --date 2026.11.09

Supported Input Formats

FormatConversion MethodNotes
.mdpandocSupports YAML front-matter, GFM tables, lists, code blocks, images
.docxpandocHeading styles → section titles; tables → styled tables; images auto-extracted
.txtBuilt-in parserBlank lines separate paragraphs; short lines with numbering (一、/ 1. / Chapter X) → section headers
.htmlBuilt-in parserExtracts <body>, removes script/style; <title> used as document title

Cover Page: YAML Front-Matter

Write this at the very top of your markdown file (for other formats, use --title etc.):

---
title: Urban Cycling Safety White Paper
subtitle: Observations and Recommendations Based on a Three-City Sample Survey
kick: Traffic Governance Research
en: Urban Cycling Safety White Paper
slug: urban-cycling-safety
nature: Public Version
project: Urban Slow Traffic System Research Plan
party_a: Client Name
party_b: Contractor Name
stage: Research → Writing → Finalization
source: 01_Observation Records 02_Interview Notes 03_Plan
date: 2026.11.10
version: v2.1
badges: Public; !Critical; Data Supported
---
  • badges separated by ;, prefix ! for black-background white text.
  • If date is empty, today's date is used automatically.
  • It works without front-matter; the cover page will show only the title + date + version placeholder.

Body: Six Styled Blocks

In markdown, wrap them with ::: (each line inside = one item):

::: note
Supplementary notes, caliber reminders. Gray background.
:::

::: quote
Key conclusion citations, 3pt left vertical line.
:::

::: inverse
Black background, white text block – use for the most important statement.
:::

::: warn
Red box warning – the only place where color is allowed.
:::

::: stat
4,280 | Total observation samples || 24 | Observation points || 3 | Cities covered
:::

::: check
Morning rush hour intersections (must be in place by 7:30 AM)
Nighttime illuminated sections (need supplementary lighting)
:::

::: kv
Pages | Plan A 20 pages / Plan C 32 pages
Recommendation | Plan C, but need additional city data
:::

stat uses number | description per cell, || separates cells. kv uses key | value.

Automatic Behaviors

BehaviorRule
Chapter numbering# H1 → large number 00/01/02…; if the title already contains "01 " or "一、" numbering, that is retained
Table of ContentsGenerated only when there are ≥ 5 sections (# + ##) combined; otherwise the page is omitted; section titles have anchors for clickable navigation in the PDF
TablesColumns ≥ 6 automatically reduce font size (table.wide); pure number cells are right-aligned with monospaced font
Headers and FootersCover page has no header/footer; body page header shows running title on the left, footer shows "Page i of N" on the right
Printing@page A4, margins 14/15/13mm; titles are not separated from body text; table rows do not break across pages

Additional HTML Source Processing (Verified Extensions)

When the source is a "self-designed webpage", this skill automatically normalizes the following to avoid orphaned numbering or duplicate titles after reflow:

Source StructureProcessing
<div class="sec-head"><span class="sec-num">3</span><h2>Title</h2></div>Convert to template .sec section header (number from span, title from h2)
<h1>03 Title</h1> / <h1>Title</h1>Same as above; if no number, auto-increment based on occurrence order
Standalone paragraph with pure number <p>0</p>Discard (common when section numbers are used as decorative numerals)
<header class="masthead"> / <nav>Discard (cover page is separate; duplicate kicker/big title/table of contents would pollute the body)
<section> divisionRetained as a section container; titles still need sec-head or h1 to enter the TOC
<div id="xxx" class="chart"> + echartsScreenshot using headless browser and embed as PNG; wait for networkidle + 2.5s before screenshot

Prerequisite for chart screenshots: Need network access if echarts uses CDN; for offline environments, inline echarts.min.js in the source.

Fonts and Punctuation

Fonts: Built-in Source Han Sans, Inlined via @font-face

The fonts/ directory contains two files shipped with the repo; make.py inlines their base64 into <style> when generating HTML, registering them as the font family Swiss Sans SC:

FileWeight
SourceHanSansCN-Regular.subset.ttf400
SourceHanSansCN-Bold.subset.ttf700

The subset covers about 7100 characters (ASCII + CJK punctuation + all 6763 GB2312 Chinese characters); see fonts/charset.txt. Benefit: Consistent output on any machine without requiring users to install fonts.

Users can replace or download fonts themselves – three approaches (see fonts/README.md for details):

  1. Replace files (recommended): Rename your TTF files to the two filenames above and overwrite. Subset them first, otherwise the HTML will be bloated with the full 10 MB:
    pyftsubset YourFont-Regular.ttf --text-file=fonts/charset.txt --output-file=fonts/SourceHanSansCN-Regular.subset.ttf
    
  2. Delete fonts/: make.py will skip @font-face if it can't find the files, falling back to the CSS system font stack (Source Han Sans SC/CN → Noto Sans SC → Noto Sans CJK SC → Source Han Sans CN → PingFang SC → Segoe UI Symbol).
  3. Only modify CSS: Edit the first font name in --sans at the top of templates/swiss.css.

⚠️ Must use .ttf (TrueType outlines), not .otf (CFF). Chromium PDF printing downgrades CFF Web fonts to Type3 glyph instructions (each glyph stored as a drawing path), bloating file size several times. Same 4-page document: OTF → 328 KB / 205 Type3 glyphs; TTF → 79 KB / 1 Type3 glyph.

Microsoft YaHei lacks glyphs for · (U+00B7), → ↓ ≤ ≥ etc. under Chromium, which renders as boxes, so it has been removed from the primary font stack.

Quotes: National Standard Curly Quotes Enforced Throughout

convert.normalize_quotes() handles this uniformly in the final step, covering titles, cover page, headers, TOC, tables, and styled blocks:

InputOutput
「」“”
『』‘’
Straight quotes in code " 'Markdown: pandoc +smart auto-converts to curly quotes; code blocks <code> / <pre> remain unchanged

The implementation only replaces 4 codepoints (U+300C/D/E/F → U+201C/D/8/9), without touching HTML tag attributes or CSS/JS.

Verified Real Run Records

InputSizePagesTime
md29 KB15~6s
html105 KB + 2 echarts charts21~15s
docxheading styles + table2~6s
txt (samples/)1.2 KB2~5s
md long document regression71 KB21~24s

Environment Dependencies

DependencyCheck CommandBehavior When Missing
pandoc (≥2.19)Run pandoc -vmd/docx conversion will fail
playwright + chromiumOr system Chrome / EdgePDF rendering will fail
python-docx (optional)Only used for auxiliary parsingDoes not affect main flow

Specify browser: set SWISS_PDF_CHROME=<path to chrome.exe>

File Structure

swiss-pdf/
├── SKILL.md              This file
├── swiss-pdf.bat         Drag-and-drop entry point
├── make.py               Main flow: convert → assemble → render
├── convert.py            Format → HTML fragment + block-level syntax
├── templates/
│   └── swiss.css         All styles (design tokens + print rules)
└── samples/              Sample files in three formats for comparison

Limitations (What It Cannot Do)

  • Manual pagination like a hardcover book (carefully placed content per page, cross-page spread design) – the template cannot achieve this; hand-crafted HTML is still needed. This skill is positioned as "quick draft for routine documents."
  • Illustration layout depends on the original document's image dimensions; no cropping or bleed handling.
  • Printed page numbers in the table of contents are not implemented (page numbers cannot be retrieved for elements within the page); section titles are clickable, and the footer shows "Page i / Page N".
  • Short table blocks may be pushed entirely to the next page – to avoid key-value tables being broken, .kv blocks are set to be non-breakable across pages.
  • Complex Word styles (text boxes, WordArt, multi-level numbered lists) will be degraded when converted by pandoc.

Related Skills