How to Convert Two-Column HTML to PDF
Convert two-column HTML to PDF with print CSS and browser rendering. Learn how to handle flowing text columns, side-by-side panels, page breaks, and common rendering issues.

To convert two-column HTML to PDF, define how the layout should behave on paper, set the page size and margins, then render the page with a browser or HTML-to-PDF engine. The key first question is what “two-column” means in your document:
- Flowing text columns: content runs down one column and continues in the next, as with a newspaper. Use CSS multicolumn properties such as
column-countandcolumn-gap. - Independent panels: two blocks sit beside each other, often built with grid or flexbox. Set their print widths and decide how they should continue onto later pages.
A PDF renderer commonly uses print styles, even when the browser view uses screen styles. That can change widths, colors, visibility, and pagination. Inspect the generated PDF at its intended paper size rather than assuming a browser screenshot predicts the print result.
1. Identify the kind of two-column layout
Start by checking the HTML and CSS, not by changing PDF settings. If one long text block has column-count: 2, you have flowing columns. If two separate elements are arranged with display: grid or display: flex, you have side-by-side panels. These layouts paginate differently.

Flowing text columns
CSS multicolumn layout distributes content across columns within a container. A basic layout might look like this:
<article class="article-columns">
<h1>A two-column article</h1>
<p>First paragraph of the article...</p>
<p>More content continues here...</p>
</article>
.article-columns {
column-count: 2;
column-gap: 24px;
column-rule: 1px solid #ddd;
}
@media print {
.article-columns {
column-count: 2;
column-gap: 8mm;
}
}
This is a starting point, not a guarantee that every block will split where you expect. Multicolumn fragmentation and pagination can interact, especially with constrained heights, spanning elements, and forced breaks. WeasyPrint documents support for simple multicolumn layouts and also describes limitations; check its documentation against the CSS you use.
Independent side-by-side panels
For two separate panels, make the print layout explicit. For example:
<main class="comparison">
<section><h2>Panel A</h2><p>Content...</p></section>
<section><h2>Panel B</h2><p>Content...</p></section>
</main>
.comparison {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 20px;
}
@media print {
.comparison {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 8mm;
}
.comparison > section {
min-width: 0;
}
}
Check that both panels fit in the printable width after page margins. For long panels, determine whether they may split across pages or whether each should begin on a new page. A renderer’s PDF API does not promise identical behavior for every grid, flexbox, or positioning combination, so validate your own content.
2. Add print rules and choose page dimensions
Use @media print to tailor the document for printing, and @page to describe page size and margins. Keep the screen layout separate where it needs different widths or visibility.

