Invalid Date

Your HTML looks perfect in the browser, but the PDF output has broken layouts, split tables, and missing fonts. This is one of the most common frustrations when working with HTML to PDF conversion.

The good news: most of these issues have well-known CSS solutions. This guide covers the practical techniques you need when generating PDFs with FUNBREW PDF, Puppeteer, or any HTML to PDF tool.

CSS PDF Control — Three Pillars

Controlling PDF output with CSS relies on three core mechanisms:

CSS Feature Purpose Key Properties
@media print Separate screen and PDF styles display, font-size, color
@page Paper size, margins, headers/footers size, margin, @bottom-center
break-* / page-break-* Page break positioning break-inside, break-before, orphans

Combining these three gives you full control over PDF layout independent of the browser view. Let's explore each in detail.

Page Break Control

The most frequent PDF issue: content breaks at the wrong place.

Prevent Elements from Splitting

/* Keep tables and figures on one page */
table, figure, .card {
  break-inside: avoid;
  page-break-inside: avoid; /* Fallback for older engines */
}

/* Never break right after a heading */
h1, h2, h3 {
  break-after: avoid;
  page-break-after: avoid;
}

Force Page Breaks

/* Start a new page at section boundaries */
.page-break {
  break-before: page;
  page-break-before: always;
}

/* Each chapter starts on a new page */
.chapter {
  break-before: page;
}

/* Start on a right (odd) page — for booklet printing */
.chapter-odd {
  break-before: right;
}

/* Start on a left (even) page */
.chapter-even {
  break-before: left;
}

Prevent Orphans and Widows

Orphans are the first 1–2 lines of a paragraph left alone at the bottom of a page. Widows are the last few lines pushed to the top of the next page. Use these properties to prevent both.

p {
  /* Minimum lines to keep at the bottom of a page */
  orphans: 3;
  /* Minimum lines to keep at the top of the next page */
  widows: 3;
}

Practical Example: Invoice Footer

<div class="invoice-body">
  <table class="line-items">
    <thead><tr><th>Item</th><th>Amount</th></tr></thead>
    <tbody>{{line_items}}</tbody>
  </table>
</div>
<div class="invoice-footer" style="break-inside: avoid;">
  <!-- Keep total and payment info on the same page -->
  <p>Total: ${{total}}</p>
  <p>Wire to: Bank Account #1234567</p>
</div>

Combine this with the template engine for page-break-aware template design.

Using @media print in Depth

PDF engines typically render using the print media type. Use this to define PDF-specific styles.

Hiding Unwanted Elements

/* Visible on screen */
.sidebar, .navigation, .cookie-banner {
  display: block;
}

/* Hidden in PDF output */
@media print {
  .sidebar, .navigation, .cookie-banner, .no-print {
    display: none !important;
  }

  body {
    font-size: 11pt;
    line-height: 1.5;
  }

  /* Show link URLs in print */
  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 9pt;
    color: #6b7280;
  }
}

Optimizing Color and Contrast for PDF

Light colors that look fine on screen can become unreadable in PDF. Redefine your color palette inside @media print.

@media print {
  /* Reset backgrounds to white */
  body {
    background: #ffffff;
    color: #000000;
  }

  /* Strengthen borders */
  table, th, td {
    border-color: #000000 !important;
  }

  /* Darken light placeholder text */
  .text-gray-400 {
    color: #374151 !important;
  }

  /* Replace box shadows with borders */
  .shadow, .shadow-md, .shadow-lg {
    box-shadow: none !important;
    border: 1px solid #d1d5db;
  }
}

Defining Print-Only Layout Classes

/* Hidden on screen, visible only in PDF */
.print-only {
  display: none;
}

@media print {
  .print-only {
    display: block;
  }

  /* Two-column layout adjusted for PDF */
  .content-wrapper {
    display: flex;
    flex-direction: row;
    gap: 20mm;
  }

  .content-main {
    flex: 2;
  }

  .content-aside {
    flex: 1;
    border-left: 1pt solid #e2e8f0;
    padding-left: 10mm;
  }
}

Engine support for @media print varies. See wkhtmltopdf vs Chromium for details. FUNBREW PDF's quality engine (Chromium-based) has full support.

@page Rules for Paper Settings

