Documentation
¶
Overview ¶
Package pdf implements the subset of ISO 32000-1/2 that DSS's PAdES support needs: a lenient reader for the whole corpus of signed PDFs DSS validates, and an append-only incremental writer for the ones it produces.
Provenance ¶
Upstream DSS gets its PDF object model from a third party — Apache PDFBox (org.apache.pdfbox:pdfbox:3.0.7, driven by dss-pades-pdfbox's PdfBoxDocumentReader / PdfBoxSignatureService) — so, exactly as PORTING.md prescribes for BouncyCastle-replacement machinery, this package has no Java class to mirror one-to-one and therefore lives under internal/. It imports the standard library only and never imports a DSS package.
Everything here is scoped by what dss-pades-pdfbox actually calls; see internal/pdf/DESIGN.md §0.1 for the enumerated contract. It is deliberately not a general-purpose PDF library: no rasterisation, no content-stream interpretation, no image filters, no full-rewrite serializer, no PDF/A, linearization or tagged PDF.
The reader's leniency is not "best effort": every tolerance is a copy of a specific pdfbox 3.0.7 recovery path (pdfbox runs isLenient=true by default and Loader.loadPDF never turns it off, so lenient is the only mode), enumerated in DESIGN.md §2.7 and pinned by a unit test named for its rule ID. Behavioural divergence from pdfbox is a bug even when pdfbox is the one behaving oddly — the /ByteRange arithmetic DSS performs downstream depends on reproducing it.
Layering ¶
pades (ported Java classes: PAdESUtils, PdfSigDictWrapper, SingleDssDict, ByteRange, …) │ imports ▼ internal/pdf ← this package. stdlib only.
This package knows nothing about CMS, certificates, OCSP or ETSI; it hands []byte upward. In particular it parses /ByteRange into []int64 and stops there: eu.europa.esig.dss.pades.validation.ByteRange owns validate() and getLength().
Index ¶
- Constants
- Variables
- func ApplyPredictor(data []byte, predictor, colors, bpc, columns int, warn *[]Warning) []byte
- func ContentsRange(br []int64) ([2]int64, error)
- func Decode(raw []byte, filters []Name, parms []*Dict, warn *[]Warning) ([]byte, error)
- func DecodeTextString(b []byte) string
- func EncodeName(n Name) []byte
- func EncodeString(s String) []byte
- func FlateDecode(raw []byte, warn *[]Warning) []byte
- func FlateEncode(data []byte) []byte
- func FormatDate(t time.Time) string
- func FormatReal(v float64) string
- func ReplaceContents(doc []byte, cms []byte) ([]byte, error)
- func SignedRanges(br []int64) ([2][2]int64, error)
- type Annotation
- type Array
- type Bool
- type DSSDictionary
- type Dict
- type Document
- func (d *Document) AcroForm() (*Dict, bool)
- func (d *Document) Annotations(page int) ([]Annotation, error)
- func (d *Document) Bytes() ([]byte, error)
- func (d *Document) Catalog() (*Dict, error)
- func (d *Document) Close() error
- func (d *Document) Encryption() *Encryption
- func (d *Document) Get(dict *Dict, key Name) Object
- func (d *Document) GetArray(dict *Dict, key Name) (Array, bool)
- func (d *Document) GetBool(dict *Dict, key Name) (bool, bool)
- func (d *Document) GetDate(dict *Dict, key Name) (time.Time, bool)
- func (d *Document) GetDict(dict *Dict, key Name) (*Dict, bool)
- func (d *Document) GetInt(dict *Dict, key Name) (int64, bool)
- func (d *Document) GetName(dict *Dict, key Name) (Name, bool)
- func (d *Document) GetReal(dict *Dict, key Name) (float64, bool)
- func (d *Document) GetStream(dict *Dict, key Name) (*Stream, bool)
- func (d *Document) GetString(dict *Dict, key Name) ([]byte, bool)
- func (d *Document) HasHybridXRef() bool
- func (d *Document) HeaderVersion() float32
- func (d *Document) HighestObjectNumber() int64
- func (d *Document) ID() [2][]byte
- func (d *Document) IndexRef(a Array, i int) (ObjectKey, bool)
- func (d *Document) Info() (*Dict, error)
- func (d *Document) IsEncrypted() bool
- func (d *Document) NumberOfPages() int
- func (d *Document) Object(k ObjectKey) (Object, error)
- func (d *Document) ObjectKeys() []ObjectKey
- func (d *Document) Page(page int) (*Dict, ObjectKey, error)
- func (d *Document) PageBox(page int) (Rect, error)
- func (d *Document) PageRotation(page int) int
- func (d *Document) Permissions() Permissions
- func (d *Document) RawStreamData(s *Stream) ([]byte, error)
- func (d *Document) RawStreamSize(s *Stream) int64
- func (d *Document) RefAt(dict *Dict, key Name) (ObjectKey, bool)
- func (d *Document) Resolve(o Object) Object
- func (d *Document) Revisions() []Revision
- func (d *Document) SignatureCoversWholeDocument(sd SignatureDictionary) bool
- func (d *Document) SignatureDictionaries() ([]SignatureDictionary, error)
- func (d *Document) SignatureFields() ([]SignatureField, error)
- func (d *Document) Size() int64
- func (d *Document) StartXref() int64
- func (d *Document) StreamData(s *Stream) ([]byte, error)
- func (d *Document) Trailer() *Dict
- func (d *Document) Version() float32
- func (d *Document) Warnings() []Warning
- func (d *Document) XRefSections() []XRefSection
- type Encryption
- type FilterError
- type Integer
- type Name
- type Null
- type Object
- type ObjectKey
- type Options
- type Permissions
- type Placeholder
- type Real
- type Rect
- type Ref
- type Result
- type Revision
- type SignatureDictionary
- type SignatureField
- type SignatureOptions
- type Stream
- type String
- type TokenRef
- type Updater
- func (u *Updater) Add(obj Object) ObjectKey
- func (u *Updater) AddSignature(opts SignatureOptions) (*Placeholder, error)
- func (u *Updater) Alloc() ObjectKey
- func (u *Updater) Catalog() *Dict
- func (u *Updater) Put(k ObjectKey, obj Object)
- func (u *Updater) Scheduled() []ObjectKey
- func (u *Updater) SetDSSDictionary(dss DSSDictionary) error
- func (u *Updater) Update(k ObjectKey) (Object, error)
- func (u *Updater) Write() (*Result, error)
- type VRIEntry
- type Warning
- type WarningCode
- type Writer
- type XRefSection
- type XRefStyle
Constants ¶
const DefaultContentSize = 9472
DefaultContentSize is the reserved /Contents size in bytes (R17), matching both PAdESSignatureParameters.signatureSize and SignatureOptions.DEFAULT_SIGNATURE_SIZE upstream.
Variables ¶
var ( // ErrNotPDF is returned when no %PDF- marker is found in the first 1024 bytes. ErrNotPDF = errors.New("pdf: missing %PDF- header") // ErrBrokenCatalog is the port of pdfbox's // IOException("Page tree root must be a dictionary"): the one defect pdfbox // does not recover from, and neither do we. ErrBrokenCatalog = errors.New("pdf: page tree root must be a dictionary") // ErrInvalidPassword maps onto upstream InvalidPasswordException. ErrInvalidPassword = errors.New("pdf: invalid password") // ErrUnsupportedSecurityHandler is returned for any /Filter other than /Standard. ErrUnsupportedSecurityHandler = errors.New("pdf: unsupported security handler") // ErrUnsupportedFilter is what a *FilterError unwraps to. ErrUnsupportedFilter = errors.New("pdf: unsupported stream filter") // ErrLimitExceeded reports a resource guard (MaxObjects, MaxDepth, // MaxStreamSize). These are ours, not pdfbox's; see DESIGN.md §2.7. ErrLimitExceeded = errors.New("pdf: resource limit exceeded") // ErrNoSuchObject is returned by Document.Object for an absent key. Note that // Resolve never returns it: a dangling reference resolves to Null{} (§2.7 O3). ErrNoSuchObject = errors.New("pdf: object not found") // ErrSignatureAlreadyAdded ports PDDocument.addSignature's IllegalStateException. ErrSignatureAlreadyAdded = errors.New("pdf: only one signature may be added per increment") // ErrContentsTooLarge is returned when the CMS does not fit the reserved space. ErrContentsTooLarge = errors.New("pdf: CMS does not fit the reserved /Contents space") // ErrByteRangeTooLarge is returned when the formatted /ByteRange exceeds the // 35 reserved bytes (R18). ErrByteRangeTooLarge = errors.New("pdf: /ByteRange does not fit the reserved space") )
var ErrEncryptedWriteUnsupported = errors.New("pdf: writing an increment into an encrypted document requires the crypt.go encryptor")
ErrEncryptedWriteUnsupported is returned by NewUpdater for an encrypted source document that has no usable security handler.
Functions ¶
func ApplyPredictor ¶
ApplyPredictor undoes /Predictor: 1 = none, 2 = TIFF, 10..15 = PNG (each row carries its own filter-type byte, so a declared 15 still dispatches per row over 0..4). Row length is ceil(Colors*BPC*Columns/8); bpp = ceil(Colors*BPC/8). A truncated final row is zero-padded and warned about.
func ContentsRange ¶
ContentsRange returns the span of the /Contents hex string including its < >:
contents = [br[0]+br[1], br[2])
func Decode ¶
Decode applies the filter chain named by /Filter with the parameters in /DecodeParms to raw. Unsupported filters yield a *FilterError. Any recovered defect appends to warn.
func DecodeTextString ¶
DecodeTextString renders a PDF text string as Go text, reproducing PDFBox's COSString#getString: a UTF-16BE byte-order mark selects UTF-16BE, a UTF-16LE one selects UTF-16LE (PDFBox accepts it although the PDF specification does not define it), and anything else is PDFDocEncoding - see codeToUnicode above for why that is not the same as Latin-1.
func EncodeName ¶
EncodeName renders a name under R5: the byte is emitted verbatim only if it is in [A-Za-z0-9+\-_@*$;.], else as '#' plus two uppercase hex digits. This is deliberately stricter than ISO 32000-1 (PDFBOX-2073) — '!', ',', '~' and '\” are escaped even though the spec allows them literally.
func EncodeString ¶
EncodeString renders a string under R6: hex form when any byte is >= 0x80 or is CR or LF, or when Hex is set; otherwise literal form escaping only '(', ')' and '\'. No octal escape is ever emitted.
func FlateDecode ¶
FlateDecode reproduces pdfbox FlateFilterDecoderStream exactly: it discards the first two bytes, inflates as raw DEFLATE with no zlib-header and no Adler-32 validation, and on a corrupt stream returns the bytes decoded so far with a WarnFlateTruncated warning and no error.
Do not "fix" this to use compress/zlib: the corpus contains non-conforming headers and truncated checksums that zlib.NewReader rejects and pdfbox accepts.
func FlateEncode ¶
FlateEncode produces zlib-wrapped DEFLATE at the fixed compression level 6. Output is byte-stable for a given toolchain but NOT across Go releases — Go 1.27 changed compress/flate's encoder output — which is fine: nothing signs or pins these compressed bytes (signatures cover ByteRanges and decompressed content), only self-consistency within one produced revision. The zlib wrapper is written by hand rather than taken from compress/zlib so the header bytes are pinned here and cannot drift.
func FormatDate ¶
FormatDate renders t as a PDF date string "D:YYYYMMDDHHmmSS+HH'mm'", the format pdfbox's DateConverter.toString produces. A zero UTC offset is written "+00'00'", never "Z".
func FormatReal ¶
FormatReal renders v the way pdfbox writes a COSFloat (R8): Java's Float.toString, and — when that produced exponent notation — the BigDecimal(s).stripTrailingZeros().toPlainString() expansion of it. NaN and both infinities render as "0.0" rather than producing a syntactically invalid PDF number.
Note that reals we merely echo never reach this function: Real.Raw is written verbatim, which is what makes round-tripping /Rect [0.0 0.0 595.276 841.89] byte-exact.
func ReplaceContents ¶
ReplaceContents is the port of PAdESUtils.replaceSignature for the cached to-be-signed path: it finds the single all-zero hex placeholder in doc and substitutes cms, lowercase-hex encoded. It errors when zero or more than one placeholder is present.
The scanner is upstream's, byte for byte: a '<' arms a suspicion, a run of exactly len(hex(cms)) '0' bytes after it is the placeholder, any other byte disarms it, and the reserved bytes beyond the CMS keep their '0's — so the document's length never changes.
func SignedRanges ¶
SignedRanges returns the two covered spans as [start,end) pairs:
signed = [br[0], br[0]+br[1]) u [br[2], br[2]+br[3])
Types ¶
type Annotation ¶
type Annotation struct {
Key ObjectKey
Dict *Dict
Rect Rect
Name string // /T
Signed bool // /V present
Hidden bool // /F bit 2
NoRotate bool // /F bit 5
}
Annotation is one /Annots entry.
type DSSDictionary ¶
type DSSDictionary struct {
Certs []TokenRef
CRLs []TokenRef
OCSPs []TokenRef
VRI []VRIEntry // omitted from output when empty
}
DSSDictionary is the document security store to write.
type Dict ¶
type Dict struct {
// contains filtered or unexported fields
}
Dict is an insertion-ordered PDF dictionary. Order is part of the contract.
func DictOf ¶
DictOf builds a dictionary from alternating Name, Object pairs. It panics on a malformed pair — it is a construction helper for tests and for the writer, not a parser entry point.
type Document ¶
type Document struct {
// contains filtered or unexported fields
}
Document is a parsed PDF file.
func OpenBytes ¶
OpenBytes parses b. The slice is retained, not copied: the writer needs the original bytes verbatim (R: §3.1) and callers must not mutate it.
func (*Document) Annotations ¶
func (d *Document) Annotations(page int) ([]Annotation, error)
Annotations returns the annotations of a 1-based page.
func (*Document) Bytes ¶
Bytes returns the whole source. The slice is the document's own; callers must not modify it.
func (*Document) Close ¶
Close releases nothing: Document holds no OS resources. It exists so callers can treat it like upstream's PDDocument.
func (*Document) Encryption ¶
func (d *Document) Encryption() *Encryption
Encryption describes the security handler, or nil when the document is not encrypted.
func (*Document) GetDate ¶
GetDate parses a PDF date string "D:YYYYMMDDHHmmSSOHH'mm'" leniently: any truncation from the right is accepted, as is a missing "D:" prefix.
func (*Document) GetDict ¶
GetDict returns dict[key] as a dictionary. A stream counts as a dictionary only through GetStream; pdfbox's getCOSDictionary behaves the same way.
func (*Document) GetInt ¶
GetInt returns dict[key] as an integer. A real is truncated, as COSNumber.intValue does.
func (*Document) HasHybridXRef ¶
HasHybridXRef reports whether any section carried /XRefStm.
func (*Document) HeaderVersion ¶
HeaderVersion is the version from %PDF-x.y.
func (*Document) HighestObjectNumber ¶
HighestObjectNumber is the maximum object number over all xref sections, including free entries and /Size - 1 (R16).
func (*Document) IndexRef ¶
IndexRef is the array-element form: upstream's PdfArray.getObjectKey(i).
func (*Document) Info ¶
Info returns the document information dictionary, or (nil, nil) when absent.
func (*Document) IsEncrypted ¶
IsEncrypted reports whether the document carries an /Encrypt dictionary.
func (*Document) NumberOfPages ¶
NumberOfPages is the number of leaves in the page tree.
func (*Document) ObjectKeys ¶
ObjectKeys returns every key in the resolved xref, ascending by Num then Gen. Free entries are not included, matching COSDocument.getXrefTable().
func (*Document) PageBox ¶
PageBox returns the page's /MediaBox, inherited through /Parent. The default is US Letter, as PDPage.getMediaBox does.
func (*Document) PageRotation ¶
PageRotation returns /Rotate normalised into {0,90,180,270}.
func (*Document) Permissions ¶
func (d *Document) Permissions() Permissions
Permissions returns the decoded /P bits. An unencrypted document grants everything, matching AccessPermission's default.
func (*Document) RawStreamData ¶
RawStreamData returns the decrypted but still encoded stream bytes. This is what DefaultPdfObjectModificationsFinder.compareDictStreams compares.
func (*Document) RawStreamSize ¶
RawStreamSize is the length of the raw bytes, or -1 when s is nil.
func (*Document) RefAt ¶
RefAt returns the key of the indirect reference stored at key, if it is one. This is upstream's PdfDict.getObjectKey.
func (*Document) Resolve ¶
Resolve follows Ref chains. A dangling reference resolves to Null{}, never an error (§2.7 O3) — visitFromDictionary upstream skips nil-valued entries, and a hard error here would fail documents pdfbox parses.
func (*Document) Revisions ¶
Revisions returns the %%EOF revision list of the source document. The result is computed once and cached.
func (*Document) SignatureCoversWholeDocument ¶
func (d *Document) SignatureCoversWholeDocument(sd SignatureDictionary) bool
SignatureCoversWholeDocument reproduces PdfBoxDocumentReader.isSignatureCoversWholeDocument including its arithmetic, which is not the obvious one:
(br[1]-br[0]) + (br[2]-br[1]-br[0]) + br[3] == fileLength
Do not "fix" the formula, or documents upstream reports as fully covered will stop matching.
func (*Document) SignatureDictionaries ¶
func (d *Document) SignatureDictionaries() ([]SignatureDictionary, error)
SignatureDictionaries returns one entry per distinct /V signature dictionary, deduplicated by object key exactly as PdfBoxDocumentReader does with sigDictObject.getKey().getNumber().
func (*Document) SignatureFields ¶
func (d *Document) SignatureFields() ([]SignatureField, error)
SignatureFields reproduces PDDocument.getSignatureFields().
func (*Document) StreamData ¶
StreamData returns the decrypted, fully decoded stream bytes.
func (*Document) Warnings ¶
Warnings returns every recovered defect, in the order they were noticed.
func (*Document) XRefSections ¶
func (d *Document) XRefSections() []XRefSection
XRefSections returns the /Prev chain, newest first.
type Encryption ¶
type Encryption struct {
Handler Name // always "Standard" (others are rejected at Open)
V, R int
KeyLength int
StmF Name // "StdCF" | "Identity"
StrF Name
CFM Name // "V2" | "AESV2" | "AESV3" | "None"
}
Encryption describes the document's security handler as configured.
type FilterError ¶
type FilterError struct{ Filter Name }
FilterError names the filter that could not be decoded. It unwraps to ErrUnsupportedFilter, so errors.Is(err, ErrUnsupportedFilter) holds.
func (*FilterError) Error ¶
func (e *FilterError) Error() string
func (*FilterError) Unwrap ¶
func (e *FilterError) Unwrap() error
type Integer ¶
type Integer int64
Integer is a PDF integer. Literals exceeding int64 are clamped with a warning.
type Null ¶
type Null struct{}
Null is the PDF null object. A dangling indirect reference resolves to Null{}, never to an error (DESIGN.md §2.7 O3).
type Object ¶
type Object interface {
// contains filtered or unexported methods
}
Object is the sum type of PDF object kinds: Null, Bool, Integer, Real, String, Name, Array, *Dict, *Stream, Ref.
type ObjectKey ¶
ObjectKey identifies an indirect object slot. It is the analogue of upstream's PdfObjectKey / PdfBoxObjectKey. The zero value means "no key". It is a distinct type from Ref on purpose: Ref is a value inside the object graph, ObjectKey identifies a slot.
type Options ¶
type Options struct {
// Password is tried as the user password, then as the owner password.
Password []byte
// Random supplies AES initialisation vectors on write. nil means crypto/rand.
Random io.Reader
MaxObjects int // default 500000
MaxDepth int // default 512
MaxStreamSize int64 // default 512 << 20
}
Options configures Open. The zero value is valid: every limit falls back to its default.
type Permissions ¶
type Permissions struct {
Raw int32
OwnerAccess bool // the owner password matched
CanModify bool // bit 4
CanModifyAnnots bool // bit 6
CanFillInForm bool // bit 9
CanPrint bool // bit 3
CanExtract bool // bit 5
CanAssemble bool // bit 11
CanPrintFaithful bool // bit 12
}
Permissions is the decoded /P bitfield plus which password matched.
type Placeholder ¶
Placeholder names the objects AddSignature created.
type Real ¶
Real carries the literal it was parsed from; Raw is "" for values built in memory. See R8: the writer echoes Raw verbatim when it is set.
type Rect ¶
type Rect struct{ MinX, MinY, MaxX, MaxY float64 }
Rect is a PDF rectangle, normalised so Min <= Max on both axes.
func RectFromArray ¶
RectFromArray reads a rectangle from a four-number array, normalising the corners. The array must already have had its elements resolved.
type Result ¶
type Result struct {
Bytes []byte // original || increment
OriginalLength int64
// ByteRange is zero-valued when the update carries no signature.
ByteRange [4]int64
ContentsOffset int64 // absolute offset of the '<'
ContentsLength int64 // reserved bytes including '<' and '>'
StartXref int64
XRefStyle XRefStyle
HighestObjectNumber int64
}
Result is the laid-out increment, before the CMS is inserted.
func (*Result) InsertContents ¶
InsertContents writes cms as uppercase hex into the reserved /Contents span (R17), leaving the unused reserved bytes as ASCII '0'. It returns ErrContentsTooLarge when cms does not fit.
Upstream is not self-consistent here and we deliberately are not either: COSWriter.writeExternalSignature uses pdfbox's Hex.getBytes (uppercase) while PAdESUtils.replaceSignature uses Utils.toHex (lowercase). Both are legal PDF hex strings, both parse, and each function below matches the upstream path it ports. See DESIGN.md §3.6.
func (*Result) SignedData ¶
SignedData is the byte stream the CMS must be computed over: exactly the two spans named by ByteRange. When the update carries no signature the whole output is returned, since there is no excluded span.
type Revision ¶
Revision is one %%EOF-delimited prefix: the revision is bytes [0, End).
func ScanRevisions ¶
ScanRevisions reproduces PAdESUtils.extractRevisions byte for byte, including its %%EOF + EOL lookahead. It does not walk /Prev, and it deliberately counts %%EOF sequences that occur inside object data: PAdESUtils.getPreviousRevision picks the candidate whose length is the largest below byteRange[0]+byteRange[1], so a different revision list changes which document DSS reports as the signed original.
type SignatureDictionary ¶
type SignatureDictionary struct {
Key ObjectKey
Dict *Dict
Type Name // "Sig" | "DocTimeStamp" | ""
Filter Name
SubFilter Name
Contents []byte // decoded string bytes, i.e. the DER CMS
ByteRange []int64 // verbatim, length not forced to 4
Fields []int // indices into the SignatureFields slice that point here
}
SignatureDictionary is the raw /V content. All ETSI semantics live in `pades`.
type SignatureField ¶
type SignatureField struct {
Key ObjectKey
Dict *Dict
Name string // fully-qualified /T, dot-joined
Value *Dict // /V, nil for an empty field
ValueKey ObjectKey // key of the /V reference; zero when /V is direct or absent
Widgets []*Dict
WidgetKeys []ObjectKey
Page int // 0 when the widget is not on any page
Rect Rect
Lock *Dict // /Lock, nil when absent
}
SignatureField is one /FT /Sig field of the AcroForm.
type SignatureOptions ¶
type SignatureOptions struct {
Type Name // "Sig" (default) or "DocTimeStamp"
Filter Name // default "Adobe.PPKLite"
SubFilter Name // e.g. "ETSI.CAdES.detached", "ETSI.RFC3161"
ContentSize int // reserved /Contents bytes; default DefaultContentSize
SignerName string
Reason string
Location string
ContactInfo string
SigningTime time.Time // /M; the zero value omits the key
AppName string // /Prop_Build /App /Name
FieldID string // fill this existing empty field; "" creates a new one
Page int // 1-based; used only when creating a field
Rect Rect // the zero Rect creates an invisible field
Appearance *Stream // /AP /N; nil for an invisible field
DocMDP int // 1..3; 0 = none
Lock *Dict // /Lock of the target field, drives FieldMDP
DocumentID []byte // second element of /ID
}
SignatureOptions describes the signature dictionary and the field it goes in.
type Stream ¶
type Stream struct {
Dict *Dict
Raw []byte
// Offset and Length locate Raw in the source file; both are 0 for streams
// built in memory.
Offset int64
Length int64
// contains filtered or unexported fields
}
Stream is a PDF stream: its dictionary plus its still-encoded, still-encrypted bytes.
type TokenRef ¶
type TokenRef struct {
Key ObjectKey // non-zero reuses an existing object; zero writes a new stream
Data []byte // DER; ignored when Key is non-zero
}
TokenRef is one validation-data token: either a reference to an object the document already carries, or the DER bytes of a new one.
type Updater ¶
type Updater struct {
// contains filtered or unexported fields
}
Updater accumulates an incremental update over a Document. The Document's bytes are never modified; Write emits original||increment.
func NewUpdater ¶
NewUpdater starts an incremental update over d.
func (*Updater) AddSignature ¶
func (u *Updater) AddSignature(opts SignatureOptions) (*Placeholder, error)
AddSignature installs a signature dictionary with placeholder /Contents and /ByteRange, wires it into a signature field, the AcroForm and (for a new field) the page's /Annots. It returns ErrSignatureAlreadyAdded on a second call (R19), matching PDDocument.addSignature's IllegalStateException: multiple signatures are multiple increments.
func (*Updater) Catalog ¶
Catalog returns a mutable clone of the catalog, already scheduled for writing. Repeated calls return the same instance.
func (*Updater) Put ¶
Put schedules obj to be written under an existing key, replacing that object in this revision. It never touches the original bytes.
func (*Updater) SetDSSDictionary ¶
func (u *Updater) SetDSSDictionary(dss DSSDictionary) error
SetDSSDictionary writes /Root /DSS and marks the catalog updated. Order is the caller's; nothing is sorted or deduplicated here.
func (*Updater) Update ¶
Update returns a mutable clone of an existing object, already scheduled. Repeated calls for one key return the same instance, so two features editing the same dictionary compose instead of clobbering each other.
func (*Updater) Write ¶
Write lays out the increment and patches /ByteRange (R18). /Contents still holds the placeholder.
Object write order is ascending object number. This is a deliberate deviation from pdfbox (R15): COSWriter drains an ArrayDeque seeded from a HashSet, so its order is not reproducible across runs, and reproducing it would mean reproducing a bug. Offsets in the xref come from the actual write positions, so nothing about validity changes. We also never emit an object stream on write — every new object is a plain "N G obj".
type VRIEntry ¶
type VRIEntry struct {
Name string // uppercase base-16 SHA-1 of the signature, computed by pades
Certs []TokenRef
CRLs []TokenRef
OCSPs []TokenRef
TU time.Time
TS []byte
}
VRIEntry is one /VRI sub-dictionary.
type Warning ¶
type Warning struct {
Code WarningCode
Offset int64
Message string
}
Warning is one recovered defect. Offset is the byte offset in the source file where the defect was noticed, or -1 when it is not tied to a position.
type WarningCode ¶
type WarningCode string
WarningCode classifies a recovered defect. Codes are stable across releases; the oracle compares codes, never message text.
const ( WarnHeaderGarbage WarningCode = "header-garbage" WarnHeaderVersion WarningCode = "header-version-default" WarnMissingEOF WarningCode = "missing-eof" WarnXRefOffsetRepaired WarningCode = "xref-offset-repaired" WarnXRefBruteForce WarningCode = "xref-brute-force" WarnXRefEntryInvalid WarningCode = "xref-entry-invalid" WarnXRefStmSkipped WarningCode = "xrefstm-skipped" WarnObjectHeaderFixed WarningCode = "object-header-fixed" WarnDanglingReference WarningCode = "dangling-reference" WarnStreamLengthFixed WarningCode = "stream-length-fixed" WarnStreamEndFixed WarningCode = "stream-end-fixed" WarnFlateTruncated WarningCode = "flate-truncated" WarnPredictorTruncated WarningCode = "predictor-truncated" WarnObjStmBroken WarningCode = "objstm-broken" WarnKidRemoved WarningCode = "kid-removed" // WarnNumberClamped records an integer literal that exceeded int64 (§2.1). WarnNumberClamped WarningCode = "number-clamped" // WarnLexer records a tolerated lexical defect (bad hex digit, malformed #XX). WarnLexer WarningCode = "lexer" )
type Writer ¶
type Writer struct {
// contains filtered or unexported fields
}
Writer emits PDF syntax under the byte-determinism rules R1..R14 of DESIGN.md. It tracks line state so WriteEOL is suppressed at a line start (R2).
func (*Writer) WriteEOL ¶
WriteEOL writes the end-of-line byte \n unless the last byte written was already an EOL written by WriteEOL (R1, R2). Without this suppression every dictionary gains a blank line and no golden matches.
func (*Writer) WriteIndirect ¶
WriteIndirect writes "N G obj … endobj" (R10).
func (*Writer) WriteObject ¶
WriteObject writes o in PDF syntax, dispatching on its kind. Composite values are written direct: an indirect value is a Ref, never a *Dict.
type XRefSection ¶
type XRefSection struct {
// Offset is the byte offset of `xref`, or of the `N G obj` of the xref stream.
Offset int64
Style XRefStyle
Trailer *Dict
Prev int64 // -1 when absent
XRefStm int64 // hybrid: /XRefStm offset, -1 when absent
Entries int // entries contributed by this section
Recovered bool // true when the offset had to be repaired (§2.7 X3)
}
XRefSection is one hop of the /Prev chain.