Convert Markdown to publication-quality PDF with reportlab — CJK/Latin mixed text, themes, cover pages, watermarks, callouts, formulas, and interactive theme selection
npx skills add https://github.com/cosmicstack-labs/mercury-agent-skills --skill any2pdf
> Credits. This skill is adapted from 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.
Trigger this skill whenever the user:
.md → .pdfAfter installing this skill via the Mercury CLI:
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:
# 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)"
python <path-to>/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.
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.
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.
| Choice | CLI argument |
|---|---|
| Design style a–j | --theme <value-from-table-below> |
| Frontispiece local | --frontispiece <path> |
| Frontispiece AI | Generate image first, then --frontispiece /tmp/frontispiece.png |
| Watermark text | --watermark "TEXT" |
| Back cover image | --banner <path> |
| Back cover text | --disclaimer "..." and/or --copyright "..." |
| 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 | — |
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.
Markdown
→ Preprocess (split merged headings)
→ Parse (code-fence aware)
→ Story (reportlab flowables)
→ PDF build
Key components:
_font_wrap() wraps CJK character runs in <font> tags for automatic font switching._draw_mixed() handles CJK/Latin mixed text on canvas (cover, headers, footers).esc_code() preserves indentation and line breaks in reportlab Paragraphs.ChapterMark flowable creates PDF sidebar bookmarks and named anchors._preprocess_md() splits merged headings like # Part## Chapter into separate lines.file://, and remote markdown images are scaled into the body frame with fallback text on errors.> [!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.
These are real bugs that came out of shipping real reports — preserve the fixes if you're customising the script.
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.
reportlab treats \n as whitespace. Fix: esc_code() converts \n → <br/> and all spaces → , preserving indentation and mid-line alignment before _font_wrap().
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.
drawString() / drawCentredString() with a Latin font can't render 年/月/日 etc. Fix: use _draw_mixed() for all user-content canvas text (dates, stats, disclaimers).
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 |
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/.
pip install reportlab
# Optional — render display formulas as images instead of styled text:
pip install matplotlib
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.
!alt with graceful fallback text> [!NOTE], warnings, tips, quotes, and related callout types$...$ and display $$...$$ / \[...\]; install matplotlib for rendered math imagesThis skill entry is a derivative work licensed under MIT, matching the upstream project.
If you ship this skill in a product, retain the upstream attribution and the MIT license notice from the original repository.
Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. When Claude needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks
Comprehensive PDF manipulation toolkit for extracting text and tables, creating new PDFs, merging/splitting documents, and handling forms. When Claude needs to fill in a PDF form or programmatically process, generate, or analyze PDF documents at scale.
Presentation creation, editing, and analysis. When Claude needs to work with presentations (.pptx files) for: (1) Creating new presentations, (2) Modifying or editing content, (3) Working with layouts, (4) Adding comments or speaker notes, or any other presentation tasks
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.
Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill.
Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx files). Triggers include: any mention of 'Word doc', 'word document', '.docx', or requests to produce professional documents with formatting like tables of contents, headings, page numbers, or letterheads. Also use when extracting or reorganizing content from .docx files, inserting or replacing images in documents, performing find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a 'report', 'memo', 'letter', 'template', or similar deliverable as a Word or .docx file, use this skill. Do NOT use for PDFs, spreadsheets, Google Docs, or general coding tasks unrelated to document generation.
Use this skill any time a .pptx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx file (even if the extracted content will be used elsewhere, like in an email or summary); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates, layouts, speaker notes, or comments. Trigger whenever the user mentions \"deck,\" \"slides,\" \"presentation,\" or references a .pptx filename, regardless of what they plan to do with the content afterward. If a .pptx file needs to be opened, created, or touched, use this skill.
Create and edit Obsidian Flavored Markdown with wikilinks, embeds, callouts, properties, and other Obsidian-specific syntax. Use when working with .md files in Obsidian, or when the user mentions wikilinks, callouts, frontmatter, tags, embeds, or Obsidian notes.
Take cosmicstack-labs/any2pdf from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference pip, npx.
Without those the skill loads but fails at the first command.