HTML-first PDF production skill for reports, papers, and structured documents. Must be applied before generating PDF deliverables from HTML.
This skill governs authoring and converting HTML into print-quality PDF.
Primary output goals:
For HTML->PDF, use html_to_pdf only.
Forbidden:
Reason: image-stitch paths degrade text quality and create pagination discontinuity.
counter-reset, counter-increment, counter()).Classify the task before execution:
| Intent | Typical user request | Pipeline |
|---|---|---|
| Build | "写一份报告并导出 PDF" | build-pdf |
| Transform | "把这篇内容翻译后做成 PDF" | transform-pdf |
| Existing PDF ops | "提取/合并/拆分 PDF" | process-existing-pdf |
| LaTeX explicit | "请用 LaTeX/.tex/Tectonic" | latex-compile |
Clarification policy:
Important clarification behavior:
Recommended citation style for this skill: IEEE numeric.
Sample reference list:
[1] R. Patel and L. Chen, "A comparative study on model routing," Journal of Applied AI, vol. 8, no. 3, pp. 44-58, 2025.
[2] M. Rivera, Systems Design Handbook, 2nd ed. New York, NY, USA: Northbridge Press, 2024.
[3] T. Huang, "Model evaluation checklist," Research Notes, https://example.org/eval (accessed Feb. 14, 2026).
When transforming existing material (translation/rewrite/reformat), preserve source fidelity:
href.preserve_links=true.Use a three-pass check:
<img> tags in HTML| Pattern | Why unstable | Replacement |
|---|---|---|
| CSS content counters for numbering | pagination DOM shifts can break numbering | explicit labels in markup |
| Dynamic JS chart libraries at render-time | print pagination conflicts | pre-rendered static charts |
| Emoji/icon-heavy typography | Linux fallback inconsistency | plain text labels |
Chart image policy:
width > height) to reduce page-break artifacts./* Keep printable blocks inside page width */
pre, table, figure, img, svg, .diagram, blockquote, .eq-block {
max-inline-size: 100%;
box-sizing: border-box;
}
pre {
overflow-x: auto;
white-space: pre-wrap;
overflow-wrap: anywhere;
}
figure img, figure svg {
max-inline-size: 82%;
max-block-size: 42vh;
height: auto;
}
table { overflow-x: auto; }
.katex-display { overflow-x: auto; }
code { overflow-wrap: anywhere; }
a { overflow-wrap: anywhere; }
tr { break-inside: avoid; }
body {
text-align: justify;
text-align-last: start;
}
@page {
size: A4;
margin: 2.4cm 1.9cm;
@top-center { content: string(doc_title); }
@bottom-center { content: counter(page); }
}
@page :first {
@top-center { content: none; }
@bottom-center { content: none; }
}
@page titlepage {
@top-center { content: none; }
@bottom-center { content: none; }
}
@page contents {
@top-center { content: none; }
@bottom-center { content: none; }
}
body { string-set: doc_title ""; }
h1 { string-set: doc_title content(); }
.cover-page { page: titlepage; }
.toc-sheet { page: contents; }
Pagination notes:
break-inside: avoid only to compact units (single figure, single row, callout box).thead { display: table-header-group; } for multi-page table headers.Default target is print-academic, not dashboard aesthetics.
Avoid:
Prefer:
Type scale suggestion:
Full-bleed baseline:
*,
*::before,
*::after { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
}
@page :first {
margin: 0;
}
.cover-page {
inline-size: 210mm;
block-size: 297mm;
position: relative;
display: grid;
place-items: center;
overflow: hidden;
break-after: page;
}
Cover variants:
If using image background, do not use CSS background-image. Use absolute <img>:
<section class="cover-page">
<img class="cover-photo" src="cover.jpg" alt="">
<div class="cover-layer">...</div>
</section>
.cover-photo {
position: absolute;
inset: 0;
inline-size: 100%;
block-size: 100%;
object-fit: cover;
object-position: center;
z-index: 0;
}
.cover-layer {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 50%;
transform: translate(-50%, -50%);
z-index: 1;
}
Use explicit labels in markup, not CSS counters.
<figure id="arch-overview">
<img src="system-overview.png" alt="System overview">
<figcaption data-caption="Figure 1">Architecture Overview</figcaption>
</figure>
<table id="latency-table">
<caption data-caption="Table 1">Latency by Scenario</caption>
...
</table>
<div class="eq-block" data-eq="(1)">$$f(x)=x^2+1$$</div>
figcaption::before {
content: attr(data-caption) " ";
font-weight: 700;
}
caption::before {
content: attr(data-caption) " ";
font-weight: 700;
}
.eq-block::after {
content: attr(data-eq);
float: right;
}
Anchor placement rule:
id on the highest logical container (figure, table, section wrapper), not on inline caption text.TOC example with computed page numbers:
<nav class="toc-sheet" aria-label="Contents">
<ul class="toc-list">
<li><a href="#sec-intro">1 Intro</a></li>
<li><a href="#sec-method">2 Method</a></li>
</ul>
</nav>
.toc-list {
list-style: none;
margin: 0;
padding: 0;
}
.toc-list li { margin: 0.45em 0; }
.toc-list a {
display: flex;
gap: 0.5em;
color: inherit;
text-decoration: none;
}
.toc-list a::after {
margin-inline-start: auto;
content: target-counter(attr(href url), page);
}
Optional in-text page reference:
a.page-ref::after {
content: " (p." target-counter(attr(href url), page) ")";
opacity: 0.72;
font-size: 0.86em;
}
Definition block:
.definition {
border-inline-start: 3px solid #475569;
padding-inline-start: 1rem;
margin: 1rem 0;
}
.definition-title { font-weight: 700; }
.definition-body { font-style: italic; }
Procedure block:
.procedure {
border: 1px solid #cbd5e1;
padding: 0.75rem;
background: #f8fafc;
}
Fixed-size centered badges must use flexbox:
.badge-index {
inline-size: 1.7em;
block-size: 1.7em;
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid #cbd5e1;
border-radius: 999px;
}
Reference anchor consistency:
<a href="#ref-n">[n]</a> must map to one <li id="ref-n">.a.citation {
color: #1f2937;
text-decoration: none;
vertical-align: super;
font-size: 0.78em;
}
.ref-list li {
padding-inline-start: 2em;
text-indent: -2em;
}
Paged footnote pattern:
.fn {
float: footnote;
}
.fn::footnote-call {
content: counter(footnote);
vertical-align: super;
font-size: 0.78em;
}
.fn::footnote-marker {
content: counter(footnote) ". ";
}
@page {
@footnote {
margin-top: 1.1em;
border-top: 1px solid #d1d5db;
padding-top: 0.55em;
}
}
When page count or layout does not meet targets, adjust in this priority order:
| Symptom | First move | Second move | Avoid |
|---|---|---|---|
| Page count exceeds target | reduce heading sizes | reduce line-height slightly | aggressive body font shrink |
| Page count below target | increase line-height | increase page margins slightly | adding low-value filler text |
| Table overflows page width | reduce cell padding | allow word wrapping on long tokens | forcing fixed table widths |
| Figure breaks layout | reduce figure max-width/max-height | move figure near paragraph boundary | avoid on large parent containers |
| Text looks cramped | raise line-height | increase side margins slightly | oversized heading jumps |
| Resume too sparse/dense | tune margins first | then adjust heading scale | changing section order silently |
Run all gates before final delivery.
href.preserve_links=true.<img> count and mappingid on top-level containers (figure, section wrapper), not inner text nodes.target-counter).ZIP package — ready to use