--- name: any2pdf description: 'Convert Markdown to publication-quality PDF with reportlab — CJK/Latin mixed text, themes, cover pages, watermarks, callouts, formulas, and interactive theme selection' metadata: author: lovstudio curator: cosmicstack-labs version: 1.1.0 category: pdf-generation tags: [markdown, pdf, cjk, reportlab, typesetting, themes, any2pdf] source: https://github.com/lovstudio/any2pdf license: MIT --- # any2pdf — Markdown to Professionally Typeset PDF > **Credits.** This skill is adapted from [`lovstudio/any2pdf`](https://github.com/lovstudio/any2pdf) by **lovstudio**, distributed under the MIT license. The original repository ships the Python implementation (`md2pdf.py`), preview gallery, and reference theme JSONs. This document is the Mercury-compatible `SKILL.md` adaptation; the runtime code and design credit belong entirely to the original author. > > - Upstream: https://github.com/lovstudio/any2pdf > - License: MIT (see upstream `LICENSE`) > - Author: lovstudio > - Curator (this entry): cosmicstack-labs This skill converts any Markdown file into a publication-quality PDF using Python's `reportlab`. It was developed through extensive iteration on real Chinese technical reports and solves several hard problems that naive MD→PDF converters get wrong — CJK/Latin mixed text wrapping, canvas CJK rendering on cover/headers/footers, code-block whitespace preservation, and mixed-font fallbacks across macOS, Linux, and Windows. --- ## When to Use Trigger this skill whenever the user: - Wants to convert `.md` → `.pdf` - Has a markdown report or document and wants professional typesetting - Mentions **"markdown to PDF"**, **"md2pdf"**, **"any2pdf"**, **"md转pdf"**, **"报告生成"**, or asks for a **"typeset"** or **"professionally formatted"** PDF from markdown source - Has a document with CJK characters mixed with Latin text - Has fenced code blocks, markdown tables, or nested lists that need to survive conversion - Needs local/remote images, Obsidian callouts, emoji, or LaTeX-style math formulas - Wants a cover page, table of contents, watermark, or back cover in their PDF --- ## Where the Runtime Lives After installing this skill via the Mercury CLI: ```bash mercury skills install pdf-generation/any2pdf ``` the SKILL.md lands at `~/.mercury/skills/pdf-generation/any2pdf/SKILL.md`. The Python implementation (`md2pdf.py`), preview images, and theme files are **not** vendored into Mercury Skills — they live in the upstream repository. To use the skill end-to-end, also clone or install the upstream runtime: ```bash # Option A: clone alongside (recommended for development) git clone https://github.com/lovstudio/any2pdf.git # The script is at: any2pdf/lovstudio-any2pdf/scripts/md2pdf.py # Option B: install via the upstream's own installer npx skills add lovstudio/any2pdf -g -y ``` When the AI agent runs the conversion, treat the script path as wherever the user cloned it. If unsure, ask: > "Where is the `md2pdf.py` script on your system? (e.g. `./any2pdf/lovstudio-any2pdf/scripts/md2pdf.py`)" --- ## Quick Start ```bash python /md2pdf.py \ --input report.md \ --output report.pdf \ --title "My Report" \ --author "Author Name" \ --theme warm-academic ``` All parameters except `--input` are optional — sensible defaults are applied. --- ## Pre-Conversion Options (MANDATORY) **IMPORTANT for AI agents:** You MUST present these options to the user **before** running the conversion. Use whatever interactive question primitive your runtime provides (`AskUserQuestion`, a structured prompt, an MCP tool, etc.). Present **all** options in a **single** question so the user answers once. The tone should be a friendly design assistant, not a config form. ### Suggested Prompt Template (English) ``` Starting PDF conversion — quick choices first ━━━ Design Style ━━━ a) Warm Academic — terracotta tones, refined and elegant; humanities / social science b) Classic Thesis — brown tones, LaTeX classicthesis inspired; academic papers c) Tufte — minimal whitespace, deep red accents; data narratives, technical writing d) IEEE Journal — navy blue, journal-formal; conferences and journals e) Elegant Book — coffee tones, book-like; long-form monographs / technical books f) Chinese Red — vermilion on warm paper; Chinese formal reports / whitepapers g) Ink Wash — pure grayscale, restrained and elegant; literary / design content h) GitHub — blue-and-white minimal; developer-familiar i) Nord Frost — Nordic blue-gray; clean modern j) Ocean Breeze — teal-green; fresh and natural ━━━ Frontispiece (full-page image after cover) ━━━ 1) Skip 2) I'll provide a local image path 3) AI generates one based on document content ━━━ Watermark ━━━ 1) None 2) Custom text (e.g. "DRAFT", "Internal Use Only") ━━━ Back Cover Material (business card / QR code / brand) ━━━ 1) Skip 2) I'll provide an image 3) Plain text only Example reply: "a, frontispiece skip, watermark: For Reference Only, back cover: /path/qr.png" Plain English is fine — no need to memorize the letters. ``` ### Mapping User Choices to CLI Args | Choice | CLI argument | |---|---| | Design style a–j | `--theme ` | | Frontispiece local | `--frontispiece ` | | Frontispiece AI | Generate image first, then `--frontispiece /tmp/frontispiece.png` | | Watermark text | `--watermark "TEXT"` | | Back cover image | `--banner ` | | Back cover text | `--disclaimer "..."` and/or `--copyright "..."` | ### Theme Name Mapping | Choice | `--theme` value | Inspiration | |---|---|---| | a) Warm Academic | `warm-academic` | Lovstudio design system | | b) Classic Thesis | `classic-thesis` | LaTeX classicthesis | | c) Tufte | `tufte` | Edward Tufte's books | | d) IEEE Journal | `ieee-journal` | IEEE journal format | | e) Elegant Book | `elegant-book` | LaTeX ElegantBook | | f) Chinese Red | `chinese-red` | Chinese formal documents | | g) Ink Wash | `ink-wash` | 水墨画 / ink wash painting | | h) GitHub | `github-light` | GitHub Markdown style | | i) Nord Frost | `nord-frost` | Nord color scheme | | j) Ocean Breeze | `ocean-breeze` | — | ### Handling AI-Generated Frontispiece If the user chose **AI generation**: read the document title and the first few paragraphs, use an image-generation tool to create a themed illustration matching the chosen design style, show the result for approval, then pass via `--frontispiece /path/to/image.png`. --- ## Architecture ``` Markdown → Preprocess (split merged headings) → Parse (code-fence aware) → Story (reportlab flowables) → PDF build ``` **Key components:** 1. **Font system** — Palatino (Latin body), Songti SC (CJK body), Menlo (code) on macOS; auto-fallback on Linux / Windows. 2. **CJK wrapper** — `_font_wrap()` wraps CJK character runs in `` tags for automatic font switching. 3. **Mixed text renderer** — `_draw_mixed()` handles CJK/Latin mixed text on canvas (cover, headers, footers). 4. **Code block handler** — `esc_code()` preserves indentation and line breaks in reportlab Paragraphs. 5. **Smart table widths** — proportional column widths based on content length, with 18 mm minimum. 6. **Bookmark system** — `ChapterMark` flowable creates PDF sidebar bookmarks and named anchors. 7. **Heading preprocessor** — `_preprocess_md()` splits merged headings like `# Part## Chapter` into separate lines. 8. **Image handler** — local, relative, `file://`, and remote markdown images are scaled into the body frame with fallback text on errors. 9. **Callout renderer** — Obsidian-style `> [!NOTE]` blocks render as themed boxed callouts. 10. **Formula renderer** — display formulas use optional `matplotlib` mathtext images, with styled text fallback. 11. **Emoji fallback** — emojis render as cached Twemoji PNGs when available, or with a local emoji font fallback. --- ## Hard-Won Lessons These are real bugs that came out of shipping real reports — preserve the fixes if you're customising the script. ### CJK Characters Rendering as □ `reportlab`'s `Paragraph` only uses the font set in `ParagraphStyle`. If `fontName="Mono"` but the text contains Chinese, characters render as □. **Fix:** always apply `_font_wrap()` to all text that might contain CJK, including code blocks. ### Code Blocks Losing Line Breaks `reportlab` treats `\n` as whitespace. **Fix:** `esc_code()` converts `\n` → `
` and all spaces → ` `, preserving indentation and mid-line alignment before `_font_wrap()`. ### CJK/Latin Word Wrapping Default reportlab breaks lines only at spaces, causing ugly splits like `Claude\nCode`. **Fix:** set `wordWrap='CJK'` on body / bullet styles to allow breaks at CJK character boundaries. ### Canvas Text with CJK (Cover / Footer) `drawString()` / `drawCentredString()` with a Latin font can't render 年/月/日 etc. **Fix:** use `_draw_mixed()` for **all** user-content canvas text (dates, stats, disclaimers). --- ## Configuration Reference Most options can also be set in the markdown file's YAML frontmatter. Explicit CLI arguments take precedence over frontmatter values. | CLI Argument | Frontmatter Key | Default | Description | |---|---|---|---| | `--input` | — | (required) | Path to markdown file | | `--output` | — | `output.pdf` | Output PDF path | | `--title` | `title` | From first H1 | Document title for cover page | | `--subtitle` | `subtitle` | `""` | Subtitle text | | `--author` | `author` | `""` | Author name | | `--date` | `date` | Today | Date string | | `--version` | `version` | `""` | Version string for cover | | `--watermark` | `watermark` | `""` | Watermark text (empty = none) | | `--theme` | `theme` | `warm-academic` | Color theme name | | `--theme-file` | — | `""` | Custom theme JSON file path | | `--cover` | `cover` | `true` | Generate cover page | | `--toc` | `toc` | `true` | Generate table of contents | | `--page-size` | `page-size` | `A4` | Page size (`A4` or `Letter`) | | `--frontispiece` | `frontispiece` | `""` | Full-page image after cover | | `--banner` | `banner` | `""` | Back cover banner image | | `--header-title` | `header-title` | `""` | Report title in page header | | `--footer-left` | `footer-left` | author | Brand / author in footer | | `--stats-line` | `stats-line` | `""` | Stats on cover | | `--stats-line2` | `stats-line2` | `""` | Second stats line | | `--edition-line` | `edition-line` | `""` | Edition line at cover bottom | | `--disclaimer` | `disclaimer` | `""` | Back cover disclaimer | | `--copyright` | `copyright` | `""` | Back cover copyright | | `--code-max-lines` | `code-max-lines` | `30` | Max lines per code block | --- ## Themes Built-in: `warm-academic`, `classic-thesis`, `tufte`, `ieee-journal`, `elegant-book`, `chinese-red`, `ink-wash`, `github-light`, `nord-frost`, `ocean-breeze`. Each theme defines: page background, ink color, accent color, faded text color, border color, code background, and watermark tint. Preview images for every theme live in the upstream repository under [`previews/`](https://github.com/lovstudio/any2pdf/tree/main/previews). --- ## Dependencies ```bash pip install reportlab # Optional — render display formulas as images instead of styled text: pip install matplotlib ``` ### Fonts - **macOS**: Palatino, Songti SC (宋体), Menlo — pre-installed. - **Windows**: Times New Roman, SimSun / 微软雅黑, Consolas — pre-installed. - **Linux (Ubuntu/Debian recommended setup)**: ```bash sudo apt install fonts-dejavu-core fonts-liberation fonts-freefont-ttf \ fonts-noto fonts-noto-cjk fonts-noto-color-emoji ``` Fonts are auto-discovered from system paths. Missing fonts produce a helpful error with the exact install command for your OS. --- ## What You Get - **Cover page** — title, subtitle, author, version, stats lines - **Clickable table of contents** — with PDF bookmark sidebar - **Frontispiece** — full-page image after cover (local or AI-generated) - **Running headers** — stable report / document title without page-lagged chapter labels - **Running footers** — author / brand, page number, date - **Watermark** — faint diagonal text on every content page - **Back cover** — banner image or text branding (QR codes, business cards) - **10 design themes** — from warm academic to ink wash minimalist - **Markdown images** — local and remote `![alt](src)` with graceful fallback text - **Obsidian callouts** — `> [!NOTE]`, warnings, tips, quotes, and related callout types - **LaTeX-style formulas** — inline `$...$` and display `$$...$$` / `\[...\]`; install `matplotlib` for rendered math images - **Emoji fallback** — Twemoji image rendering when online, with local emoji-font fallback when available - **YAML frontmatter** — set title, theme, watermark, cover, TOC, and other options inside the Markdown file --- ## License & Attribution This skill entry is a derivative work licensed under MIT, matching the upstream project. - **Original work**: [lovstudio/any2pdf](https://github.com/lovstudio/any2pdf) — Copyright (c) lovstudio - **License**: MIT - **This SKILL.md adaptation**: curated for Mercury Skills by cosmicstack-labs; no code is redistributed, only documentation and workflow guidance. If you ship this skill in a product, retain the upstream attribution and the MIT license notice from the original repository.