pdfsign

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 6, 2026 License: BSD-2-Clause Imports: 25 Imported by: 0

README

Signing PDF files with Go

golangci-lint Go Report Card Coverage Status Go Reference

A PDF signing and verification library written in Go. This library provides both command-line tools and Go APIs for digitally signing and verifying PDF documents.

See also our PDFSigner, a more advanced digital signature server that is using this project.

Quick Start

# Sign a PDF
./pdfsign sign -name "John Doe" input.pdf output.pdf certificate.crt private_key.key

# Verify a PDF signature
./pdfsign verify document.pdf

Package Architecture

The library is organized into specialized subpackages, though most users will primarily interact with the root pdfsign package.

Package Purpose
pdfsign Primary entry point. Provides the fluent API for signing, verification, and document management.
extract Low-level signature inspection. Allows extracting raw PKCS#7 envelopes and signed data.
forms PDF form handling. Logic for field discovery and generating PDF object updates.
initials Configuration and placement logic for signing initials across multiple pages.
signers/... External Signers. Optional integrations for remote signing (CSC, KMS, PKCS#11).
fonts Font resource management and TrueType (TTF) metric parsing for accurate positioning.
images Image resource handling for visual signatures.
internal/... Private implementation details for PDF scanning and rendering.

PDF Signing

Command Line Usage
./pdfsign sign [options] <input.pdf> <output.pdf> <certificate.crt> <private_key.key> [chain.crt]
Signing Options
Option Type Default Description
-name string Name of the signatory
-location string Location of the signatory
-reason string Reason for signing
-contact string Contact information for signatory
-certType string CertificationSignature Certificate type: CertificationSignature, ApprovalSignature, DocumentTimestamp. A certification signature must be the first signature in a document; use ApprovalSignature to sign a document that is already signed or certified
-tsa string https://freetsa.org/tsr URL for Time-Stamp Authority
Signing Examples
# Basic signing
./pdfsign sign -name "John Doe" input.pdf output.pdf cert.crt key.key

# Signing with additional metadata
./pdfsign sign -name "John Doe" -location "New York" -reason "Document approval" input.pdf output.pdf cert.crt key.key

# Timestamp-only signature
./pdfsign sign -certType "DocumentTimestamp" input.pdf output.pdf

PDF Verification

Command Line Usage
./pdfsign verify [options] <input.pdf>
Verification Options
Option Type Default Description
-external bool false Enable external OCSP and CRL checking
-require-digital-signature bool true Require Digital Signature key usage in certificates
-require-non-repudiation bool false Require Non-Repudiation key usage in certificates (for highest security)
-trust-signature-time bool false Trust the signature time embedded in the PDF if no timestamp is present (untrusted by default)
-validate-timestamp-certs bool true Validate timestamp token certificates
-allow-untrusted-roots bool false Allow certificates embedded in the PDF to be used as trusted roots (use with caution)
-http-timeout duration 10s Timeout for external revocation checking requests
Verification Examples
# Basic verification (uses trusted embedded timestamps when present)
./pdfsign verify document.pdf

# Verification with external revocation checking
./pdfsign verify -external -http-timeout=30s document.pdf

# Verification trusting signature time as fallback
./pdfsign verify -trust-signature-time document.pdf

# Highest security verification (requires Non-Repudiation key usage)
./pdfsign verify -require-non-repudiation -external document.pdf

# Verification allowing self-signed certificates
./pdfsign verify -allow-untrusted-roots self-signed.pdf
Verification Output

The verification command outputs a JSON object with three top-level fields:

Field Description
document_info Document metadata (author, title, page count, ...)
signers One entry per signature found in the document, described below
valid Whether every signature in the document is valid

Each entry in signers has the following key fields:

Field Description
SignerName Name of the signatory, as recorded in the signature
SigningTime Time the signature claims to have been made
Reason / Location / Contact Signature metadata, as set at signing time
Certificate The signer's X.509 certificate (full crypto/x509.Certificate structure)
Timestamp Embedded RFC 3161 timestamp info (Time, Authority, Certificate), or null if none
Valid Whether the signature is valid overall (cryptographically valid, chain trusted, not revoked, and no other validation errors)
TrustedChain Whether the certificate chain is trusted (by system roots, TrustedRoots(), or TrustSelfSigned())
Revoked Whether the signer's certificate is revoked
TimestampValid Whether an embedded timestamp's certificate chain is trusted
Errors Validation errors that made the signature invalid, if any
Warnings Non-fatal warnings (e.g. using untrusted signature time as a fallback, an RFC-noncompliant revocation response Content-Type)
Go Library Usage

The pdfsign package provides a modern, fluent API for signing and verification.

Opening Documents

You can open PDFs from a file path or any io.ReaderAt.

// Open from file
doc, err := pdfsign.OpenFile("document.pdf")

// Open from memory (byte slice)
data, _ := os.ReadFile("document.pdf")
reader := bytes.NewReader(data)
doc, err := pdfsign.Open(reader, int64(len(data)))
Basic Signing
package main

import (
    "os"
    "github.com/digitorus/pdfsign"
)

func main() {
    doc, _ := pdfsign.OpenFile("contract.pdf")
    
    // Create visual appearance
    appearance := pdfsign.NewAppearance(250, 80)
    appearance.Text("Signed by: {{Name}}").Position(10, 60)
    
    // Configure and write
    output, _ := os.Create("signed.pdf")
    doc.Sign(privateKey, certificate).
        Reason("Approved").
        Location("New York").
        Appearance(appearance, 400, 50)
        
    _, err := doc.Write(output)
}
Form Filling

You can interact with PDF forms by listing, setting, and unsetting fields.

List Form Fields
fields := doc.FormFields()
for _, f := range fields {
    fmt.Printf("Field: %s (Type: %s, Value: %v)\n", f.Name, f.Type, f.Value)
}
Fill Form Fields

Form changes are applied when Write() is called, alongside any signatures.

// Set a text field
if err := doc.SetField("Full Name", "John Doe"); err != nil {
    log.Fatal(err)
}

// Unset/Clear a field
if err := doc.SetField("Comments", ""); err != nil {
    log.Fatal(err)
}

// Write the changes (with or without a signature)
// If you only want to fill forms without signing:
// doc.Write(output)

// If you want to sign AND fill:
doc.Sign(signer, cert).Reason("Form Filled").Write(output)
Signature Extraction

You can iterate over all signatures in a document to inspect their properties or extract raw data without performing full cryptographic verification.

// Iterate through signatures
for sig, err := range doc.Signatures() {
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Signer: %s\n", sig.Name())
    fmt.Printf("Filter: %s\n", sig.Filter())
    fmt.Printf("SubFilter: %s\n", sig.SubFilter())

    // Access raw PKCS#7 signature envelope
    envelope := sig.Contents()
    fmt.Printf("Signature size: %d bytes\n", len(envelope))

    // Read the actual bytes of the document covered by the signature
    reader, _ := sig.SignedData()
    data, _ := io.ReadAll(reader)
    fmt.Printf("Signed data size: %d bytes\n", len(data))
}
Initials
// Add initials to all pages (except page 1)
initials := pdfsign.NewAppearance(60, 40)

// Optional: Use custom font
// font := doc.AddFont("Handwriting", fontBytes)
// initials.Text("JD").Font(font, 24).Center()

initials.Text("{{Initials}}").Font(nil, 12).Center()

doc.AddInitials(initials).
    Position(pdfsign.BottomRight, 30, 30).
    ExcludePages(1)

Verification

The verification API uses a fluent builder pattern with lazy execution. Verification is triggered when you access result properties.

doc, _ := pdfsign.OpenFile("signed.pdf")

// Configure verification with chainable methods
result := doc.Verify().
    TrustSelfSigned(false).   // Security: Reject self-signed/untrusted CAs
    ExternalChecks(true).     // Enable online revocation checks
    MinRSAKeySize(2048)       // Enforce minimum key size

// Accessing .Valid() triggers the actual verification
if result.Valid() {
    fmt.Println("Document is valid!")
    for _, sig := range result.Signatures() {
        fmt.Printf("Signed by %s at %s\n", sig.SignerName, sig.SigningTime)
    }
}

// Check for errors
if result.Err() != nil {
    fmt.Printf("Verification error: %v\n", result.Err())
}
Strict Verification

Use Strict() to enable all security checks at once:

result := doc.Verify().Strict()  // Enables all security constraints

if result.Valid() {
    fmt.Printf("Document passed strict verification (%d signatures)\n", result.Count())
}
Compression

Optimize file size by configuring compression levels (powered by compress/zlib).

import "compress/zlib"

// ...
doc, _ := pdfsign.OpenFile("large.pdf")

// Options: zlib.DefaultCompression, zlib.BestCompression, zlib.BestSpeed, zlib.NoCompression
doc.SetCompression(zlib.BestCompression)
Custom Fonts

Embed TrueType fonts for realistic signatures and styling. Font metrics are automatically parsed for accurate text positioning.

fontData, _ := os.ReadFile("MySignatureFont.ttf")
customFont := doc.AddFont("MySig", fontData)

appearance := pdfsign.NewAppearance(200, 80)
appearance.Text("John Doe").Font(customFont, 24).Center()

Note: When you call AddFont() with TTF data, the library automatically parses glyph widths for accurate text measurement and centering.

Standard Appearance

Use the built-in standard appearance for a professional signature with metadata:

// Standard appearance with Name, Reason, Location, and Date
appearance := pdfsign.NewAppearance(300, 100).Standard()

doc.Sign(signer, cert).
    SignerName("John Doe").
    Reason("Contract Agreement").
    Location("Amsterdam, NL").
    Appearance(appearance, 100, 100)

The Standard() method automatically adds:

  • Signer name (larger font)
  • Reason
  • Location
  • Date

Template variables ({{Name}}, {{Reason}}, {{Location}}, {{Date}}) are expanded at render time.

Crypto Settings

Configure hash algorithms and use custom signers (HSM, Tokens, Cloud):

import "crypto"

// Custom hash algorithm (default: SHA256)
doc.Sign(signer, cert).
    Digest(crypto.SHA384)  // or crypto.SHA512

// HSM/Token signing - any crypto.Signer works
hsmSigner := pkcs11.NewSigner(slot, pin)  // Your HSM implementation
doc.Sign(hsmSigner, cert).
    Digest(crypto.SHA256)

The signature algorithm is determined by your crypto.Signer implementation (RSA, ECDSA, Ed25519, or ML-DSA).

Post-quantum-only signing (ML-DSA)

Go 1.27 and the current digitorus/pkcs7 dependency support ML-DSA-44, ML-DSA-65, and ML-DSA-87 CMS signatures. To avoid a hidden classical public-key signature, every asymmetric component must use ML-DSA: the PDF signer, issuing CA chain, CRL or OCSP responder, and RFC 3161 TSA chain. A PQC signer combined with an RSA/ECDSA TSA or CA is not a post-quantum-only signature path.

Assuming privateKey is an *mldsa.PrivateKey, certificate and every entry in intermediates use ML-DSA, and pqcTSAURL serves ML-DSA-signed timestamp tokens:

doc, err := pdfsign.OpenFile("contract.pdf")
if err != nil {
    log.Fatal(err)
}

// PAdES B-T adds an RFC 3161 signature-time-stamp. SHA-512 is used as the
// interoperable RFC 9882 CMS digest for all supported ML-DSA parameter sets.
doc.Sign(privateKey, certificate, intermediates...).
    Format(pdfsign.PAdES_B_T).
    Digest(crypto.SHA512).
    Timestamp(pqcTSAURL).
    Reason("Post-quantum approval")

output, err := os.Create("contract-pqc-signed.pdf")
if err != nil {
    log.Fatal(err)
}
defer output.Close()
if _, err := doc.Write(output); err != nil {
    log.Fatal(err)
}

The library selects SHA-512 automatically for an ML-DSA signer; specifying it explicitly makes the cryptographic policy visible. A different Digest is rejected because the PDF /DigestMethod must agree with the CMS emitted by the PKCS#7 implementation. ML-DSA AlgorithmIdentifier parameters are absent, and the CMS signature uses RFC 9882 pure mode.

Verify the whole signer and TSA paths under an ML-DSA-only policy, and enable the PQC OCSP/CRL services published by the certificate:

signed, err := pdfsign.OpenFile("contract-pqc-signed.pdf")
if err != nil {
    log.Fatal(err)
}

result := signed.Verify().
    TrustedRoots(pqcRoots).
    AllowedAlgorithms(x509.MLDSA).
    ValidateFullChain(true).
    ValidateTimestampCertificates(true).
    ExternalChecks(true)

if !result.Valid() || !result.Signatures()[0].TimestampValid {
    log.Fatal("the ML-DSA signature or timestamp path is invalid")
}

For ML-DSA chains, OCSP CertIDs use SHA-512 and ML-DSA-signed OCSP responses and CRLs are verified with the issuer. The command-line key loader accepts ML-DSA private keys in unencrypted PKCS#8 PEM (BEGIN PRIVATE KEY) format.

This support does not add a classical fallback, hybrid/composite signatures, SLH-DSA, or PAdES B-LT/B-LTA. PAdES B-T revocation information is checked online when ExternalChecks(true) is enabled; it is not embedded for offline long-term validation. PDF validator support for RFC 9882 is still emerging, so test every relying-party product you intend to support.

References: FIPS 204, RFC 9881, RFC 9882, and the Go 1.27 release notes.

Timeouts and Cancellation

Signing (with a TSA) and verification (with ExternalChecks) can make outbound HTTP requests to a Time-Stamp Authority, OCSP responder, or CRL distribution point. Both SignBuilder and VerifyBuilder accept a context.Context to bound these requests, in addition to VerifyBuilder.HTTPTimeout:

ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()

doc.Sign(signer, cert).
    Timestamp(tsaURL).
    Context(ctx) // aborts the TSA request if ctx is cancelled/expires

result := doc.Verify().
    ExternalChecks(true).
    Context(ctx) // aborts in-flight OCSP/CRL requests if ctx is cancelled/expires

This is also how the CLI ties Ctrl-C to an immediate abort of a stuck TSA/OCSP/CRL request, via signal.NotifyContext, instead of waiting out the full timeout.

External Signers (Integrations)

The signers/ directory contains "best-effort" implementations and skeletons for various external signing systems. These are provided as examples to help you integrate with hardware security modules and cloud services.

Cloud Signing (CSC API)

Sign documents using a remote Cloud Signature Consortium (CSC) compliant service:

import "github.com/digitorus/pdfsign/signers/csc"

// Connect to your CSC-compliant signing service
signer, _ := csc.NewSigner(csc.Config{
    BaseURL:      "https://signing-service.example.com/csc/v1",
    CredentialID: "my-signing-key",
    AuthToken:    "Bearer ey...",
    PIN:          "123456",  // Optional
})

doc.Sign(signer, cert).Reason("Cloud Signed")
Cloud KMS and PKCS#11

We provide functional implementations for major cloud providers and HSMs. Each integration is a standalone module to minimize core dependencies.

import (
    "github.com/digitorus/pdfsign/signers/aws"
    "github.com/digitorus/pdfsign/signers/gcp"
    "github.com/digitorus/pdfsign/signers/azure"
    "github.com/digitorus/pdfsign/signers/pkcs11"
)

// Example: AWS KMS Signer
// client is a *kms.Client from aws-sdk-go-v2
awsSigner, _ := aws.NewSigner(client, "key-id-or-arn", publicKey)
doc.Sign(awsSigner, cert)

// Example: Google Cloud KMS Signer
// client is a *kms.KeyManagementClient
gcpSigner, _ := gcp.NewSigner(client, "key-resource-name", publicKey)
doc.Sign(gcpSigner, cert)

// Example: Azure Key Vault Signer
// client is a *azkeys.Client
azureSigner, _ := azure.NewSigner(client, "key-name", "key-version", publicKey)
doc.Sign(azureSigner, cert)

// Example: PKCS#11 / HSM Signer
hsmSigner, _ := pkcs11.NewSigner("/usr/lib/libpkcs11.so", "token-label", "key-label", "pin", publicKey)
doc.Sign(hsmSigner, cert)

[!TIP] Each integration is designed to be a standalone module. This prevents your core application from dragging in heavy cloud SDKs unless you specifically import and use them.

Development

DSS Validation

For automated validation of signed PDFs against European standards, we use the Digital Signature Service (DSS).

Local Setup

A local DSS instance is required for running the signature validation tests. You can set it up using the provided script, which supports both Docker and Apple's native container CLI:

[!NOTE] On Apple Silicon (M1/M2/M3), the container tool requires Rosetta 2. If you haven't installed it yet, you can do so with: softwareupdate --install-rosetta --agree-to-license

# Build and start the DSS service
./scripts/setup-dss.sh

The script will:

  1. Detect if container (Apple Silicon native) or docker is available.
  2. Build the dss-validator image from source.
  3. Start the service on http://localhost:8080.
Running Validation Tests

Once the DSS service is running, you can run the validation tests:

export DSS_API_URL=http://localhost:8080/services/rest/validation/v2/validateSignature
go test -v ./sign -run TestSignDSSValidation

Legacy API (Deprecated)

The old sign.SignFile and verify.VerifyFile APIs are deprecated. Please migrate to the pdfsign package.

Documentation

Overview

Package pdfsign provides tools for signing and verifying PDF documents. It supports PAdES, C2PA, and JAdES signature formats with configurable visual appearances, form filling, and PDF/A compliance.

Basic usage:

doc, err := pdf.OpenFile("document.pdf")
if err != nil {
    log.Fatal(err)
}

doc.Sign(signer, cert).
    Reason("Approved").
    Location("Amsterdam")

result, err := doc.Write(output)

See https://www.etsi.org/deliver/etsi_en/319100_319199/31914201/ for PAdES specification.

Example (VerifyStrict)

Example_verifyStrict demonstrates strict verification mode.

package main

import (
	"fmt"
	"log"

	"github.com/digitorus/pdfsign"
)

func main() {
	doc, err := pdfsign.OpenFile("testfiles/testfile_multi.pdf")
	if err != nil {
		log.Fatal(err)
	}

	// Strict() enables all security checks
	result := doc.Verify().Strict()

	fmt.Printf("Found %d signatures\n", result.Count())
	fmt.Printf("All valid: %v\n", result.Valid())

}
Output:
Found 3 signatures
All valid: false

Index

Examples

Constants

View Source
const (
	// AlignLeft aligns text to the left.
	AlignLeft = render.AlignLeft
	// AlignCenter aligns text to the center.
	AlignCenter = render.AlignCenter
	// AlignRight aligns text to the right.
	AlignRight = render.AlignRight

	// ScaleStretch stretches the image to fill the rectangle.
	ScaleStretch = render.ScaleStretch
	// ScaleFit proportionally scales the image to fit within the rectangle.
	ScaleFit = render.ScaleFit
	// ScaleFill proportionally scales the image to fill the rectangle (may crop).
	ScaleFill = render.ScaleFill
)
View Source
const (
	// TopLeft positions at top-left corner.
	TopLeft = initials.TopLeft
	// TopRight positions at top-right corner.
	TopRight = initials.TopRight
	// BottomLeft positions at bottom-left corner.
	BottomLeft = initials.BottomLeft
	// BottomRight positions at bottom-right corner.
	BottomRight = initials.BottomRight
)
View Source
const (

	// Millimeter represents the number of PDF user space units in one millimeter.
	Millimeter = 72.0 / 25.4
	// Centimeter represents the number of PDF user space units in one centimeter.
	Centimeter = 72.0 / 2.54
	// Inch represents the number of PDF user space units in one inch.
	Inch = 72.0
)
View Source
const (
	// Helvetica is the standard Helvetica font.
	Helvetica = fonts.Helvetica
	// HelveticaBold is bold Helvetica.
	HelveticaBold = fonts.HelveticaBold
	// HelveticaOblique is oblique Helvetica.
	HelveticaOblique = fonts.HelveticaOblique
	// TimesRoman is Times Roman font.
	TimesRoman = fonts.TimesRoman
	// TimesBold is bold Times Roman.
	TimesBold = fonts.TimesBold
	// Courier is Courier font.
	Courier = fonts.Courier
	// CourierBold is bold Courier.
	CourierBold = fonts.CourierBold
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Appearance

type Appearance struct {
	// contains filtered or unexported fields
}

Appearance represents the visual elements of a signature widget (text, images, shapes). All dimensions and coordinates within an Appearance are in PDF user space units (typically 1/72 inch).

Example (Advanced)

ExampleAppearance_advanced demonstrates building a complex visual signature and verifying it.

package main

import (
	"bytes"
	"fmt"
	"log"

	"github.com/digitorus/pdfsign"
	"github.com/digitorus/pdfsign/internal/testpki"
)

func main() {
	// Setup: Create a PKI environment
	pki := testpki.NewTestPKI(nil)
	pki.StartCRLServer()
	defer pki.Close()
	key, cert := pki.IssueLeaf("Advanced Signer")

	// Open a PDF to sign
	docToSign, _ := pdfsign.OpenFile("testfiles/testfile_form.pdf")

	// Create appearance with 300x100 dimensions
	app := pdfsign.NewAppearance(300, 100)

	// 1. Add Background and Border
	app.Background(240, 240, 240)  // Light Gray
	app.Border(1.0, 100, 100, 100) // Dark Gray Border

	// 2. Add styled text
	app.Text("Digitally Signed").
		Font(nil, 10). // Standard font (Helvetica)
		SetColor(50, 50, 50).
		Position(10, 80)

	// 3. Add dynamic variables
	app.Text("{{Name}}").
		Font(pdfsign.StandardFont(pdfsign.HelveticaBold), 14).
		SetColor(0, 0, 0).
		Position(10, 60)

	app.Text("Date: {{Date}}").
		Font(nil, 9).
		Position(10, 40)

	// 4. Add a "Seal" (simulated with text here)
	app.Text("[ SEAL ]").
		Font(pdfsign.StandardFont(pdfsign.Courier), 12).
		SetColor(0, 0, 128).
		Position(220, 40)

	// Sign the document with this appearance
	docToSign.Sign(key, cert, pki.Chain()...).Appearance(app, 100, 300)

	// Write signature
	var signedBuffer bytes.Buffer
	if _, err := docToSign.Write(&signedBuffer); err != nil {
		log.Fatal(err)
	}

	// Verify the result
	doc, err := pdfsign.Open(bytes.NewReader(signedBuffer.Bytes()), int64(signedBuffer.Len()))
	if err != nil {
		log.Fatal(err)
	}

	result := doc.Verify().TrustSelfSigned(true) // Trust our test PKI

	fmt.Printf("Signature valid: %v\n", result.Valid())
	fmt.Printf("Signer: %s\n", result.Signatures()[0].SignerName)

}
Output:
Signature valid: true
Signer: Advanced Signer

func NewAppearance

func NewAppearance(width, height float64) *Appearance

NewAppearance initializes a new signature appearance box with the given width and height. Dimensions are in PDF user space units (typically 1/72 inch). You can use the Millimeter or Centimeter constants for conversion (e.g., pdfsign.Millimeter * 50).

Example

ExampleNewAppearance demonstrates creating a basic appearance.

package main

import (
	"fmt"

	"github.com/digitorus/pdfsign"
)

func main() {
	app := pdfsign.NewAppearance(200, 80)
	app.Text("Digitally Signed").Position(10, 40)

	fmt.Printf("Appearance: %gx%g\n", app.Width(), app.Height())
}
Output:
Appearance: 200x80

func (*Appearance) Background

func (a *Appearance) Background(r, g, b uint8) *Appearance

Background sets the fill color for the signature widget background.

func (*Appearance) Border

func (a *Appearance) Border(width float64, r, g, b uint8) *Appearance

Border draws a rectangular border around the signature widget with the specified width and RGB color.

func (*Appearance) Circle

func (a *Appearance) Circle(cx, cy, r float64) *ShapeBuilder

Circle adds a circle centered at (cx, cy) with radius r. Style with Fill(), Stroke(), StrokeWidth() - order doesn't matter.

func (*Appearance) DrawRect

func (a *Appearance) DrawRect(x, y, width, height float64) *ShapeBuilder

DrawRect adds a rectangle at (x, y) with given dimensions. Style with Fill(), Stroke(), StrokeWidth() - order doesn't matter.

func (*Appearance) Height

func (a *Appearance) Height() float64

Height returns the appearance height.

func (*Appearance) Image

func (a *Appearance) Image(img *Image) *ImageBuilder

Image adds a raster image element (JPEG, PNG) to the appearance. Returns an ImageBuilder to configure position, size, and scaling.

func (*Appearance) Line

func (a *Appearance) Line(x1, y1, x2, y2 float64) *LineBuilder

Line adds a line from (x1,y1) to (x2,y2).

func (*Appearance) PDFObject

func (a *Appearance) PDFObject(data []byte) *PDFObjectBuilder

PDFObject adds a PDF page as a vector graphic element (Form XObject). This is useful for embedding vector graphics, logos, or other PDF content. The first page (page 1) is used by default.

func (*Appearance) RenderInfo

func (a *Appearance) RenderInfo() *render.AppearanceInfo

RenderInfo returns the internal representation of the appearance for rendering.

func (*Appearance) Standard

func (a *Appearance) Standard() *Appearance

Standard populates the appearance with a professional signature layout. It displays the signer's name prominently, followed by the reason, location, and signing date.

Template variables ({{Name}}, {{Reason}}, {{Location}}, {{Date}}) are automatically expanded with the values from the SignBuilder.

Example:

app := pdf.NewAppearance(300, 100).Standard()
Example

ExampleAppearance_Standard demonstrates using the built-in standard appearance.

package main

import (
	"bytes"
	"fmt"

	"github.com/digitorus/pdfsign"
	"github.com/digitorus/pdfsign/internal/testpki"
)

func main() {
	// Create a standard appearance with professional metadata layout
	app := pdfsign.NewAppearance(300, 100).Standard()

	// The Standard() method pre-populates the appearance with:
	// - {{Name}} - Signer name (larger font)
	// - Reason: {{Reason}}
	// - Location: {{Location}}
	// - Date: {{Date}}

	// These template variables are automatically expanded when the signature
	// is rendered, using values from SignBuilder's SignerName(), Reason(), Location().

	fmt.Printf("Standard appearance with dimensions %gx%g\n", app.Width(), app.Height())

	// Demonstrate actual usage
	pki := testpki.NewTestPKI(nil)
	pki.StartCRLServer()
	defer pki.Close()
	key, cert := pki.IssueLeaf("Standard Signer")
	docToSign, _ := pdfsign.OpenFile("testfiles/testfile_form.pdf")
	docToSign.Sign(key, cert).Appearance(app, 100, 100)

	var buf bytes.Buffer
	_, _ = docToSign.Write(&buf)

	// Verify
	signedDoc, _ := pdfsign.Open(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
	if signedDoc.Verify().TrustSelfSigned(true).Valid() {
		fmt.Println("Successfully signed and verified with standard appearance")
	}

}
Output:
Standard appearance with dimensions 300x100
Successfully signed and verified with standard appearance

func (*Appearance) Text

func (a *Appearance) Text(content string) *TextBuilder

Text adds a text string to the appearance and returns a TextBuilder for configuration. Supports template variables which are expanded at signing time.

func (*Appearance) Width

func (a *Appearance) Width() float64

Width returns the appearance width.

type Color

type Color = render.Color

Color is an alias for render.Color for backward compatibility. Deprecated: Use render.Color directly.

type Compliance

type Compliance int

Compliance represents PDF/A compliance levels.

const (
	// DefaultCompliance means no specific compliance enforcement (default).
	// The library will produce a valid PDF signature but will not strictly enforce all PDF/A constraints.
	DefaultCompliance Compliance = iota

	// PDFA_1B is not yet enforced.
	PDFA_1B

	// PDFA_2B is not yet enforced.
	PDFA_2B

	// PDFA_3B is not yet enforced.
	PDFA_3B
)

type Document

type Document struct {
	// contains filtered or unexported fields
}

Document represents a PDF document that can be signed, verified, or modified.

Concurrency: a Document (and the SignBuilder(s) returned by Sign) is a sequential staging area, not safe for concurrent use - build it up from a single goroutine, then call Write. This differs from VerifyBuilder (see its doc comment), which is safe to query concurrently once configured; staging signing operations is inherently a mutable, ordered process (each Sign call appends to the document's pending operations), so unlike verification there's no natural "configure once, read many times from many goroutines" use case to make safe.

func Open

func Open(reader io.ReaderAt, size int64) (*Document, error)

Open initializes a PDF Document from an io.ReaderAt (e.g., an open file or memory buffer). The size parameter must be the total size of the PDF in bytes. Encrypted documents can only be opened if the user password is empty; use OpenWithPassword otherwise.

func OpenFile

func OpenFile(path string) (*Document, error)

OpenFile is a convenience method to initialize a PDF Document from a file on disk.

func OpenFileWithPassword

func OpenFileWithPassword(path, password string) (*Document, error)

OpenFileWithPassword is a convenience method to initialize a PDF Document from an encrypted file on disk, see OpenWithPassword.

func OpenWithPassword

func OpenWithPassword(reader io.ReaderAt, size int64, password string) (*Document, error)

OpenWithPassword initializes a PDF Document like Open, using password (the user or owner password) to open an encrypted document. Objects added to an encrypted document, such as signatures, are encrypted with the document's key, and the document stays encrypted.

func (*Document) AddFont

func (d *Document) AddFont(name string, data []byte) *Font

AddFont registers a new font with the document. If a font with the same name already exists, the existing font is returned. If data is provided, it will be parsed for metrics (for accurate text measurement).

Example

ExampleDocument_AddFont demonstrates usage of custom fonts for signing and initials.

package main

import (
	"bytes"
	"fmt"
	"log"
	"os"

	"github.com/digitorus/pdfsign"
	"github.com/digitorus/pdfsign/internal/testpki"
)

func main() {
	testFile := "testfiles/testfile20.pdf"
	// Ensure test file and font exist
	if _, err := os.Stat(testFile); os.IsNotExist(err) {
		fmt.Println("Test file not found")
		return
	}
	fontFile := "testfiles/fonts/GreatVibes-Regular.ttf"
	fontData, err := os.ReadFile(fontFile)
	if err != nil {
		// Fallback for example if file missing in some envs
		fmt.Println("Font file not found")
		return
	}

	doc, err := pdfsign.OpenFile(testFile)
	if err != nil {
		fmt.Printf("Error opening file: %v\n", err)
		return
	}

	// 1. Register the custom font
	// This embeds the font subset in the PDF when used.
	customFont := doc.AddFont("GreatVibes", fontData)

	// 2. Use the font in an appearance
	appearance := pdfsign.NewAppearance(200, 50)
	appearance.Text("Signed with Style").
		Font(customFont, 24).
		Position(10, 15)

	// 3. Or use for Initials
	initials := pdfsign.NewAppearance(50, 30)
	initials.Text("JD").Font(customFont, 20).Center()
	// ... sign and write ...
	pki := testpki.NewTestPKI(nil)
	pki.StartCRLServer()
	defer pki.Close()
	key, cert := pki.IssueLeaf("Custom Font Signer")
	doc.Sign(key, cert).Appearance(appearance, 100, 100)

	var buf bytes.Buffer
	if _, err := doc.Write(&buf); err != nil {
		log.Fatal(err)
	}

	// Verify
	signedDoc, _ := pdfsign.Open(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
	if signedDoc.Verify().TrustSelfSigned(true).Valid() {
		fmt.Println("Successfully signed and verified with custom font")
	}

}
Output:
Successfully signed and verified with custom font

func (*Document) AddImage

func (d *Document) AddImage(name string, data []byte) *Image

AddImage registers an image with the document. If an image with the same name already exists, the existing image is returned.

func (*Document) AddInitials

func (d *Document) AddInitials(appearance *Appearance) *initials.Builder

AddInitials adds initials to all pages.

Example

ExampleDocument_AddInitials demonstrates adding initials to pages.

package main

import (
	"bytes"
	"fmt"
	"os"

	"github.com/digitorus/pdfsign"
	"github.com/digitorus/pdfsign/internal/testpki"
)

func main() {
	testFile := "testfiles/testfile20.pdf"
	if _, err := os.Stat(testFile); os.IsNotExist(err) {
		fmt.Println("Test file not found")
		return
	}

	doc, err := pdfsign.OpenFile(testFile)
	if err != nil {
		fmt.Printf("Error opening file: %v\n", err)
		return
	}

	pki := testpki.NewTestPKI(nil)
	pki.StartCRLServer()
	defer pki.Close()
	key, cert := pki.IssueLeaf("Initials Signer")

	// Define initials appearance
	initials := pdfsign.NewAppearance(40, 20)
	initials.Text("{{Initials}}").Center()

	// Apply initials
	doc.AddInitials(initials).
		Position(pdfsign.BottomRight, 30, 30).
		ExcludePages(1) // Don't put initials on cover page

	// We create a temporary output file for the example
	outputFile, _ := os.CreateTemp("", "initials-example-*.pdf")
	defer func() { _ = os.Remove(outputFile.Name()) }()
	defer func() { _ = outputFile.Close() }()

	// Sign to finalize
	doc.Sign(key, cert).SignerName("Initials Signer")

	var buf bytes.Buffer
	if _, err := doc.Write(&buf); err != nil {
		fmt.Printf("Error writing: %v\n", err)
		return
	}

	// Verify
	signedDoc, _ := pdfsign.Open(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
	if signedDoc.Verify().TrustSelfSigned(true).Valid() {
		fmt.Printf("Successfully added initials and verified: %s\n", signedDoc.Verify().Signatures()[0].SignerName)
	}

}
Output:
Successfully added initials and verified: Initials Signer

func (*Document) Font

func (d *Document) Font(name string) *Font

Font returns a specific font by name, or nil if not found.

func (*Document) Fonts

func (d *Document) Fonts() []*Font

Fonts returns all registered fonts in the document.

func (*Document) FormFields

func (d *Document) FormFields() []FormField

FormFields returns all form fields in the document.

func (*Document) Image

func (d *Document) Image(name string) *Image

Image returns a registered image by name.

func (*Document) Images

func (d *Document) Images() []*Image

Images returns all registered images in the document.

func (*Document) Reader

func (d *Document) Reader() *pdflib.Reader

Reader returns the low-level PDF reader, allowing direct access to the PDF Cross-Reference (XRef) table and objects.

func (*Document) SetCompliance

func (d *Document) SetCompliance(c Compliance)

SetCompliance sets the PDF/A compliance level.

WARNING: This method is currently a placeholder and does not enforce PDF/A compliance. It is preserved for future implementation.

func (*Document) SetCompression

func (d *Document) SetCompression(level int)

SetCompression configures the zlib compression level for new objects added to the PDF. Supported levels are zlib.NoCompression, zlib.BestSpeed, zlib.BestCompression, or zlib.DefaultCompression.

Example

ExampleDocument_SetCompression demonstrates how to configure compression levels.

package main

import (
	"bytes"
	"fmt"
	"log"
	"os"

	"github.com/digitorus/pdfsign"
	"github.com/digitorus/pdfsign/internal/testpki"
)

func main() {
	testFile := "testfiles/testfile20.pdf"
	if _, err := os.Stat(testFile); os.IsNotExist(err) {
		fmt.Println("Test file not found")
		return
	}

	doc, err := pdfsign.OpenFile(testFile)
	if err != nil {
		fmt.Printf("Error opening file: %v\n", err)
		return
	}

	// ... continue with signing ...
	pki := testpki.NewTestPKI(nil)
	pki.StartCRLServer()
	defer pki.Close()
	key, cert := pki.IssueLeaf("Compressed Signer")

	doc.Sign(key, cert).Reason("Compression Test")

	var buf bytes.Buffer
	if _, err := doc.Write(&buf); err != nil {
		log.Fatal(err)
	}

	// Verify
	signedDoc, _ := pdfsign.Open(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
	if signedDoc.Verify().TrustSelfSigned(true).Valid() {
		fmt.Println("Signed and verified with BestCompression")
	}

}
Output:
Signed and verified with BestCompression

func (*Document) SetField

func (d *Document) SetField(name string, value any) error

SetField sets the value of a form field.

func (*Document) SetFields

func (d *Document) SetFields(fields map[string]any) error

SetFields sets multiple form field values.

func (*Document) SetUnit

func (d *Document) SetUnit(u float64)

SetUnit sets the default coordinate system scale for all subsequent operations on this document (e.g., signing appearances). By default, the unit is 1.0 (one PDF point = 1/72 inch).

func (*Document) Sign

func (d *Document) Sign(signer crypto.Signer, cert *x509.Certificate, intermediates ...*x509.Certificate) *SignBuilder

Sign begins the process of adding a digital signature to the document. It returns a SignBuilder for fluent configuration of the signature properties. The signature is only finalized and written to the document when doc.Write() is called.

  • signer: The private key used for signing.
  • cert: The signer's public certificate.
  • intermediates: Optional intermediate certificates to include in the certificate chain.
Example

ExampleDocument_Sign demonstrates the flow for signing a document.

package main

import (
	"bytes"
	"fmt"
	"log"

	"github.com/digitorus/pdfsign"
	"github.com/digitorus/pdfsign/internal/testpki"
)

func main() {
	// 1. Open Document
	doc, err := pdfsign.OpenFile("testfiles/testfile_form.pdf")
	if err != nil {
		log.Fatal(err)
	}

	// 2. Prepare visual appearance
	appearance := pdfsign.NewAppearance(200, 80)
	appearance.Text("Digitally Signed").Position(10, 40)

	// 3. Load Certificate and Private Key using test PKI
	pki := testpki.NewTestPKI(nil)
	pki.StartCRLServer()
	defer pki.Close()

	key, cert := pki.IssueLeaf("Example Signer")

	// 4. Create Output
	var buf bytes.Buffer

	// 5. Sign with fluent API
	doc.Sign(key, cert, pki.Chain()...).
		Reason("Contract Agreement").
		Location("New York").
		Appearance(appearance, 100, 100)

	_, err = doc.Write(&buf)
	if err != nil {
		log.Fatal(err)
	}

	// 6. Verify the signed document
	signedDoc, _ := pdfsign.Open(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
	result := signedDoc.Verify().TrustSelfSigned(true)

	if result.Valid() {
		fmt.Printf("Successfully signed and verified: %s\n", result.Signatures()[0].SignerName)
	}

}
Output:
Successfully signed and verified: Example Signer
Example (Multiple)

ExampleDocument_Sign_multiple demonstrates staging more than one signature on the same document. Each call to Sign() stages an independent signing operation; nothing is executed yet. Write() then applies every staged signature in the order Sign() was called, each one an incremental update chained on top of the previous signature's output - the same as an approver countersigning a document someone else already signed.

package main

import (
	"bytes"
	"fmt"
	"log"

	"github.com/digitorus/pdfsign"
	"github.com/digitorus/pdfsign/internal/testpki"
)

func main() {
	doc, err := pdfsign.OpenFile("testfiles/testfile_form.pdf")
	if err != nil {
		log.Fatal(err)
	}

	pki := testpki.NewTestPKI(nil)
	pki.StartCRLServer()
	defer pki.Close()

	aliceKey, aliceCert := pki.IssueLeaf("Alice")
	bobKey, bobCert := pki.IssueLeaf("Bob")

	// Stage Alice's signature, then Bob's. Order matters: Bob's signature
	// will be applied on top of Alice's, not the other way around.
	doc.Sign(aliceKey, aliceCert, pki.Chain()...).Reason("First approval")
	doc.Sign(bobKey, bobCert, pki.Chain()...).Reason("Second approval")

	var buf bytes.Buffer
	if _, err := doc.Write(&buf); err != nil {
		log.Fatal(err)
	}

	signedDoc, _ := pdfsign.Open(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
	result := signedDoc.Verify().TrustSelfSigned(true)

	fmt.Printf("Found %d signatures, all valid: %v\n", result.Count(), result.Valid())
	for _, sig := range result.Signatures() {
		fmt.Printf("Signed by: %s\n", sig.SignerName)
	}

}
Output:
Found 2 signatures, all valid: true
Signed by: Alice
Signed by: Bob
Example (PostQuantumOnly)

ExampleDocument_Sign_postQuantumOnly signs and validates a PAdES B-T PDF without introducing a classical public-key signature in the signer, CA, revocation, or timestamp paths.

package main

import (
	"bytes"
	"crypto"
	"crypto/x509"
	"fmt"
	"log"

	"github.com/digitorus/pdfsign"
	"github.com/digitorus/pdfsign/internal/testpki"
)

func main() {
	doc, err := pdfsign.OpenFile("testfiles/testfile12.pdf")
	if err != nil {
		log.Fatal(err)
	}

	pki := testpki.NewTestPKIWithConfig(nil, testpki.TestPKIConfig{
		Profile:         testpki.MLDSA_65,
		IntermediateCAs: 1,
	})
	pki.StartCRLServer()
	defer pki.Close()

	privateKey, certificate := pki.IssueLeaf("PQC PDF Signer")
	tsaURL := pki.StartMockTSA()

	doc.Sign(privateKey, certificate, pki.Chain()...).
		Format(pdfsign.PAdES_B_T).
		Digest(crypto.SHA512).
		Timestamp(tsaURL).
		Reason("Post-quantum approval")

	var output bytes.Buffer
	if _, err := doc.Write(&output); err != nil {
		log.Fatal(err)
	}

	signed, err := pdfsign.Open(bytes.NewReader(output.Bytes()), int64(output.Len()))
	if err != nil {
		log.Fatal(err)
	}
	result := signed.Verify().
		TrustedRoots(pki.RootPool()).
		AllowedAlgorithms(x509.MLDSA).
		ValidateFullChain(true).
		ValidateTimestampCertificates(true).
		ExternalChecks(true)

	signature := result.Signatures()[0]
	pqcSignerAndTSA := signature.Certificate.PublicKeyAlgorithm == x509.MLDSA &&
		signature.Timestamp != nil && signature.Timestamp.Certificate.PublicKeyAlgorithm == x509.MLDSA

	fmt.Printf("ML-DSA signer and TSA: %t\n", pqcSignerAndTSA)
	fmt.Printf("OCSP and CRL checked: %t\n", pki.OCSPRequests > 0 && pki.Requests > 0)
	fmt.Printf("Signature and timestamp valid: %t\n", result.Valid() && signature.TimestampValid)

}
Output:
ML-DSA signer and TSA: true
OCSP and CRL checked: true
Signature and timestamp valid: true

func (*Document) Signatures

func (d *Document) Signatures() iter.Seq2[*Signature, error]

Signatures returns an iterator over all signature dictionaries in the document.

func (*Document) Timestamp

func (d *Document) Timestamp(tsaURL string) *SignBuilder

Timestamp adds a document-level timestamp signature.

func (*Document) UseFont

func (d *Document) UseFont(name string, data []byte) *Font

UseFont returns an existing font or adds a new one.

func (*Document) Verify

func (d *Document) Verify() *VerifyBuilder

Verify initializes a VerifyBuilder to configure and execute signature verification. The verification process is lazy and only executes when you access the results (e.g., via Valid() or Signatures()).

Example

ExampleDocument_Verify demonstrates how to verify a signed PDF with the fluent API.

package main

import (
	"bytes"
	"crypto/x509"
	"fmt"
	"log"

	"github.com/digitorus/pdfsign"
	"github.com/digitorus/pdfsign/internal/testpki"
)

func main() {
	// Setup: Create a signed PDF in memory to verify
	pki := testpki.NewTestPKI(nil) // Use nil for examples (uses log.Fatal on error)
	pki.StartCRLServer()
	defer pki.Close()

	key, cert := pki.IssueLeaf("Example Signer")

	docToSign, _ := pdfsign.OpenFile("testfiles/testfile_form.pdf")
	appearance := pdfsign.NewAppearance(200, 80)
	appearance.Text("Digitally Signed").Position(10, 40)
	docToSign.Sign(key, cert, pki.Chain()...).Appearance(appearance, 100, 100)

	var signedBuffer bytes.Buffer
	if _, err := docToSign.Write(&signedBuffer); err != nil {
		log.Fatal(err)
	}

	// --- Verification with Fluent API ---
	doc, err := pdfsign.Open(bytes.NewReader(signedBuffer.Bytes()), int64(signedBuffer.Len()))
	if err != nil {
		log.Fatal(err)
	}

	// Configure verification with chainable methods
	// Access .Valid() triggers lazy execution
	// TrustedRoots is used here (rather than the TrustSelfSigned(true)
	// escape hatch) to exercise real chain-of-trust validation against the
	// example's test CA root, the way a caller with a real private root CA
	// would.
	result := doc.Verify().
		TrustedRoots(pki.RootPool()).
		TrustSignatureTime(true).
		MinRSAKeySize(2048).
		AllowedAlgorithms(x509.ECDSA)

	// Check validity (this triggers the actual verification)
	if result.Valid() {
		fmt.Println("Document is valid")
		for _, sig := range result.Signatures() {
			fmt.Printf("Signed by: %s\n", sig.SignerName)
		}
	} else {
		fmt.Println("Document has invalid signatures")
		if result.Err() != nil {
			fmt.Printf("Error: %v\n", result.Err())
		}
	}

}
Output:
Document is valid
Signed by: Example Signer

func (*Document) Write

func (d *Document) Write(output io.Writer) (*Result, error)

Write finalizes the document by executing all staged operations (signatures, form filling, initials). It performs incremental updates to the PDF and writes the resulting bytes to the provided writer.

Calling Sign() more than once before Write() stages multiple signatures; each is applied in the order Sign() was called, as an incremental update chained on top of the previous signature's output (e.g. an approver countersigning a document someone else already signed). See ExampleDocument_Sign_multiple.

type DocumentInfo

type DocumentInfo struct {
	Author       string
	Creator      string
	Title        string
	Subject      string
	Producer     string
	Pages        int
	CreationDate time.Time
	ModDate      time.Time
}

DocumentInfo contains information about the PDF document.

type Font

type Font = fonts.Font

Font is an alias for fonts.Font for backward compatibility. Deprecated: Use fonts.Font directly.

func StandardFont

func StandardFont(ft StandardFontType) *Font

StandardFont returns a Font for a standard PDF font. Deprecated: Use fonts.Standard directly.

type FontMetrics

type FontMetrics = fonts.Metrics

FontMetrics is an alias for fonts.Metrics for backward compatibility. Deprecated: Use fonts.Metrics directly.

func ParseTTFMetrics

func ParseTTFMetrics(data []byte) (*FontMetrics, error)

ParseTTFMetrics parses a TrueType font file and extracts glyph metrics. Deprecated: Use fonts.ParseTTFMetrics directly.

type FormField

type FormField = forms.FormField

FormField is an alias for forms.FormField for backward compatibility. Deprecated: Use forms.FormField directly.

type Format

type Format int

Format represents the signature format.

const (
	// DefaultFormat keeps the legacy /adbe.pkcs7.detached profile with embedded
	// revocation info (OCSP/CRL). It is not PAdES: it carries a CMS signing-time
	// attribute, which the baseline profiles forbid.
	DefaultFormat Format = iota

	// PAdES_B (PAdES Baseline B-B) selects the supported ETSI signature
	// construction rules. It does not by itself establish certificate trust,
	// legal qualification, or compliance of caller-controlled inputs.
	// It does not embed revocation information.
	PAdES_B

	// PAdES_B_T (Baseline-Timestamp) extends PAdES-B-B by requiring a timestamp from a
	// Time Stamp Authority (TSA). The response is cryptographically checked and
	// bound to the request, but callers remain responsible for selecting a TSA
	// whose certificate and policy they trust. Requires a TSA URL to be configured.
	PAdES_B_T

	// PAdES_B_LT (Baseline-Long-Term) extends PAdES-B-T by embedding validation material
	// (OCSP responses and/or CRLs) into the signature. This allows the signature to be validated
	// even if the original CA services are offline or the certificate has expired (provided
	// the revocation data was valid at signing time).
	//
	// Not yet supported: rejected until the DSS dictionary it requires is written.
	PAdES_B_LT

	// PAdES_B_LTA (Baseline-Long-Term-Availability) is not yet supported.
	// It would add document-level timestamps to protect the LTV data over time.
	PAdES_B_LTA

	// C2PA is Content Authenticity signature format (not yet supported).
	C2PA

	// JAdES_B_T is JAdES Baseline B-T level (not yet supported).
	JAdES_B_T

	// PAdES_B_B is the standards-aligned name for PAdES_B. PAdES_B is retained
	// for source compatibility.
	PAdES_B_B = PAdES_B
)

type Image

type Image = images.Image

Image is an alias for images.Image for backward compatibility. Deprecated: Use images.Image directly.

type ImageBuilder

type ImageBuilder struct {
	// contains filtered or unexported fields
}

ImageBuilder builds an image element within an appearance.

func (*ImageBuilder) Opacity

func (b *ImageBuilder) Opacity(percent float64) *ImageBuilder

Opacity sets the image opacity as a percentage from 0 (fully transparent) to 100 (fully opaque).

func (*ImageBuilder) Rect

func (b *ImageBuilder) Rect(x, y, width, height float64) *ImageBuilder

Rect sets the position and size of the image.

func (*ImageBuilder) ScaleFit

func (b *ImageBuilder) ScaleFit() *ImageBuilder

ScaleFit sets the scaling to fit within bounds.

func (*ImageBuilder) ScaleStretch

func (b *ImageBuilder) ScaleStretch() *ImageBuilder

ScaleStretch sets the scaling to stretch to fill bounds.

type ImageScale

type ImageScale = render.ImageScale

ImageScale is an alias for render.ImageScale for backward compatibility. Deprecated: Use render.ImageScale directly.

type InitialsBuilder

type InitialsBuilder = initials.Builder

InitialsBuilder is an alias for initials.Builder for backward compatibility. Deprecated: Use initials.Builder directly.

type InitialsConfig

type InitialsConfig = initials.Config

InitialsConfig is an alias for initials.Config for backward compatibility. Deprecated: Use initials.Config directly.

type LineBuilder

type LineBuilder struct {
	// contains filtered or unexported fields
}

LineBuilder builds a line element.

func (*LineBuilder) Stroke

func (b *LineBuilder) Stroke(r, g, b_ uint8) *LineBuilder

Stroke sets the line color.

func (*LineBuilder) Width

func (b *LineBuilder) Width(w float64) *LineBuilder

Width sets the line width.

type PDFObjectBuilder

type PDFObjectBuilder struct {
	// contains filtered or unexported fields
}

PDFObjectBuilder builds a PDF Form XObject element from an embedded PDF.

func (*PDFObjectBuilder) Page

Page sets which page of the PDF to use (1-indexed).

func (*PDFObjectBuilder) Rect

func (b *PDFObjectBuilder) Rect(x, y, width, height float64) *PDFObjectBuilder

Rect sets the position and size of the PDF object.

type Permission

type Permission int

Permission represents document modification permissions for certification signatures.

const (
	// NoChanges guarantees that the document has not been modified in any way.
	// Any subsequent change will invalidate the signature.
	NoChanges Permission = iota + 1

	// AllowFormFilling permits the user to fill in form fields and sign the document,
	// but not to add comments or annotations.
	AllowFormFilling

	// AllowFormFillingAndAnnotations permits the user to fill forms, sign, and add
	// comments or annotations (e.g., sticky notes).
	AllowFormFillingAndAnnotations
)

type Position

type Position = initials.Position

Position is an alias for initials.Position for backward compatibility. Deprecated: Use initials.Position directly.

type Result

type Result struct {
	Signatures []SignatureInfo
	Document   *Document
}

Result contains the result of a Write operation.

type ShapeBuilder

type ShapeBuilder struct {
	// contains filtered or unexported fields
}

ShapeBuilder builds rect and circle elements.

func (*ShapeBuilder) Fill

func (b *ShapeBuilder) Fill(r, g, b_ uint8) *ShapeBuilder

Fill sets the fill color.

func (*ShapeBuilder) IsElement

func (*ShapeBuilder) IsElement()

func (*ShapeBuilder) Stroke

func (b *ShapeBuilder) Stroke(r, g, b_ uint8) *ShapeBuilder

Stroke sets the stroke color.

func (*ShapeBuilder) StrokeWidth

func (b *ShapeBuilder) StrokeWidth(w float64) *ShapeBuilder

StrokeWidth sets the stroke width.

type SignBuilder

type SignBuilder struct {
	// contains filtered or unexported fields
}

SignBuilder builds a signature configuration.

func (*SignBuilder) Appearance

func (b *SignBuilder) Appearance(a *Appearance, x, y float64) *SignBuilder

Appearance sets the visual appearance of the signature widget and its position on the page. The appearance can include text, images, or graphics.

  • x, y: The coordinates in the current Unit (default is PDF points). (0, 0) is usually the bottom-left corner of the page.

Placed on page 1 by default; use Page to place it on a different page.

func (*SignBuilder) C2PAClaimGenerator

func (b *SignBuilder) C2PAClaimGenerator(generator string) *SignBuilder

C2PAClaimGenerator sets the C2PA claim generator.

func (*SignBuilder) C2PACreator

func (b *SignBuilder) C2PACreator(creator string) *SignBuilder

C2PACreator sets the C2PA creator tool name.

func (*SignBuilder) CertificateChains

func (b *SignBuilder) CertificateChains(chains [][]*x509.Certificate) *SignBuilder

CertificateChains sets the certificate chains for the signature. Deprecated: Use the variadic arguments in `doc.Sign` instead to provide intermediate certificates.

func (*SignBuilder) Contact

func (b *SignBuilder) Contact(contact string) *SignBuilder

Contact provides contact information for the signer (e.g., email address or phone number) to allow recipients to verify the signature.

func (*SignBuilder) Context

func (b *SignBuilder) Context(ctx context.Context) *SignBuilder

Context bounds external HTTP requests made while signing: TSA requests from Timestamp() and OCSP/CRL requests made by the builder's default revocation handler. Cancelling ctx aborts an in-flight request immediately. Use this to tie signing to a caller's own deadline or cancellation signal (e.g. an incoming HTTP request's context, or os/signal.NotifyContext). If not set, requests run under context.Background(), bounded by an internal default timeout. A custom RevocationFunction remains responsible for its own context.

func (*SignBuilder) Digest

func (b *SignBuilder) Digest(hash crypto.Hash) *SignBuilder

Digest sets the CMS content digest and PDF /DigestMethod. The default is SHA-256 for classical signers and SHA-512 for ML-DSA signers. ML-DSA callers must use SHA-512 because that is the RFC 9882 digest emitted by the PKCS#7 implementation.

func (*SignBuilder) Format

func (b *SignBuilder) Format(f Format) *SignBuilder

Format configures the signature format (e.g., PAdES_B). This determines whether revocation info is embedded (LTV) and other compliance features. DefaultFormat (revocation embedded) is used if not specified.

Example

ExampleSignBuilder_Format creates a PAdES B-B signature (ETSI EN 319 142-1) with Format(PAdES_B). The unsupported levels PAdES_B_LT and PAdES_B_LTA are rejected by Write until DSS support is implemented.

package main

import (
	"bytes"
	"fmt"
	"log"

	"github.com/digitorus/pdfsign"
	"github.com/digitorus/pdfsign/internal/testpki"
)

func main() {
	doc, err := pdfsign.OpenFile("testfiles/testfile_form.pdf")
	if err != nil {
		log.Fatal(err)
	}

	pki := testpki.NewTestPKI(nil)
	pki.StartCRLServer()
	defer pki.Close()
	key, cert := pki.IssueLeaf("Example Signer")

	doc.Sign(key, cert, pki.Chain()...).Format(pdfsign.PAdES_B)

	var buf bytes.Buffer
	if _, err := doc.Write(&buf); err != nil {
		log.Fatal(err)
	}

	signedDoc, _ := pdfsign.Open(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
	result := signedDoc.Verify().TrustSelfSigned(true)

	fmt.Printf("valid: %t, PAdES: %t\n", result.Valid(),
		bytes.Contains(buf.Bytes(), []byte("/SubFilter /ETSI.CAdES.detached")))
}
Output:
valid: true, PAdES: true
Example (PadesBT)

ExampleSignBuilder_Format_padesBT creates a PAdES B-T signature: B-B plus a signature-time-stamp from the TSA that Format(PAdES_B_T) requires.

package main

import (
	"bytes"
	"fmt"
	"log"

	"github.com/digitorus/pdfsign"
	"github.com/digitorus/pdfsign/internal/testpki"
)

func main() {
	doc, err := pdfsign.OpenFile("testfiles/testfile_form.pdf")
	if err != nil {
		log.Fatal(err)
	}

	pki := testpki.NewTestPKI(nil)
	pki.StartCRLServer()
	defer pki.Close()
	key, cert := pki.IssueLeaf("Example Signer")

	tsaURL := testpki.StartMockTSA(nil) // replace with your TSA URL
	doc.Sign(key, cert, pki.Chain()...).Format(pdfsign.PAdES_B_T).Timestamp(tsaURL)

	var buf bytes.Buffer
	if _, err := doc.Write(&buf); err != nil {
		log.Fatal(err)
	}

	signedDoc, _ := pdfsign.Open(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
	result := signedDoc.Verify().TrustSelfSigned(true)

	fmt.Printf("valid: %t, PAdES: %t\n", result.Valid(),
		bytes.Contains(buf.Bytes(), []byte("/SubFilter /ETSI.CAdES.detached")))
}
Output:
valid: true, PAdES: true

func (*SignBuilder) Location

func (b *SignBuilder) Location(location string) *SignBuilder

Location specifies the physical location of the signer (e.g., "New York, USA").

func (*SignBuilder) Page

func (b *SignBuilder) Page(page int) *SignBuilder

Page sets the page number (starting from 1 for the first page) that the appearance configured via Appearance is placed on. Defaults to 1 if not called. Can be chained before or after Appearance.

Example

ExampleSignBuilder_Page demonstrates placing signature appearances on specific pages of a multi-page document: an explicit fixed page, and the last page computed dynamically via Reader().NumPage() so it still works if pages are added or removed.

package main

import (
	"bytes"
	"fmt"
	"log"

	"github.com/digitorus/pdfsign"
	"github.com/digitorus/pdfsign/internal/testpki"
)

func main() {
	doc, err := pdfsign.OpenFile("testfiles/testfile12.pdf")
	if err != nil {
		log.Fatal(err)
	}

	pki := testpki.NewTestPKI(nil)
	pki.StartCRLServer()
	defer pki.Close()

	authorKey, authorCert := pki.IssueLeaf("Author")
	approverKey, approverCert := pki.IssueLeaf("Approver")

	appearance := pdfsign.NewAppearance(200, 80)
	appearance.Text("Signed").Position(10, 40)

	lastPage := doc.Reader().NumPage()

	// Sign the first page explicitly.
	doc.Sign(authorKey, authorCert, pki.Chain()...).
		Reason("Drafted").
		Appearance(appearance, 100, 100).
		Page(1)

	// Sign the last page, whatever it currently is.
	doc.Sign(approverKey, approverCert, pki.Chain()...).
		Reason("Approved").
		Appearance(appearance, 100, 100).
		Page(lastPage)

	var buf bytes.Buffer
	if _, err := doc.Write(&buf); err != nil {
		log.Fatal(err)
	}

	signedDoc, _ := pdfsign.Open(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
	result := signedDoc.Verify().TrustSelfSigned(true)
	fmt.Printf("Document has %d pages, both signatures valid: %v\n", lastPage, result.Valid())

}
Output:
Document has 2 pages, both signatures valid: true

func (*SignBuilder) Permission

func (b *SignBuilder) Permission(p Permission) *SignBuilder

Permission limits what changes are allowed to the document after signing. This is only applicable for CertificationSignatures. Default is AllowFormFilling if not specified for certification.

func (*SignBuilder) PreferCRL

func (b *SignBuilder) PreferCRL(prefer bool) *SignBuilder

PreferCRL sets whether the legacy DefaultFormat handler prefers CRL over OCSP for revocation checks. By default, it prefers OCSP (if available) as it produces smaller signatures. PAdES_B and PAdES_B_T do not fetch or embed revocation data by default.

func (*SignBuilder) Reason

func (b *SignBuilder) Reason(reason string) *SignBuilder

Reason sets the signing reason (e.g., "I agree to the terms", "I am the author"). This text appears in the signature widget and signature properties.

func (*SignBuilder) RevocationCache

func (b *SignBuilder) RevocationCache(cache sign.RevocationCache) *SignBuilder

RevocationCache sets the cache for revocation data (CRL/OCSP) used by the legacy DefaultFormat handler. PAdES_B and PAdES_B_T do not fetch or embed revocation data by default.

func (*SignBuilder) RevocationFunction

func (b *SignBuilder) RevocationFunction(fn sign.RevocationFunction) *SignBuilder

RevocationFunction sets a custom function to handle revocation fetching (CRL/OCSP). If not set, the library uses its built-in handler. PAdES_B and PAdES_B_T reject functions that add Adobe revocation information to the CMS; PAdES validation material belongs in the PDF DSS dictionary at B-LT or later.

func (*SignBuilder) SignerName

func (b *SignBuilder) SignerName(name string) *SignBuilder

SignerName sets the visual name of the signer. Ideally this matches the Common Name (CN) in the signing certificate, but it can be customized.

func (*SignBuilder) Timestamp

func (b *SignBuilder) Timestamp(url string) *SignBuilder

Timestamp enables RFC 3161 timestamping using the provided Time Stamp Authority (TSA) URL. The response signature and request binding are checked; selecting and trusting the TSA and its policy remain caller responsibilities. The timestamp is embedded in the signature to provide evidence of signing time.

func (*SignBuilder) TimestampAuth

func (b *SignBuilder) TimestampAuth(username, password string) *SignBuilder

TimestampAuth sets TSA authentication credentials.

func (*SignBuilder) Type

Type specifies the type of signature (Approval, Certification, or Timestamp). Default is ApprovalSignature if not specified. Certification signatures must be the first signature in the document.

func (*SignBuilder) Unit

func (b *SignBuilder) Unit(u float64) *SignBuilder

Unit sets the coordinate system scale for subsequent calls to Appearance. By default, the unit is 1.0 (one PDF point = 1/72 inch).

Example:

// Place signature at (20mm, 50mm)
builder.Unit(pdfsign.Millimeter).Appearance(app, 20, 50)

type Signature

type Signature = extract.Signature

Signature is an alias for extract.Signature for backward compatibility. Deprecated: Use extract.Signature directly.

type SignatureInfo

type SignatureInfo struct {
	SignerName  string
	SigningTime time.Time
	Reason      string
	Location    string
	Contact     string
	Certificate *x509.Certificate
	Timestamp   *TimestampInfo
	ByteRange   [4]int64
	Format      Format
}

SignatureInfo contains information about a signature.

type SignatureType

type SignatureType int

SignatureType represents the type of signature.

const (
	// ApprovalSignature indicates that the signer approves the content of the document.
	// This is the most common type of signature.
	ApprovalSignature SignatureType = iota

	// CertificationSignature indicates that the signer is the author of the document
	// and specifies what changes are permitted after signing.
	CertificationSignature

	// DocumentTimestamp is a document-level timestamp that proves the document existed
	// at a specific time without certifying authorship.
	DocumentTimestamp
)

type SignatureVerifyResult

type SignatureVerifyResult struct {
	SignatureInfo
	Valid          bool
	TrustedChain   bool
	Revoked        bool
	TimestampValid bool
	Errors         []error
	Warnings       []error
}

SignatureVerifyResult contains verification result for a single signature.

type StandardFontType

type StandardFontType = fonts.StandardType

StandardFontType is an alias for fonts.StandardType for backward compatibility. Deprecated: Use fonts.StandardType directly.

type TextAlign

type TextAlign = render.TextAlign

TextAlign is an alias for render.TextAlign for backward compatibility. Deprecated: Use render.TextAlign directly.

type TextBuilder

type TextBuilder struct {
	// contains filtered or unexported fields
}

TextBuilder builds a text element within an appearance.

func (*TextBuilder) Align

func (b *TextBuilder) Align(align TextAlign) *TextBuilder

Align sets the text alignment.

func (*TextBuilder) AutoScale

func (b *TextBuilder) AutoScale() *TextBuilder

AutoScale enables automatic font resizing to fit the text within the appearance bounds.

func (*TextBuilder) Center

func (b *TextBuilder) Center() *TextBuilder

Center centers the text in the appearance.

func (*TextBuilder) Font

func (b *TextBuilder) Font(font *Font, size float64) *TextBuilder

Font sets the font for the text.

func (*TextBuilder) Position

func (b *TextBuilder) Position(x, y float64) *TextBuilder

Position sets the position of the text.

func (*TextBuilder) SetColor

func (tb *TextBuilder) SetColor(r, g, b uint8) *TextBuilder

SetColor sets the text color.

type TimestampInfo

type TimestampInfo struct {
	Time        time.Time
	Authority   string
	Certificate *x509.Certificate
}

TimestampInfo contains information about a timestamp.

type VerifyBuilder

type VerifyBuilder struct {
	// contains filtered or unexported fields
}

VerifyBuilder provides a fluent API for configuring and executing PDF signature verification. Verification is performed lazily when result accessor methods (Valid, Signatures, Err) are called.

Concurrency: configure a VerifyBuilder from a single goroutine, the same way you would build up any other value. Once configuration is done, the result accessor methods (Valid, Signatures, Document, Err, Count) are safe to call concurrently from multiple goroutines - verification runs exactly once no matter how many goroutines call them, and every caller observes the same completed result. Calling a configuration method (TrustedRoots, TrustSelfSigned, Strict, ...) concurrently with another call, or after an accessor has already triggered verification, is not safe and has no effect on a verification that has already run.

func (*VerifyBuilder) AllowedAlgorithms

func (b *VerifyBuilder) AllowedAlgorithms(algos ...x509.PublicKeyAlgorithm) *VerifyBuilder

AllowedAlgorithms restricts the permitted public key algorithms (e.g. x509.RSA, x509.ECDSA).

func (*VerifyBuilder) AllowedEKUs

func (b *VerifyBuilder) AllowedEKUs(ekus ...x509.ExtKeyUsage) *VerifyBuilder

AllowedEKUs sets the allowed Extended Key Usages.

func (*VerifyBuilder) AtTime

func (b *VerifyBuilder) AtTime(t time.Time) *VerifyBuilder

AtTime sets the point in time at which the certificate chain's validity should be checked. By default, certificates are checked against the current system time.

func (*VerifyBuilder) Context

func (b *VerifyBuilder) Context(ctx context.Context) *VerifyBuilder

Context bounds external OCSP/CRL requests made when ExternalChecks is enabled: cancelling ctx aborts an in-flight request immediately, in addition to whatever HTTPTimeout applies. Use this to tie verification to a caller's own deadline or cancellation signal (e.g. an incoming HTTP request's context, or os/signal.NotifyContext). If not set, requests run under context.Background() and are bounded only by HTTPTimeout.

func (*VerifyBuilder) Count

func (b *VerifyBuilder) Count() int

Count returns the number of signatures found. Triggers verification if not already executed.

func (*VerifyBuilder) Document

func (b *VerifyBuilder) Document() DocumentInfo

Document returns document metadata. Triggers verification if not already executed.

func (*VerifyBuilder) Err

func (b *VerifyBuilder) Err() error

Err returns any error that occurred during verification. Triggers verification if not already executed.

func (*VerifyBuilder) ExternalChecks

func (b *VerifyBuilder) ExternalChecks(enable bool) *VerifyBuilder

ExternalChecks enables or disables network access to fetch revocation data from the web. If enabled, the library will attempt to contact OCSP responders and download CRLs from distribution points specified in the certificates.

func (*VerifyBuilder) HTTPTimeout

func (b *VerifyBuilder) HTTPTimeout(d time.Duration) *VerifyBuilder

HTTPTimeout sets the timeout for external HTTP requests (OCSP/CRL checks) made when ExternalChecks is enabled. If not set, a default of 10 seconds is used.

func (*VerifyBuilder) MinECDSAKeySize

func (b *VerifyBuilder) MinECDSAKeySize(bits int) *VerifyBuilder

MinECDSAKeySize constrains the minimum curve size for ECDSA keys.

func (*VerifyBuilder) MinRSAKeySize

func (b *VerifyBuilder) MinRSAKeySize(bits int) *VerifyBuilder

MinRSAKeySize constrains the minimum bit size for RSA keys.

func (*VerifyBuilder) RequireDigitalSignature

func (b *VerifyBuilder) RequireDigitalSignature(require bool) *VerifyBuilder

RequireDigitalSignature requires the Digital Signature key usage bit.

func (*VerifyBuilder) RequireNonRepudiation

func (b *VerifyBuilder) RequireNonRepudiation(require bool) *VerifyBuilder

RequireNonRepudiation requires the Non-Repudiation key usage bit.

func (*VerifyBuilder) Signatures

func (b *VerifyBuilder) Signatures() []SignatureVerifyResult

Signatures returns the verification results for each signature. Triggers verification if not already executed.

func (*VerifyBuilder) SkipCRL

func (b *VerifyBuilder) SkipCRL(skip bool) *VerifyBuilder

SkipCRL disables CRL (embedded or external) as a revocation source.

func (*VerifyBuilder) SkipOCSP

func (b *VerifyBuilder) SkipOCSP(skip bool) *VerifyBuilder

SkipOCSP disables OCSP (embedded or external) as a revocation source.

func (*VerifyBuilder) SkipRevocationCheck

func (b *VerifyBuilder) SkipRevocationCheck(skip bool) *VerifyBuilder

SkipRevocationCheck disables all revocation checks (OCSP and CRL, both embedded and external).

func (*VerifyBuilder) Strict

func (b *VerifyBuilder) Strict() *VerifyBuilder

Strict is a convenience method that enables all security and revocation checks. Revocation checking against embedded data is already on by default; Strict additionally sets:

  • `ExternalChecks(true)`
  • `ValidateFullChain(true)`
  • `RequireDigitalSignature(true)`
  • `RequireNonRepudiation(true)`
  • `TrustSelfSigned(false)`

func (*VerifyBuilder) TrustSelfSigned

func (b *VerifyBuilder) TrustSelfSigned(trust bool) *VerifyBuilder

TrustSelfSigned allows verification to succeed even if the certificate is self-signed or not signed by a CA in the TrustedRoots pool.

WARNING: Enabling this bypasses certificate chain trust validation and should only be used for testing or internal environments where certificates are manually trusted.

func (*VerifyBuilder) TrustSignatureTime

func (b *VerifyBuilder) TrustSignatureTime(trust bool) *VerifyBuilder

TrustSignatureTime determines whether to use the time specified in the PDF signature dictionary as the validation time, rather than the current system time. Note: This time is provided by the signer and should only be trusted if verified by a TSA.

func (*VerifyBuilder) TrustedRoots

func (b *VerifyBuilder) TrustedRoots(pool *x509.CertPool) *VerifyBuilder

TrustedRoots sets the pool of root certificates that are trusted to verify the signer's certificate chain. If not set, verification will fail unless TrustSelfSigned(true) is enabled.

func (*VerifyBuilder) Valid

func (b *VerifyBuilder) Valid() bool

Valid returns true if all signatures are valid. Triggers verification if not already executed.

func (*VerifyBuilder) ValidateFullChain

func (b *VerifyBuilder) ValidateFullChain(validate bool) *VerifyBuilder

ValidateFullChain sets whether to enforce cryptographic policy constraints (key size, algorithms) on the entire chain.

By default (false), these constraints are only enforced on the leaf (signer) certificate. Revocation and standard trust verification are always performed on the full chain.

func (*VerifyBuilder) ValidateTimestampCertificates

func (b *VerifyBuilder) ValidateTimestampCertificates(validate bool) *VerifyBuilder

ValidateTimestampCertificates when true, validates the timestamp token's signing certificate. Only a successfully validated timestamp can supply historical validation time. Validation is enabled by default; disabling it retains timestamp metadata.

Directories

Path Synopsis
Package fonts provides font resources and metrics for PDF documents.
Package fonts provides font resources and metrics for PDF documents.
Package images provides image resources for PDF documents.
Package images provides image resources for PDF documents.
internal
acroform
Package acroform walks the interactive form field tree of a document (ISO 32000-1 12.7.3, "Field dictionaries").
Package acroform walks the interactive form field tree of a document (ISO 32000-1 12.7.3, "Field dictionaries").
ocspx
Package ocspx adds ML-DSA response support around golang.org/x/crypto/ocsp.
Package ocspx adds ML-DSA response support around golang.org/x/crypto/ocsp.
pdf
testdss
Package testdss provides test-only integration with the EU Digital Signature Services (DSS) validation API.
Package testdss provides test-only integration with the EU Digital Signature Services (DSS) validation API.
testpdf
Package testpdf builds small PDF files for tests.
Package testpdf builds small PDF files for tests.
signers
aws module
csc module
pkcs11 module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL