New features
- Added {class}
pikepdf.JobBuilder, a fluent, Pythonic builder for qpdf jobs. It assembles a job specification with chained, snake_case methods (input,output,encrypt,add_pages,split_pages,linearize,compress,add_attachment,add_overlay,limits, ...) and runs it via the existing {class}pikepdf.Job, without hand-writing qpdf's camelCase job JSON. Encryption permissions are expressed with the familiar {class}pikepdf.Permissions/{class}pikepdf.Encryptionmodels, and a.set(**kwargs)escape hatch reaches any other job option. Additional methods cover image optimization (optimize_images,externalize_inline_images), page/content transforms (flatten_annotations,flatten_rotation,generate_appearances,coalesce_contents,normalize_content), content removal (remove_metadata,remove_info,remove_acroform,remove_structure,remove_page_labels), page labels (set_page_labels), version control (min_version,force_version), and reproducible/inspection helpers (deterministic_id,static_id,check). - Exposed several pieces of qpdf functionality that pikepdf had not previously bound:
- Whole-document qpdf JSON: {meth}
pikepdf.Pdf.write_qpdf_json, {meth}pikepdf.Pdf.from_qpdf_jsonand {meth}pikepdf.Pdf.update_from_qpdf_jsonserialize and reconstruct an entire PDF as qpdf JSON (theqpdf --json-output/--json-inputformat, version 2). This complements the existing object-level {meth}pikepdf.Object.to_json. Added {class}pikepdf.JSONStreamDatato control how stream data is represented. - {meth}
pikepdf.Pdf.get_xref_tablereturns the cross-reference table as structured data ({class}pikepdf.XrefEntry), complementing the print-only {meth}pikepdf.Pdf.show_xref_table. - {meth}
pikepdf.Pdf.fix_dangling_referencesrepairs references to objects that are not present in the file. - {meth}
pikepdf.Page.flatten_rotationbakes a page's/Rotatevalue into its content stream. - {meth}
pikepdf.Page.copy_annotationscopies annotations (and associated form fields) from another page, applying a transformation matrix. - {meth}
pikepdf.Page.get_matrix_for_transformationsand {meth}pikepdf.Page.get_matrix_for_form_xobject_placementexpose qpdf's page/form-XObject placement matrices. - {meth}
pikepdf.AcroForm.validate, {meth}pikepdf.AcroForm.invalidate_cacheand {meth}pikepdf.AcroForm.transform_annotationsfor working with interactive forms after manual structural edits. - Added {meth}
pikepdf.Page.get_images, which by default recurses into nested form XObjects to find images. The {attr}pikepdf.Page.imagesproperty is now deprecated: it only reports images referenced directly by the page and silently omits images drawn through form XObjects, which made it appear as if a page "has no images" when it clearly did. Useget_images()instead, orget_images(recursive=False)for the old behavior. - Added {attr}
pikepdf.Page.rotation, a property that reports a page's effective clockwise rotation normalized to[0, 360). Unlike the rawpage.Rotateattribute, it resolves a/Rotatevalue inherited from the page tree and reports0when no rotation is set, instead of raising. Assigning to it sets the absolute rotation. This addresses the long-standing confusion between thepage.Rotateattribute and thepage.rotate()method (#467). - {meth}
pikepdf.Page.rotatenow defaultsrelativetoFalse, sopage.rotate(90)sets an absolute rotation. Passingrelativeas a positional argument is deprecated and emits aDeprecationWarning; pass it as a keyword argument instead, e.g.page.rotate(90, relative=True). Positional support will be removed in pikepdf 11. - Added {meth}
pikepdf.Pdf.add_pages_fromto copy pages between documents while preserving interactive AcroForm form fields, returning a {class}pikepdf.PageCopyResult. Naivepages.extend()across documents andsave()of documents with orphaned form widgets now emit {class}pikepdf.PageCopyWarning. (#670, [#207])
When copying pages, named destinations referenced by the copied pages'
annotations (e.g. table-of-contents links) are now carried into the
destination document — both the PDF 1.2 Names.Dests name tree and the
legacy PDF 1.1 Root.Dests dictionary — so internal links keep working
regardless of merge order. Name collisions are renamed and reported via
{class}pikepdf.PageCopyResult (named_dests_added, renamed_dests,
dropped_dests). Naive pages.extend() now also warns when copied pages
reference named destinations. (#148)
Fixes
- Fixed image extraction ignoring the
/Decodearray, which caused colors to be inverted (or otherwise mismapped) when a PDF specified a non-default/Decodesuch as[1, 0]. {meth}pikepdf.PdfImage.as_pil_imageand {meth}pikepdf.PdfImage.extract_tonow apply/Decodeas a linear per-channel mapping for grayscale, RGB and CMYK raster images, matching how a PDF viewer renders the image. Previously/Decodewas honored only for CCITTFax-encoded images. Thanks to Mark-Joy for the report. {issue}650Both methods gained anapply_decode_arrayparameter (defaultTrue). Passapply_decode_array=Falseto retrieve the raw stored sample values with the least processing -- useful for forensic inspection of the underlying image data. Some image types are intentionally not affected: Indexed-colorspace images (where/Decoderemaps palette indices rather than colors -- a non-identity/Decodethere now emits a warning), and DCT (JPEG) / JPX (JPEG 2000) images, whose codecs carry their own color semantics (such as the Adobe APP14 marker for inverted CMYK) that Pillow already honors; re-applying/Decodewould double-invert them. - Fixed {meth}
pikepdf.Pdf.savedecompressing streams when called withcompress_streams=Falseand no explicitstream_decode_level. qpdf 11.10 changed its default stream decode level togeneralized, which caused such saves to decompress (without recompressing) streams and balloon the output file. pikepdf now pins the decode level tononein this case, restoring the documented behavior thatcompress_streams=Falsealone does not trigger decompression. Fixes {issue}676. - The minimum required qpdf version is now 12.3.2. The new
{meth}
pikepdf.AcroForm.validatebinding calls qpdf'sQPDFAcroFormDocumentHelper::validate, which was added in qpdf 12.3.0, so pikepdf no longer builds against older qpdf releases.
Documentation
- Documented a long-standing page-deletion pitfall: deleting a page unlinks it
from the page tree, but a page that is still referenced by an outline
(bookmark), link annotation, or named destination remains in the saved file.
The {ref}
Deleting pages <deleting_pages>topic now explains the behavior and gives workarounds. Thanks to m-holger. Closes {issue}196. - Documented how to copy metadata between documents, in a new
{ref}
Copying metadata between documents <copymetadata>topic, including why blindly copying all fields (or the raw XMP stream) can import false conformance claims and identifiers. Closes {issue}188.