Skip to content

FreePDF 7.5.0

Choose a tag to compare

@christianz christianz released this 04 Nov 14:40
04d3bee

FreePDF 7.5.0

FreePDF 7.5.0 makes Append mode respect what a document's signatures permit,
and opens encrypted documents in Append mode.

Packages

dotnet add package io7.FreePDF --version 7.5.0
dotnet add package io7.FreePDF.Layout --version 7.5.0

New

  • Saves that signatures do not permit are refused. Saving a document opened
    with PdfDocumentOpenMode.Append throws InvalidOperationException before
    anything is written when the update would change what the document's
    signatures protect:

    • signed signature fields, in every signed document: the field, the fields
      above it, its widget and everything they refer to (the signature, its lock,
      the appearance and its resources). They cannot change, be removed, be renamed
      by moving them in the field tree, or move to another page;
    • fields that a signature locks (FieldMDP, or the /Lock of its signature
      field), protected the same way;
    • a certification signature's permission (DocMDP, ISO 32000-1 12.8.2.2), or a
      /Lock with /P:
    P=1 P=2 P=3 no certification
    Field values and appearances refused yes yes yes
    New signature fields, signing refused yes yes yes
    Document information, XMP metadata refused yes yes yes
    Add, change or remove comments and other annotations refused refused yes yes
    New non-signature fields, removing fields refused refused refused yes
    Page content, pages, bookmarks, other catalog entries refused refused refused yes

    The message names the permission and the change, for example "...permit only
    filling in forms and signing (DocMDP P=2), so saving would make validators
    report them as broken: changing page 1 (object 3): /Contents. Nothing was
    written." A save without changes is byte for byte, also at P=1.

  • Encrypted documents in Append mode. An encrypted document opens in
    Append mode with its owner password, or with its user password and
    PdfReaderOptions.AllowModifyWithoutOwnerPassword, as in Modify mode. The
    update keeps the file's encryption (RC4 and AES-128/256 of the standard
    security handler): its objects are encrypted with the file's key, the trailer
    keeps /Encrypt and the first /ID, and a new signature's /Contents stays
    unencrypted. A password-protected signed form can now be filled in and signed
    again with its signatures valid.

Fixed

  • An Append-mode update added /PageMode /UseOutlines to the catalog of a
    document with bookmarks, which validators report as a change.
  • An update of an AES-256 file whose header says 1.7 (Chrome, LibreOffice, qpdf)
    wrote a catalog /Version 2.0, even when nothing else changed.

Upgrading from 7.4.x

Update all FreePDF packages to 7.5.0 together and rebuild; the assembly version
is now 7.5.0.0. The public API is unchanged. Behaviour changes to check:

  • An Append-mode save of a signed document can now throw
    InvalidOperationException where 7.4 saved a change that broke a signature.
    After a refusal the document is marked saved; open the file again to try a
    different change.
  • Save(path) in Append mode saves in memory first and replaces the file
    through a temporary file next to it, so a refused or failed save leaves the
    file as it was, also when it is the file the document was opened from.
  • Reading page.Annotations, document.Outlines, document.ViewerPreferences
    or a form field's Kids creates them when the file has none, and saving
    writes that as a change, which a signed document may now refuse. Read a field's
    kids with GetKids().
  • Encrypted documents open in Append mode instead of throwing
    NotSupportedException. Changing or removing the encryption (passwords,
    permissions, algorithm) is refused, as is a changed save of a file that carries
    an integrity code over its encryption (PDF MAC, ISO/TS 32004; pyHanko adds one
    to AES-256 by default). With the user password and
    AllowModifyWithoutOwnerPassword, the file's permission flags are not
    enforced, as in Modify mode.

See "Signed documents" in the migration guide:
https://github.com/io7/FreePDF/blob/v7.5.0/docs/migrating-to-7.md#signed-documents

Validation

All 7.4.1 release gates apply, plus:

  • regression tests for each permission level against filling in fields,
    signing, the document information, comments, page content, pages and the
    catalog; for changing, removing and moving signed and locked fields and the
    objects they refer to; for RC4-128, AES-128 and AES-256 files in Append mode
    (unchanged saves, encrypted changes, drawing, signing, a directly held
    encryption dictionary, files without /ID, unencrypted metadata, refusals);
    582 regression tests in all;
  • pyHanko 0.29.1 cross-check: forms that pyHanko certified (P=1, 2, 3) or signed,
    with and without field locks, also nested, changed by FreePDF. Every change
    FreePDF saved for which pyHanko has a rule was accepted, and every change it
    refused was rejected by pyHanko too. pyHanko's default policy is stricter than
    the permissions in four places, which FreePDF follows the permissions in:
    added comments, new visible signature fields after a certification, page
    changes after an ordinary signature, and new signature fields after a lock of
    all fields;
  • a form that pyHanko encrypted with AES-256 and signed, filled and countersigned
    by FreePDF: both signatures VALID in pyHanko and poppler's pdfsig;
  • corpus: 5 certified veraPDF files are refused a catalog change and keep their
    bytes on an unchanged save; the 13 encrypted files save unchanged byte for byte
    and with an encrypted change;
  • independent reviews of both features (two rounds for the permission check),
    whose findings were fixed before release.

The attached packages are the verified CI artifacts from source revision 04d3beea5555d3d4c575174ad56c4b270681e5a6, and the same files that were
uploaded to NuGet.org. SHA256SUMS and validation.json record their hashes and package-install evidence.
NuGet.org adds its repository signature, so a package downloaded from NuGet.org has a different hash from the attached file.

CI: https://github.com/io7/FreePDF/actions/runs/41825118018 (core),
https://github.com/io7/FreePDF/actions/runs/41825118046 (packages/API/independent checks),
https://github.com/io7/FreePDF/actions/runs/41825117937 (comparisons),
https://github.com/io7/FreePDF/actions/runs/41826677051 (NuGet publication).