/* A4 paper with margins */
@page {
  size: A4;
  margin: 20mm 15mm;
}

/* Larger top margin on first page (for cover) */
@page :first {
  margin-top: 60mm;
}

/* Landscape orientation */
@page {
  size: A4 landscape;
}

Mixing Portrait and Landscape with Named Pages

Use named pages to mix orientations within a single document.

@page wide-table {
  size: A4 landscape;
  margin: 15mm 10mm;
}

/* Apply landscape to a specific section */
.wide-section {
  page: wide-table;
}

Headers and Footers with @page Margin Boxes

The @page rule supports placing content in margin areas. Chromium-based engines (FUNBREW PDF's quality engine) fully support this feature.

@page {
  size: A4;
  margin: 25mm 20mm 20mm 20mm;

  /* Page number centered at the bottom */
  @bottom-center {
    content: counter(page) " / " counter(pages);
    font-size: 9pt;
    color: #6b7280;
  }

  /* Document name at the bottom left */
  @bottom-left {
    content: "Confidential: Project Proposal";
    font-size: 8pt;
    color: #9ca3af;
  }

  /* Date at the top right */
  @top-right {
    content: "April 2026";
    font-size: 9pt;
    color: #6b7280;
  }
}

/* Hide page number on the first (cover) page */
@page :first {
  @bottom-center { content: none; }
  @bottom-left { content: none; }
}

For dynamic header/footer content (such as per-chapter titles), use the API's headerHtml/footerHtml parameters instead. See the API docs for details.

Font Rendering Best Practices

Missing fonts in PDF output result in tofu characters (□) or fallback fonts that break your design.

Font Stack Priority

body {
  /* Recommended font stack for Latin text */
  font-family:
    'Inter',             /* Modern geometric sans-serif */
    'Noto Sans',         /* Google Fonts, broad language support */
    'Helvetica Neue',    /* macOS / iOS */
    'Arial',             /* Windows */
    sans-serif;
}

/* Serif for formal documents */
.serif {
  font-family:
    'Noto Serif',
    'Georgia',
    'Times New Roman',
    serif;
}

/* Monospace for code blocks */
code, pre {
  font-family:
    'Noto Sans Mono',
    'Source Code Pro',
    'Courier New',
    monospace;
}

CJK Font Support

For documents that include Chinese, Japanese, or Korean text, explicit font specification is critical.

/* Japanese text */
:lang(ja) {
  font-family: 'Noto Sans JP', 'Hiragino Sans', 'Yu Gothic', sans-serif;
}

/* Chinese (Simplified) */
:lang(zh-Hans) {
  font-family: 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
}

/* Korean */
:lang(ko) {
  font-family: 'Noto Sans KR', 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif;
}

FUNBREW PDF ships with Noto Sans JP pre-installed — no additional configuration needed for CJK text. Other tools may require manual font installation.

Explicit Font Weights

PDF rendering can drop bold styling if the bold variant is not explicitly loaded. Always declare font-weight explicitly.

h1, h2, h3 { font-weight: 700; }
p { font-weight: 400; }
strong, b { font-weight: 700; }
.light { font-weight: 300; }

Loading Web Fonts

/* Google Fonts — load only the weights you use */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=Noto+Sans+JP:wght@400;700&display=swap');

External font loading adds latency to PDF generation. For frequently generated PDFs, prefer pre-installed fonts. See the production guide for performance tips.

Table Handling

Repeating Table Headers Across Pages

thead {
  display: table-header-group;
}

tfoot {
  display: table-footer-group;
}

Preventing Row Splits

tr {
  break-inside: avoid;
  page-break-inside: avoid;
}

td {
  overflow-wrap: break-word;
  word-break: break-all;
}

Handling Multi-Group Tables

For long tables with logical groups (e.g., monthly subtotals in an invoice), split the table into multiple <tbody> elements and apply break-inside: avoid to each group.

<table>
  <thead>
    <tr><th>Month</th><th>Item</th><th>Amount</th></tr>
  </thead>
  <!-- Each month is a separate tbody — won't be split across pages -->
  <tbody style="break-inside: avoid;">
    <tr><td rowspan="2">January</td><td>Development</td><td>$5,000</td></tr>
    <tr><td>Hosting</td><td>$500</td></tr>
  </tbody>
  <tbody style="break-inside: avoid;">
    <tr><td rowspan="2">February</td><td>Development</td><td>$4,800</td></tr>
    <tr><td>Hosting</td><td>$500</td></tr>
  </tbody>
</table>

Table Styling for PDF

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 10pt;
}