@page {
size: A4 portrait;
margin: 15mm;
}
@media print {
html, body {
margin: 0;
}
.screen-only {
display: none !important;
}
h1, h2, h3 {
break-after: avoid;
}
.keep-together {
break-inside: avoid;
}
* {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
}
Choose the paper size your readers need, such as A4 or Letter, rather than relying on a renderer default. Set margins intentionally: they reduce the area available to the columns. Long words, wide tables, and fixed-width elements can overflow a narrow column, so consider max-width: 100%, wrapping rules, or a different print arrangement.
Print output may alter colors unless color adjustment is specified, and the user’s print settings can also affect backgrounds. Browser print dialogs can add headers or footers in available page margin space; check those settings when using manual printing. Chrome’s paged-media documentation describes page margin boxes, including generated content support added in Chrome 131.
3. Generate the PDF with Playwright
Playwright’s PDF operation uses print CSS by default. The following Node.js example loads a local HTML file and writes a PDF with explicit paper size, margins, and background printing. Install Playwright and its browser as described in the official [Playwright documentation](https://playwright.dev/docs/intro).
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
try {
const page = await browser.newPage();
await page.goto('file:///absolute/path/to/document.html', {
waitUntil: 'networkidle'
});
await page.pdf({
path: 'document.pdf',
format: 'A4',
printBackground: true,
preferCSSPageSize: true,
margin: {
top: '15mm',
right: '15mm',
bottom: '15mm',
left: '15mm'
}
});
} finally {
await browser.close();
}
})();
Replace the example file URL with your document’s absolute path or a page URL. If the page depends on authentication, custom headers, or JavaScript that loads data after navigation, make those inputs available and wait for the relevant content before calling page.pdf(). Do not assume that networkidle means every application is finished; a page with ongoing network requests may never reach it, while delayed client-side work may need a specific readiness signal.
The format, margin, printBackground, and preferCSSPageSize options control common output details. Playwright documents Letter as its default format. When your CSS @page size should govern, use preferCSSPageSize: true; otherwise align the API format and CSS deliberately. Check the installed Playwright version’s API docs because options can change over time.
4. Generate the PDF with Puppeteer
Puppeteer also generates PDF output using print CSS by default. This example follows the same pattern:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('file:///absolute/path/to/document.html', {
waitUntil: 'networkidle0'
});
await page.pdf({
path: 'document.pdf',
format: 'A4',
printBackground: true,
preferCSSPageSize: true,
margin: {
top: '15mm',
right: '15mm',
bottom: '15mm',
left: '15mm'
}
});
} finally {
await browser.close();
}
})();
If exact color reproduction matters, Puppeteer documents using -webkit-print-color-adjust to request exact colors. This setting and background printing do not remove the need to inspect the final file: renderer versions, CSS, and user expectations all affect what counts as correct.
5. Use screen styling only when that is the goal
Sometimes you want a PDF that resembles the screen view, not a print-specific layout. In that case, explicitly emulate screen media before generating the PDF. For Playwright:
await page.emulateMedia({ media: 'screen' });
await page.pdf({
path: 'screen-layout.pdf',
format: 'A4',
printBackground: true
});
Use this intentionally. If print CSS is supposed to simplify navigation, expand content, or make the columns fit the page, switching to screen media may bring back screen-only elements or widths that do not fit paper.
6. Convert manually from a browser
For a one-off PDF, open the page in a browser and choose Print or Save as PDF. Before saving, check the destination, paper size, margins, scaling, background graphics, and headers or footers. The same HTML can produce different PDFs under different print-dialog settings, so record the settings if the result needs to be repeatable.
7. Consider WeasyPrint for document generation
WeasyPrint is an HTML/CSS-to-PDF option for workflows that fit its paged-media support. Its documentation covers PDF hyperlinks, bookmarks, attachments, forms, page rules, and simple multicolumn layouts. Review its documented limitations against the styles in your document before choosing it, especially if you rely on constrained-height columns, spanning content, unusual column breaks, or complex pagination. No renderer is a universal best choice for every stylesheet.
Or skip the browser setup
If you need a screenshot of a web page rather than a paginated document PDF, ScreenshotNeo returns a PNG, JPEG, WebP, or PDF from one GET request. Its PDF options include paper size, margins, landscape orientation, and page ranges. It is a screenshot API, so it is not a substitute for validating a document’s two-column pagination.
One call in cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Equivalent Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
Equivalent Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the ScreenshotNeo API documentation for request options. Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The Free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Every feature is on every plan.
Sign up for ScreenshotNeo and get 1,000 free screenshots a month, no card required.
8. Inspect and troubleshoot the PDF
Open the actual PDF at the intended paper size and review every page. Check both column boundaries, page breaks, fonts, images, backgrounds, and any element that should be hidden or retained.
| Symptom | Likely cause | What to try |
|---|---|---|
| PDF looks unlike the browser | PDF generation uses print media by default, or print rules change layout. | Decide whether print or screen appearance is intended. Adjust @media print, or explicitly emulate screen media for a screen-style PDF. |
| One column is missing or squeezed | Paper width minus margins is too small for both columns, gaps, and borders. | Reduce gaps or font size, choose landscape or a larger paper size, or stack panels in print CSS. |
| Content is cut off | Fixed widths, non-wrapping content, or constrained column heights exceed the printable area. | Remove unnecessary fixed heights, allow long text to wrap, constrain media to the column width, and inspect page breaks. |
| Columns break at awkward places | Column fragmentation and page pagination interact; a heading or block may be split. | Try break rules such as break-inside: avoid on small blocks, then inspect for large elements that cannot fit intact. |
| Backgrounds or colors differ | Background printing is disabled or print color adjustment changes output. | Enable background printing in the API or dialog and set print color adjustment where supported. |
| Fonts or images are missing | Assets have not loaded, paths are inaccessible, or the PDF was generated too early. | Use reachable asset URLs, wait for required resources or a page-specific ready condition, and check browser logs. |
| PDF has unwanted header/footer text | Browser print settings add page headers or footers. | Disable them in the print dialog or use the renderer’s PDF options where available. |
9. Performance, reliability, and cost
Local browser rendering gives you control over the browser and output settings, but requires installing and maintaining the rendering engine and ensuring that the HTML, fonts, and linked assets are available. For repeatable jobs, pin the renderer version and use a clear readiness condition so changes in dependencies or page timing are easier to diagnose.
Large images, many pages, heavy client-side scripts, and waiting for a quiet network can increase conversion time. Keep the document’s assets local or reliably reachable, avoid waiting indefinitely for background requests, and set an operational timeout around the conversion in your application. Reuse a browser process for batches when your deployment model supports it, while isolating individual pages and closing resources cleanly.
Cost depends on the path: browser automation has infrastructure and maintenance costs; hosted conversion services may charge per request or usage, but compare their current terms directly. The sources here do not establish a fair performance ranking across renderers. Test representative documents and page counts before estimating throughput or cost.
10. Frequently asked questions
Should I use CSS columns or a two-column grid?
Use CSS multicolumn layout when one continuous text flow should move from column to column. Use grid or flexbox when the columns are separate panels with independent content.
Why does my two-column PDF have different breaks than the web page?
PDF generation paginates content for a paper size, commonly using print media. The available width and page boundaries differ from the browser viewport, so inspect and tune print rules.
Can I make the PDF look exactly like the screen?
You can request screen media in browser automation, but page dimensions still impose pagination. Exact output depends on the page CSS, renderer, and assets; review the generated file.
Which renderer should I choose?
Choose based on the CSS features your document uses, JavaScript needs, page controls, and the output you observe. Puppeteer and Playwright provide browser rendering; WeasyPrint may suit document-focused paged-media workflows. Validate with representative files.


