PDF Letter Size Guide: Dimensions & Code Fixes 2026
Your HTML looked correct in the browser. The PDF does not.
The first page breaks early, the footer jumps, a table row spills onto a second page, and someone on the team says the CSS must be wrong. Usually it isn't. The problem is simpler and more annoying. Your layout was designed for one paper standard, and the renderer produced another.
That mismatch hits fast in cross-platform apps. A frontend dev tests in a browser print preview, a backend job generates PDFs on Linux, and a customer opens the file on a North American printer. If the app assumes A4 somewhere in that chain while the requirement is US Letter, the output drifts just enough to break line wrapping, pagination, and printed margins.
The fix isn't guesswork. It's explicit page sizing, explicit margins, and knowing which flag each stack respects. If you're evaluating approaches before locking one in, this overview of PDF conversion software options is a useful companion. The rest of this guide stays focused on one thing: getting pdf Letter size right in real code.
Introduction The Common PDF Sizing Problem
A common failure pattern looks like this. You build an invoice, report, or form in HTML. The browser view is stable, the typography is fine, and the spacing feels done. Then your server-side conversion job produces a PDF with clipped content or a weird amount of empty space near the bottom.
That usually means the renderer used a different page box than your layout expected. The issue often goes unnoticed until the document reaches production because the browser viewport hides the issue. PDF engines don't. They paginate against a physical sheet size, and even a small difference changes where lines wrap and where blocks break.
What usually goes wrong
Three issues show up again and again:
- Implicit defaults: The rendering library picks a default paper size because you didn't set one.
- Conflicting instructions: CSS says one thing, runtime options say another.
- Print-stage scaling: The generated file is correct, but the desktop print dialog scales it anyway.
A broken PDF layout often isn't a bad template. It's a missing page-size declaration.
For North American workflows, the practical fix is to treat Letter as a first-class requirement from the start. Set it in CSS. Set it again in the rendering options if your library allows that. Then test with realistic margins and an actual print path, not just a browser tab.
Understanding PDF Letter Size vs A4
PDF Letter size is not just a label. It is a specific sheet dimension with specific pagination consequences. According to Drumlin Security's PDF page size reference, US Letter is 8.5 inches by 11 inches (216 mm × 279 mm), while A4 is 8.27 inches by 11.7 inches. Letter also has a fixed aspect ratio of approximately 1.294:1. That difference is why an HTML document tuned for A4 can overflow or truncate when rendered on Letter unless you explicitly set page-size: 8.5in x 11in or use the Letter keyword.

The dimensions that matter
For developers, these are the values worth keeping in your head:
| Unit | US Letter | A4 |
|---|---|---|
| Inches | 8.5 × 11 | 8.27 × 11.7 |
| Millimeters | 216 × 279 | 210 × 297 |
| Aspect ratio | approximately 1.294:1 | ISO-based ratio |
That doesn't look dramatic on paper. In layout terms, it is. A slightly narrower page with more height behaves differently from a slightly wider page with less height. A heading wraps one line earlier. A table row grows taller. A page break moves upward. That one shift cascades through the rest of the document.
Why browser-perfect HTML still breaks in PDF
HTML layouts often get built in responsive units and tested visually, not physically. A PDF engine has to commit to a real page box. Once it does, all the soft assumptions in the template become hard limits.
Typical symptoms of an A4 versus Letter mismatch include:
- Footer drift: The footer lands higher or lower than expected.
- Late-page clipping: The final lines on a page vanish into the margin or next page.
- Orphaned rows: One extra wrap pushes a row or paragraph onto the next sheet.
- Unexpected whitespace: Content ends early because pagination happened sooner than your browser preview suggested.
If the PDF is intended for print, resolution matters too. This guide on DPI for professional printing is worth a quick read because print clarity issues often get confused with page-size issues. They're separate problems, but they surface together in production.
For teams working across stacks, this is also why using a shared rendering pattern matters more than language choice. If your CSS expects Letter, every implementation should honor the same contract. This walkthrough of an HTML to PDF library selection approach is useful when you're standardizing that across services.
Declarative Control Using the CSS @page Rule
The most portable fix starts in CSS, not in app code. If the document is intended to print on Letter, declare that in the template itself.
Use @page so the HTML carries its own print instructions:

The two declarations that work
Named size:
@page {
size: Letter;
margin: 0.75in 0.375in 0.375in 0.75in;
}
Explicit dimensions:
@page {
size: 8.5in 11in;
margin: 0.75in 0.375in 0.375in 0.75in;
}
Both are valid. In practice, Letter is easier to scan, while 8.5in 11in makes intent obvious when someone audits the stylesheet later.
Why margins are not a cosmetic detail
The page size can be correct and the PDF can still be wrong because the margins are too aggressive. The FDA PDF specifications require a minimum left margin of 3/4 inch and other margins of at least 3/8 inch for US Letter documents in regulated workflows. With those margins applied to an 8.5" × 11" page, the usable region becomes 6.75" × 10.25", reducing effective canvas by approximately 20%. The same document also recommends 300 dpi for legibility and names 12-point Times New Roman as the recommended narrative font size for official documents.
That matters outside regulated documents too. If you build a form to the full page and then add realistic print margins later, the layout won't degrade gracefully. It will reflow.
Practical rule: Design against the printable area, not the full sheet.
A stable baseline stylesheet
This is a good default for document templates:
@page {
size: Letter;
margin: 0.75in 0.375in 0.375in 0.75in;
}
html, body {
margin: 0;
padding: 0;
}
body {
font-family: serif;
font-size: 12pt;
line-height: 1.4;
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
The key gotcha is duplication. If your runtime also injects margins, you can end up applying margins twice. Pick one source of truth for spacing. If the library respects CSS print rules consistently in your environment, keep it in CSS. If the runtime is more reliable, keep the CSS minimal and set margins there.
Generating Letter PDFs with JavaScript and Puppeteer
If you're using a headless browser stack, the most common bug is simple: you call page.pdf() with too few options and assume the CSS will carry everything. Sometimes it does. Sometimes it doesn't. For production, set the format explicitly.
The mistake
This generates a PDF, but it leaves page sizing up to defaults and renderer behavior:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(html, { waitUntil: 'networkidle0' });
await page.pdf({
path: 'output.pdf'
});
await browser.close();
If your template was designed for Letter, subtle breakage starts.
The fix that should be your default
Set the format and tell the renderer to honor print CSS:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(html, { waitUntil: 'networkidle0' });
await page.pdf({
path: 'output-letter.pdf',
format: 'Letter',
printBackground: true,
preferCSSPageSize: true,
margin: {
top: '0.375in',
right: '0.375in',
bottom: '0.375in',
left: '0.75in'
}
});
await browser.close();
This setup avoids the most common sources of drift:
format: 'Letter'forces the physical page box.preferCSSPageSize: truelets your@pagerule control size when present.printBackground: trueavoids missing background colors and subtle layout changes.- Explicit margins keep runtime behavior aligned with the template.
If you need a deeper implementation walkthrough, this guide to HTML to PDF in JavaScript with Puppeteer covers the broader setup.
When explicit width and height are better
Sometimes named formats are too indirect, especially when you want the code to mirror a design spec exactly. In that case, use dimensions:
await page.pdf({
path: 'output-letter-explicit.pdf',
width: '8.5in',
height: '11in',
printBackground: true,
preferCSSPageSize: false,
margin: {
top: '0.375in',
right: '0.375in',
bottom: '0.375in',
left: '0.75in'
}
});
Use one strategy consistently. If you set format, width, height, and a conflicting @page rule at the same time, you create ambiguity for the next developer and more debugging for yourself.
The gotchas that waste time
A few behaviors are worth watching closely:
- Headers and footers change available space: If you enable them, your content area shrinks. Re-test page breaks.
- Viewport size is not paper size:
page.setViewport()affects screen rendering, not the PDF sheet. - Fonts load later than you think: If the PDF renders before fonts are ready, text metrics change and page breaks move.
- Zero margins are risky: They look fine in a file viewer and fail during physical printing.
If the page count changes between local and production, check fonts and page-size settings before touching the HTML.
Code Examples for Python NET and Wkhtmltopdf
The fix is the same across stacks. Set Letter explicitly, then keep margins and print rules aligned with that choice. The syntax changes. The idea doesn't.
Python with a wrapper around wkhtmltopdf
If you're using a Python wrapper, the underlying engine usually accepts the same page-size flag as the command line version.
import pdfkit
options = {
'page-size': 'Letter',
'margin-top': '0.375in',
'margin-right': '0.375in',
'margin-bottom': '0.375in',
'margin-left': '0.75in',
'print-media-type': None
}
pdfkit.from_string(html, 'output-letter.pdf', options=options)
What matters here is not the wrapper itself. What matters is that you don't leave page-size implicit.
A related pattern shows up when developers generate text-heavy documents from lightweight markup. If your team already writes structured content in plain text formats, this article on why use Markdown for your resume is a good example of why markup-first authoring can simplify document generation workflows.
Python with a headless browser approach
If your Python stack mirrors a browser-rendering model, set the PDF format the same way you would elsewhere:
import asyncio
from pyppeteer import launch
async def generate_pdf(html):
browser = await launch()
page = await browser.newPage()
await page.setContent(html)
await page.pdf({
'path': 'output-letter.pdf',
'format': 'Letter',
'printBackground': True,
'preferCSSPageSize': True,
'margin': {
'top': '0.375in',
'right': '0.375in',
'bottom': '0.375in',
'left': '0.75in'
}
})
await browser.close()
asyncio.get_event_loop().run_until_complete(generate_pdf(html))
The same warning applies here. Don't mix runtime dimensions and CSS dimensions unless you know which layer wins in your environment.
.NET with a browser-driven renderer
In .NET, the stable path is the same. Pick Letter, set margins, keep print CSS enabled.
using System.Threading.Tasks;
using PuppeteerSharp;
public class PdfGenerator
{
public static async Task GenerateAsync(string html)
{
await new BrowserFetcher().DownloadAsync();
await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true
});
await using var page = await browser.NewPageAsync();
await page.SetContentAsync(html);
await page.PdfAsync("output-letter.pdf", new PdfOptions
{
Format = PaperFormat.Letter,
PrintBackground = true,
PreferCSSPageSize = true,
MarginOptions = new MarginOptions
{
Top = "0.375in",
Right = "0.375in",
Bottom = "0.375in",
Left = "0.75in"
}
});
}
}
What breaks most often in .NET isn't the page format flag. It's environment drift. One machine has the right browser binary and fonts. Another doesn't. The resulting PDF has the same page size but different line wrapping.
Raw wkhtmltopdf command line
When you want the lowest-level behavior exposed directly, use the command line:
wkhtmltopdf \
--page-size Letter \
--margin-top 0.375in \
--margin-right 0.375in \
--margin-bottom 0.375in \
--margin-left 0.75in \
input.html output-letter.pdf
That flag matters because many wrappers and app integrations eventually translate to this same underlying setting. If the wrapper behaves strangely, test the command directly. It gives you a cleaner debugging baseline.
For teams troubleshooting old deployments and command-line behavior, this wkhtmltopdf tutorial and troubleshooting guide is a practical reference.
A quick rule for all three stacks
Use this order of operations:
- Declare Letter in CSS when the template owns print layout.
- Set Letter in runtime options to avoid silent defaults.
- Keep margins consistent between CSS and code.
- Test with real fonts and real print output, not just a file preview.
That discipline removes most of the cross-language surprises.
Troubleshooting Common PDF Sizing Issues
When a PDF is the wrong size, the code is only one suspect. The renderer, the desktop print dialog, and the document structure can all sabotage an otherwise correct implementation.