th {
  background: #f8fafc;
  font-weight: 700;
  text-align: left;
  padding: 10px 12px;
  border-bottom: 2px solid #e2e8f0;
  /* Required to render background colors in PDF */
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

td {
  padding: 8px 12px;
  border-bottom: 1px solid #e2e8f0;
  vertical-align: top;
}

tr:nth-child(even) td {
  background: #f8fafc;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

/* Subtotal row emphasis */
tr.subtotal td {
  font-weight: 700;
  border-top: 2px solid #e2e8f0;
  border-bottom: 2px solid #e2e8f0;
  background: #eff6ff;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

Image Optimization CSS

Images in PDFs can overflow their containers or stretch unexpectedly without proper CSS.

Basic Image Containment

/* Prevent images from overflowing their containers */
img {
  max-width: 100%;
  height: auto;
}

/* Scale down oversized images in print */
@media print {
  img {
    max-width: 100% !important;
    page-break-inside: avoid;
  }
}

Page Break Control for Images

/* Full-page figures — place on their own page */
figure.full-page {
  break-before: page;
  break-after: page;
  text-align: center;
}

figure.full-page img {
  max-height: 240mm; /* A4 height minus margins */
  width: auto;
  max-width: 100%;
}

/* Caption-wrapped figures — keep together */
figure {
  break-inside: avoid;
  margin: 0 0 12pt 0;
}

figcaption {
  font-size: 9pt;
  color: #6b7280;
  text-align: center;
  margin-top: 4pt;
}

SVG and Icons

SVGs render crisply in PDF at any size. Prefer inline SVG over <img src="...svg"> for reliability.

/* Fixed-size icon */
.icon svg {
  width: 16px;
  height: 16px;
  vertical-align: middle;
  fill: currentColor;
}

/* Crisp rendering for high-DPI print */
@media print {
  .icon img {
    image-rendering: -webkit-optimize-contrast;
    image-rendering: crisp-edges;
  }
}

Handling Transparent PNG Logos

/* White background prevents transparent PNGs from showing unexpected colors */
.logo {
  background: #ffffff;
  padding: 4px;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

Background Colors and Images

Many PDF engines skip backgrounds by default.

/* Force background rendering (Chromium-based engines) */
* {
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

You may also need printBackground: true in your API request. FUNBREW PDF renders backgrounds by default.

Fixing Responsive Layout for PDF

Responsive HTML may render in mobile layout when converted to PDF.

@media print {
  .container {
    width: 100% !important;
    max-width: none !important;
  }

  /* Force desktop grid layout */
  .grid-2col {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
  }

  /* Prevent flex wrapping */
  .flex-row {
    display: flex;
    flex-direction: row !important;
    flex-wrap: nowrap !important;
  }
}

CSS Custom Properties in PDF

CSS custom properties (variables) are fully supported in Chromium-based PDF engines. Use them for consistent theming across your PDF templates.

:root {
  /* Brand colors */
  --color-primary: #2563eb;
  --color-accent: #f59e0b;

  /* Typography */
  --font-base: 'Inter', 'Noto Sans', sans-serif;
  --font-size-base: 10.5pt;
  --line-height-base: 1.7;

  /* Spacing — use pt for print */
  --spacing-sm: 6pt;
  --spacing-md: 12pt;
  --spacing-lg: 24pt;
}

body {
  font-family: var(--font-base);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
}

h1 {
  color: var(--color-primary);
  font-size: calc(var(--font-size-base) * 2);
  margin-bottom: var(--spacing-lg);
}

Flexbox and Grid in PDF Layout

Modern CSS layouts work in PDF, but with some caveats.

Flexbox Page Break Limitations

break-inside: avoid on flex items may be ignored by some engines. Wrap flex items in block elements for reliable page breaks.

.flex-container {
  display: flex;
  flex-wrap: wrap;
  gap: 12pt;
}

.flex-item-wrapper {
  break-inside: avoid;
  width: calc(50% - 6pt);
}

@media print {
  .flex-container {
    display: block; /* Switch to block when page breaks matter */
  }

  .flex-item-wrapper {
    display: inline-block;
    vertical-align: top;
  }
}

Fixed Grid Layouts for PDF

Grid layouts work well in PDF. Use fixed units (mm, pt) instead of fr for more predictable output.

@media print {
  .pdf-grid {
    display: grid;
    grid-template-columns: 60mm 1fr;
    gap: 8pt;
    width: 100%;
  }

  .pdf-sidebar {
    grid-column: 1;
    border-right: 0.5pt solid #d1d5db;
    padding-right: 8pt;
  }

  .pdf-main {
    grid-column: 2;
  }
}

Label–Value Grid Pattern

A common pattern in invoices and reports — labels on the left, values on the right:

.info-grid {
  display: grid;
  grid-template-columns: 80pt 1fr;
  row-gap: 4pt;
  font-size: 10pt;
}

.info-grid dt {
  font-weight: 700;
  color: #374151;
}

.info-grid dd {
  margin: 0;
}
<dl class="info-grid">
  <dt>Invoice Date</dt><dd>April 13, 2026</dd>
  <dt>Due Date</dt><dd>May 13, 2026</dd>
  <dt>Bill To</dt><dd>Acme Corporation</dd>
</dl>

Top 5 CSS Mistakes in HTML to PDF Conversion

Here are the most common CSS pitfalls developers hit when generating PDFs, and how to fix them.

Mistake 1: Not Using @media print for PDF-Only Styles

Applying styles globally instead of scoping them to @media print causes navigation bars, cookie banners, and other screen-only elements to leak into your PDF.

/* ❌ Bad: Affects both screen and print */
.sidebar { display: none; }

/* ✅ Good: PDF-only override */
@media print {
  .sidebar, .navigation, .footer-links {
    display: none !important;
  }
}

Why it matters: PDF engines render using the print media type. Styles outside @media print affect screen display too.

Mistake 2: Forgetting page-break-inside: avoid on Tables and Cards

Without explicit break control, tables and card components split at awkward positions across pages, making data unreadable.

/* ❌ Bad: No page break control */
.data-card {
  padding: 16px;
  border: 1px solid #e5e7eb;
}

/* ✅ Good: Prevent mid-element page breaks */
.data-card {
  padding: 16px;
  border: 1px solid #e5e7eb;
  break-inside: avoid;
  page-break-inside: avoid; /* Fallback for older engines */
}

Why it matters: break-inside: avoid is the modern standard; page-break-inside: avoid is the fallback for engines like wkhtmltopdf. Include both for maximum compatibility.

Mistake 3: Missing @page Rules — Margins Too Large or Too Small

Without @page rules, the engine applies its default margins (usually quite generous), shrinking your content area significantly.

/* ❌ Bad: No @page rule — engine-dependent defaults */

/* ✅ Good: Explicit paper size and margins */
@page {
  size: A4;
  margin: 20mm 15mm;
}

/* Wider top margin on cover page */
@page :first {
  margin-top: 50mm;
}

Why it matters: Use physical units like mm (millimeters) or pt (points) for print-accurate sizing.

Mistake 4: Backgrounds Disappearing Without print-color-adjust

Most PDF engines strip background colors by default. Your carefully designed colored tables and cards render as plain white.

/* ❌ Bad: Background won't render in PDF */
.highlight-row {
  background-color: #fef3c7;
}

/* ✅ Good: Force background rendering */
.highlight-row {
  background-color: #fef3c7;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

Why it matters: You can set * { print-color-adjust: exact; } globally, but for performance it's better to target only elements that use backgrounds. FUNBREW PDF renders backgrounds by default.

Mistake 5: Responsive CSS Triggering Mobile Layout in PDF

If the rendering viewport is narrow, media queries activate mobile layouts — single-column grids, hidden sidebars, and stacked elements.

/* ❌ Bad: Mobile layout triggers during PDF generation */
@media (max-width: 768px) {
  .grid-2col { grid-template-columns: 1fr; }
}

/* ✅ Good: Force desktop layout in print */
@media print {
  .grid-2col {
    display: grid !important;
    grid-template-columns: 1fr 1fr !important;
  }
  .container {
    width: 100% !important;
    max-width: none !important;
  }
}

Why it matters: Some engines let you set viewport width via API options. FUNBREW PDF supports viewport.width — see the API docs.

Debugging Tips

When your PDF layout doesn't match expectations:

  1. Browser print preview: In Chrome DevTools, Ctrl+Shift+P → "Emulate CSS media type" → "print"
  2. Playground: Paste HTML and instantly see PDF output
  3. Switch engines: If fast and quality produce different results, it's a CSS support difference. See the engine comparison
  4. Start small: Test complex layouts one page at a time
  5. Use pt units: In print CSS, pt (points) maps more predictably to physical size than px (1pt = 1/72 inch)

Common Issues and Fixes

Symptom Cause Fix
Tofu characters (□) Missing font Add fallback fonts or specify Noto Sans
Backgrounds not rendered Engine default Add print-color-adjust: exact
Tables split mid-row Missing break-inside Add break-inside: avoid to tr
Margins too wide No @page rule Add @page { margin: 20mm 15mm; }
Mobile layout in PDF Viewport width Set viewport width via API options

For more in-depth debugging, see the HTML to PDF troubleshooting guide.

Summary

Key CSS techniques for HTML to PDF conversion:

  • Page breaks: break-inside: avoid and break-before: page; use orphans/widows to prevent isolated lines
  • Print styles: @media print for PDF-specific CSS; optimize colors and contrast
  • Paper settings: @page rules for margins, size, headers, and footers
  • Fonts: Explicitly specify fonts, use pre-installed ones when possible; handle CJK separately
  • Tables: thead repetition, row split prevention, and multiple tbody for group control
  • Images: max-width: 100% and break-inside: avoid to prevent overflow and splits
  • Backgrounds: print-color-adjust: exact to force rendering

Test your CSS live in the Playground. For API integration in your language, see the quickstart guide.

Frequently Asked Questions

What is the difference between @media print and @page?

@media print is a CSS block that applies styles only during PDF rendering — use it to hide navigation bars, resize fonts, and separate screen from print layouts. @page is a CSS at-rule that configures the physical paper: size, margins, headers, and footers. They are complementary: @page rules are commonly used alongside @media print blocks.

Why is my page break CSS not working?

Three common causes:

  1. Using only the old page-break-inside / page-break-before properties instead of the modern break-inside / break-before — always include both for maximum compatibility.
  2. Applying break-inside: avoid directly to flex items, which some engines ignore — wrap flex items in block elements instead.
  3. Using an old engine like wkhtmltopdf that does not fully support break-* properties. FUNBREW PDF's quality engine (Chromium-based) has full support.

Why are background colors missing from my PDF?

Most PDF engines suppress background colors and images by default. Add the following to force rendering:

* {
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

FUNBREW PDF renders backgrounds by default — no additional configuration needed.

Why does Japanese text appear as boxes (tofu) in my PDF?

The rendering server doesn't have the required Japanese font installed. Specify font-family: 'Noto Sans JP', sans-serif; in your CSS and load it from Google Fonts, or embed it as Base64. FUNBREW PDF ships with Noto Sans JP pre-installed, so the font-family declaration alone is sufficient.

How do I add page numbers to a PDF with CSS?

Use the @page margin-box approach:

@page {
  @bottom-center {
    content: counter(page) " / " counter(pages);
    font-size: 9pt;
    color: #6b7280;
  }
}

Chromium-based engines support this natively. You can also use the FUNBREW PDF API's displayHeaderFooter and footerTemplate options for more dynamic control — see the API docs.

How do I prevent a table from splitting across pages?

Add break-inside: avoid; page-break-inside: avoid; to tr elements to stop rows from splitting mid-row. For logical groups (monthly subtotals, etc.), split the table into multiple <tbody> elements and apply break-inside: avoid to each group. Also set thead { display: table-header-group; } to repeat headers on every page.

Why does CSS behave differently between wkhtmltopdf and Chromium engines?

wkhtmltopdf is based on an older version of WebKit and has limited support for CSS Grid, modern Flexbox features, the break-* page break properties, and @page margin boxes. Chromium-based engines render CSS much more accurately and support all modern features. FUNBREW PDF's quality engine is Chromium-based. See the full engine comparison for details.


Related

Powered by FUNBREW PDF