A PDF that should be 200 KB often ends up being 5 MB. Oversized PDFs slow down downloads, fill up inboxes, and frustrate users. If your application generates PDFs at scale — invoices, reports, certificates, or catalogs — file size optimization directly impacts storage costs, bandwidth, and user experience.
What Makes PDFs Large?
PDF file size is driven by three main factors:
- Images — By far the biggest contributor. A single uncompressed PNG can add megabytes to a document.
- Fonts — Embedding full font families (every glyph in every weight) inflates the file unnecessarily.
- Metadata and structure — Excessive embedded metadata, JavaScript, form fields, and annotation layers add overhead.
Image Optimization
Images typically account for 80-95% of PDF file size. Here are the key strategies:
Choose the Right Format
- JPEG — Best for photographs and complex images with many colors. Use quality 70-85 for good balance.
- PNG — Best for screenshots, diagrams, and images with text or sharp edges. Use PNG-8 when possible.
- WebP — Chromium-based PDF renderers can process WebP images, which are 25-35% smaller than JPEG at equivalent quality.
- SVG — For icons, logos, and illustrations, SVG produces the smallest output because it is vector-based.
Resize Before Rendering
Do not send a 4000x3000 pixel image when it will be displayed at 400x300. Resize images to their display dimensions before including them in the HTML:
const sharp = require('sharp');
async function optimizeImage(inputPath, maxWidth = 800) {
const metadata = await sharp(inputPath).metadata();
if (metadata.width <= maxWidth) {
return sharp(inputPath).jpeg({ quality: 80 }).toBuffer();
}
return sharp(inputPath)
.resize(maxWidth)
.jpeg({ quality: 80, progressive: true })
.toBuffer();
}
// Convert to base64 data URI for embedding in HTML
const optimized = await optimizeImage('chart.png', 600);
const dataUri = `data:image/jpeg;base64,${optimized.toString('base64')}`;
Use CSS for Decorative Elements
Gradients, borders, shadows, and simple shapes should be CSS, not images:
<!-- Instead of a gradient image -->
<div style="background: linear-gradient(135deg, #667eea, #764ba2); height: 100px;"></div>
<!-- Instead of a divider image -->
<hr style="border: none; border-top: 2px solid #e2e8f0; margin: 20px 0;">
Font Optimization
Limit Font Variants
Each font weight and style is a separate file. Load only what you use:
<!-- Bad: loading 6 weights -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900" rel="stylesheet">
<!-- Good: only the weights you actually use -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700" rel="stylesheet">
Use System Fonts When Possible
For internal documents that do not need custom branding, system font stacks eliminate font embedding entirely:
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
Subset Custom Fonts
If you must use a custom font, subset it to include only the characters you need. Tools like glyphhanger can analyze your HTML and generate a minimal font file:
npx glyphhanger --whitelist="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,!?@#$%&*()-+= " --subset=CustomFont.woff2
CSS Optimization for Print
Remove visual elements that only matter on screen:
@media print {
/* Hide navigation, sidebars, and interactive elements */
nav, .sidebar, .no-print, button, .tooltip { display: none !important; }
/* Remove shadows and transitions (they add rendering complexity) */
* { box-shadow: none !important; text-shadow: none !important; transition: none !important; }
/* Use solid backgrounds instead of gradients where possible */
.header { background: #1a1a2e !important; }
/* Avoid background images in print */
.hero { background-image: none !important; }
}
TongoRender Quality Settings
TongoRender offers several options that affect output size:
const response = await fetch('https://api.tongorender.io/v1/pdf', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.TONGORENDER_API_KEY,
},
body: JSON.stringify({
html: optimizedHtml,
format: 'A4',
// Reduce quality for smaller files
printBackground: false, // Skip background colors/images if not needed
preferCSSPageSize: false, // Use standard page size
scale: 1, // Don't upscale (scale > 1 increases size)
}),
});
Measuring Results
Track your optimization progress by measuring file sizes before and after each technique:
const fs = require('fs');
function formatSize(bytes) {
if (bytes < 1024) return bytes + ' B';
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
return (bytes / 1048576).toFixed(1) + ' MB';
}
const pdf = await generatePDF(html);
console.log(`PDF size: ${formatSize(pdf.length)}`);
// Compare with unoptimized version
const unoptimized = await generatePDF(originalHtml);
const savings = ((1 - pdf.length / unoptimized.length) * 100).toFixed(1);
console.log(`Size reduction: ${savings}%`);
Quick Reference Checklist
- Resize images to display dimensions before rendering
- Use JPEG (quality 75-85) for photos, SVG for icons
- Load only the font weights you actually use
- Consider system fonts for internal documents
- Use CSS instead of images for gradients, borders, and shapes
- Add
@media printrules to hide screen-only elements - Set
printBackground: falsewhen backgrounds are not needed - Measure file sizes and set target budgets
With these optimizations, most documents can be reduced by 50-80% without visible quality loss. TongoRender renders optimized HTML efficiently, so the effort you put into template optimization translates directly into smaller PDFs.
Generate optimized PDFs with TongoRender — 100 free renders per month, no credit card required.