Start with the failure mode, not the library
If the PDF looks wrong on screen, inspect generation settings first. If it looks correct on screen but prints wrong, inspect print settings first. Those are different bugs.
A useful checklist:
- Content clipped in the PDF: Check margins, header/footer settings, and whether the content exceeds the printable area.
- Everything looks slightly smaller on paper: Check for print scaling such as "fit" behavior.
- Page breaks change across environments: Check font loading and OS-level font availability.
- Only some pages break: Check elements with fixed heights, large tables, and unbreakable blocks.
The print dialog trap
One of the most frustrating cases is a correct Letter PDF that gets scaled during printing. The Notary Cafe discussion on Letter-size printing behavior states that 70% of users incorrectly print Letter-sized PDFs cropped or scaled because defaults like "Fit to Printable Area" override intended dimensions. In that same workflow, users of a common desktop PDF reader must set "Page Size and Handling" to "Actual Size" and enable "Choose paper source by PDF page size" to avoid automatic scaling.
That means support tickets often blame generation code for a print-dialog problem.
Don't assume the customer is printing the file at actual size just because the file itself is correct.
If your app delivers PDFs that will be printed by end users, add a short note near the download action. Tell them to print at actual size. It saves a surprising amount of confusion.
Mixed Letter and Legal pages
Mixed-size PDFs are another ugly edge case. When one document contains both Letter and Legal pages, mainstream desktop workflows often can't print them cleanly in one pass. The practical workaround is usually to split pages by size, print them separately, then recombine if needed.
If you're building form flows around generated PDFs, this matters for downstream operations too. Features such as page overlays and field placement get harder when page sizes vary. This guide on adding form fields to PDF files is relevant if your sizing problems show up during form assembly rather than initial rendering.
The Easiest Path to Perfect Letter PDFs with Transformy.io
Managing PDF generation in-house is doable. It also creates a maintenance trail widely underestimated. You own browser binaries, command-line dependencies, font behavior, OS quirks, and the gap between what the template says and what the runtime honors.
That gets worse when the app spans multiple stacks. One service generates invoices, another creates labels, and a third injects form data into an existing template. Each path needs the same paper rules, margin rules, and rendering behavior. Keeping those aligned takes work that users never see.

