PDF - Document Production Workbench
Quick Setup
bash "$PDF_SKILL_DIR/scripts/setup.sh" # Interactive environment check + install
python3 "$PDF_SKILL_DIR/scripts/pdf.py" env.check # Detailed dependency status (JSON: add -j)
python3 "$PDF_SKILL_DIR/scripts/pdf.py" env.fix # Auto-install missing Python packages
Triage
Determine task weight to control how much context to load:
| Weight |
Triggers |
What to Load |
| Light |
Format conversion, form fill, text extract, merge/split, simple certificate |
SKILL.md + briefs/process.md only |
| Standard |
Multi-page report, poster, academic paper, resume, reformat - any document with design decisions |
SKILL.md + matched brief + typesetting assets on demand |
Light tasks skip typesetting files entirely. Standard tasks load them on demand per the brief's instructions.
⚠️ Pre-Routing Checks (run BEFORE matching brief)
- Emoji Check - Scan user content for intentional emoji (decorative 📊🎯🔥, not OS-level emoji input). If found → force Creative brief regardless of document type. ReportLab renders emoji as □ squares; LaTeX drops them entirely.
- CJK Check - Chinese/Japanese/Korean content needs font coverage. Report brief must use
UniSong/UniHei registered fonts; Creative brief must load Google Fonts Noto Sans SC with font-display: swap; Academic brief must use \usepackage{ctex}.
- Size Check - Non-standard page sizes (not A4/Letter/A3) → prefer Creative brief (Playwright handles any dimension). ReportLab can do custom sizes but pagination is manual.
- Character Safety Check - Before writing any content string, scan for Japanese kana (の、が、は etc.), unusual Unicode symbols, or non-CJK characters that may corrupt during encoding transit ( Especially when code is written via heredoc/base64/LLM output). Replace with plain Chinese equivalents:
の→之/的/缔, 々→omit or write full character. If content must preserve Japanese, use only standard CJK Unified Ideographs (U+4E00-U+9FFF) and common kana; avoid rare/private-use codepoints.
Briefing
Match the user's intent to a production brief. Each brief contains the full workflow, tech stack specifics, and references to shared typesetting assets.
User Request
│
├─ Work with existing PDF? ─────────────┬─ Extract/merge/split/fill/convert → briefs/process.md
│ ├─ Reformat/redesign → briefs/process.md (extract) → delegate to report or creative brief
│ └─ User provides a PDF template/reference to match style
│ → briefs/process.md "Template-Guided Reformat" → delegate to matched brief
│
├─ Report / proposal / white paper / contract / analysis?
│ └─ ────────────────────────────────── → briefs/report.md (ReportLab)
│
├─ Poster / invitation / infographic / dashboard / creative layout?
│ └─ ────────────────────────────────── → briefs/creative.md (Playwright)
│
├─ Academic paper / thesis / math / IEEE / ACM / LaTeX?
│ └─ ────────────────────────────────── → briefs/academic.md (Tectonic)
│
├─ Math-heavy doc / TikZ diagram / algorithm pseudocode / Beamer slides?
│ └─ ────────────────────────────────── → briefs/academic.md (Tectonic, Scenarios A-D)
│
├─ Document needs complex embedded diagrams (flowcharts, architecture, neural nets)?
│ └─ Route by target brief:
│ ├─ Report → Playwright+CSS → PNG → ReportLab Image() flowable
│ ├─ Creative → directly in HTML (CSS flexbox/grid + connectors)
│ └─ Academic → complexity-based:
│ ├─ Simple (≤6 nodes, linear/tree) → TikZ native (vector)
│ └─ Complex (>6 nodes, branches, annotations) → Playwright+CSS → PNG → \includegraphics
│
└─ Resume / CV?
├─ ATS-safe / corporate ─────────── → briefs/report.md (resume sub-section)
├─ Creative / design industry ────── → briefs/creative.md (resume sub-section)
└─ Academic CV / publications ────── → briefs/academic.md (resume sub-section)
Detection Keywords
| Brief |
Keywords |
| Report |
报告, report, 分析, analysis, 白皮书, white paper, 提案, proposal, 合同, contract, 方案, 规划, 发票, invoice, 收据, receipt, 试卷, exam, quiz, test paper, 练习, exercise, worksheet, 考试, 测验 |
| Creative |
海报, poster, 邀请函, invitation, 信息图, infographic, 仪表盘, dashboard, 传单, flyer, 证书, certificate, 菜单, menu, 名片, business card, 奖状, award, 标签, label, 信封, envelope, 贺卡, greeting card |
| Creative (Poster) |
海报, poster, 传单, flyer, 宣传页, 宣传单 → additionally load briefs/poster.md scene layer rules |
| Academic |
论文, paper, 学术, academic, LaTeX, 数学, math, IEEE, ACM, 毕业, thesis, 研究, research, Beamer, slides, 开题报告, 学位, dissertation, proposal |
| Process |
提取, extract, 合并, merge, 拆分, split, 填写, fill, 转换, convert, OCR, 重排, reformat, 重新排版, redesign, 模板, template, 参照, 照着这个做, match this style, 压缩, compress, 水印, watermark, 加密, encrypt, 签名, sign |
Complete Scenario Routing Matrix
Below is an exhaustive map of every known PDF request type to its handling strategy. If a scenario is not listed, route to the closest match or ask the user.
📄 Creation (Generate PDF from scratch)
| Scenario |
Route |
Notes |
| Report / white paper / analysis |
report.md |
ReportLab structured document |
| Report with emoji |
creative.md |
🚨 Emoji rule override |
| Business proposal |
report.md |
Structured + data tables |
| Contract / legal document |
report.md |
Add signature placeholders (dotted line + label) |
| Invoice / receipt |
report.md |
Table-heavy, precision alignment |
| Exam / quiz / test paper / worksheet |
report.md |
Indented options, answer space reservation, structured numbering (see Exam Paper Rules in report.md) |
| Math exam / math worksheet (with formulas/equations) |
academic.md |
LaTeX for proper math typesetting. See §Exam Paper Rules in academic.md |
| Poster / flyer |
creative.md + poster.md |
Visual design + poster density/sizing rules |
| Invitation / greeting card |
creative.md |
Non-standard size, decorative |
| Certificate / award |
creative.md |
Single page, centered layout, decorative border |
| Business card |
creative.md |
Tiny size (90×54mm), Playwright native support |
| Envelope / label |
creative.md |
Non-standard size, simple layout |
| Menu / price list |
creative.md |
Visual layout + may contain emoji |
| Resume (ATS) |
report.md |
Plain text structure |
| Resume (creative) |
creative.md |
Visual design |
| Resume (academic CV) |
academic.md |
Publication list + BibTeX |
| Academic paper |
academic.md |
LaTeX/Tectonic |
| Math-heavy document |
academic.md |
LaTeX typesetting |
| Presentation / PPT-style |
creative.md |
Landscape (1280×720), one topic per page |
| Book / long document |
report.md |
Add TOC + chapter numbering, validate with toc_validate.py |
| CJK vertical text |
creative.md |
HTML writing-mode: vertical-rl + text-orientation: upright + white-space: nowrap + Playwright |
| RTL document (Arabic/Hebrew) |
creative.md |
HTML dir="rtl" + Playwright |
| Batch generation (mail merge) |
report.md |
Python loop + template variable substitution |
| Infographic |
creative.md |
Data visualization + design |
| Calendar / schedule |
creative.md |
Grid layout + custom dimensions |
🔧 Processing (Manipulate existing PDF)
| Scenario |
Route |
Command / Method |
| Merge multiple PDFs |
process.md |
pages.merge a.pdf b.pdf -o out.pdf |
| Split PDF |
process.md |
pages.split input.pdf -o ./output/ |
| Extract text |
process.md |
extract.text input.pdf |
| Extract tables |
process.md |
extract.table input.pdf |
| Extract images |
process.md |
extract.image input.pdf |
| Fill forms |
process.md |
form.fill input.pdf |
| Office → PDF |
process.md |
convert.office input.docx |
| HTML → PDF (documents) |
process.md |
convert.html input.html or node html2pdf-next.js |
| HTML → PDF (posters) |
poster.md |
node html2poster.js poster.html |
| Image → PDF |
process.md |
pikepdf: one image per page, embed as XObject |
| PDF → image |
process-advanced.md |
pypdfium2 render each page to PNG |
| Encrypt / decrypt |
process-advanced.md |
pikepdf encryption |
| Add watermark |
process.md |
pikepdf overlay: create watermark page → merge onto each page |
| Compress PDF |
process.md |
Ghostscript: gs -sDEVICE=pdfwrite -dPDFSETTINGS=/screen |
| OCR scanned PDF |
process-advanced.md |
ocrmypdf or Tesseract |
| Rotate pages |
process.md |
pages.rotate input.pdf 90 -o out.pdf |
| Crop pages |
process.md |
pages.crop input.pdf l,b,r,t -o out.pdf |
| Remove blank pages |
process.md |
pages.clean input.pdf |
| Reformat by template |
process.md → delegate |
Extract content → regenerate via report/creative |
| PDF diff / compare |
process.md |
diff-pdf CLI or Python per-page text comparison |
| Digital signature |
process.md |
pyhanko library (requires extra install) |
| Edit metadata |
process.md |
meta.set input.pdf -o out.pdf -d '{...}' |
Special Routing Rules
🚨 Emoji rule (CRITICAL - check FIRST): Content with intentional emoji (📊🎯🔥💡 etc.) → force briefs/creative.md regardless of document type. ReportLab renders emoji as □ squares; LaTeX silently drops them. This rule overrides all other routing. Even if the user says "report" - if the content has emoji, use Creative pipeline.
Non-standard page size rule: Dimensions other than A4/Letter/A3 → strongly prefer briefs/creative.md. Playwright handles any arbitrary page size natively. ReportLab requires manual pagination math.
Academic auto-detect: Papers, theses, or heavy math → briefs/academic.md even without explicit "LaTeX" mention.
Template-guided rule: When the user uploads a PDF and says "match this template" / "follow this style" / "reformat like this" → briefs/process.md Template-Guided Reformat section. This is a Standard triage (not Light), because it involves design decisions.
Resume routing: Default to Report brief (ATS-safe). Creative industry → Creative brief. Academic CV with publications → Academic brief.
Shared Assets
These are referenced by multiple briefs. Do not load upfront - each brief tells you when and what to load.
| Asset |
Path |
Used By |
Purpose |
| Palette & Typography |
typesetting/palette.md |
Report, Creative |
Color system, font rules, anti-patterns, spacing |
| Cover Layout System V2.1 |
typesetting/cover.md |
Report + Creative + Academic |
7 industrial-grade templates with absolute anchor grid, Z-index layers, typography weight system, mandatory Summary Block, code-level safety (5 checks), base unit U = W*0.05. Unified HTML/Playwright cover system for all routes. |
| Chart Styling & Anti-Stacking |
typesetting/charts.md |
Report, Creative, Academic |
Chart defaults, collision prevention, axis/grid/legend rules |
| Overflow Prevention |
typesetting/overflow.md |
Report, Creative, Academic |
Bounding box system, text/image/table overflow prevention, fallback strategies |
| Fill Engine (Anti-Void) |
typesetting/fill-engine.md |
Report, Creative, Academic |
Anti-Void Engine V2.0: font floor enforcement, fill ratio calculation, paragraph inflation, component elevation, Y-axis golden-ratio anchoring |
| Pagination & Flow Control |
typesetting/pagination.md |
Report, Creative |
Cross-page integrity, orphan/widow control, CJK punctuation rules |
| Typography System |
typesetting/typography.md |
Report, Creative |
Font size scale, line-height, spacing hierarchy |
| Geometric Anchors |
typesetting/geometry.md |
Creative + Report |
Decorative geometric elements, anchor placement rules |
| Cover Backgrounds |
typesetting/cover-backgrounds.md |
Report + Creative + Academic |
Cover background rendering, transparency constraints |
| Visual Framework |
configs/visual_framework.md |
Creative |
Palette mode, color harmony, SVG background params |
| Components Library |
configs/components.md |
Creative |
Non-grid composition components (floating cards, oversized text, etc.) |
| Font Stacks |
configs/fonts.md |
All pipelines |
Font families per pipeline (Google Fonts, ReportLab, LaTeX) |
Content Rules
- Language: Match user's query language. Chinese query → Chinese PDF.
- Page/word count: Respect explicit constraints (±20%). Unspecified → completeness over brevity.
- Outline: User-provided outlines are sacred. No reordering without asking.
- Citations: No fabrication. Chinese → GB/T 7714, English → APA. Search to verify.
- Multi-part requests: Generate ALL parts - never silently drop a component.
HTML Image Source Path Rules
When embedding images in HTML documents (Creative pipeline, Playwright-rendered diagrams, or any HTML→PDF flow):
| Image location |
<img src> value |
Example |
| Local file |
Relative path from the HTML file's directory |
<img src="images/chart.png"> or <img src="./diagram.png"> |
| Remote URL |
Full URL (no change needed) |
<img src="https://example.com/photo.jpg"> |
Iron rules:
- NEVER use absolute paths for local files in HTML
<img>, <source>, CSS url(), or any other asset reference (e.g. /Users/alice/project/img.png). Absolute paths break portability across machines and environments.
- Always use relative paths anchored to the HTML file's own directory. If the image lives in a subdirectory, use
images/foo.png or ./images/foo.png.
- Remote URLs (
http:// / https://) are fine as-is — do not convert them to local paths.
- When generating HTML from a script or blueprint, ensure all referenced assets are either (a) in the same directory as the output HTML, or (b) in a clearly named subdirectory (e.g.
assets/, images/), and referenced with relative paths.
- If a build script needs to resolve paths programmatically, compute relative paths at generation time (e.g.
os.path.relpath(image_path, html_dir)) rather than embedding absolute filesystem paths.
Figure & Diagram Embedding (All Briefs)
Iron Rule: Figures Are Block-Level
Figures, diagrams, and charts MUST be independent block elements occupying full width. Never float/wrap figures alongside body text - this causes the text-diagram overlap badcase.
| Brief |
Correct embedding |
Forbidden |
| Report (ReportLab) |
story.append(Image(...)) as standalone Flowable |
Placing images inside Paragraph text, simulating float |
| Creative (Playwright) |
<figure style="display:block; width:100%; margin:2em auto"> |
float:right, display:flex with text, wrapfigure-style CSS |
| Academic (LaTeX) |
\begin{figure}[t] ... \end{figure} |
Bare \includegraphics in text body (no figure env), bare tikzpicture in multi-column |
Complex Diagram Strategy
When a diagram has >12 nodes, >3 subgroups, or intricate connections, do NOT try to render it as one giant figure. Instead:
- Table for details - structured data (phases, components, specs) goes into a proper table
- Simplified overview diagram - a stripped-down flowchart/Mermaid showing only the top-level flow (≤8 nodes)
- Cross-reference - table caption + diagram caption reference each other
This "table + simple diagram" pattern prevents:
- Diagrams overflowing page boundaries
- Text becoming unreadably small to fit everything
- Layout engines mishandling oversized graphics
Diagram Content Quality Rules (Cross-reference: charts)
The rules above handle how to embed diagrams in PDF. For what the diagram itself looks like (node layout, connector routing, color, readability), follow the charts skill rules:
Before generating ANY flowchart/diagram for PDF embedding, check these:
- Connectors must not pass through nodes - If 3+ layers exist, connect adjacent layers only (top→mid, mid→bottom). Never draw top→bottom lines through middle nodes. Use detour paths if cross-layer links are needed.
- Multiple arrows into one node must not pile up - Distribute entry points evenly along target edge, or use merge-then-enter pattern (sources converge to a vertical merge line, then single arrow to target).
- Low-saturation fills only - Node backgrounds must be pale (
#EFF6FF, #F0FDF4). High-saturation colors (#3B82F6, #10B981) only for borders or small accents. No children's-art color schemes.
- Phase titles vs sub-steps must be visually distinct - Different background color, font size, and font weight. Never same-style boxes for both.
- Font sizes must be readable at final output size - Sizes depend on the embedding context:
| Output context |
Node title min |
Description min |
Label min |
| Standalone PNG (web/presentation, ≥1200px wide) |
14px |
12px |
11px |
| Embedded in A4 PDF (ReportLab/LaTeX, ~450pt content width) |
10pt |
8pt |
7pt |
| Embedded in slide deck (landscape, ~720pt wide) |
12pt |
10pt |
9pt |
Principle: After embedding, the smallest text in the diagram must still be legible when the document is viewed at 100% zoom. If the diagram is scaled down to fit page width, recalculate: effective_size = original_size × (display_width / canvas_width). If effective size drops below the minimum, either increase original font size or reduce diagram complexity.
- Legend/annotations must not overlap content - Separate container, ≥ 40px gap from last node, fully within canvas bounds.
For Playwright-rendered diagrams: Use low-saturation fills (#EFF6FF, #F0FDF4), CSS flexbox/grid for node layout, SVG <line>/<path> for connectors, and verify no overlap at final render size.
For ReportLab-drawn diagrams: Same principles apply - use Drawing() with explicit coordinates, check node bounding boxes for overlap before finalizing.
Diagram Generation Strategy (Per-Brief)
Diagram rendering depends on the target brief - NOT a one-size-fits-all TikZ pipeline.
| Target Brief |
Diagram Method |
Rationale |
| Report (ReportLab) |
Playwright+CSS → PNG → Image() |
No LaTeX compiler in this route; HTML/CSS handles any layout natively |
| Creative (Playwright) |
Directly in HTML (CSS flexbox/grid + JS connectors) |
Already in browser context |
| Academic (Tectonic) - simple (≤6 nodes) |
TikZ native tikzpicture |
Vector output, font consistency, LaTeX-native |
| Academic (Tectonic) - complex (>6 nodes) |
Playwright+CSS → PNG @2× → \includegraphics |
TikZ branch logic is error-prone for models; 300dpi PNG is publication-ready |
Playwright+CSS diagram pipeline (Report & Academic-complex):
# 1. Write diagram HTML (CSS grid/flexbox + connectors)
cat > diagram.html << 'EOF'
<!-- LLM generates: nodes as divs, arrows as SVG/CSS -->
EOF
# 2. Screenshot at 2× for print quality (300dpi equivalent)
python3 "$PDF_SKILL_DIR/scripts/pdf.py" convert.blueprint diagram.html --device-scale-factor 2 --output diagram.png
# Or via Playwright directly:
# page.screenshot(path='diagram.png', scale='device', device_scale_factor=2)
# 3a. Embed in ReportLab (Report brief)
from reportlab.platypus import Image
img = Image('diagram.png', width=450) # auto height via aspect ratio
story.append(img)
# 3b. Embed in LaTeX (Academic brief, complex diagrams only)
# \includegraphics[width=\columnwidth]{diagram.png}
🚫 FORBIDDEN for Report/Creative briefs: Do NOT use TikZ standalone → compile → pdftoppm → PNG pipeline. This route has no LaTeX compiler and the extra compilation steps are error-prone.
TikZ remains valid ONLY for:
- Academic brief with simple diagrams (≤6 nodes, linear/hierarchical)
- Direct
tikzpicture embedding in LaTeX documents
- Math-annotated diagrams where LaTeX math rendering matters
See briefs/academic.md Scenario B for TikZ templates (simple diagrams only).
Vector Rendering Iron Rule
The final PDF MUST be generated via page.pdf() (Playwright) or ReportLab/LaTeX native output - NEVER via screenshot-to-PDF.
| Scenario |
Correct Method |
Forbidden |
| Creative pipeline (single/multi-page) |
page.pdf() via convert.blueprint or html2pdf-next.js |
page.screenshot() → image → wrap as PDF |
| Report cover (HTML/Playwright) |
page.pdf() → merge via pypdf |
Screenshot cover → embed as image |
| Academic cover |
page.pdf() → merge via pypdf |
Screenshot → \includegraphics for cover |
| Full-page posters/infographics |
html2poster.js (auto overflow:hidden + height measurement + page.pdf()) |
Any raster pipeline for the final output |
Why: page.pdf() produces vector text + vector shapes. Text remains selectable, sharp at any zoom, and file size is smaller. Screenshot-based PDFs are raster images - blurry when zoomed, unsearchable, and 3-5× larger.
The ONLY place screenshot/PNG embedding is acceptable:
- Diagrams embedded as sub-elements inside a larger document (e.g., flowcharts in a Report). These use
page.screenshot() at 2× device scale factor for 300dpi print quality, then embed via Image() (ReportLab) or \includegraphics (LaTeX).
- Chart images generated by matplotlib/plotly saved as PNG, then embedded.
These are sub-elements, not the document itself. The document-level PDF output must always be vector.
Quick test: Open the generated PDF, zoom to 400%. If text is blurry, you used a screenshot pipeline. Fix it.
HTML→PDF Engine Selection Rules
There are two dedicated scripts for HTML→PDF. Choose based on document type:
| Document type |
Script |
Reason |
| Posters, infographics, long-image single-page designs |
html2poster.js |
Auto overflow:hidden, auto height measurement, zero margin, single-page output |
| Cover pages (Report/Academic route) |
html2poster.js |
Covers are single-page fixed layouts with absolute positioning — same nature as posters. html2pdf-next.js would convert absolute→static and destroy the layout |
| Multi-page documents, reports, academic papers, resumes |
html2pdf-next.js |
A4/custom pagination, 20mm margin fallback, cover adaptation, pdf-lib metadata |
| Creative pipeline (Blueprint → HTML → PDF) |
html2pdf-next.js via convert.blueprint |
Called internally by design_engine pipeline |
Poster / Single-Page Long-Image → html2poster.js
node "$PDF_SKILL_DIR/scripts/html2poster.js" poster.html --output poster.pdf --width 720px
html2poster.js automatically:
- Forces
overflow: hidden on .poster / .page containers (clips decorative overflow)
- Injects
@page { margin: 0 } (zero margins always)
- Syncs
html/body background with poster background color
- Measures
.poster scrollHeight and uses it as PDF height
- Generates a single-page vector PDF with exact content dimensions
Use this for ANY fixed-width, dynamic-height, single-page design.
Documents / Multi-Page → html2pdf-next.js
node "$PDF_SKILL_DIR/scripts/html2pdf-next.js" input.html --output output.pdf --width 210mm --height 297mm
# Or via pdf.py wrapper:
python3 "$PDF_SKILL_DIR/scripts/pdf.py" convert.html input.html --output output.pdf
Pre-render hooks auto-handle @page injection, overflow detection, cover adaptation, font loading, and pdf-lib metadata.
⚠️ Iron Rule: No Hand-Written Playwright Scripts
Common issues with hand-written Python page.pdf() (the dedicated scripts handle these automatically):
- Missing
@page rule → browser default margin causes content overflow to second page or white edges
- Oversized elements not fixed → large elements with
break-inside: avoid block pagination, content gets truncated
- Rendering before fonts are loaded → Chinese text displays as squares or falls back to wrong font
- No overflow detection → content exceeds page boundary without awareness
- No metadata → PDF title, author, and other info missing
Iron rule: Posters and cover pages use html2poster.js, multi-page documents use html2pdf-next.js. Do not write hand-written Python Playwright scripts.
⚠️ Cover page gotcha: Cover HTML uses position: absolute for layout. html2pdf-next.js pre-render hooks convert absolute-positioned elements to static flow (to prevent multi-page overlap), which destroys cover layouts. Always use html2poster.js for cover pages.
No overflow:hidden on Fixed-Size Pages (html2pdf-next.js only)
When using html2pdf-next.js for documents, NEVER set overflow: hidden on html, body, or the main page container.
Note: This rule does NOT apply to posters rendered via html2poster.js — that script automatically adds overflow: hidden to .poster/.page containers to clip decorative overflow. You don't need to add or remove it manually.
| Problem |
Cause |
Fix |
| Browser preview cuts off bottom content, can't scroll |
overflow: hidden on container + viewport < design height |
Remove overflow: hidden |
| html2pdf-next.js "Fixed vertical overflow" warning, layout may break |
Pre-render detects scrollHeight > clientHeight + hidden overflow, force-expands container |
Remove overflow: hidden |
Always pair fixed-size pages with @media screen auto-scale so the full page is visible in any browser window without scrolling. See briefs/creative.md § 0.5 for the CSS pattern.
Full-Bleed Rule (No White Margins)
When generating HTML for Playwright page.pdf(), the content MUST fill the entire page with zero margins. White side margins = broken layout.
Mandatory CSS for any HTML → PDF:
@page {
size: <width> <height>; /* e.g., 720px 960px, or A4 */
margin: 0;
}
html, body {
margin: 0;
padding: 0;
}
Common causes of white margins:
- Missing
@page { margin: 0 } - browser default margins kick in (~1cm each side)
- Content width doesn't match page width - e.g., canvas is 720px but page is A4 (794px)
- Missing
@page { size } declaration in the HTML
- Content has explicit
max-width that's narrower than the page
For blueprint pipeline: design_engine.py now injects @page { size: var(--canvas-w) var(--canvas-h); margin: 0; } automatically.
For raw HTML: YOU must include the @page rule. No exceptions.
For direct Playwright: Pass margin: { top: 0, right: 0, bottom: 0, left: 0 } to page.pdf().
Background Color Consistency (No Color Mismatch)
html / body background color must match the content canvas background color.
Playwright page.pdf({ printBackground: true }) renders the body background color. If body is white while the content area is gray/colored, color-inconsistent borders/gaps will appear in the PDF.
Single-color documents (all pages same background)
/* MANDATORY: body background = content background */
html, body {
margin: 0;
padding: 0;
background: var(--c-bg); /* Same color as content canvas */
}
Multi-page documents with mixed backgrounds (e.g. dark cover + white body pages)
Root cause: Playwright resolves .page { width: 210mm } and @page { size: 210mm } to slightly different sub-pixel values (e.g. 793.688px vs 793.701px). This creates a <1px gap at the right/bottom edge of each .page div where body's background shows through. On dark pages, a white body background makes this gap visible as a white edge.
Fix — set body background to the document's dominant dark color:
:root {
--primary: #0f172a; /* darkest page background */
}
html, body {
margin: 0;
padding: 0;
width: 210mm; /* match @page size */
background: var(--primary); /* fallback for sub-pixel gaps */
}
Why this works and doesn't break white pages:
- Dark pages: sub-pixel gap reveals dark
body → gap invisible.
- White pages:
.page-white { background: #ffffff } fully covers body → dark body never visible.
- The gap is <1px — even on white pages, the dark body at the extreme pixel edge is imperceptible after anti-aliasing.
Rule: when generating multi-page HTML with mixed backgrounds, always set html, body { background } to the darkest page's background color. If all pages are light/white, use the lightest content background (e.g. #f8fafc). Never leave body background unset (browser default = white = guaranteed white edges on dark pages).
### Content Centering (No Left/Right Drift)
**After HTML-to-PDF conversion, content must be centered, no left or right drift allowed.**
Common drift causes:
1. `@page { margin }` not 0 — browser default margin causes drift
2. `.safe-zone` or content container `inset` / `padding` left-right asymmetric
3. Content container has `max-width` but no `margin: 0 auto`
4. Grid components only occupy partial column width (e.g. `1/1 → X/7` only uses left half)
5. **Decorative elements overflow page boundary** — elements with `width > 100%` or negative offsets (e.g. glow circles, gradient overlays) inflate `scrollWidth` beyond page width. Playwright shrinks all content to fit, causing left-shift. **Fix: add `overflow: hidden` to `.page` containers.** See `typesetting/overflow.md` §3.5 for horizontal flex overflow rules.
### Anti-Void Edges (No Large Blank Margins)
**Content should not have large meaningless whitespace at page edges, top, or bottom.**
- Content should make full use of page area; do not cram all content in the top half while leaving the bottom blank
- For multi-page documents, each page's fill rate should be ≥ 60% (see `pagination.md` last page ≥ 40% rule)
- For single-page posters/infographics, fill rate should be ≥ 70%
---
## Preflight (Quality Assurance)
Every PDF must pass preflight checks before delivery. Each brief specifies the exact commands.
### HTML Pre-Render Validation (MANDATORY for ALL HTML→PDF paths)
**Before** calling `html2pdf-next.js`, `html2poster.js`, `convert.blueprint`, or any Playwright `page.pdf()`, run:
```bash
python3 "$PDF_SKILL_DIR/scripts/poster_validate.py" check-html <your_file>.html
| Result |
Action |
| PASS (no errors) |
Proceed to PDF generation |
| ERROR items |
Must fix before generating PDF. Use --fix --output <file>.html for auto-repair |
| WARNING items |
Review; non-blocking but should be addressed |
Key checks:
OVERFLOW_HIDDEN_CONTAINER (error): overflow:hidden on html/body/.page clips content in browser preview and triggers html2pdf-next.js auto-fix that may break layout
FIXED_SIZE_NO_SCREEN_ADAPT (warning): fixed-size page without @media screen auto-scale — browser preview requires scrolling
SCREEN_ADAPT_NO_SCALE (warning): @media screen exists but lacks scale/transform/zoom
FONT_NO_FALLBACK (error): font-family without generic fallback
COLOR_CONTRAST (warning): text/background contrast ratio < 3:1
- Plus: remote images, absolute paths, missing margin reset, tiny fonts, background mismatch, etc.
This applies to all three HTML routes: Creative blueprint pipeline, Report HTML covers, and bypass/custom HTML.
Overflow Prevention System
→ Full spec: typesetting/overflow.md - read it for any document with tables, images, or multi-column layouts.
Core principles:
- Measure first, draw second - never render content without pre-calculating its dimensions
- Bounding Box constraint - every element's width ≤ its parent container's
Max_Width
- Text: use font metrics, not character count, for width calculation
- Images: proportional scaling - never insert at original size
- Tables: weight-based column width +
Paragraph() wrapping (never plain strings)
- Fallback ladder: wrap → shrink font (max -3pt) → reduce padding → split element → log warning
- Vertical: KeepTogether for heading+body, chart+caption;
repeatRows=1 for long tables
Table Overflow Prevention (ReportLab)
Most common layout bug: table columns exceed page margins.
Before building any ReportLab Table:
- Calculate
available_width = page_width - left_margin - right_margin
- Use proportional colWidths (
[0.25, 0.40, 0.20, 0.15] × available_width) or fixed+flex pattern
sum(colWidths) must be ≤ available_width - verify this in code
- Long text columns must use
Paragraph() wrapping, not plain strings (plain strings don't wrap)
- CJK text is wider: budget ~12pt per character at 10pt font size
See briefs/report.md § "Table Width Management" for code patterns.
Table Overflow Prevention (LaTeX/Academic)
Most common bug in dual-column papers: wide tables overflow single-column width.
Before writing any LaTeX table:
- Count data columns - ≤ 4 fits single column; 5-6 needs
\small; 7-8 needs \resizebox; ≥ 9 use table* (full width)
- Use
tabular*{\columnwidth} or tabularx{\columnwidth} instead of plain tabular for 5+ columns
- Never use plain
tabular with 8+ columns in twocolumn layout - guaranteed overflow
\resizebox{\columnwidth}{!} as last resort - verify smallest text ≥ 6pt after scaling
See briefs/academic.md § "Table width management" for LaTeX patterns.
Playwright PDF CSS Blacklist
These CSS properties silently break in Playwright's PDF renderer:
backdrop-filter / -webkit-backdrop-filter - drops entire element content. Use solid rgba() backgrounds.
overflow: hidden on content containers - clips content. Only safe on small decorative elements (< 200px).
After generating any Playwright PDF, verify every page has content (pypdf text extraction, check non-empty).
PDF Metadata (all briefs)
ALL PDFs must have: Title, Author (default "Z.ai"), Creator, Subject.
Delivery Summary (all briefs)
Report to user: file path, size, page count. Academic adds word/image count. Creative adds per-page verification.
HTML→PDF route deliverables (MANDATORY — applies to ALL briefs that use Playwright/HTML to generate PDF):
Whenever the HTML→PDF pipeline is used (Creative route, Report cover bypass, Direct HTML Flow posters, or any Playwright page.pdf() path), you MUST deliver both files to the user:
- HTML — the source HTML file, so the user can edit and reuse the design
- PDF — the final vector PDF (
page.pdf() output)
Optionally also provide:
3. Image — a full-page screenshot/preview image (PNG or JPG) for quick sharing on chat/social media
All file paths must be reported to the user. Never deliver only the PDF without the HTML source.
Tooling Reference
CLI: python3 "$PDF_SKILL_DIR/scripts/pdf.py" <command>
# Environment
env.check # Check deps
env.fix # Auto-install missing
# Quality
code.sanitize <script> # Sanitize forbidden Unicode
content.sanitize <file> [--apply] # Fix content issues (CJK, encoding)
meta.brand <pdf> # Add Z.ai metadata
font.check <pdf> # Scan for missing glyphs
toc.check <pdf> # Validate TOC
# Conversion
convert.blueprint <llm_json_response.md> -o final.pdf # CRITICAL FOR CREATIVE: Auto-extracts JSON, compiles, and renders PDF.
convert.html <html> # HTML → PDF (Playwright)
convert.latex <tex> # LaTeX → PDF (Tectonic). Bundled binary is macOS arm64 only; see academic.md for other-platform install.
convert.office <file> # Office → PDF (LibreOffice)
# Processing
extract.text <pdf> # Extract text
extract.table <pdf> # Extract tables
extract.image <pdf> # Extract images
pages.merge a.pdf b.pdf -o out.pdf
pages.split <pdf>
pages.clean <pdf> # Remove blank pages
form.info <pdf> # Inspect form fields
form.fill <pdf> # Fill form
form.annotate <pdf> # Fill via annotations
meta.get <pdf>
meta.set <pdf> -o out.pdf -d '{"Title": "..."}'
Poster/HTML/LaTeX Validator: python3 "$PDF_SKILL_DIR/scripts/poster_validate.py"
check-html <html> # Pre-render validation (overflow:hidden, @media screen, fonts, contrast, etc.)
check-html <html> --fix --output <fixed.html> # Auto-fix errors (remove overflow:hidden, add font fallback)
check-pdf <pdf> --source-html <html> # Post-render validation
check-pdf <pdf> --poster # Poster mode: suppress ORPHAN_PAGE warning
check-tex <tex> # LaTeX source validation (table overflow, image width, etc.)
check-html checks include:
OVERFLOW_HIDDEN_CONTAINER (error): overflow:hidden on html/body/.page/.poster — clips content
FIXED_SIZE_NO_SCREEN_ADAPT (warning): fixed-size page without @media screen auto-scale
SCREEN_ADAPT_NO_SCALE (warning): @media screen exists but lacks scale/transform/zoom
FONT_NO_FALLBACK (error): font-family without generic fallback (sans-serif/serif)
COLOR_CONTRAST (warning): text/background contrast ratio < 3:1
BG_COLOR_MISMATCH (warning): body background differs from .canvas/.poster background
SCREEN_BG_MISMATCH (warning): @media screen html background differs from body/canvas background
MULTIPAGE_BODY_BG_MISSING (warning): multi-page document with dark .page backgrounds but no html/body background color. Sub-pixel gaps at page edges reveal white body, causing visible white edges on dark pages. Resolves var() references via :root variables.
SCREEN_NO_BG (warning): fixed-size page's @media screen block lacks html background color
OVERFLOW_DECORATION (warning): negative position values may cause black edges
NO_PAGE_SIZE / MISSING_MARGIN_RESET / WHITE_BACKGROUND / TINY_FONT / etc.
check-tex checks include:
BARE_TABULAR_OVERFLOW (error): \begin{tabular} with 5+ columns in two-column layout, not wrapped in resizebox/adjustbox/table*
RESIZEBOX_TEXTWIDTH (error): \resizebox{\textwidth} used inside single-column float in two-column layout. \textwidth = full page width, but table float is one column. Fix: use \resizebox{\columnwidth} or table*
TABULAR_OVERFLOW_RISK (warning): 4-column tabular in two-column layout without width constraint
TABULAR_WIDE (warning): 7+ column tabular in single-column layout without width constraint
TABULAR_NO_FLOAT (warning): tabular not inside table/table* float environment
TABULARX_NOT_LOADED (warning): document has tabular but tabularx package not loaded
IMAGE_NO_WIDTH (warning): \includegraphics without width/height/scale constraint
EQUATION_DUAL_ON_LINE (warning): equation environment has 2+ equations joined by \quad without line breaks. Guaranteed overflow in dual-column
EQUATION_OVERFLOW_RISK (warning): equation body has >80 math characters. Likely overflows single column
ALGORITHM_NO_SMALL_FONT (warning): algorithm environment in dual-column without \SetAlFnt{\small}
ALGORITHM_LONG_IO (warning): Algorithm Input/Output line >120 chars. Will overflow narrow column
CJK_ASCII_QUOTES (error): ASCII " found adjacent to CJK characters. LaTeX interprets " as right double quote, so "北漂" renders incorrectly. Skips verbatim/lstlisting/minted environments and \texttt{}/\url{}/\href{}{}/\verb|| inline commands.
Design Engine: python3 "$PDF_SKILL_DIR/scripts/design_engine.py"
compile --blueprint <json_file> --output poster.html # CRITICAL: Compile JSON blueprint to HTML
derive "document title or description" # Auto-derive intent from content
palette --intent calm --mode dark # Generate HSL-locked palette
palette-cascade --intent cold --mode minimal # Generate role-based cascade palette (V2, preferred)
svg --intent flow --dimensions 720x960 # Generate SVG background
full --intent energy --mode dark --dimensions 720x960 --output-dir ./assets/
audit --palette-json palette.json # Check palette constraints
Palette Ge
…(truncated)
1---2name: pdf3metadata:4 author: Z.AI5 version: "1.0"6description: "Professional PDF toolkit with four production lines: (1) Report - structured documents via ReportLab (reports, proposals, contracts, white papers) (2) Creative - visual design via JSON Blueprint → design_engine.py → Playwright snapshot (posters, infographics, invitations, dashboards). The LLM acts as Art Director outputting ONLY JSON spatial blueprints; convert.blueprint compiles to pixel-perfect PDF. (3) Academic - scholarly work via LaTeX/Tectonic (papers, theses, math-heavy documents) (4) Process - manipulate existing PDFs (extract, merge, split, fill forms, convert) Auto-routes based on document type. Includes ATS/creative/academic resume sub-paths.7license: Proprietary. LICENSE.txt has complete terms8---910# PDF - Document Production Workbench1112## Quick Setup1314```bash15bash "$PDF_SKILL_DIR/scripts/setup.sh" # Interactive environment check + install16python3 "$PDF_SKILL_DIR/scripts/pdf.py" env.check # Detailed dependency status (JSON: add -j)17python3 "$PDF_SKILL_DIR/scripts/pdf.py" env.fix # Auto-install missing Python packages18```1920## Triage2122Determine task weight to control how much context to load:2324| Weight | Triggers | What to Load |25|--------|----------|--------------|26| **Light** | Format conversion, form fill, text extract, merge/split, simple certificate | SKILL.md + `briefs/process.md` only |27| **Standard** | Multi-page report, poster, academic paper, resume, reformat - any document with design decisions | SKILL.md + matched brief + typesetting assets on demand |2829Light tasks skip typesetting files entirely. Standard tasks load them on demand per the brief's instructions.3031### ⚠️ Pre-Routing Checks (run BEFORE matching brief)32331. **Emoji Check** - Scan user content for intentional emoji (decorative 📊🎯🔥, not OS-level emoji input). If found → **force Creative brief** regardless of document type. ReportLab renders emoji as □ squares; LaTeX drops them entirely.342. **CJK Check** - Chinese/Japanese/Korean content needs font coverage. Report brief must use `UniSong`/`UniHei` registered fonts; Creative brief must load Google Fonts Noto Sans SC with `font-display: swap`; Academic brief must use `\usepackage{ctex}`.353. **Size Check** - Non-standard page sizes (not A4/Letter/A3) → prefer Creative brief (Playwright handles any dimension). ReportLab can do custom sizes but pagination is manual.364. **Character Safety Check** - Before writing any content string, scan for Japanese kana (の、が、は etc.), unusual Unicode symbols, or non-CJK characters that may corrupt during encoding transit ( Especially when code is written via heredoc/base64/LLM output). Replace with plain Chinese equivalents: `の`→`之/的/缔`, `々`→omit or write full character. **If content must preserve Japanese, use only standard CJK Unified Ideographs (U+4E00-U+9FFF) and common kana; avoid rare/private-use codepoints.**3738---3940## Briefing4142Match the user's intent to a production brief. Each brief contains the full workflow, tech stack specifics, and references to shared typesetting assets.4344```45User Request46│47├─ Work with existing PDF? ─────────────┬─ Extract/merge/split/fill/convert → briefs/process.md48│ ├─ Reformat/redesign → briefs/process.md (extract) → delegate to report or creative brief49│ └─ User provides a PDF template/reference to match style50│ → briefs/process.md "Template-Guided Reformat" → delegate to matched brief51│52├─ Report / proposal / white paper / contract / analysis?53│ └─ ────────────────────────────────── → briefs/report.md (ReportLab)54│55├─ Poster / invitation / infographic / dashboard / creative layout?56│ └─ ────────────────────────────────── → briefs/creative.md (Playwright)57│58├─ Academic paper / thesis / math / IEEE / ACM / LaTeX?59│ └─ ────────────────────────────────── → briefs/academic.md (Tectonic)60│61├─ Math-heavy doc / TikZ diagram / algorithm pseudocode / Beamer slides?62│ └─ ────────────────────────────────── → briefs/academic.md (Tectonic, Scenarios A-D)63│64├─ Document needs complex embedded diagrams (flowcharts, architecture, neural nets)?65│ └─ Route by target brief:66│ ├─ Report → Playwright+CSS → PNG → ReportLab Image() flowable67│ ├─ Creative → directly in HTML (CSS flexbox/grid + connectors)68│ └─ Academic → complexity-based:69│ ├─ Simple (≤6 nodes, linear/tree) → TikZ native (vector)70│ └─ Complex (>6 nodes, branches, annotations) → Playwright+CSS → PNG → \includegraphics71│72└─ Resume / CV?73 ├─ ATS-safe / corporate ─────────── → briefs/report.md (resume sub-section)74 ├─ Creative / design industry ────── → briefs/creative.md (resume sub-section)75 └─ Academic CV / publications ────── → briefs/academic.md (resume sub-section)76```7778### Detection Keywords7980| Brief | Keywords |81|-------|----------|82| Report | 报告, report, 分析, analysis, 白皮书, white paper, 提案, proposal, 合同, contract, 方案, 规划, 发票, invoice, 收据, receipt, 试卷, exam, quiz, test paper, 练习, exercise, worksheet, 考试, 测验 |83| Creative | 海报, poster, 邀请函, invitation, 信息图, infographic, 仪表盘, dashboard, 传单, flyer, 证书, certificate, 菜单, menu, 名片, business card, 奖状, award, 标签, label, 信封, envelope, 贺卡, greeting card |84| Creative (Poster) | 海报, poster, 传单, flyer, 宣传页, 宣传单 → additionally load `briefs/poster.md` scene layer rules |85| Academic | 论文, paper, 学术, academic, LaTeX, 数学, math, IEEE, ACM, 毕业, thesis, 研究, research, Beamer, slides, 开题报告, 学位, dissertation, proposal |86| Process | 提取, extract, 合并, merge, 拆分, split, 填写, fill, 转换, convert, OCR, 重排, reformat, 重新排版, redesign, 模板, template, 参照, 照着这个做, match this style, 压缩, compress, 水印, watermark, 加密, encrypt, 签名, sign |8788### Complete Scenario Routing Matrix8990Below is an exhaustive map of every known PDF request type to its handling strategy. If a scenario is not listed, route to the closest match or ask the user.9192#### 📄 Creation (Generate PDF from scratch)9394| Scenario | Route | Notes |95|----------|-------|-------|96| Report / white paper / analysis | report.md | ReportLab structured document |97| Report with emoji | **creative.md** | 🚨 Emoji rule override |98| Business proposal | report.md | Structured + data tables |99| Contract / legal document | report.md | Add signature placeholders (dotted line + label) |100| Invoice / receipt | report.md | Table-heavy, precision alignment |101| Exam / quiz / test paper / worksheet | report.md | Indented options, answer space reservation, structured numbering (see Exam Paper Rules in report.md) |102| Math exam / math worksheet (with formulas/equations) | academic.md | LaTeX for proper math typesetting. See §Exam Paper Rules in academic.md |103| Poster / flyer | creative.md + **poster.md** | Visual design + poster density/sizing rules |104| Invitation / greeting card | creative.md | Non-standard size, decorative |105| Certificate / award | creative.md | Single page, centered layout, decorative border |106| Business card | creative.md | Tiny size (90×54mm), Playwright native support |107| Envelope / label | creative.md | Non-standard size, simple layout |108| Menu / price list | creative.md | Visual layout + may contain emoji |109| Resume (ATS) | report.md | Plain text structure |110| Resume (creative) | creative.md | Visual design |111| Resume (academic CV) | academic.md | Publication list + BibTeX |112| Academic paper | academic.md | LaTeX/Tectonic |113| Math-heavy document | academic.md | LaTeX typesetting |114| Presentation / PPT-style | creative.md | Landscape (1280×720), one topic per page |115| Book / long document | report.md | Add TOC + chapter numbering, validate with toc_validate.py |116| CJK vertical text | creative.md | HTML `writing-mode: vertical-rl` + `text-orientation: upright` + `white-space: nowrap` + Playwright |117| RTL document (Arabic/Hebrew) | creative.md | HTML `dir="rtl"` + Playwright |118| Batch generation (mail merge) | report.md | Python loop + template variable substitution |119| Infographic | creative.md | Data visualization + design |120| Calendar / schedule | creative.md | Grid layout + custom dimensions |121122#### 🔧 Processing (Manipulate existing PDF)123124| Scenario | Route | Command / Method |125|----------|-------|------------------|126| Merge multiple PDFs | process.md | `pages.merge a.pdf b.pdf -o out.pdf` |127| Split PDF | process.md | `pages.split input.pdf -o ./output/` |128| Extract text | process.md | `extract.text input.pdf` |129| Extract tables | process.md | `extract.table input.pdf` |130| Extract images | process.md | `extract.image input.pdf` |131| Fill forms | process.md | `form.fill input.pdf` |132| Office → PDF | process.md | `convert.office input.docx` |133| HTML → PDF (documents) | process.md | `convert.html input.html` or `node html2pdf-next.js` |134| HTML → PDF (posters) | poster.md | `node html2poster.js poster.html` |135| Image → PDF | process.md | pikepdf: one image per page, embed as XObject |136| PDF → image | process-advanced.md | pypdfium2 render each page to PNG |137| Encrypt / decrypt | process-advanced.md | pikepdf encryption |138| Add watermark | process.md | pikepdf overlay: create watermark page → merge onto each page |139| Compress PDF | process.md | Ghostscript: `gs -sDEVICE=pdfwrite -dPDFSETTINGS=/screen` |140| OCR scanned PDF | process-advanced.md | ocrmypdf or Tesseract |141| Rotate pages | process.md | `pages.rotate input.pdf 90 -o out.pdf` |142| Crop pages | process.md | `pages.crop input.pdf l,b,r,t -o out.pdf` |143| Remove blank pages | process.md | `pages.clean input.pdf` |144| Reformat by template | process.md → delegate | Extract content → regenerate via report/creative |145| PDF diff / compare | process.md | `diff-pdf` CLI or Python per-page text comparison |146| Digital signature | process.md | `pyhanko` library (requires extra install) |147| Edit metadata | process.md | `meta.set input.pdf -o out.pdf -d '{...}'` |148149### Special Routing Rules150151**🚨 Emoji rule (CRITICAL - check FIRST)**: Content with intentional emoji (📊🎯🔥💡 etc.) → force **briefs/creative.md** regardless of document type. ReportLab renders emoji as □ squares; LaTeX silently drops them. This rule overrides all other routing. Even if the user says "report" - if the content has emoji, use Creative pipeline.152153**Non-standard page size rule**: Dimensions other than A4/Letter/A3 → strongly prefer **briefs/creative.md**. Playwright handles any arbitrary page size natively. ReportLab requires manual pagination math.154155**Academic auto-detect**: Papers, theses, or heavy math → **briefs/academic.md** even without explicit "LaTeX" mention.156157**Template-guided rule**: When the user uploads a PDF and says "match this template" / "follow this style" / "reformat like this" → **briefs/process.md** Template-Guided Reformat section. This is a Standard triage (not Light), because it involves design decisions.158159**Resume routing**: Default to Report brief (ATS-safe). Creative industry → Creative brief. Academic CV with publications → Academic brief.160161---162163## Shared Assets164165These are referenced by multiple briefs. **Do not load upfront** - each brief tells you when and what to load.166167| Asset | Path | Used By | Purpose |168|-------|------|---------|---------|169| Palette & Typography | `typesetting/palette.md` | Report, Creative | Color system, font rules, anti-patterns, spacing |170| Cover Layout System V2.1 | `typesetting/cover.md` | **Report + Creative + Academic** | 7 industrial-grade templates with absolute anchor grid, Z-index layers, typography weight system, mandatory Summary Block, code-level safety (5 checks), base unit `U = W*0.05`. **Unified HTML/Playwright cover system for all routes.** |171| Chart Styling & Anti-Stacking | `typesetting/charts.md` | Report, Creative, Academic | Chart defaults, collision prevention, axis/grid/legend rules |172| Overflow Prevention | `typesetting/overflow.md` | Report, Creative, Academic | Bounding box system, text/image/table overflow prevention, fallback strategies |173| **Fill Engine (Anti-Void)** | `typesetting/fill-engine.md` | **Report, Creative, Academic** | **Anti-Void Engine V2.0: font floor enforcement, fill ratio calculation, paragraph inflation, component elevation, Y-axis golden-ratio anchoring** |174| Pagination & Flow Control | `typesetting/pagination.md` | Report, Creative | Cross-page integrity, orphan/widow control, CJK punctuation rules |175| Typography System | `typesetting/typography.md` | Report, Creative | Font size scale, line-height, spacing hierarchy |176| Geometric Anchors | `typesetting/geometry.md` | Creative + Report | Decorative geometric elements, anchor placement rules |177| Cover Backgrounds | `typesetting/cover-backgrounds.md` | **Report + Creative + Academic** | Cover background rendering, transparency constraints |178| Visual Framework | `configs/visual_framework.md` | Creative | Palette mode, color harmony, SVG background params |179| Components Library | `configs/components.md` | Creative | Non-grid composition components (floating cards, oversized text, etc.) |180| Font Stacks | `configs/fonts.md` | All pipelines | Font families per pipeline (Google Fonts, ReportLab, LaTeX) |181182---183184## Content Rules185186- **Language**: Match user's query language. Chinese query → Chinese PDF.187- **Page/word count**: Respect explicit constraints (±20%). Unspecified → completeness over brevity.188- **Outline**: User-provided outlines are sacred. No reordering without asking.189- **Citations**: No fabrication. Chinese → GB/T 7714, English → APA. Search to verify.190- **Multi-part requests**: Generate ALL parts - never silently drop a component.191192### HTML Image Source Path Rules193194When embedding images in HTML documents (Creative pipeline, Playwright-rendered diagrams, or any HTML→PDF flow):195196| Image location | `<img src>` value | Example |197|---|---|---|198| **Local file** | **Relative path** from the HTML file's directory | `<img src="images/chart.png">` or `<img src="./diagram.png">` |199| **Remote URL** | Full URL (no change needed) | `<img src="https://example.com/photo.jpg">` |200201**Iron rules:**2021. **NEVER use absolute paths** for local files in HTML `<img>`, `<source>`, CSS `url()`, or any other asset reference (e.g. `/Users/alice/project/img.png`). Absolute paths break portability across machines and environments.2032. **Always use relative paths** anchored to the HTML file's own directory. If the image lives in a subdirectory, use `images/foo.png` or `./images/foo.png`.2043. **Remote URLs (`http://` / `https://`) are fine as-is** — do not convert them to local paths.2054. When generating HTML from a script or blueprint, ensure all referenced assets are either (a) in the same directory as the output HTML, or (b) in a clearly named subdirectory (e.g. `assets/`, `images/`), and referenced with relative paths.2065. If a build script needs to resolve paths programmatically, compute relative paths at generation time (e.g. `os.path.relpath(image_path, html_dir)`) rather than embedding absolute filesystem paths.207208---209210## Figure & Diagram Embedding (All Briefs)211212### Iron Rule: Figures Are Block-Level213214Figures, diagrams, and charts MUST be independent block elements occupying full width. **Never** float/wrap figures alongside body text - this causes the text-diagram overlap badcase.215216| Brief | Correct embedding | Forbidden |217|-------|-------------------|-----------|218| Report (ReportLab) | `story.append(Image(...))` as standalone Flowable | Placing images inside Paragraph text, simulating float |219| Creative (Playwright) | `<figure style="display:block; width:100%; margin:2em auto">` | `float:right`, `display:flex` with text, `wrapfigure`-style CSS |220| Academic (LaTeX) | `\begin{figure}[t] ... \end{figure}` | Bare `\includegraphics` in text body (no figure env), bare `tikzpicture` in multi-column |221222### Complex Diagram Strategy223224When a diagram has **>12 nodes, >3 subgroups, or intricate connections**, do NOT try to render it as one giant figure. Instead:2252261. **Table for details** - structured data (phases, components, specs) goes into a proper table2272. **Simplified overview diagram** - a stripped-down flowchart/Mermaid showing only the top-level flow (≤8 nodes)2283. **Cross-reference** - table caption + diagram caption reference each other229230This "table + simple diagram" pattern prevents:231- Diagrams overflowing page boundaries232- Text becoming unreadably small to fit everything233- Layout engines mishandling oversized graphics234235### Diagram Content Quality Rules (Cross-reference: charts)236237The rules above handle **how** to embed diagrams in PDF. For **what the diagram itself looks like** (node layout, connector routing, color, readability), follow the `charts` skill rules:238239**Before generating ANY flowchart/diagram for PDF embedding, check these:**2402411. **Connectors must not pass through nodes** - If 3+ layers exist, connect adjacent layers only (top→mid, mid→bottom). Never draw top→bottom lines through middle nodes. Use detour paths if cross-layer links are needed.2422. **Multiple arrows into one node must not pile up** - Distribute entry points evenly along target edge, or use merge-then-enter pattern (sources converge to a vertical merge line, then single arrow to target).2433. **Low-saturation fills only** - Node backgrounds must be pale (`#EFF6FF`, `#F0FDF4`). High-saturation colors (`#3B82F6`, `#10B981`) only for borders or small accents. No children's-art color schemes.2444. **Phase titles vs sub-steps must be visually distinct** - Different background color, font size, and font weight. Never same-style boxes for both.2455. **Font sizes must be readable at final output size** - Sizes depend on the embedding context:246 | Output context | Node title min | Description min | Label min |247 |---------------|----------------|-----------------|-----------|248 | Standalone PNG (web/presentation, ≥1200px wide) | 14px | 12px | 11px |249 | Embedded in A4 PDF (ReportLab/LaTeX, ~450pt content width) | 10pt | 8pt | 7pt |250 | Embedded in slide deck (landscape, ~720pt wide) | 12pt | 10pt | 9pt |251252 **Principle**: After embedding, the smallest text in the diagram must still be legible when the document is viewed at 100% zoom. If the diagram is scaled down to fit page width, recalculate: `effective_size = original_size × (display_width / canvas_width)`. If effective size drops below the minimum, either increase original font size or reduce diagram complexity.2536. **Legend/annotations must not overlap content** - Separate container, ≥ 40px gap from last node, fully within canvas bounds.254255**For Playwright-rendered diagrams**: Use low-saturation fills (`#EFF6FF`, `#F0FDF4`), CSS flexbox/grid for node layout, SVG `<line>`/`<path>` for connectors, and verify no overlap at final render size.256**For ReportLab-drawn diagrams**: Same principles apply - use `Drawing()` with explicit coordinates, check node bounding boxes for overlap before finalizing.257258### Diagram Generation Strategy (Per-Brief)259260Diagram rendering depends on the target brief - **NOT** a one-size-fits-all TikZ pipeline.261262| Target Brief | Diagram Method | Rationale |263|---|---|---|264| **Report** (ReportLab) | Playwright+CSS → PNG → `Image()` | No LaTeX compiler in this route; HTML/CSS handles any layout natively |265| **Creative** (Playwright) | Directly in HTML (CSS flexbox/grid + JS connectors) | Already in browser context |266| **Academic** (Tectonic) - simple (≤6 nodes) | TikZ native `tikzpicture` | Vector output, font consistency, LaTeX-native |267| **Academic** (Tectonic) - complex (>6 nodes) | Playwright+CSS → PNG @2× → `\includegraphics` | TikZ branch logic is error-prone for models; 300dpi PNG is publication-ready |268269**Playwright+CSS diagram pipeline (Report & Academic-complex):**270271```bash272# 1. Write diagram HTML (CSS grid/flexbox + connectors)273cat > diagram.html << 'EOF'274<!-- LLM generates: nodes as divs, arrows as SVG/CSS -->275EOF276277# 2. Screenshot at 2× for print quality (300dpi equivalent)278python3 "$PDF_SKILL_DIR/scripts/pdf.py" convert.blueprint diagram.html --device-scale-factor 2 --output diagram.png279# Or via Playwright directly:280# page.screenshot(path='diagram.png', scale='device', device_scale_factor=2)281282# 3a. Embed in ReportLab (Report brief)283from reportlab.platypus import Image284img = Image('diagram.png', width=450) # auto height via aspect ratio285story.append(img)286287# 3b. Embed in LaTeX (Academic brief, complex diagrams only)288# \includegraphics[width=\columnwidth]{diagram.png}289```290291**🚫 FORBIDDEN for Report/Creative briefs:** Do NOT use TikZ standalone → compile → pdftoppm → PNG pipeline. This route has no LaTeX compiler and the extra compilation steps are error-prone.292293**TikZ remains valid ONLY for:**294- Academic brief with simple diagrams (≤6 nodes, linear/hierarchical)295- Direct `tikzpicture` embedding in LaTeX documents296- Math-annotated diagrams where LaTeX math rendering matters297298See `briefs/academic.md` Scenario B for TikZ templates (simple diagrams only).299300---301302## Vector Rendering Iron Rule303304**The final PDF MUST be generated via `page.pdf()` (Playwright) or ReportLab/LaTeX native output - NEVER via screenshot-to-PDF.**305306| Scenario | Correct Method | Forbidden |307|----------|---------------|-----------|308| Creative pipeline (single/multi-page) | `page.pdf()` via `convert.blueprint` or `html2pdf-next.js` | `page.screenshot()` → image → wrap as PDF |309| Report cover (HTML/Playwright) | `page.pdf()` → merge via pypdf | Screenshot cover → embed as image |310| Academic cover | `page.pdf()` → merge via pypdf | Screenshot → `\includegraphics` for cover |311| Full-page posters/infographics | `html2poster.js` (auto overflow:hidden + height measurement + `page.pdf()`) | Any raster pipeline for the final output |312313**Why:** `page.pdf()` produces vector text + vector shapes. Text remains selectable, sharp at any zoom, and file size is smaller. Screenshot-based PDFs are raster images - blurry when zoomed, unsearchable, and 3-5× larger.314315**The ONLY place screenshot/PNG embedding is acceptable:**316- **Diagrams** embedded as sub-elements inside a larger document (e.g., flowcharts in a Report). These use `page.screenshot()` at 2× device scale factor for 300dpi print quality, then embed via `Image()` (ReportLab) or `\includegraphics` (LaTeX).317- **Chart images** generated by matplotlib/plotly saved as PNG, then embedded.318319These are sub-elements, not the document itself. The document-level PDF output must always be vector.320321**Quick test:** Open the generated PDF, zoom to 400%. If text is blurry, you used a screenshot pipeline. Fix it.322323### HTML→PDF Engine Selection Rules324325There are **two dedicated scripts** for HTML→PDF. Choose based on document type:326327| Document type | Script | Reason |328|---------------|--------|--------|329| **Posters, infographics, long-image single-page designs** | `html2poster.js` | Auto overflow:hidden, auto height measurement, zero margin, single-page output |330| **Cover pages (Report/Academic route)** | `html2poster.js` | Covers are single-page fixed layouts with absolute positioning — same nature as posters. `html2pdf-next.js` would convert absolute→static and destroy the layout |331| **Multi-page documents, reports, academic papers, resumes** | `html2pdf-next.js` | A4/custom pagination, 20mm margin fallback, cover adaptation, pdf-lib metadata |332| **Creative pipeline (Blueprint → HTML → PDF)** | `html2pdf-next.js` via `convert.blueprint` | Called internally by design_engine pipeline |333334#### Poster / Single-Page Long-Image → `html2poster.js`335336```bash337node "$PDF_SKILL_DIR/scripts/html2poster.js" poster.html --output poster.pdf --width 720px338```339340`html2poster.js` automatically:341- Forces `overflow: hidden` on `.poster` / `.page` containers (clips decorative overflow)342- Injects `@page { margin: 0 }` (zero margins always)343- Syncs `html/body` background with poster background color344- Measures `.poster` scrollHeight and uses it as PDF height345- Generates a single-page vector PDF with exact content dimensions346347**Use this for ANY fixed-width, dynamic-height, single-page design.**348349#### Documents / Multi-Page → `html2pdf-next.js`350351```bash352node "$PDF_SKILL_DIR/scripts/html2pdf-next.js" input.html --output output.pdf --width 210mm --height 297mm353# Or via pdf.py wrapper:354python3 "$PDF_SKILL_DIR/scripts/pdf.py" convert.html input.html --output output.pdf355```356357Pre-render hooks auto-handle @page injection, overflow detection, cover adaptation, font loading, and pdf-lib metadata.358359#### ⚠️ Iron Rule: No Hand-Written Playwright Scripts360361Common issues with hand-written Python `page.pdf()` (the dedicated scripts handle these automatically):3621. **Missing `@page` rule** → browser default margin causes content overflow to second page or white edges3632. **Oversized elements not fixed** → large elements with `break-inside: avoid` block pagination, content gets truncated3643. **Rendering before fonts are loaded** → Chinese text displays as squares or falls back to wrong font3654. **No overflow detection** → content exceeds page boundary without awareness3665. **No metadata** → PDF title, author, and other info missing367368**Iron rule: Posters and cover pages use `html2poster.js`, multi-page documents use `html2pdf-next.js`. Do not write hand-written Python Playwright scripts.**369370> **⚠️ Cover page gotcha:** Cover HTML uses `position: absolute` for layout. `html2pdf-next.js` pre-render hooks convert absolute-positioned elements to `static` flow (to prevent multi-page overlap), which **destroys** cover layouts. Always use `html2poster.js` for cover pages.371372### No overflow:hidden on Fixed-Size Pages (html2pdf-next.js only)373374When using `html2pdf-next.js` for documents, **NEVER set `overflow: hidden` on `html`, `body`, or the main page container**.375376> **Note:** This rule does NOT apply to posters rendered via `html2poster.js` — that script automatically adds `overflow: hidden` to `.poster`/`.page` containers to clip decorative overflow. You don't need to add or remove it manually.377378| Problem | Cause | Fix |379|---------|-------|-----|380| Browser preview cuts off bottom content, can't scroll | `overflow: hidden` on container + viewport < design height | Remove `overflow: hidden` |381| html2pdf-next.js "Fixed vertical overflow" warning, layout may break | Pre-render detects `scrollHeight > clientHeight` + hidden overflow, force-expands container | Remove `overflow: hidden` |382383**Always pair fixed-size pages with `@media screen` auto-scale** so the full page is visible in any browser window without scrolling. See `briefs/creative.md` § 0.5 for the CSS pattern.384385### Full-Bleed Rule (No White Margins)386387When generating HTML for Playwright `page.pdf()`, the content **MUST fill the entire page** with zero margins. White side margins = broken layout.388389**Mandatory CSS for any HTML → PDF:**390```css391@page {392 size: <width> <height>; /* e.g., 720px 960px, or A4 */393 margin: 0;394}395html, body {396 margin: 0;397 padding: 0;398}399```400401**Common causes of white margins:**4021. Missing `@page { margin: 0 }` - browser default margins kick in (~1cm each side)4032. Content width doesn't match page width - e.g., canvas is 720px but page is A4 (794px)4043. Missing `@page { size }` declaration in the HTML4054. Content has explicit `max-width` that's narrower than the page406407**For blueprint pipeline:** `design_engine.py` now injects `@page { size: var(--canvas-w) var(--canvas-h); margin: 0; }` automatically.408**For raw HTML:** YOU must include the `@page` rule. No exceptions.409**For direct Playwright:** Pass `margin: { top: 0, right: 0, bottom: 0, left: 0 }` to `page.pdf()`.410411### Background Color Consistency (No Color Mismatch)412413**`html` / `body` background color must match the content canvas background color.**414415Playwright `page.pdf({ printBackground: true })` renders the body background color. If body is white while the content area is gray/colored, color-inconsistent borders/gaps will appear in the PDF.416417#### Single-color documents (all pages same background)418419```css420/* MANDATORY: body background = content background */421html, body {422 margin: 0;423 padding: 0;424 background: var(--c-bg); /* Same color as content canvas */425}426```427428#### Multi-page documents with mixed backgrounds (e.g. dark cover + white body pages)429430**Root cause:** Playwright resolves `.page { width: 210mm }` and `@page { size: 210mm }` to slightly different sub-pixel values (e.g. 793.688px vs 793.701px). This creates a <1px gap at the right/bottom edge of each `.page` div where `body`'s background shows through. On dark pages, a white `body` background makes this gap visible as a white edge.431432**Fix — set `body` background to the document's dominant dark color:**433434```css435:root {436 --primary: #0f172a; /* darkest page background */437}438html, body {439 margin: 0;440 padding: 0;441 width: 210mm; /* match @page size */442 background: var(--primary); /* fallback for sub-pixel gaps */443}444```445446**Why this works and doesn't break white pages:**447- Dark pages: sub-pixel gap reveals dark `body` → gap invisible.448- White pages: `.page-white { background: #ffffff }` fully covers `body` → dark body never visible.449- The gap is <1px — even on white pages, the dark body at the extreme pixel edge is imperceptible after anti-aliasing.450451**Rule: when generating multi-page HTML with mixed backgrounds, always set `html, body { background }` to the darkest page's background color.** If all pages are light/white, use the lightest content background (e.g. `#f8fafc`). Never leave `body` background unset (browser default = white = guaranteed white edges on dark pages).452```453454### Content Centering (No Left/Right Drift)455456**After HTML-to-PDF conversion, content must be centered, no left or right drift allowed.**457458Common drift causes:4591. `@page { margin }` not 0 — browser default margin causes drift4602. `.safe-zone` or content container `inset` / `padding` left-right asymmetric4613. Content container has `max-width` but no `margin: 0 auto`4624. Grid components only occupy partial column width (e.g. `1/1 → X/7` only uses left half)4635. **Decorative elements overflow page boundary** — elements with `width > 100%` or negative offsets (e.g. glow circles, gradient overlays) inflate `scrollWidth` beyond page width. Playwright shrinks all content to fit, causing left-shift. **Fix: add `overflow: hidden` to `.page` containers.** See `typesetting/overflow.md` §3.5 for horizontal flex overflow rules.464465### Anti-Void Edges (No Large Blank Margins)466467**Content should not have large meaningless whitespace at page edges, top, or bottom.**468469- Content should make full use of page area; do not cram all content in the top half while leaving the bottom blank470- For multi-page documents, each page's fill rate should be ≥ 60% (see `pagination.md` last page ≥ 40% rule)471- For single-page posters/infographics, fill rate should be ≥ 70%472473---474475## Preflight (Quality Assurance)476477Every PDF must pass preflight checks before delivery. Each brief specifies the exact commands.478479### HTML Pre-Render Validation (MANDATORY for ALL HTML→PDF paths)480481**Before** calling `html2pdf-next.js`, `html2poster.js`, `convert.blueprint`, or any Playwright `page.pdf()`, run:482483```bash484python3 "$PDF_SKILL_DIR/scripts/poster_validate.py" check-html <your_file>.html485```486487| Result | Action |488|--------|--------|489| **PASS** (no errors) | Proceed to PDF generation |490| **ERROR** items | Must fix before generating PDF. Use `--fix --output <file>.html` for auto-repair |491| **WARNING** items | Review; non-blocking but should be addressed |492493**Key checks:**494- `OVERFLOW_HIDDEN_CONTAINER` (error): `overflow:hidden` on html/body/.page clips content in browser preview and triggers html2pdf-next.js auto-fix that may break layout495- `FIXED_SIZE_NO_SCREEN_ADAPT` (warning): fixed-size page without `@media screen` auto-scale — browser preview requires scrolling496- `SCREEN_ADAPT_NO_SCALE` (warning): `@media screen` exists but lacks scale/transform/zoom497- `FONT_NO_FALLBACK` (error): font-family without generic fallback498- `COLOR_CONTRAST` (warning): text/background contrast ratio < 3:1499- Plus: remote images, absolute paths, missing margin reset, tiny fonts, background mismatch, etc.500501This applies to **all three HTML routes**: Creative blueprint pipeline, Report HTML covers, and bypass/custom HTML.502503### Overflow Prevention System504505**→ Full spec: `typesetting/overflow.md`** - read it for any document with tables, images, or multi-column layouts.506507Core principles:5081. **Measure first, draw second** - never render content without pre-calculating its dimensions5092. **Bounding Box constraint** - every element's width ≤ its parent container's `Max_Width`5103. **Text: use font metrics**, not character count, for width calculation5114. **Images: proportional scaling** - never insert at original size5125. **Tables: weight-based column width** + `Paragraph()` wrapping (never plain strings)5136. **Fallback ladder**: wrap → shrink font (max -3pt) → reduce padding → split element → log warning5147. **Vertical: KeepTogether** for heading+body, chart+caption; `repeatRows=1` for long tables515516### Table Overflow Prevention (ReportLab)517**Most common layout bug: table columns exceed page margins.**518519Before building any ReportLab Table:5201. Calculate `available_width = page_width - left_margin - right_margin`5212. Use proportional colWidths (`[0.25, 0.40, 0.20, 0.15]` × available_width) or fixed+flex pattern5223. `sum(colWidths)` must be ≤ `available_width` - **verify this in code**5234. Long text columns must use `Paragraph()` wrapping, not plain strings (plain strings don't wrap)5245. CJK text is wider: budget ~12pt per character at 10pt font size525526See `briefs/report.md` § "Table Width Management" for code patterns.527528### Table Overflow Prevention (LaTeX/Academic)529**Most common bug in dual-column papers: wide tables overflow single-column width.**530531Before writing any LaTeX table:5321. Count data columns - ≤ 4 fits single column; 5-6 needs `\small`; 7-8 needs `\resizebox`; ≥ 9 use `table*` (full width)5332. Use `tabular*{\columnwidth}` or `tabularx{\columnwidth}` instead of plain `tabular` for 5+ columns5343. Never use plain `tabular` with 8+ columns in twocolumn layout - guaranteed overflow5354. `\resizebox{\columnwidth}{!}` as last resort - verify smallest text ≥ 6pt after scaling536537See `briefs/academic.md` § "Table width management" for LaTeX patterns.538539### Playwright PDF CSS Blacklist540These CSS properties **silently break** in Playwright's PDF renderer:541- `backdrop-filter` / `-webkit-backdrop-filter` - **drops entire element content**. Use solid `rgba()` backgrounds.542- `overflow: hidden` on content containers - clips content. Only safe on small decorative elements (< 200px).543544After generating any Playwright PDF, **verify every page has content** (pypdf text extraction, check non-empty).545546### PDF Metadata (all briefs)547ALL PDFs must have: Title, Author (default "Z.ai"), Creator, Subject.548549### Delivery Summary (all briefs)550Report to user: file path, size, page count. Academic adds word/image count. Creative adds per-page verification.551552**HTML→PDF route deliverables (MANDATORY — applies to ALL briefs that use Playwright/HTML to generate PDF):**553Whenever the HTML→PDF pipeline is used (Creative route, Report cover bypass, Direct HTML Flow posters, or any Playwright `page.pdf()` path), you MUST deliver **both files** to the user:5541. **HTML** — the source HTML file, so the user can edit and reuse the design5552. **PDF** — the final vector PDF (`page.pdf()` output)556557Optionally also provide:5583. **Image** — a full-page screenshot/preview image (PNG or JPG) for quick sharing on chat/social media559560All file paths must be reported to the user. **Never deliver only the PDF without the HTML source.**561562---563564## Tooling Reference565566### CLI: `python3 "$PDF_SKILL_DIR/scripts/pdf.py" <command>`567568```bash569# Environment570env.check # Check deps571env.fix # Auto-install missing572573# Quality574code.sanitize <script> # Sanitize forbidden Unicode575content.sanitize <file> [--apply] # Fix content issues (CJK, encoding)576meta.brand <pdf> # Add Z.ai metadata577font.check <pdf> # Scan for missing glyphs578toc.check <pdf> # Validate TOC579580# Conversion581convert.blueprint <llm_json_response.md> -o final.pdf # CRITICAL FOR CREATIVE: Auto-extracts JSON, compiles, and renders PDF.582convert.html <html> # HTML → PDF (Playwright)583convert.latex <tex> # LaTeX → PDF (Tectonic). Bundled binary is macOS arm64 only; see academic.md for other-platform install.584convert.office <file> # Office → PDF (LibreOffice)585586# Processing587extract.text <pdf> # Extract text588extract.table <pdf> # Extract tables589extract.image <pdf> # Extract images590pages.merge a.pdf b.pdf -o out.pdf591pages.split <pdf>592pages.clean <pdf> # Remove blank pages593form.info <pdf> # Inspect form fields594form.fill <pdf> # Fill form595form.annotate <pdf> # Fill via annotations596meta.get <pdf>597meta.set <pdf> -o out.pdf -d '{"Title": "..."}'598```599600### Poster/HTML/LaTeX Validator: `python3 "$PDF_SKILL_DIR/scripts/poster_validate.py"`601```bash602check-html <html> # Pre-render validation (overflow:hidden, @media screen, fonts, contrast, etc.)603check-html <html> --fix --output <fixed.html> # Auto-fix errors (remove overflow:hidden, add font fallback)604check-pdf <pdf> --source-html <html> # Post-render validation605check-pdf <pdf> --poster # Poster mode: suppress ORPHAN_PAGE warning606check-tex <tex> # LaTeX source validation (table overflow, image width, etc.)607```608609**check-html checks include:**610- `OVERFLOW_HIDDEN_CONTAINER` (error): overflow:hidden on html/body/.page/.poster — clips content611- `FIXED_SIZE_NO_SCREEN_ADAPT` (warning): fixed-size page without @media screen auto-scale612- `SCREEN_ADAPT_NO_SCALE` (warning): @media screen exists but lacks scale/transform/zoom613- `FONT_NO_FALLBACK` (error): font-family without generic fallback (sans-serif/serif)614- `COLOR_CONTRAST` (warning): text/background contrast ratio < 3:1615- `BG_COLOR_MISMATCH` (warning): body background differs from .canvas/.poster background616- `SCREEN_BG_MISMATCH` (warning): @media screen html background differs from body/canvas background617- `MULTIPAGE_BODY_BG_MISSING` (warning): multi-page document with dark `.page` backgrounds but no `html/body` background color. Sub-pixel gaps at page edges reveal white body, causing visible white edges on dark pages. Resolves `var()` references via `:root` variables.618- `SCREEN_NO_BG` (warning): fixed-size page's @media screen block lacks html background color619- `OVERFLOW_DECORATION` (warning): negative position values may cause black edges620- `NO_PAGE_SIZE` / `MISSING_MARGIN_RESET` / `WHITE_BACKGROUND` / `TINY_FONT` / etc.621622**check-tex checks include:**623- `BARE_TABULAR_OVERFLOW` (error): `\begin{tabular}` with 5+ columns in two-column layout, not wrapped in resizebox/adjustbox/table*624- `RESIZEBOX_TEXTWIDTH` (error): `\resizebox{\textwidth}` used inside single-column float in two-column layout. `\textwidth` = full page width, but `table` float is one column. Fix: use `\resizebox{\columnwidth}` or `table*`625- `TABULAR_OVERFLOW_RISK` (warning): 4-column tabular in two-column layout without width constraint626- `TABULAR_WIDE` (warning): 7+ column tabular in single-column layout without width constraint627- `TABULAR_NO_FLOAT` (warning): tabular not inside table/table* float environment628- `TABULARX_NOT_LOADED` (warning): document has tabular but tabularx package not loaded629- `IMAGE_NO_WIDTH` (warning): `\includegraphics` without width/height/scale constraint630- `EQUATION_DUAL_ON_LINE` (warning): `equation` environment has 2+ equations joined by `\quad` without line breaks. Guaranteed overflow in dual-column631- `EQUATION_OVERFLOW_RISK` (warning): equation body has >80 math characters. Likely overflows single column632- `ALGORITHM_NO_SMALL_FONT` (warning): `algorithm` environment in dual-column without `\SetAlFnt{\small}`633- `ALGORITHM_LONG_IO` (warning): Algorithm Input/Output line >120 chars. Will overflow narrow column634- `CJK_ASCII_QUOTES` (error): ASCII `"` found adjacent to CJK characters. LaTeX interprets `"` as right double quote, so `"北漂"` renders incorrectly. Skips verbatim/lstlisting/minted environments and `\texttt{}`/`\url{}`/`\href{}{}`/`\verb||` inline commands.635636### Design Engine: `python3 "$PDF_SKILL_DIR/scripts/design_engine.py"`637```bash638compile --blueprint <json_file> --output poster.html # CRITICAL: Compile JSON blueprint to HTML639derive "document title or description" # Auto-derive intent from content640palette --intent calm --mode dark # Generate HSL-locked palette641palette-cascade --intent cold --mode minimal # Generate role-based cascade palette (V2, preferred)642svg --intent flow --dimensions 720x960 # Generate SVG background643full --intent energy --mode dark --dimensions 720x960 --output-dir ./assets/644audit --palette-json palette.json # Check palette constraints645```646647### Palette Ge648649…(truncated)