Signature Shapes
Single, multi-shared, and multi-chained signatures, certification (DocMDP), multi-party re-signing, and explicit field-lock
How many signature fields a document gets, whether the first signature certifies the document, and how a second party can safely add another signature on top — this page covers the shape of the signature layer itself, independent of what's inside each signature. Every recipe is a self-contained snippet for your own project; see Cookbook for how they're written.
Recipes
| Recipe | What it shows | Feature |
|---|---|---|
multi_signature | Single vs MultiShared vs MultiChained | pfx |
certify_and_lock | DocMDP: approval vs Locked / FormsAndSignatures / …Annotations | pfx |
certified_variants | Certified Single (1 panel entry) / MultiShared (N entries) / + encrypted | pfx |
certify_then_approve | Certify (P3) then a second party appends an approval signature; both valid | pfx |
resign_multiparty | Auto-append: signer A then B; plain and password-protected | pfx |
field_lock | Explicit FieldMDP /Action All field-lock via add_locked_signature | pfx |
Single vs MultiShared vs MultiChained
Method | Result |
|---|---|
Single | ONE signature; a clickable widget on every resolved page (one revision). |
MultiShared | N fields sharing ONE signature value (one revision; Adobe shows N fields). |
MultiChained | N independent signatures, each its own revision (e.g. multi-party). |
use mudrit::prelude::*;
let signer = PfxSigner::from_file("signer.pfx", "password")?;
let cfg = SignConfig::builder()
.method(Method::MultiChained)
.place("F", [350, 60, 560, 160])?
.place("5", [350, 60, 560, 160])?
.place("L", [350, 60, 560, 160])?
.timestamp(Timestamp::url(mudrit::DEFAULT_TSA_URL))
.ltv(true)
.build();
let signed = sign_pdf(&pdf, &signer, &cfg)?;Repo example:
multi_signature— ships with the licensed source bundle.
Certification levels (DocMDP)
An approval signature (the default) just signs. A certifying signature — which must be the first signature — sets the document's DocMDP permission level:
| Config | DocMDP P | Meaning |
|---|---|---|
.approval() (default) | — | ordinary signature; more allowed |
.certify(Certify::Locked) | 1 | no changes allowed after |
.certify(Certify::FormsAndSignatures) | 2 | form-fill + signing allowed |
.certify(Certify::FormsSignaturesAndAnnotations) | 3 | + annotations allowed |
use mudrit::prelude::*;
let cfg = SignConfig::builder()
.place("F", [350, 60, 560, 160])?
.certify(Certify::Locked)
.timestamp(Timestamp::url(mudrit::DEFAULT_TSA_URL))
.ltv(true)
.build();
let signed = sign_pdf(&pdf, &signer, &cfg)?;Repo examples:
certify_and_lock,certified_variants(source bundle). See the Certifying guide.
Certify, then a second party approves
A P2/P3 certification permits later signatures — a P1 Locked certification would forbid
them and any later signature would invalidate it. sign_pdf detects the already-signed input and
auto-appends the approval as a new revision, so both signatures stay valid.
use mudrit::prelude::*;
// 1) authority certifies at P3 — a later VISIBLE signature adds a widget annotation, which
// needs P3 (FormsSignaturesAndAnnotations); an invisible later signature could use P2.
let certify_cfg = SignConfig::builder()
.place("F", [350, 60, 560, 160])?
.certify(Certify::FormsSignaturesAndAnnotations)
.ltv(true)
.timestamp(Timestamp::url(mudrit::DEFAULT_TSA_URL))
.build();
let certified = sign_pdf(&pdf, &signer, &certify_cfg)?;
// 2) a second party appends an ordinary approval — auto-detected as a new revision
let approve_cfg = SignConfig::builder().place("F", [40, 60, 250, 160])?.approval().build();
let signed = sign_pdf(&certified, &second_signer, &approve_cfg)?;If the prior signature was password-protected, pass the same password via
PdfReader::from(bytes).password("…") and the appended revision is encrypted with the document's own
key.
Repo examples:
certify_then_approve,resign_multiparty(source bundle).
Explicit field-lock
add_locked_signature is the lower-level alternative: it appends a signature carrying an explicit
FieldMDP /Action All reference (no document-level DocMDP change) — use it to lock form fields
without certifying the whole document. Pass a TSA URL for a timestamp, or None for none.
use mudrit::prelude::*;
let locked = add_locked_signature(
&already_signed_pdf,
1, // page
[350, 60, 560, 160], // rect
"DirectorSignature", // field name
&signer,
Some("http://timestamp.comodoca.com"), // TSA URL, or None
)?;Repo example:
field_lock(source bundle). Seeadd_locked_signature.
Certification order and re-signing
.certify(...) must be the first signature — certifying an already-signed document is rejected.
Multi-party flows rely on sign_pdf's auto-append: passing an already-signed PDF back into sign_pdf
appends a new revision instead of rebuilding the document, so earlier signatures stay valid. If the
prior signature was password-protected, pass the same password via PdfReader(...).password(...) and
the appended revision is encrypted with the document's own key.
Test cert; network for timestamp/LTV
The source-bundle programs use the bundled samples/ABC12.pfx test certificate — supply your own
DSC in your project. Recipes that add .timestamp(...) or .ltv(true) need outbound network for the
TSA / CRL / OCSP fetch.