Why a managed API simplifies this
A managed conversion API removes most of the moving parts from your codebase. Instead of installing and tuning a renderer in every environment, you send HTML and ask for Letter output.
A typical request payload looks like this:
{
"html": "<html><body>...</body></html>",
"pdf": {
"size": "Letter",
"margin": {
"top": "0.375in",
"right": "0.375in",
"bottom": "0.375in",
"left": "0.75in"
},
"printBackground": true
}
}
That doesn't remove the need for a good template. It does remove a lot of infrastructure debugging.
Where this matters most
The biggest benefit is consistency. Your JavaScript app, Python worker, and .NET service don't each need their own rendering edge-case playbook. They can call the same endpoint and get the same paper sizing behavior.
There's also a structural reason to avoid overengineering around mixed-size print workflows. The Adobe community discussion about mixed Letter and Legal PDFs highlights a contrarian but useful point: the problem is systemic, not just a user mistake. In that workflow, mainstream tools can't process mixed sizes in a single print job, which forces manual workarounds. That means some PDF output problems shouldn't be "fixed" with more application logic. They should be avoided in the document design itself.
Keep each generated document to one page standard whenever you can. It prevents a whole class of downstream print failures.
If you want the shortest path to stable pdf Letter size output, use a service that accepts explicit size settings and keeps the rendering environment consistent. That's the practical appeal of Transformy.io: fewer environment-specific bugs, less renderer maintenance, and a simpler contract for generating Letter PDFs reliably.