From 649dbf0af6854708f4e47d0eeebf2c41f681f0d3 Mon Sep 17 00:00:00 2001 From: Gunnsteinn Hall Date: Thu, 17 Jan 2019 22:45:10 +0000 Subject: [PATCH] Removed large whitespace in signature dictionary to get valid signatures. Whitespace was causing ByteRange to not cover the entire document. Ideally should be everything outside the < > in the signature Contents field. --- pdf/model/appender.go | 14 ++++++++++---- pdf/model/signature.go | 31 ++++++++++++++++++------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/pdf/model/appender.go b/pdf/model/appender.go index 5a5d6001..fde9eb59 100644 --- a/pdf/model/appender.go +++ b/pdf/model/appender.go @@ -19,7 +19,7 @@ import ( "github.com/unidoc/unidoc/pdf/core" ) -// PdfAppender appends a new Pdf content to an existing Pdf document. +// PdfAppender appends new PDF content to an existing PDF document via incremental updates. type PdfAppender struct { rs io.ReadSeeker parser *core.PdfParser @@ -416,7 +416,6 @@ func (a *PdfAppender) Sign(pageNum int, handler SignatureHandler) (acroForm *Pdf appearance.V = sig.ToPdfObject() appearance.FT = core.MakeName("Sig") appearance.V = sig.ToPdfObject() - //appearance.Ff = core.MakeInteger(0) appearance.T = core.MakeString("Signature1") appearance.F = core.MakeInteger(132) appearance.P = page.ToPdfObject() @@ -424,9 +423,14 @@ func (a *PdfAppender) Sign(pageNum int, handler SignatureHandler) (acroForm *Pdf core.MakeInteger(0)) appearance.Signature = sig + // Add the signature appearance to the page annotations. + page.Annotations = append(page.Annotations, appearance.PdfAnnotationWidget.PdfAnnotation) + a.pages[pageIndex] = page + // Update acroform. a.ReplaceAcroForm(acroForm) + return acroForm, appearance, nil } @@ -437,7 +441,6 @@ func (a *PdfAppender) ReplaceAcroForm(acroForm *PdfAcroForm) { // Write writes the Appender output to io.Writer. func (a *PdfAppender) Write(w io.Writer) error { - writer := NewPdfWriter() pagesDict, ok := core.GetDict(writer.pages) @@ -499,7 +502,7 @@ func (a *PdfAppender) Write(w io.Writer) error { common.Log.Trace("Page Parent: %T", parent) parentDict, ok := parent.PdfObject.(*core.PdfObjectDictionary) if !ok { - return errors.New("Invalid Parent object") + return errors.New("invalid Parent object") } for _, field := range inheritedFields { common.Log.Trace("Field %s", field) @@ -593,10 +596,13 @@ func (a *PdfAppender) Write(w io.Writer) error { writerW = bytes.NewBuffer(nil) } + // Do a mock write to get offsets. + // TODO(gunnsth): Only needed when dealing with signatures? if err := writer.Write(writerW); err != nil { return err } + // TODO(gunnsth): Consider whether the dynamic content can be handled with generic write hooks? if hasSigDict { bufferData := writerW.(*bytes.Buffer).Bytes() byteRange := core.MakeArray() diff --git a/pdf/model/signature.go b/pdf/model/signature.go index cf62748d..d8d3da5d 100644 --- a/pdf/model/signature.go +++ b/pdf/model/signature.go @@ -12,7 +12,8 @@ import ( "github.com/unidoc/unidoc/pdf/core" ) -// pdfSignDictionary is needed because of the digital checksum calculates after a new file creation and writes as a value into PdfDictionary in the existing file. +// pdfSignDictionary is used as a wrapper around PdfSignature for digital checksum calculation +// and population of /Contents and /ByteRange. type pdfSignDictionary struct { *core.PdfObjectDictionary handler *SignatureHandler @@ -52,15 +53,15 @@ func (d *pdfSignDictionary) WriteString() string { out.WriteString(" ") d.byteRangeOffsetStart = out.Len() out.WriteString(v.WriteString()) - out.WriteString(" ") - d.byteRangeOffsetEnd = out.Len() + out.WriteString(" ") + d.byteRangeOffsetEnd = out.Len() - 1 case "Contents": out.WriteString(k.WriteString()) out.WriteString(" ") d.contentsOffsetStart = out.Len() out.WriteString(v.WriteString()) - out.WriteString(" ") - d.contentsOffsetEnd = out.Len() + out.WriteString(" ") + d.contentsOffsetEnd = out.Len() - 1 default: out.WriteString(k.WriteString()) out.WriteString(" ") @@ -100,7 +101,11 @@ type PdfSignature struct { // NewPdfSignature creates a new PdfSignature object. func NewPdfSignature() *PdfSignature { sig := &PdfSignature{} - sigDict := &pdfSignDictionary{PdfObjectDictionary: core.MakeDict(), handler: &sig.Handler, signature: sig} + sigDict := &pdfSignDictionary{ + PdfObjectDictionary: core.MakeDict(), + handler: &sig.Handler, + signature: sig, + } sig.container = core.MakeIndirectObject(sigDict) return sig } @@ -172,7 +177,9 @@ func (sig *PdfSignature) ToPdfObject() core.PdfObject { if sig.Contents != nil { dict.Set("Contents", sig.Contents) } - // FIXME: ByteRange and Contents need to be updated dynamically. + // NOTE: ByteRange and Contents need to be updated dynamically. + // TODO: Currently dynamic update is only in the appender, need to support in the PdfWriter + // too for the initial signature on document creation. return container } @@ -251,11 +258,9 @@ func (r *PdfReader) newPdfSignatureFromIndirect(container *core.PdfIndirectObjec type PdfSignatureField struct { container *core.PdfIndirectObject - V *PdfSignature - // Type: /Sig - // V: *PdfSignature... - Lock *core.PdfIndirectObject // Shall be an indirect reference. - SV *core.PdfIndirectObject // Shall be an indirect reference. + V *PdfSignature + Lock *core.PdfIndirectObject + SV *core.PdfIndirectObject Kids *core.PdfObjectArray } @@ -304,7 +309,7 @@ type PdfSignatureFieldLock struct { // (Table 234 - p. 455 in PDF32000_2008). type PdfSignatureFieldSeed struct { container *core.PdfIndirectObject - // Type + Ff *core.PdfObjectInteger Filter *core.PdfObjectName SubFilter *core.PdfObjectArray