Skip to content
Sign and Configure DocMDP Restriction with Invalid Changes

Sign and Configure DocMDP Restriction with Invalid Changes

This is the counterpart to the valid changes guide: the same certified document, but the later revision adds a page instead of an annotation. Page changes are rejected at every DocMDP level, so validation reports the signature as invalid and lists what went wrong.

The revisions, numbered from zero as the diff report numbers them:

RevisionWhat happens
0The original document.
1addSignature certifies it at mdp.FillFormsAndAnnots.
2addSomeInvalidChanges appends a blank page.

The rejected change

appender, err := model.NewPdfAppender(reader)
if err != nil {
    return err
}

appender.AddPages(model.NewPdfPage())

if err := appender.WriteToFile(outputPath); err != nil {
    return err
}

Nothing here fails or warns. The appender writes the page happily; the change is only rejected later, when a validator compares the revisions. Certifying a document does not stop anyone from editing it, it only makes the edit detectable.

Adding a page produces two errors, because the default diff policy notices both the new page and the change to the page tree that had to accompany it. That is also why the level makes no difference: the page tree checks in the default policy are unconditional, so a page added, removed or replaced is an error even at mdp.NoRestrictions.

What counts as a rejected change

The default policy compares pages, the page tree, annotations and form fields between revisions. Within that scope, and leaving aside the page tree, the level decides:

  • mdp.NoChanges rejects everything tracked.
  • mdp.FillForms rejects removing a form field, adding or changing an annotation that is not a widget, and removing any annotation at all, widgets included.
  • mdp.FillFormsAndAnnots rejects removing a form field, and nothing else.
  • mdp.NoRestrictions rejects nothing outside the page tree.

Full details are in Sign and configure DocMDP restriction.

Limitations

The scope cuts both ways. The default policy documents itself as comparing only pages, the page tree, annotations and form fields, so a later revision that rewrites a page’s content stream, changing the text a reader sees, is not reported at any level. If detecting that matters, implement mdp.DiffPolicy and pass it through ValidateWithOpts; the default policy will not do it, and reader.ValidateSignatures cannot be given a custom policy.

Errors carry a revision number and a description, and nothing else. There is no object number or diff to inspect programmatically, so a report saying Pages were changed is as specific as the SDK gets.

Run the example

addSignature certifies the document, addSomeInvalidChanges adds the page, and ValidateFile reopens the file and prints the outcome.

git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/signatures
go run pdf_sign_docmdp_invalid_changes.go <INPUT_PDF_PATH> <OUTPUT_PDF_PATH>

If this is your first time using UniPDF, follow the getting started guide to create an API key and set up your development environment.

View the full source

Sample output

Run against a one-page input:

== Signature 1
Name: Test Signature Appearance Name
Date: 2026-08-04 00:12:32 +0700 UTC+0700
Reason: TestSignatureAppearance Reason
Location not specified
Contact info not specified
Fields: 1
Signed: Document is signed
Signature validation: Is invalid
Trusted: Untrusted certificate
diff is permitted: false
MDP errors:
    Pages were changed in revisions #2
    page #2 was added in revisions #2
Revocation data: CRL not found
Revocation data: OCSP not found
Done

The page number in the second error is the position of the added page, so it tracks the input’s page count.

Signed with DocMDP for Invalid Changes

Last updated on