Repository navigation
Releases
FreePDF 7.9.0
FreePDF 7.9.0
FreePDF 7.9.0 draws the barcodes invoices, labels and tickets need (QR, Data
Matrix, Code 128, EAN and UPC) as vector shapes, renders MigraDoc barcode
elements, and creates fillable form fields, so the whole form cycle is covered:
create, fill, flatten, merge, sign and certify.
Packages
io7.FreePDFhttps://www.nuget.org/packages/io7.FreePDF/7.9.0io7.FreePDF.Layouthttps://www.nuget.org/packages/io7.FreePDF.Layout/7.9.0io7.FreePDF.Gdihttps://www.nuget.org/packages/io7.FreePDF.Gdi/7.9.0io7.FreePDF.Layout.Gdihttps://www.nuget.org/packages/io7.FreePDF.Layout.Gdi/7.9.0io7.FreePDF.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Wpf/7.9.0io7.FreePDF.Layout.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Layout.Wpf/7.9.0
dotnet add package io7.FreePDF --version 7.9.0
dotnet add package io7.FreePDF.Layout --version 7.9.0
New
- Barcodes as vectors.
CodeQR: QR model 2, versions 1 to 40, levels L/M/Q/H, smallest version
chosen to fit, UTF-8 with ECI 26 for text that is not ASCII, one path.CodeDataMatrix: a new ECC 200 encoder written from ISO/IEC 16022, all 30
sizes, ASCII and Base256, ECI and GS1 FNC1, vector modules in every build.Code128with shortest code-set choice and GS1-128 (Code128.Fnc1);
Ean13,Ean8andUpcAwith check digits and guard bars.
- MigraDoc renders barcodes.
Barcodeelements were silently left out of the
PDF; they now draw 2 of 5, Code 39, Code 128, EAN-13, EAN-8, UPC-A, QR and Data
Matrix, withAddBarcode()on sections, cells, text frames, headers and
footers. - Creating form fields.
PdfForm.AddTextField,AddCheckBox,
AddRadioButtonGroup,AddComboBox,AddListBoxandAddSignatureField,
styled withPdfFormFieldStyle, with appearances every viewer shows. - Certifying a document.
DigitalSignatureOptions.Certificationwrites a
certification signature (DocMDP) permitting no changes, form filling, or form
filling and comments; Append mode enforces it. Signature fields whose seed
value names the signature type (/MDP) can now be signed.
Fixed
- Text beyond U+FFFF (emoji, mathematical letters) is extracted correctly; large
ToUnicode maps are split into sections of at most 100. - MigraDoc: default tab stops reach the right edge of the text, and a blank after
a tab no longer breaks the line. - Data Matrix: the inherited encoder drew nothing in the Core build, lost
characters in C40, Text and X12, and wrote unreadable EDIFACT. It appears to
derive from the GPL-licensed iec16022 library and carried no notice of that
origin, so it is removed and replaced (seePROVENANCE.md). - 1-, 2- and 4-bit grayscale and 2-bit palette PNGs load in the Core build.
DigitalSignatureOptions.ContactInfois written; Code 39 refuses an
apostrophe; DDL escapes barcode codes.
Upgrading from 7.8.0
Update all FreePDF packages to 7.9.0 together and rebuild; the assembly version
is now 7.9.0.0. The public API only gains members. Behaviour changes to check:
- MigraDoc documents with
Barcodeelements now draw them: an invalid code makes
rendering throw, and withTaggedPdfa barcode needsAlternativeTextor
Decorative. - Data Matrix C40, Text, X12 and EDIFACT requests are written in ASCII
encodation, so a fixed size can now be too small. - A signature field with a seed value /MDP is signed when
Certification
matches it and refused with anArgumentExceptionwhen it does not. - MigraDoc lines with tabs near the right edge, or a blank after a tab, can be
laid out differently.
Validation
All 7.8.0 release gates apply, plus:
- 124 new regression tests, 962 in all.
- zxing-cpp, as an external decoder only, read 1,004 QR symbols (every version,
level and mode at full capacity), 212 Data Matrix symbols (all 30 sizes), the
linear codes and MigraDoc barcodes; a strict checker written from ISO/IEC 16022
passed 305 Data Matrix symbols. - MuPDF and pypdf read every created field in new, filled and reopened files.
pyHanko 0.6.0 judged certifications at levels 1 to 3 VALID, and filling text
fields, check boxes and combo boxes after levels 2 and 3 permitted. Its
default policy rejects selecting a radio button after a certification, also
when pypdf writes the same minimal update; the support policy lists it. - Independent reviews (two rounds for barcodes, one each for forms and the text
fixes) found issues, including a regression in a tab fix, which were fixed
before release. - A clean install from nuget.org, with an empty package cache: zxing-cpp decodes
every code drawn withXGraphics(QR in ASCII and UTF-8, Data Matrix square and
rectangular, Code 128, GS1-128, EAN-13, EAN-8, UPC-A) and every MigraDoc barcode
(QR, Data Matrix, EAN-13, Code 128 in a table cell); a form with every field
type, certified at levels 1, 2 and 3, is VALID in pyHanko, filling a check box
and a combo box afterwards stays VALID at levels 2 and 3, and level 1 refuses the
fill; poppler extracts emoji drawn with DejaVu Sans; a center tab in a justified
MigraDoc line keeps its gap. The same check passed on the CI packages before
publication.
See the release notes in docs/releases/7.9.0.md and the support policy.
The attached packages are the verified CI artifacts from source revision b65e9859bbd8303cbb34f351f2a17cb4dc629058, 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/42854130454 (core),
https://github.com/io7/FreePDF/actions/runs/42854129991 (packages/API/independent checks),
https://github.com/io7/FreePDF/actions/runs/42854129600 (comparisons),
https://github.com/io7/FreePDF/actions/runs/42856275483 (NuGet publication).
FreePDF 7.8.0
FreePDF 7.8.0
FreePDF 7.8.0 opens damaged files that viewers open, fixes MigraDoc documents
that crashed or never finished rendering, and keeps PDF/A files conforming: CID
fonts get the CIDSet PDF/A-1 requires, and the XMP metadata of generated and
opened files agrees with the document information.
Packages
io7.FreePDFhttps://www.nuget.org/packages/io7.FreePDF/7.8.0io7.FreePDF.Layouthttps://www.nuget.org/packages/io7.FreePDF.Layout/7.8.0io7.FreePDF.Gdihttps://www.nuget.org/packages/io7.FreePDF.Gdi/7.8.0io7.FreePDF.Layout.Gdihttps://www.nuget.org/packages/io7.FreePDF.Layout.Gdi/7.8.0io7.FreePDF.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Wpf/7.8.0io7.FreePDF.Layout.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Layout.Wpf/7.8.0
dotnet add package io7.FreePDF --version 7.8.0
dotnet add package io7.FreePDF.Layout --version 7.8.0
New
- Rebuilding unreadable cross-reference information. A file whose
cross-reference table or stream cannot be read (a wrongstartxref, offsets
that point elsewhere, a missing table, a damaged cross-reference stream) is
rebuilt from its objects, as viewers do, instead of being refused. The newest
copy of each object wins, object streams supply their objects (not in
encrypted files), and the trailer entries come from the newest dictionary that
names a/Root. The work grows linearly with the file size.Appendmode
still refuses a rebuilt file.
Fixed
- Reading files.
endstreamis found past a padded or mis-sized/Length(IBM i invoices,
Bluebeam); such files failed with "Cannot retrieve stream length".- An update that gives an object the number of an older cross-reference stream
no longer loses that object (a form could vanish). - Importing a page whose
/Rotateor boxes are indirect objects (pdf-lib)
works withAddPage,InsertRangeandAppendPages. - A distant
startxref, huge object numbers, a missingendobj, a stray),
an LZW special case and streams with short reads no longer stop a file. - Names in content streams in legacy code pages (Shift-JIS, GBK) match their
resource keys.
- MigraDoc.
- Justified paragraphs with a tab and soft hyphens no longer render forever,
and justified lines with tabs or fields are spaced correctly. - Bordered paragraphs at a page bottom, a
KeepWithpast the table end and
charts without axis settings no longer throw; charts without X axis settings
no longer draw at NaN coordinates. - Rows taller than a page, still cut off, now log a warning.
- Justified paragraphs with a tab and soft hyphens no longer render forever,
- PDF/A and metadata.
- PDF/A-1 CID fonts get the CIDSet veraPDF requires (rule 6.3.5-3).
- Generated XMP escapes text, keeps line breaks, leaves out empty dates and no
longer throws on information entries that are not text. SetPdfAon an opened file with its own XMP updates that XMP in place with
the PDF/A identification and the document information, sets the Print flag
on annotations and removes PDF/A-1 page transparency groups.- A
Modifysave of an opened file whose XMP claims PDF/A keeps it conforming:
the XMP follows the changed document information, annotations get the Print
flag, and no page transparency group is added.
Upgrading from 7.7.0
Update all FreePDF packages to 7.8.0 together and rebuild; the assembly version
is now 7.8.0.0. The public API is unchanged except for one addition,
PdfFontDescriptor.Keys.CIDSet. Behaviour changes to check:
- Damaged files that threw
PdfReaderExceptionmay now open through the
rebuild;Appendmode still throws for them. - A recovered stream length no longer includes the line break before
endstream;CNamevalues with bytes above 127 are decoded text. - MigraDoc: justified lines with tabs or fields can be spaced differently;
charts without X axis settings show a category axis; column, line and area
charts without Y axis settings render without a value axis. - Opened files whose XMP claims PDF/A: a
Modifysave rewrites the XMP packet,
sets annotation Print flags and handles page transparency groups as above. - Generated XMP leaves out empty dates;
DocumentMetadataInforeturns "" for
entries that are not text and decodes PDFDocEncoding strings.
Validation
All 7.7.0 release gates apply, plus:
- 115 new regression tests, 838 in all.
- The 31 sample files attached to the upstream issues open as before or better;
the Bluebeam files of #186 and #216 open, and #213 keeps its form. - A differential fuzz of 4,000 damaged files: the final reader opens 8 more than
the previous review round's build and none fewer. - veraPDF passes PDF/A-1b output with CID subsets and complete fonts, mixed
text, re-saved and converted opened files and XMP edge cases; 2b, 3b (Factur-X)
and ua1 output pass too. - MigraDoc: 2,882 scenarios compared before and after; of 1,332 justified
scenarios with tabs, fields and soft hyphens, 7.7.0 hung on 172 and 7.8.0 on
none. - Independent reviews (five rounds for the reader, two for PDF/A and metadata,
one for MigraDoc) found issues, including regressions in earlier rounds'
fixes, which were fixed before release. - A clean install from nuget.org, with an empty package cache: a damaged file (wrong
startxref, a padded stream) opens through the rebuild; a PDF/A-1b document with CID text and the title "Profit & Loss <2026>", the same file re-saved inModifymode with a new title, and a plain file with its own XMP and a link made PDF/A-1b after opening all pass veraPDF 1b; column, line and bar charts without axis settings render without NaN coordinates; a justified list with soft hyphens and aKeepWithpast the table end finishes. The earlier install checks still pass: pyHanko judges the certified form after filling, the countersigned encrypted form and the signed existing field VALID, and the edits of the signed nested page tree keep the signature valid in pdfsig.
The attached packages are the verified CI artifacts from source revision d6109ab76417a038ded37dc4d2a7bf8ae61afc18, 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/42086912044 (core),
https://github.com/io7/FreePDF/actions/runs/42086912350 (packages/API/independent checks),
https://github.com/io7/FreePDF/actions/runs/42086912382 (comparisons),
https://github.com/io7/FreePDF/actions/runs/42109398401 (NuGet publication).
FreePDF 7.7.0
FreePDF 7.7.0
FreePDF 7.7.0 adds, removes and moves pages in Append mode when the page tree
is nested, keeps the page attributes of new and moved pages right, accepts every
XMP packet header the XMP specification allows, and fixes how metadata is
handled in encrypted documents.
Packages
io7.FreePDFhttps://www.nuget.org/packages/io7.FreePDF/7.7.0io7.FreePDF.Layouthttps://www.nuget.org/packages/io7.FreePDF.Layout/7.7.0io7.FreePDF.Gdihttps://www.nuget.org/packages/io7.FreePDF.Gdi/7.7.0io7.FreePDF.Layout.Gdihttps://www.nuget.org/packages/io7.FreePDF.Layout.Gdi/7.7.0io7.FreePDF.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Wpf/7.7.0io7.FreePDF.Layout.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Layout.Wpf/7.7.0
dotnet add package io7.FreePDF --version 7.7.0
dotnet add package io7.FreePDF.Layout --version 7.7.0
New
-
Page changes in nested page trees. In a document opened with
PdfDocumentOpenMode.Append,Insert,Remove,RemoveAt,MovePage,
InsertRangeandPdfDocument.AppendPagesnow work when the page tree has
intermediate nodes; they threwNotSupportedException. The update writes only
the nodes between the root and the pages that come or go:- a page goes next to its neighbours in their node, preferring the node it was
in; - a node left without pages is taken out of the tree unchanged;
- a page moved under another node keeps its media box, crop box and rotation.
A certification signature (DocMDP) refuses page changes as before.
- a page goes next to its neighbours in their node, preferring the node it was
Fixed
- New pages and inherited attributes. A new page took the crop box, rotation
or resources of the page tree node it went into: inAppendmode from its
node, inModifymode from the opened file's root. - Re-inserting a page. A page taken out with
RemoveorRemoveAtcan be
inserted again;Insertthrew "Object already in table". - Rotation and boxes in the page tree.
- An inherited
/Rotategiven as an indirect object or a real number was
ignored. - A page with its own indirect media box or crop box could not be opened under
a node that has one.
- An inherited
AppendPagesand the catalog.AppendPagesno longer adds an empty
outline to the catalog when there are no bookmarks to merge.- XMP packet headers.
PdfMetadata.SetMetadata(string)accepts a header in
single quotes and a begin attribute that holds U+FEFF; both threw "Unknown
metadata encoding tag". - Compressed metadata.
SetMetadataon compressed metadata wrote plain XML
under/FlateDecode. Now the old filters are dropped and only the crypt filter
stays. - Unmarked document metadata. With
EncryptMetadatafalse, aModifysave
withDoNotResetEncryptionmarks the document's metadata with the Identity
crypt filter. Metadata that another writer left unmarked could not be read by
pyHanko and MuPDF. Metadata that names another crypt filter stays encrypted
with it. - Re-encrypted files that could not be opened. Encrypting a document again
withEncryptMetadatatrue, when the opened file had it false, kept
/EncryptMetadata false, and for V4 no one could open the file with its
password. - Crypt filters in older and unusual layouts.
- An
Appendsave of an RC4 file with encryption version 1 to 3 threw "Crypt
filters are not supported" when its encryption dictionary carried
/EncryptMetadata falseand the metadata changed. - Crypt filter parameters held by reference inside a
/DecodeParmsarray are
read.
- An
Upgrading from 7.6.1
Update all FreePDF packages to 7.7.0 together and rebuild; the assembly version
is now 7.7.0.0. The public API is unchanged. Behaviour changes to check:
- In
Modifymode, the root of the page tree no longer holds the inheritable
values it had in the opened file, because every page holds them. - In
Appendmode, a new page under a node or root with a rotation or crop box
gets an explicit/Rotate 0or crop box. SetMetadata(byte[])takes plain XML and drops the old filters except the
crypt filter.SetMetadata(string)accepts only encoding names in the begin attribute.- Metadata that names an encrypting crypt filter while
EncryptMetadatais false
stays encrypted. pyHanko 0.29.1 cannot read such V4 metadata, because it uses
the file key instead of the per-object key; MuPDF and FreePDF read it.
Validation
All 7.6.1 release gates apply, plus:
- 58 new regression tests, 723 in all:
- nested page trees: inserting, removing, moving and merging; the objects each
update writes; consistent counts and parents; inherited values; moving a page
back saves the original bytes; refusals under a certification; - XMP headers;
- document metadata in V4-RC4, AES-128 and AES-256 files;
- re-encryption.
- nested page trees: inserting, removing, moving and merging; the objects each
- Nested-tree files signed with pyHanko 0.29.1, edited in
Appendmode (insert
with drawing, move, remove, merge, FreePDF countersignature): poppler and MuPDF
show the expected page sizes and rotations, and pdfsig reports every signature
valid. A pyHanko certification with "form filling" (P=2) refuses the insert. - pyHanko and mutool read the document metadata of unmarked V4-RC4, AES-128 and
AES-256 files after aModifysave withDoNotResetEncryption. - Independent reviews (two rounds for the page tree change, three for the
metadata fixes) found issues, which were fixed before release. - A clean install from nuget.org, with an empty package cache, edited the pyHanko-signed nested-tree file (insert, move, remove, merge): the original stayed a byte-for-byte prefix, page sizes and rotation are right in poppler, and pdfsig reports the signature valid. The certified nested file refused the insert. pyHanko reads the document metadata after a save that keeps the encryption, and a file encrypted again with
EncryptMetadatatrue opens with its new password. The earlier install checks still pass: pyHanko judges the certified form after filling, the countersigned encrypted form and the signed existing field VALID.
The attached packages are the verified CI artifacts from source revision d889ee8a03f2139f129b9684ae15feb787387596, 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/42005793074 (core),
https://github.com/io7/FreePDF/actions/runs/42005793204 (packages/API/independent checks),
https://github.com/io7/FreePDF/actions/runs/42005793114 (comparisons),
https://github.com/io7/FreePDF/actions/runs/42007801101 (NuGet publication).
FreePDF 7.6.1
FreePDF 7.6.1
FreePDF 7.6.1 reads list boxes with several selected options, decodes stream
filters in every form the standard allows, and writes metadata in encrypted
documents as ISO 32000-1 specifies. Nothing else changes from 7.6.0.
Packages
io7.FreePDFhttps://www.nuget.org/packages/io7.FreePDF/7.6.1io7.FreePDF.Layouthttps://www.nuget.org/packages/io7.FreePDF.Layout/7.6.1io7.FreePDF.Gdihttps://www.nuget.org/packages/io7.FreePDF.Gdi/7.6.1io7.FreePDF.Layout.Gdihttps://www.nuget.org/packages/io7.FreePDF.Layout.Gdi/7.6.1io7.FreePDF.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Wpf/7.6.1io7.FreePDF.Layout.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Layout.Wpf/7.6.1
dotnet add package io7.FreePDF --version 7.6.1
dotnet add package io7.FreePDF.Layout --version 7.6.1
Fixed
-
List boxes with several selected options. A list box that allows multiple
selection holds its value as an array (ISO 32000-1 Table 231).
SelectedValue,SelectedIndexandFlatten()threw on it. All selected
options are read now:SelectedValueandSelectedIndexreport the first in
option order, and the drawn list box highlights each one. A value held as an
indirect string is read too. -
Stream filters and their parameters. Decoding accepts every form ISO
32000-1 7.3.8 allows:- a null
/DecodeParmsentry means no parameters; - a filter name or its parameters may be indirect objects;
- a single filter may have its parameters in a one-element array, and the
other way round.
A
/Cryptfilter passes the data on, because it was decrypted when the file
was read. Decoding threw "Filter not implemented: Crypt", for example for
FreePDF's own document metadata whenEncryptMetadatais false. - a null
-
Crypt filter entries. When FreePDF marks a stream with a crypt filter, as it
does for the document metadata whenEncryptMetadatais false,/DecodeParms
now has one entry per filter. It was a lone dictionary next to two filters,
which ISO 32000-1 Table 5 does not allow and FreePDF could not decode. A stream
whose parameters were a direct dictionary made the save throw ("A direct object
can only be added once"). Streams that earlier versions wrote still decode. -
Metadata encryption. With
EncryptMetadatafalse, only the document's own
metadata stream stays unencrypted, as ISO 32000-1 Table 20 specifies. FreePDF
wrote every metadata stream that held XML unencrypted, and pyHanko could not
read page metadata ("not a multiple of the block length"). Reading still
accepts unencrypted metadata anywhere, as some writers leave it all
unencrypted. -
With
EncryptMetadatafalse, saving a document without metadata added a
/Metadatadictionary that held only the crypt filter and no stream, which
ISO 32000-1 Table 28 does not allow. No such entry is added now.
Upgrading from 7.6.0
Update all FreePDF packages to 7.6.1 together and rebuild; the assembly version
is now 7.6.1.0. The public API is unchanged. Behaviour changes to check:
- With
EncryptMetadatafalse, metadata streams other than the document's own,
such as page metadata, are now encrypted. FreePDF 7.5.1 and earlier cannot
open such a file when that metadata is compressed (7.5.2 and later can). - Setting or resetting a crypt filter writes
/Filterand/DecodeParmsagain,
aligned; streams without a crypt filter are left as they are.
Validation
All 7.6.0 release gates apply, plus:
- regression tests: a multi-select list box flattened with both options
highlighted, indirect values and option order; null, indirect and one-element
filter parameters,/Cryptalone and in front of another filter, the layout
earlier versions wrote; document metadata compressed or not, with direct or
indirect parameters and an indirect filter name; setting and resetting a crypt
filter; other metadata encrypted, the document's not, also inAppendmode;
665 regression tests in all; - pyHanko 0.29.1 reads the document and page metadata of AES-128, AES-256 and
V4-RC4 files that FreePDF wrote, also compressed, afterModifyandAppend
saves, and all crypt filter layouts FreePDF now writes; mutool read the
samples checked, including page metadata that it misread in 7.6.0 output; - an independent review in three rounds, whose findings were fixed before
release. Every stream that decoded before decodes to the same bytes, streams
without a crypt filter are written byte for byte as in 7.6.0, and resetting
the encryption of a whole document no longer crashes on some parameter
layouts.
The attached packages are the verified CI artifacts from source revision 90c50b5f5ef6b13f9673fe036609c22e19b0b585, 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/41924320872 (core),
https://github.com/io7/FreePDF/actions/runs/41924320762 (packages/API/independent checks),
https://github.com/io7/FreePDF/actions/runs/41924320747 (comparisons),
https://github.com/io7/FreePDF/actions/runs/41984593505 (NuGet publication).
FreePDF 7.6.0
FreePDF 7.6.0
FreePDF 7.6.0 signs a form's existing, empty signature field, such as a "sign
here" field, instead of always adding a new field next to it.
Packages
io7.FreePDFhttps://www.nuget.org/packages/io7.FreePDF/7.6.0io7.FreePDF.Layouthttps://www.nuget.org/packages/io7.FreePDF.Layout/7.6.0io7.FreePDF.Gdihttps://www.nuget.org/packages/io7.FreePDF.Gdi/7.6.0io7.FreePDF.Layout.Gdihttps://www.nuget.org/packages/io7.FreePDF.Layout.Gdi/7.6.0io7.FreePDF.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Wpf/7.6.0io7.FreePDF.Layout.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Layout.Wpf/7.6.0
dotnet add package io7.FreePDF --version 7.6.0
dotnet add package io7.FreePDF.Layout --version 7.6.0
New
-
Signing an existing signature field. Name the field in
DigitalSignatureOptions.FieldName:using var document = PdfReader.Open("form.pdf", PdfDocumentOpenMode.Append); DigitalSignatureHandler.ForDocument(document, signer, new DigitalSignatureOptions { FieldName = "Approval", Reason = "Approved" }); document.Save("signed.pdf");
- The signature becomes the field's value and is drawn in its widget at the
widget's size; leaveRectangleandPageIndexunset. A field without area
gives an invisible signature. - The field keeps its name and flags. The page and the form's field list stay
as they are: the form gets no new field. - A field lock (
/Lock) is recorded in the signature as a FieldMDP reference,
as Acrobat and pyHanko record it, so validators lock the fields it names from
then on. A lock with a document permission (/P) passes it on. - Required seed values (
/SV) are checked. A required reason must be given as
Reason; where the field allows none (no reasons or a single "." listed), no
reason may be given and the signature has none. Requirements FreePDF cannot
meet or check throwNotSupportedException: a signing certificate, a digest
method, a timestamp, a certification, revocation information, locking the
document or a named appearance. Optional seed values are ignored. - Mistakes throw when the handler is created: no field of that name, not a
signature field, a field with fields below it, a field that is already
signed, a lock without a valid/Action, or unmet seed values. - In
Appendmode the signature rules of 7.5 apply: a certification with
"form filling and signing" (P=2) or more allows signing an existing field,
"no changes" (P=1) refuses it, and a field that an earlier signature locks is
refused when saving. It works inModifymode too.
- The signature becomes the field's value and is drawn in its widget at the
Upgrading from 7.5.x
Update all FreePDF packages to 7.6.0 together and rebuild; the assembly version
is now 7.6.0.0. The public API gains DigitalSignatureOptions.FieldName; nothing
else changes, and signing without FieldName adds a new field as before.
pyHanko's default policy accepts signing an existing field when the field is its
own widget, as pyHanko and most tools make them. When the field's widget is a
separate object, or it has several widgets or shares its appearance with another
widget, pyHanko reports the document's earlier signatures as changed; pyHanko's
own signer fails the same way on a shared appearance, refuses fields with several
widgets, and does not update a separate widget's appearance.
Validation
All 7.5.2 release gates apply, plus:
- 35 regression tests: a field that is its own widget, one with a separate widget
whose "sign here" appearance is replaced, an invisible field, swapped widget
rectangles,Modifymode, the mistakes above, a lock recorded and then enforced
on the next save, a lock with/P, each certification level, a field locked by
an earlier signature, seed values including the reason conventions, and the
custom appearance; 641 regression tests in all; - pyHanko 0.29.1: forms that pyHanko made with an empty signature field, signed by
FreePDF inAppendmode with the field count unchanged. A plain form, a form
with a lock on another field, forms with a required reason or with no reason
allowed, and a form certified with P=2 are VALID (the certification as "signing
and form filling", also in poppler'spdfsig); P=1 and a disallowed reason are
refused. After pyHanko changed the locked field, it judged FreePDF's signature
INVALID, while the same change without the lock stayed VALID; - an independent review in two rounds, whose findings were fixed before release.
It found locks, including/P, enforced by FreePDF and pyHanko, encrypted
Appenddocuments (AES-128 and AES-256) signed correctly, and the path that
adds a new field unchanged.
The attached packages are the verified CI artifacts from source revision 7bf903164dfc351060083052357747960db4ce85, 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/41905216827 (core),
https://github.com/io7/FreePDF/actions/runs/41905216799 (packages/API/independent checks),
https://github.com/io7/FreePDF/actions/runs/41905216762 (comparisons),
https://github.com/io7/FreePDF/actions/runs/41907475660 (NuGet publication).
FreePDF 7.5.2
FreePDF 7.5.2
FreePDF 7.5.2 opens and reads encrypted documents whose metadata streams are
encrypted although the document leaves metadata unencrypted. Nothing else
changes from 7.5.1.
Packages
io7.FreePDFhttps://www.nuget.org/packages/io7.FreePDF/7.5.2io7.FreePDF.Layouthttps://www.nuget.org/packages/io7.FreePDF.Layout/7.5.2io7.FreePDF.Gdihttps://www.nuget.org/packages/io7.FreePDF.Gdi/7.5.2io7.FreePDF.Layout.Gdihttps://www.nuget.org/packages/io7.FreePDF.Layout.Gdi/7.5.2io7.FreePDF.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Wpf/7.5.2io7.FreePDF.Layout.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Layout.Wpf/7.5.2
dotnet add package io7.FreePDF --version 7.5.2
dotnet add package io7.FreePDF.Layout --version 7.5.2
Fixed
-
Encrypted metadata in documents with
EncryptMetadatafalse. ISO
32000-1 exempts only the document's own metadata from encryption, so pyHanko,
for example, encrypts a page's metadata stream. FreePDF decides from a
metadata stream's content whether it is unencrypted, because some writers
leave metadata unencrypted without marking it. It decompressed the encrypted
data of a compressed stream, which threwInvalidDataException, so the
document did not open: 199 of 200 such pyHanko files. It also took an
uncompressed stream whose encrypted data starts with<(one in 256) for XML
and did not decrypt it.The decision is now reliable:
- A stream whose own bytes are empty or blank stays unencrypted.
- A compressed stream without a zlib header, or one that does not decode,
counts as encrypted. - Otherwise the text must start with
<, and its first 16 characters must be
printable ASCII, tabs, line breaks or UTF-8, as every XMP packet's
<?xpacket begin=is.
Upgrading from 7.5.1
Update all FreePDF packages to 7.5.2 together and rebuild; the assembly version
is now 7.5.2.0. The public API is unchanged. Behaviour changes to check:
- With
EncryptMetadatafalse, unencrypted metadata that does not start like
XML text counts as encrypted, both when FreePDF writes it and when it reads
it. Examples are UTF-16 without a byte order mark, Latin-1 text or control
characters in the first 16 characters, and compressed blank metadata from
another writer. XMP must be Unicode, and UTF-16 with a byte order mark already
counted as encrypted. - Files whose unencrypted metadata could not be decoded no longer fail to open;
such a stream counts as encrypted. Examples are a mismatched predictor and an
unsupported filter.
Validation
All 7.5.1 release gates apply, plus:
- regression tests: encrypted compressed metadata with V4-RC4, AES-128 and
AES-256; 2,000 encrypted metadata streams in one file, uncompressed and
compressed, all decrypted; unencrypted metadata, ASCII or UTF-8, compressed or
not, stays unencrypted and reads back; 606 regression tests in all; - pyHanko 0.29.1: 400 AES-256 files with
EncryptMetadatafalse whose page
metadata pyHanko encrypted, 200 of them compressed. 7.5.1 failed to open 199
and misread 1; 7.5.2 reads all 400; - generated files: none of 50,000 compressed encrypted metadata streams is
misread, and all of the 3,000 files from the 7.5.1 review read correctly
(7.5.1 failed to open 1,496 and misread 6); - an independent review in two rounds, whose findings were fixed before release.
On 100,000 random encrypted inputs per filter, at most 1 was taken for
unencrypted metadata. Files that 7.5.1 wrote read the same, and ASCII and UTF-8
metadata is written byte for byte as in 7.5.1.
The attached packages are the verified CI artifacts from source revision a2d5d477061c7c0449f41064b1ede2e4271f93d2, 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/41868952682 (core),
https://github.com/io7/FreePDF/actions/runs/41868952766 (packages/API/independent checks),
https://github.com/io7/FreePDF/actions/runs/41868953407 (comparisons),
https://github.com/io7/FreePDF/actions/runs/41878623607 (NuGet publication).
FreePDF 7.5.1
FreePDF 7.5.1
FreePDF 7.5.1 encrypts empty streams and strings in AES-encrypted documents,
which pyHanko could not read. Nothing else changes from 7.5.0.
Packages
io7.FreePDFhttps://www.nuget.org/packages/io7.FreePDF/7.5.1io7.FreePDF.Layouthttps://www.nuget.org/packages/io7.FreePDF.Layout/7.5.1io7.FreePDF.Gdihttps://www.nuget.org/packages/io7.FreePDF.Gdi/7.5.1io7.FreePDF.Layout.Gdihttps://www.nuget.org/packages/io7.FreePDF.Layout.Gdi/7.5.1io7.FreePDF.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Wpf/7.5.1io7.FreePDF.Layout.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Layout.Wpf/7.5.1
dotnet add package io7.FreePDF --version 7.5.1
dotnet add package io7.FreePDF.Layout --version 7.5.1
Fixed
- Empty data in AES-encrypted documents. An empty stream or string in a
document encrypted with AES-128 or AES-256 was written empty and unencrypted.
AES turns empty data into an initialization vector and a padding block, 32
bytes (ISO 32000-1, 7.6.2), and pyHanko failed to read such a file ("Invalid
IV size (0)"). Examples are a form XObject or a signature appearance that draws
nothing. With 7.5.0's encryptedAppendmode, this also hit signed forms
countersigned with an empty appearance. Empty data is now encrypted like any
other data, in new documents and inModifyandAppendmode. RC4 leaves empty
data empty, so RC4 files are written exactly as before, and empty data that other
tools wrote still reads as empty. - With
EncryptMetadatafalse, saving a metadata stream that holds only
whitespace threwIndexOutOfRangeException. Such a stream, like an empty one,
is left unencrypted, as metadata that holds XML is. - A stream whose
/Cryptfilter parameters leave out/Typeor/Namethrew
InvalidOperationExceptionwhen read. It now uses the Identity crypt filter,
as ISO 32000-1 specifies (Table 14).
Upgrading from 7.5.0
Update all FreePDF packages to 7.5.1 together and rebuild; the assembly version
is now 7.5.1.0. The public API is unchanged. Behaviour changes to check:
- Empty strings and streams in AES-encrypted files are 32 bytes instead of
0, so such files are slightly larger; files without encryption and RC4 files
are unchanged. - An empty stream with invalid
/Cryptfilter parameters (a/Typeother than
/CryptFilterDecodeParms, or a/Namethe document does not define) now
fails to save, as a non-empty one already did.
Validation
All 7.5.0 release gates apply, plus:
- regression tests for empty streams, literal and hexadecimal strings in new
documents andAppendupdates with RC4-128, AES-128 and AES-256, reading them
back, files without encryption, empty AES data from another writer, blank
metadata withEncryptMetadatafalse, and/Cryptparameters without/Type
or/Name; 596 regression tests in all; - pyHanko 0.29.1: a form that pyHanko encrypted with AES-256 and signed, filled
and countersigned by FreePDF with an empty signature appearance, has both
signatures VALID ("signing and form filling"), also in poppler'spdfsig; new
AES-128 and AES-256 documents with an empty form XObject and an empty
/Subjectread in pyHanko and mutool without errors. With 7.5.0, both failed
on the empty stream; - an independent review in two rounds, whose findings were fixed before release.
It found RC4 and unencrypted files byte for byte the same as 7.5.0's, unchanged
Append-mode saves the same bytes, and 3,000 generated AES-256 files with
unencrypted metadata read with the same outcome as in 7.5.0.
The attached packages are the verified CI artifacts from source revision 191cf0bd010a5e3df5cbc8c8d8d6a18c942f7f45, 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/41848030351 (core),
https://github.com/io7/FreePDF/actions/runs/41848030180 (packages/API/independent checks),
https://github.com/io7/FreePDF/actions/runs/41848030302 (comparisons),
https://github.com/io7/FreePDF/actions/runs/41849814232 (NuGet publication).
FreePDF 7.5.0
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
io7.FreePDFhttps://www.nuget.org/packages/io7.FreePDF/7.5.0io7.FreePDF.Layouthttps://www.nuget.org/packages/io7.FreePDF.Layout/7.5.0io7.FreePDF.Gdihttps://www.nuget.org/packages/io7.FreePDF.Gdi/7.5.0io7.FreePDF.Layout.Gdihttps://www.nuget.org/packages/io7.FreePDF.Layout.Gdi/7.5.0io7.FreePDF.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Wpf/7.5.0io7.FreePDF.Layout.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Layout.Wpf/7.5.0
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
withPdfDocumentOpenMode.AppendthrowsInvalidOperationExceptionbefore
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
/Lockof its signature
field), protected the same way; - a certification signature's permission (DocMDP, ISO 32000-1 12.8.2.2), or a
/Lockwith/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. - signed signature fields, in every signed document: the field, the fields
-
Encrypted documents in
Appendmode. An encrypted document opens in
Appendmode with its owner password, or with its user password and
PdfReaderOptions.AllowModifyWithoutOwnerPassword, as inModifymode. 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/Encryptand the first/ID, and a new signature's/Contentsstays
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 /UseOutlinesto 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
InvalidOperationExceptionwhere 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)inAppendmode 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'sKidscreates 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 withGetKids(). - Encrypted documents open in
Appendmode 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 inModifymode.
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 inAppendmode
(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'spdfsig; - 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).
FreePDF 7.4.1
FreePDF 7.4.1
FreePDF 7.4.1 fixes signing and filling signed forms in Append mode, which
7.4.0 introduced, and opens encrypted files that have incremental updates.
Nothing else changes from 7.4.0.
Packages
io7.FreePDFhttps://www.nuget.org/packages/io7.FreePDF/7.4.1io7.FreePDF.Layouthttps://www.nuget.org/packages/io7.FreePDF.Layout/7.4.1io7.FreePDF.Gdihttps://www.nuget.org/packages/io7.FreePDF.Gdi/7.4.1io7.FreePDF.Layout.Gdihttps://www.nuget.org/packages/io7.FreePDF.Layout.Gdi/7.4.1io7.FreePDF.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Wpf/7.4.1io7.FreePDF.Layout.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Layout.Wpf/7.4.1
dotnet add package io7.FreePDF --version 7.4.1
dotnet add package io7.FreePDF.Layout --version 7.4.1
Fixed
- Signatures broken by inherited page values. Opening a document copies the
/Resources,/MediaBox,/CropBoxand/Rotatea page inherits from the
page tree onto the page. When anAppend-mode update wrote such a page, for
example to add a signature or a comment, it wrote those copies too. The page's
keys changed, and pyHanko judged the earlier signatures invalid. The update
now leaves out unchanged copies; values the application changes are still
written. Files from Ghostscript, xelatex and others put these values on the
page tree: 15 of 2,950 corpus files were affected. - Signed forms with combo boxes and list boxes. Selecting an option of a
combo box or a single-selection list box added the selected index (/I),
which ISO 32000-1 says should not be used for such fields. The field's keys
changed, and pyHanko judged a signature on the form invalid after the fill.
/Iis now written only for multiple selection, when options share an export
value, or when the field already has one. - Encrypted files with incremental updates. An encrypted PDF whose update
names the encryption dictionary again, as Acrobat and pyHanko write it (a
signed encrypted PDF, for example), could not be opened inImportor
Modifymode (InvalidOperationException). - Signature appearance not printed. A new signature field's widget had the
flags Print and Locked in/Ff(field flags) instead of/F(annotation
flags), so its appearance did not print. - Bookmarks with both
/Destand/A. Saving added a/Destto every
bookmark that leads to its page through a GoTo action, which ISO 32000-1
forbids: 948 bookmarks in 264 of 2,950 corpus files. A bookmark now keeps its
action while its destination is unchanged; a new or changed destination
replaces the action with a/Dest, and an action the application sets is kept. - Drawing on a page whose inherited
/Resourcesis a direct dictionary threw
NullReferenceExceptioninModifyandAppendmode.
Upgrading from 7.4.0
Update all FreePDF packages to 7.4.1 together and rebuild; the assembly version
is now 7.4.1.0. The public API is unchanged. Behaviour changes to check:
- Single-selection choice fields no longer get
/Iwhen an option is selected. - A bookmark whose destination you change, or that you give a new
DestinationPage, loses its action (/A) in favour of a/Dest; a bookmark
with an action you set yourself gets no/Dest.
Validation
All 7.4.0 release gates apply, plus:
- regression tests for each fix (bookmark actions, inherited page values in flat
and nested page trees, choice-field indices, signature widget flags, encrypted
files with updates using RC4-128, AES-128 and AES-256); 494 regression
tests in all; - pyHanko 0.29.1: a pyHanko-signed file whose page inherits its values,
countersigned by FreePDF, keeps both signatures VALID ("signing and form
filling"); filling a combo box and a list box in a form pyHanko certified is
"signing and form filling", VALID; a pyHanko-encrypted and signed form opens; - corpus of 2,950 files: no bookmark gets both entries and none leads to another
page; no updated page gains inherited keys (checked with pyHanko's revision
reader); the 13 encrypted files open as before; - an independent review of each fix, whose findings were fixed before release;
of the review's 77 encrypted samples, every one whose updates name the
encryption dictionary (pyHanko AES and RC4 with tables, streams and object
streams, up to three updates, and hand-made cases) opens with the right
content; deliberately damaged ones fail as in 7.4.0, and an update that leaves
/Encryptout of its trailer reads as unencrypted, as in mutool and poppler.
The attached packages are the verified CI artifacts from source revision 31254c1d9e32ec0d657d4ed729da1dad8a44daf9, 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/41811189504 (core),
https://github.com/io7/FreePDF/actions/runs/41811189563 (packages/API/independent checks),
https://github.com/io7/FreePDF/actions/runs/41811189668 (comparisons),
https://github.com/io7/FreePDF/actions/runs/41812552650 (NuGet publication).
FreePDF 7.4.0
FreePDF 7.4.0
FreePDF 7.4.0 saves documents as incremental updates, so a signed PDF can be
filled in or signed again without breaking the signatures already in it.
Packages
io7.FreePDFhttps://www.nuget.org/packages/io7.FreePDF/7.4.0io7.FreePDF.Layouthttps://www.nuget.org/packages/io7.FreePDF.Layout/7.4.0io7.FreePDF.Gdihttps://www.nuget.org/packages/io7.FreePDF.Gdi/7.4.0io7.FreePDF.Layout.Gdihttps://www.nuget.org/packages/io7.FreePDF.Layout.Gdi/7.4.0io7.FreePDF.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Wpf/7.4.0io7.FreePDF.Layout.Wpfhttps://www.nuget.org/packages/io7.FreePDF.Layout.Wpf/7.4.0
dotnet add package io7.FreePDF --version 7.4.0
dotnet add package io7.FreePDF.Layout --version 7.4.0
New
-
Incremental save with
PdfDocumentOpenMode.Append. PDFsharp 6.2.4 always
writes a new file: saving a signed document after any change breaks its
signatures, and a signed document cannot be signed a second time. A document
opened inAppendmode is saved as the original file, byte for byte, followed
by one update that holds only the new and changed objects:using var document = PdfReader.Open("signed.pdf", PdfDocumentOpenMode.Append); ((PdfFormTextField)document.Catalog.GetAcroForm()!.Fields["name"]!).Text = "Ada Lovelace"; DigitalSignatureHandler.ForDocument(document, signer, options); // optional: sign it again document.Save("signed-filled.pdf");
- The signatures in the file stay valid. A new signature covers the whole
file, earlier signatures included. - The update ends with a cross-reference table, or a cross-reference stream
when the file uses one, and a trailer that points back to the previous one.
Object numbers, the first file identifier and the page tree stay as they are. - A document with no changes is saved exactly as it was. Otherwise the
document information gets a new modification date (unless you set one) and
FreePDF as its producer. - Pages can be added and removed when all pages hang directly under the page
tree's root, which is the common case. When the page tree is nested,
inserting, removing or moving pages throws before anything changes.
- The signatures in the file stay valid. A new signature covers the whole
Fixed
- Opening a form set a new modification date (
/M) on every field that is also
a widget. The date was written on the next save although the field had not
changed, and signature validators count it as an unexplained change. Fields
now keep their own date; only new widgets get one.
Upgrading from 7.3.x
Update all FreePDF packages to 7.4.0 together and rebuild; the assembly version
is now 7.4.0.0. PdfDocumentOpenMode.Append is new; documents opened in
Modify mode are saved as before, as a new file. Behaviour change to check:
- A form field that is also a widget keeps its modification date when a
document is opened and saved inModifymode. Up to 7.3.0 it got the time the
document was opened.
Limits of Append mode in 7.4.0:
- Encrypted documents cannot be opened in
Appendmode, nor can files whose
cross-reference table names the wrong objects; open those withModifyto
save a new file. A document opened inAppendmode cannot be given a password. - FreePDF does not check a certification signature's permissions (DocMDP); a
document certified with "no changes allowed" should not be changed at all. - Whether a validator accepts a change after a signature depends on the
signature's permissions and on the validator. Filling in fields and adding
signatures are permitted for an ordinary (approval) signature. Acrobat permits
added comments, but pyHanko reports any added comment. - Reading
page.Annotations,document.Outlinesordocument.ViewerPreferences
creates them when the file has none, and saving writes that as a change.
Validation
All 7.3.0 release gates apply, plus:
- regression tests for incremental saving: the original bytes are kept, only
changed objects are written, an unchanged document is saved byte for byte,
object numbers and the first identifier are kept, table and stream updates,
saving to the file it was opened from, page insertion in flat and nested page
trees, refusals, and a signature made before filling in a field or before a
second signature that still verifies (CMS over the byte range); 458
regression tests in all; - poppler's
pdfsigreported every signature valid in signed, filled,
annotated and countersigned files, and pyHanko 0.29.1's difference analysis
rated filling in and countersigning as "signing and form filling"; - a file signed by pyHanko, with cross-reference streams, was countersigned by
FreePDF, and both validators accept both signatures; - every readable file of a 2,950-file veraPDF and producer corpus (2,930) was
saved unchanged with identical bytes, and with one change writing only the
changed objects; the 13 encrypted files were refused; mutool parsed a sample
of 290 updated files, with one warning that comes from the original file; - an independent review of the incremental save code, whose findings were fixed
or documented before release.
See the migration guide's "Signed documents" section (https://github.com/io7/FreePDF/blob/8985eab0d57aa774b3771050b5222f09a7b898cd/docs/migrating-to-7.md#signed-documents)
and the support policy (https://github.com/io7/FreePDF/blob/8985eab0d57aa774b3771050b5222f09a7b898cd/docs/support-policy.md).
The attached packages are the verified CI artifacts from source revision 8985eab0d57aa774b3771050b5222f09a7b898cd, 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/41789366099 (core),
https://github.com/io7/FreePDF/actions/runs/41789366102 (packages/API/independent checks),
https://github.com/io7/FreePDF/actions/runs/41789366178 (comparisons),
https://github.com/io7/FreePDF/actions/runs/41790809228 (NuGet publication).