PDF Optimization: Pdf.Optimize
Measure and shrink a PDF without changing how any page looks. audit reports where the bytes go and what each option would save; optimize recompresses, deduplicates, subsets and tidies, and reports what it did. Every option is lossless.
bool Optimize(string op, JsonObject jsonIn, JsonObject jsonOut)The operations
| op | What it does | jsonIn | jsonOut |
|---|---|---|---|
audit | Reports where the file's bytes go and what each option would save. Changes nothing; allowed on any PDF. | The same options as optimize (which savings to estimate) | Space by category, estimated savings per option |
optimize | Applies the selected options to the PDF in memory. The result is written by WriteFile or WriteBd. | Preset and switches | What was done and bytes saved, per option |
Most of the work (deduplicating, removing unused objects and resources, merging content streams, subsetting fonts, recompressing streams) is done in memory during the optimize call, so its savings are exact. Two options take effect when the file is written — objectStreams and dropOldRevisions — so their savings are estimates until the PDF is saved. JPEG, JPEG 2000, JBIG2 and CCITT image data are never re-encoded; their bytes pass through unchanged.
Quick start
An empty jsonIn applies the lossless preset: every option at its default.
Chilkat.Pdf pdf = new Chilkat.Pdf();
pdf.LoadFile("in.pdf");
Chilkat.JsonObject jsonIn = new Chilkat.JsonObject();
Chilkat.JsonObject jsonOut = new Chilkat.JsonObject();
if (pdf.Optimize("optimize", jsonIn, jsonOut)) {
pdf.WriteFile("out.pdf");
Console.WriteLine("Saved about " + jsonOut.IntOf("bytesSaved") + " bytes");
}
Audit first, optimize only if it is worth it
pdf.Optimize("audit", jsonIn, jsonOut);
int fileSize = jsonOut.IntOf("fileSize");
int potential = jsonOut.IntOf("estimatedSavings.total");
if (potential > fileSize / 10) { // more than 10%
pdf.Optimize("optimize", jsonIn, jsonOut);
pdf.WriteFile("out.pdf");
}
The options (jsonIn)
General
| Member | Values (default in bold) | Description |
|---|---|---|
preset | lossless | The only preset: every switch below at its default. Any switch can be set to override it. |
pages | all, or a range list such as 1,3,5-9,12- | Limits the page-level work (page content and resources) to these pages. Document-wide work always applies to the whole file. |
Streams
| Member | Default | What it does |
|---|---|---|
compressUncompressed | true | Flate-encodes streams stored with no filter. |
recompressFlate | true | Re-deflates Flate streams at compressionLevel; keeps the result only if it is smaller. |
replaceLegacyFilters | true | Re-encodes LZW, ASCIIHex, ASCII85 and RunLength streams as Flate. |
imagePredictors | true | Tries PNG predictors on uncompressed and Flate images; keeps the result only if it is smaller. |
compressionLevel | 9 | The Flate level, 1–9, used by the options above. |
Duplicates
| Member | Default | What it does |
|---|---|---|
dedupeStreams | true | Stores byte-identical streams with identical dictionaries once: images, embedded fonts, form XObjects, ICC profiles, page content. (Acrobat, for example, writes an identical watermark form on every page.) |
dedupeObjects | true | Stores identical non-stream objects (font dictionaries, color spaces, graphics states) once. |
mergeFontSubsets | false | Reserved for merging several subsets of the same font into one. Not implemented in this release; when set, it is reported in skipped. |
Unused content
| Member | Default | What it does |
|---|---|---|
removeUnreferenced | true | Drops objects not reachable from the document catalog. |
removeUnusedResources | true | Drops entries in page and form resource dictionaries that the content never uses. |
removeUnusedLayers | true | Drops optional-content layers that no content refers to, such as the empty "Watermark" layer Acrobat leaves behind after removing a watermark. |
Page content
| Member | Default | What it does |
|---|---|---|
coalesceContent | true | Merges a page's multiple content streams into one, so that they compress together. |
tidyContent | true | Removes redundant white space, empty q/Q pairs and graphics-state operators that change nothing. Numbers are never rounded; marked-content tags are never dropped. |
Fonts
| Member | Default | What it does |
|---|---|---|
subsetFonts | true | Reduces fully embedded TrueType/OpenType fonts (CIDFontType2 with a FontFile2 program) to the glyphs used. Fonts that form fields need for newly typed text are left whole, as are Type 1, CFF and already-subset fonts. |
compactType1Fonts | false | Reserved for converting embedded Type 1 fonts to CFF. Not implemented in this release; when set, it is reported in skipped. |
File structure
| Member | Values (default in bold) | What it does |
|---|---|---|
objectStreams | generate, preserve, disable | generate packs small objects into compressed object streams with a cross-reference stream (PDF 1.5; the version is raised to 1.5 if lower, never lowered). preserve generates them only if the file already had them. disable writes classic objects and a classic cross-reference table, for consumers that cannot read object streams. |
dropOldRevisions | true, false | Saves a single revision, discarding the superseded content kept by earlier incremental updates. Also a privacy gain: old revisions can hold deleted text. |
Example: override individual switches
jsonIn: for an old consumer that cannot read object streams
{
"objectStreams": "disable",
"dropOldRevisions": false
}jsonOut
{
"bytesSaved": 1181810,
"bytesSavedIsEstimate": false,
"actions": { "subsetFonts": { "count": 7, "bytesSaved": 942000 },
"dedupeStreams": { "count": 39, "bytesSaved": 211800 }, ... },
"skipped": []
}jsonIn: only the first chapter's pages, faster compression
{
"pages": "1-24",
"compressionLevel": 6,
"subsetFonts": false
}jsonOut
{
"bytesSaved": 318540,
"bytesSavedIsEstimate": true,
"actions": { "dedupeStreams": { "count": 12, "bytesSaved": 201300 },
"objectStreams": { "bytesSaved": 54300, "estimate": true }, ... },
"skipped": []
}The audit report (op = audit)
jsonIn
{ }jsonOut
{
"fileSize": 4831022,
"pdfVersion": "1.4",
"pdfa": "none",
"usage": {
"images": { "bytes": 3120455, "count": 41 },
"fonts": { "bytes": 1204310, "count": 12 },
"pageContent": { "bytes": 288112, "count": 60 },
"formXObjects": { "bytes": 61200, "count": 9 },
"annotations": { "bytes": 8400, "count": 22 },
"structureTree": { "bytes": 51020 },
"metadata": { "bytes": 6400 },
"embeddedFiles": { "bytes": 0, "count": 0 },
"oldRevisions": { "bytes": 18210 },
"overhead": { "bytes": 72915 }
},
"estimatedSavings": {
"total": 1290410,
"subsetFonts": 942000,
"dedupeStreams": 211800,
"objectStreams": 54300,
"recompressFlate": 41210,
"dropOldRevisions": 18210,
"removeUnreferenced": 12890,
"coalesceContent": 10000
},
"skipped": []
}| Member | Meaning |
|---|---|
fileSize | The file size in bytes. |
pdfVersion | The version in the file header. |
pdfa | The PDF/A part and conformance the file claims in its XMP metadata, such as 2b, or none. |
usage | Where the bytes go, as stored (compressed) in the file. The categories cover the whole file, including those the lossless options cannot touch (images, metadata), so that callers see where a lossy pass would help later. overhead is cross-reference data, object headers and everything not counted elsewhere. |
estimatedSavings | total, and the estimate for each option by its name. Options that would save nothing are omitted. |
skipped | Options that could not be applied (see below). |
The optimize report (op = optimize)
jsonIn
{ }jsonOut
{
"bytesSaved": 1236110,
"bytesSavedIsEstimate": true,
"actions": {
"subsetFonts": { "count": 7, "bytesSaved": 942000 },
"dedupeStreams": { "count": 39, "bytesSaved": 211800 },
"objectStreams": { "bytesSaved": 54300, "estimate": true },
"recompressFlate": { "count": 88, "bytesSaved": 41210 },
"dropOldRevisions": { "bytesSaved": 18210, "estimate": true },
"removeUnreferenced": { "count": 17, "bytesSaved": 12890 },
"coalesceContent": { "count": 12, "bytesSaved": 10000 }
},
"skipped": [
{ "option": "tidyContent", "pages": "12",
"reason": "content could not be parsed; left unchanged" }
]
}| Member | Meaning |
|---|---|
bytesSaved | The total bytes saved. |
bytesSavedIsEstimate | True while a save-time option (objectStreams, dropOldRevisions) contributes; the exact figure is the difference in file size after WriteFile. |
actions | Per option name: count (the objects affected, where that makes sense), bytesSaved, and estimate true for the save-time options. |
skipped[i] | Options that were requested but not applied: option, pages (when page-specific) and reason — page content that could not be parsed, a reserved option, objectStreams on a PDF/A-1 file, or a font the content analysis could not account for. |
Signed, encrypted, PDF/A and tagged PDFs
Optimizing always rewrites the whole file, so it follows these rules; audit changes nothing and is always allowed.
- Signed PDFs. With the
PreserveSignaturesproperty true (the default),optimizefails on a signed PDF. With it false, the PDF is optimized and its signatures are invalidated. - Encrypted PDFs. The PDF is saved with its original security unchanged; object streams are compatible with encryption. Because Chilkat does not ignore owner restrictions,
optimizeon an encrypted PDF requires owner rights or the modify permission (see Protection). - PDF/A. Chilkat reads the PDF/A claim from the XMP metadata and stays within that part's rules: for PDF/A-1,
objectStreamsis skipped and reported; for every part, the output intent and XMP metadata are kept and fonts stay embedded (subsetting is allowed).replaceLegacyFiltersalso removes LZW, which PDF/A forbids. - Forms. Fonts that form fields need to display newly typed text are not subset.
- Tagged PDF. The structure tree is never touched;
tidyContentkeeps every marked-content tag, anddedupeStreamsnever shares a page content stream that carries marked-content IDs between two pages.
Option reference
{
"preset": "lossless",
"pages": "all",
"compressUncompressed": true, "recompressFlate": true,
"replaceLegacyFilters": true, "imagePredictors": true,
"compressionLevel": 9,
"dedupeStreams": true, "dedupeObjects": true,
"mergeFontSubsets": false,
"removeUnreferenced": true, "removeUnusedResources": true,
"removeUnusedLayers": true,
"coalesceContent": true, "tidyContent": true,
"subsetFonts": true, "compactType1Fonts": false,
"objectStreams": "generate", // generate | preserve | disable
"dropOldRevisions": true
}
Reference documentation: Pdf.Optimize, Pdf.WriteFile. Related: ExtractText, TextReplace, Watermark, Protection, Pdf class overview.
New in Chilkat v11.7.0.