Skip to content

8. `fix::` namespace — normalization

19 operations that modify the PDF and save a new file. The original is never touched.


fix:: is the only namespace that writes, so it runs under its own command:

Terminal window
pdfl fix input.pdf script.pdfl --output fixed.pdf

Options:

Option What it does
--output <file> Output PDF (required)
--dry-run Lists the operations without saving anything
--report json|csv|html|pdf Report format
--report-file <file> Writes the report to a file

Under pdfl run, any fix:: call raises an error pointing to the right command — so nobody applies corrections while believing they are only validating.

// This script needs no checks: these are commands, executed in order.
fix::set_trim_box(8.5, 8.5, 586.5, 833.5)
fix::add_page_numbers()
fix::add_watermark("DRAFT")

Every call is validated at call time (nonexistent page, invalid rotation, missing file) and only then applied. The report carries a fixes field with what was done:

"fixes": [
"TrimBox set to [8.5, 8.5, 586.5, 833.5]",
"page numbering added",
"watermark \"DRAFT\" added"
]

Nothing stops you from mixing validation and correction in the same script:

// Validate before fixing — a failed precondition shows up in the report
check "Preconditions" {
require doc.page_count > 0
assert !struct::check_encryption(), "file is encrypted, cannot fix it"
}
fix::add_page_numbers()

Sets the MediaBox on every page.

// A4 in points — or use units and let the language convert
fix::set_page_size(595, 842)
fix::set_page_size(210mm, 297mm) // identical, and more readable

fix::set_crop_box(x0, y0, x1, y1), set_trim_box, set_bleed_box

Section titled “fix::set_crop_box(x0, y0, x1, y1), set_trim_box, set_bleed_box”

Set the matching box on every page. Coordinates in points, from the bottom-left to the top-right corner.

// The file arrived from the publisher without production boxes:
// TrimBox = final area; BleedBox = with 3 mm of bleed around it
fix::set_trim_box(8.5, 8.5, 586.5, 833.5)
fix::set_bleed_box(0, 0, 595, 842)

Rotates by 90, 180 or 270 degrees. Without a page number, rotates all of them.

fix::rotate_page(90) // every page
fix::rotate_page(3, 180) // page 3 only

fix::delete_page(n) and fix::duplicate_page(n)

Section titled “fix::delete_page(n) and fix::duplicate_page(n)”
fix::delete_page(1) // drop the draft cover
fix::duplicate_page(1) // duplicate the cover (the copy goes right after)

Deleting the only page in a document is refused with a clear message.

New page order. The list must use every page exactly once.

// A 4-page document with the cover at the end: bring it to the front
fix::reorder_pages([4, 1, 2, 3])

fix::split_document(from, to, "output.pdf")

Section titled “fix::split_document(from, to, "output.pdf")”

Saves a page range to another file. The document being edited stays intact.

// Separate cover from body for different suppliers
fix::split_document(1, 2, "cover.pdf")
fix::split_document(3, 50, "body.pdf")

Appends the pages of another PDF at the end.

fix::merge_documents("attachments/warranty.pdf")
fix::merge_documents("attachments/size_chart.pdf")

Diagonal grey watermark on every page.

fix::add_watermark("DRAFT — DO NOT PRINT")

Red stamp in the top-right corner of each page.

fix::add_stamps("APPROVED 2026-08-02")

n / total numbering in the footer of every page.

fix::add_page_numbers()

fix::remove_annotations() and fix::remove_attachments()

Section titled “fix::remove_annotations() and fix::remove_attachments()”

Remove annotations (comments, review markup) and attached files.

// Before sending to the print shop: review comments must not show up,
// and attachments only inflate the file
fix::remove_annotations()
fix::remove_attachments()

Removes the optional content (OCG) structure, leaving all content permanently visible.

// Layers with "English version" turned off can be re-enabled by mistake
// at the shop — flattening removes the risk
fix::flatten_layers()

The operations in this section only write when the file gets smaller. If rewriting produces a larger file, the original is kept.

Discards objects unreachable from the trailer.

fix::remove_unused_resources()

Resamples images above the target DPI (default 300). DPI is computed from the actual printed size of the image on the page.

// An e-mail approval copy does not need 300 DPI
fix::downsample_images(96)
// A digital-print version
fix::downsample_images(200)

CMYK images are preserved. Resampling them would require converting to RGB, which would destroy the prepress separations. In print-shop files, the savings come from the RGB images.

Re-encodes images as JPEG at the given quality (1 to 100, default 85).

fix::compress_images(70)

subset_fonts and linearize_document do not exist as fix:: operations and raise an unknown-function error:

  • subset_fonts: it was implemented and measured. Professional producers already embed only the glyphs in use, so the measured gain was 0.5% at best and nil elsewhere — not worth the risk of corrupting fonts. To check whether fonts are subset, use prepress::subset_fonts().
  • linearize_document: it requires generating hint tables (§7.14 of the PDF specification). No Rust library does this, and a partial implementation would not be recognized as “Fast Web View” by readers.

Preparing a publisher’s file for the print shop

Section titled “Preparing a publisher’s file for the print shop”
prepare_for_print.pdfl
// Usage: pdfl fix publisher.pdf prepare_for_print.pdfl --output print.pdf
check "Preconditions" {
require doc.page_count > 0
assert !struct::check_encryption(),
"file is encrypted — ask the publisher for the open version"
}
// Production boxes the publisher never defined
fix::set_trim_box(8.5, 8.5, 586.5, 833.5)
fix::set_bleed_box(0, 0, 595, 842)
// Cleanup: review comments and attachments do not go to print
fix::remove_annotations()
fix::remove_attachments()
fix::flatten_layers()
fix::remove_unused_resources()
email_version.pdfl
// Usage: pdfl fix final.pdf email_version.pdfl --output approval.pdf
fix::downsample_images(96)
fix::compress_images(70)
fix::add_watermark("PROOF — NOT THE FINAL VERSION")
fix::add_page_numbers()

Checking the result with pdfl itself:

Terminal window
pdfl fix final.pdf email_version.pdfl --output approval.pdf
pdfl inspect approval.pdf # size, DPI and warnings for the new file
split.pdfl
// Usage: pdfl fix book.pdf split.pdfl --output book_processed.pdf
check "Expected structure" {
assert doc.page_count > 4,
"book has only #{doc.page_count} pages — unexpected structure"
}
fix::split_document(1, 2, "output/cover.pdf")
fix::split_document(3, doc.page_count, "output/body.pdf")

codes:: · Index · Next: data::

DigitalOceanThanks to DigitalOcean for hosting this site.