Ask Anvil

Answers to questions about automating PDFs, e-signatures, Webforms, and other paperwork problems.
E-signatures
Categories

How do I extract the signer name and signing time from a signed PDF?

Walk the AcroForm fields, keep the ones whose field type is /Sig, and read the signature dictionary hanging off /V.

from datetime import datetime
from pypdf import PdfReader

def parse_pdf_date(raw):
    # PDF dates look like D:20260415133012-07'00'
    s = str(raw).removeprefix("D:").replace("'", "")
    return datetime.strptime(s, "%Y%m%d%H%M%S%z")

reader = PdfReader("signed.pdf")
acroform = reader.trailer["/Root"].get("/AcroForm")

for field in acroform.get("/Fields", []):
    field = field.get_object()
    if field.get("/FT") != "/Sig":
        continue
    sig = field.get("/V")
    if sig is None:
        print(f"{field.get('/T')}: field present but unsigned")
        continue
    sig = sig.get_object()
    print("field:    ", field.get("/T"))
    print("signer:   ", sig.get("/Name"))
    print("signed at:", parse_pdf_date(sig.get("/M")))
    print("reason:   ", sig.get("/Reason"))
    print("location: ", sig.get("/Location"))
    print("subfilter:", sig.get("/SubFilter"))
    print("covers:   ", sig.get("/ByteRange"))

What the fields mean

A signature field is an ordinary form field with /FT set to /Sig. If it has never been signed, /V is absent, which is a cheap way to tell a prepared-but-unsigned document from a completed one. When it has been signed, /V points at the signature dictionary: /Name is the signer as recorded by the signing software, /M is the claimed signing time, /Reason and /Location are optional free text, /SubFilter names the signature flavor (for example an ETSI PAdES profile), and /ByteRange lists the byte offsets the signature actually covers.

Dates are the one annoyance. PDF stores them as a string like D:20260415133012-07'00', so strip the prefix and the apostrophes before handing it to strptime, as above.

Two caveats

This reads what the signature says. It does not validate it. Per ISO 32000-2, the byte range digest "should be the entire PDF file, including the signature dictionary but excluding the signature value itself." So print /ByteRange and check it: if the last segment ends well before the end of the file, bytes were appended after signing and the signature does not cover them. Actually verifying the cryptography in /Contents is a separate job and pypdf will not do it for you.

Second, do not treat /M as proof of when signing happened. The byte range digest does cover the signature dictionary, so /M cannot be edited afterward without breaking the signature, but it is still whatever the signing software wrote there at the time. A time you can independently verify comes from a document timestamp signature, which the spec defines as its own signature type with its own dictionary, not from /M. If your audit trail needs a defensible timestamp, look for that instead.

Back to All Questions

The fastest way to build software for documents

Anvil Document SDK is a comprehensive toolbox for product teams launching document flows where PDF filling, signing, and complex conditional scenarios are necessary.
Explore Anvil
Anvil Webforms