From 82c6046e1d57cd30d16ec278263fab1f7f9b38f9 Mon Sep 17 00:00:00 2001 From: calixteman Date: Tue, 14 Jul 2026 22:38:43 +0200 Subject: [PATCH] Fix signature fields with widget children --- src/core/document.js | 23 +++++++++++------------ test/unit/document_spec.js | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/core/document.js b/src/core/document.js index 5f6b9b743..5b99b09e5 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -2009,20 +2009,19 @@ class PDFDocument { if (!(field instanceof Dict)) { continue; } + if (isName(field.get("FT"), "Sig")) { + const sigDict = this.xref.fetchIfRef(field.get("V")); + if (sigDict instanceof Dict) { + const parsed = this.#parseSignatureDict(field, sigDict, fieldRef); + if (parsed) { + out.push(parsed); + } + } + } if (field.has("Kids")) { + // A terminal field can have Widget annotations as children, so its + // own signature must be collected before walking the field tree. this.#collectSignatureFields(field.get("Kids"), out, visitedRefs); - continue; - } - if (!isName(field.get("FT"), "Sig")) { - continue; - } - const sigDict = this.xref.fetchIfRef(field.get("V")); - if (!(sigDict instanceof Dict)) { - continue; - } - const parsed = this.#parseSignatureDict(field, sigDict, fieldRef); - if (parsed) { - out.push(parsed); } } } diff --git a/test/unit/document_spec.js b/test/unit/document_spec.js index f07afb7f1..53e599657 100644 --- a/test/unit/document_spec.js +++ b/test/unit/document_spec.js @@ -312,6 +312,39 @@ describe("document", function () { expect(signatures[0].signerName).toEqual("John Smith"); }); + it("extracts signature fields with Widget children", async function () { + const acroForm = new Dict(); + acroForm.set("SigFlags", 3); + + const sigRef = Ref.get(33, 0); + const sigFieldRef = Ref.get(34, 0); + const widgetRef = Ref.get(35, 0); + + const sigDict = makeSigDict({ + byteRange: [0, 50, 100, 150], + name: "Alice", + }); + const sigField = makeSigField({ T: "sig_alice", sigRef }); + sigField.set("Kids", [widgetRef]); + + const widget = new Dict(); + widget.set("Subtype", Name.get("Widget")); + widget.set("Parent", sigFieldRef); + + const xref = new XRefMock([ + { ref: sigRef, data: sigDict }, + { ref: sigFieldRef, data: sigField }, + { ref: widgetRef, data: widget }, + ]); + acroForm.set("Fields", [sigFieldRef]); + + const pdfDocument = getDocument(acroForm, xref); + const signatures = await pdfDocument.signatures; + expect(signatures.length).toEqual(1); + expect(signatures[0].fieldName).toEqual("sig_alice"); + expect(signatures[0].signerName).toEqual("Alice"); + }); + it("skips signatures with malformed ByteRange", async function () { const acroForm = new Dict(); acroForm.set("SigFlags", 3);