mirror of
https://github.com/unidoc/unipdf.git
synced 2025-04-27 13:48:51 +08:00
Change the prototype of the appender Sign method
This commit is contained in:
parent
c2d1efb653
commit
28ad01f4b9
@ -13,7 +13,6 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/unidoc/unidoc/common"
|
||||
"github.com/unidoc/unidoc/pdf/core"
|
||||
@ -381,61 +380,58 @@ func (a *PdfAppender) ReplacePage(pageNum int, page *PdfPage) {
|
||||
}
|
||||
|
||||
// Sign signs a specific page with a digital signature using a specified signature handler.
|
||||
// Returns an Acroform and PdfFieldSignature that can be used to customize the signature appearance.
|
||||
func (a *PdfAppender) Sign(pageNum int, handler SignatureHandler) (acroForm *PdfAcroForm, signatureField *PdfFieldSignature, err error) {
|
||||
acroForm = a.Reader.AcroForm
|
||||
// Returns a PdfFieldSignature that can be used to customize the signature appearance.
|
||||
func (a *PdfAppender) Sign(pageNum int, field *PdfFieldSignature) error {
|
||||
if field == nil {
|
||||
return errors.New("signature field cannot be nil")
|
||||
}
|
||||
|
||||
signature := field.V
|
||||
if signature == nil {
|
||||
return errors.New("field signature cannot be nil")
|
||||
}
|
||||
|
||||
// Get a copy of the selected page.
|
||||
pageIndex := pageNum - 1
|
||||
if pageIndex < 0 || pageIndex > len(a.pages)-1 {
|
||||
return fmt.Errorf("page %d not found", pageNum)
|
||||
}
|
||||
page := a.pages[pageIndex].Duplicate()
|
||||
|
||||
// Initialize signature.
|
||||
if err := signature.Initialize(); err != nil {
|
||||
return err
|
||||
}
|
||||
a.addNewObjects(signature.container)
|
||||
|
||||
// Add signature field annotations to the page annotations.
|
||||
for _, annotation := range field.Annotations {
|
||||
annotation.P = page.ToPdfObject()
|
||||
page.Annotations = append(page.Annotations, annotation.PdfAnnotation)
|
||||
}
|
||||
|
||||
// Add signature field to the form.
|
||||
acroForm := a.Reader.AcroForm
|
||||
if acroForm == nil {
|
||||
acroForm = NewPdfAcroForm()
|
||||
}
|
||||
|
||||
pageIndex := pageNum - 1
|
||||
if pageIndex < 0 || pageIndex > len(a.pages)-1 {
|
||||
return nil, nil, fmt.Errorf("page %d not found", pageNum)
|
||||
}
|
||||
page := a.pages[pageIndex].Duplicate()
|
||||
|
||||
// TODO add more checks before set the fields
|
||||
acroForm.SigFlags = core.MakeInteger(3)
|
||||
acroForm.DA = core.MakeString("/F1 0 Tf 0 g")
|
||||
n2ResourcesFont := core.MakeDict()
|
||||
n2ResourcesFont.Set("F1", DefaultFont().ToPdfObject())
|
||||
acroForm.DR = NewPdfPageResources()
|
||||
acroForm.DR.Font = n2ResourcesFont
|
||||
sig := NewPdfSignature()
|
||||
sig.M = core.MakeString(time.Now().Format("D:20060102150405-07'00'"))
|
||||
//sig.M = core.MakeString("D:20150226112648Z")
|
||||
sig.Type = core.MakeName("Sig")
|
||||
sig.Reason = core.MakeString("Test1")
|
||||
if err := handler.InitSignature(sig); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
a.addNewObjects(sig.container)
|
||||
|
||||
signatureField = NewPdfFieldSignature()
|
||||
fields := append(acroForm.AllFields(), signatureField.PdfField)
|
||||
fields := append(acroForm.AllFields(), field.PdfField)
|
||||
acroForm.Fields = &fields
|
||||
a.ReplaceAcroForm(acroForm)
|
||||
|
||||
// Replace original page.
|
||||
procPage(page)
|
||||
|
||||
signatureField.FT = core.MakeName("Sig")
|
||||
signatureField.V = sig
|
||||
signatureField.T = core.MakeString("Signature1")
|
||||
signatureField.F = core.MakeInteger(132)
|
||||
signatureField.P = page.ToPdfObject()
|
||||
signatureField.Rect = core.MakeArray(
|
||||
core.MakeInteger(0),
|
||||
core.MakeInteger(0),
|
||||
core.MakeInteger(0),
|
||||
core.MakeInteger(0),
|
||||
)
|
||||
|
||||
// Add the signature field to the page annotations.
|
||||
page.Annotations = append(page.Annotations, signatureField.PdfAnnotationWidget.PdfAnnotation)
|
||||
a.pages[pageIndex] = page
|
||||
|
||||
// Update acroform.
|
||||
a.ReplaceAcroForm(acroForm)
|
||||
return acroForm, signatureField, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReplaceAcroForm replaces the acrobat form. It appends a new form to the Pdf which
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/pkcs12"
|
||||
|
||||
@ -447,15 +448,33 @@ func TestAppenderSignPage4(t *testing.T) {
|
||||
t.Errorf("Fail: %v\n", err)
|
||||
return
|
||||
}
|
||||
_, appearance, err := appender.Sign(1, handler)
|
||||
if err != nil {
|
||||
|
||||
// Create signature field and appearance.
|
||||
signature := model.NewPdfSignature(handler)
|
||||
signature.SetName("Test Appender")
|
||||
signature.SetReason("TestAppenderSignPage4")
|
||||
signature.SetDate(time.Now(), "")
|
||||
|
||||
sigField := model.NewPdfFieldSignature(signature)
|
||||
sigField.T = core.MakeString("Signature1")
|
||||
|
||||
widget := model.NewPdfAnnotationWidget()
|
||||
widget.F = core.MakeInteger(132)
|
||||
widget.Rect = core.MakeArray(
|
||||
core.MakeInteger(0),
|
||||
core.MakeInteger(0),
|
||||
core.MakeInteger(0),
|
||||
core.MakeInteger(0),
|
||||
)
|
||||
widget.Parent = sigField.GetContainingPdfObject()
|
||||
|
||||
sigField.Annotations = append(sigField.Annotations, widget)
|
||||
|
||||
if err = appender.Sign(1, sigField); err != nil {
|
||||
t.Errorf("Fail: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
appearance.V.Name = core.MakeString("Test Appender")
|
||||
appearance.V.Reason = core.MakeString("TestAppenderSignPage4")
|
||||
|
||||
err = appender.WriteToFile(tempFile("appender_sign_page_4.pdf"))
|
||||
if err != nil {
|
||||
t.Errorf("Fail: %v\n", err)
|
||||
@ -513,16 +532,34 @@ func TestAppenderSignMultiple(t *testing.T) {
|
||||
f.Close()
|
||||
return
|
||||
}
|
||||
_, appearance, err := appender.Sign(1, handler)
|
||||
if err != nil {
|
||||
|
||||
// Create signature field and appearance.
|
||||
signature := model.NewPdfSignature(handler)
|
||||
signature.SetName(fmt.Sprintf("Test Appender - Round %d", i+1))
|
||||
signature.SetReason("TestAppenderSignPage4")
|
||||
signature.SetDate(time.Now(), "")
|
||||
|
||||
sigField := model.NewPdfFieldSignature(signature)
|
||||
sigField.T = core.MakeString("Signature1")
|
||||
|
||||
widget := model.NewPdfAnnotationWidget()
|
||||
widget.F = core.MakeInteger(132)
|
||||
widget.Rect = core.MakeArray(
|
||||
core.MakeInteger(0),
|
||||
core.MakeInteger(0),
|
||||
core.MakeInteger(0),
|
||||
core.MakeInteger(0),
|
||||
)
|
||||
widget.Parent = sigField.GetContainingPdfObject()
|
||||
|
||||
sigField.Annotations = append(sigField.Annotations, widget)
|
||||
|
||||
if err = appender.Sign(1, sigField); err != nil {
|
||||
t.Errorf("Fail: %v\n", err)
|
||||
f.Close()
|
||||
return
|
||||
}
|
||||
|
||||
appearance.V.Name = core.MakeString(fmt.Sprintf("Test Appender - Round %d", i+1))
|
||||
appearance.V.Reason = core.MakeString("TestAppenderSignPage4")
|
||||
|
||||
outPath := tempFile(fmt.Sprintf("appender_sign_multiple_%d.pdf", i+1))
|
||||
|
||||
err = appender.WriteToFile(outPath)
|
||||
|
@ -433,7 +433,6 @@ func (ch *PdfFieldChoice) ToPdfObject() core.PdfObject {
|
||||
// the name of the signer and verifying document contents.
|
||||
type PdfFieldSignature struct {
|
||||
*PdfField
|
||||
*PdfAnnotationWidget
|
||||
|
||||
V *PdfSignature
|
||||
Lock *core.PdfIndirectObject
|
||||
@ -441,22 +440,20 @@ type PdfFieldSignature struct {
|
||||
}
|
||||
|
||||
// NewPdfFieldSignature returns an initialized signature field.
|
||||
func NewPdfFieldSignature() *PdfFieldSignature {
|
||||
sig := &PdfFieldSignature{}
|
||||
sig.PdfField = NewPdfField()
|
||||
sig.PdfAnnotationWidget = NewPdfAnnotationWidget()
|
||||
sig.PdfField.SetContext(sig)
|
||||
sig.PdfAnnotationWidget.SetContext(sig)
|
||||
sig.PdfAnnotationWidget.container = sig.PdfField.container
|
||||
return sig
|
||||
func NewPdfFieldSignature(signature *PdfSignature) *PdfFieldSignature {
|
||||
field := &PdfFieldSignature{
|
||||
PdfField: NewPdfField(),
|
||||
V: signature,
|
||||
}
|
||||
field.PdfField.SetContext(field)
|
||||
field.FT = core.MakeName("Sig")
|
||||
|
||||
return field
|
||||
}
|
||||
|
||||
// ToPdfObject returns an indirect object containing the signature field dictionary.
|
||||
func (sig *PdfFieldSignature) ToPdfObject() core.PdfObject {
|
||||
// Set general field attributes
|
||||
if sig.PdfAnnotationWidget != nil {
|
||||
sig.PdfAnnotation.ToPdfObject()
|
||||
}
|
||||
sig.PdfField.ToPdfObject()
|
||||
|
||||
// Handle signature field specific attributes
|
||||
|
@ -7,6 +7,8 @@ package model
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/unidoc/unidoc/common"
|
||||
"github.com/unidoc/unidoc/pdf/core"
|
||||
@ -80,6 +82,7 @@ func (d *pdfSignDictionary) WriteString() string {
|
||||
type PdfSignature struct {
|
||||
Handler SignatureHandler
|
||||
container *core.PdfIndirectObject
|
||||
|
||||
// Type: Sig/DocTimeStamp
|
||||
Type *core.PdfObjectName
|
||||
Filter *core.PdfObjectName
|
||||
@ -102,25 +105,20 @@ type PdfSignature struct {
|
||||
}
|
||||
|
||||
// NewPdfSignature creates a new PdfSignature object.
|
||||
func NewPdfSignature() *PdfSignature {
|
||||
sig := &PdfSignature{}
|
||||
sigDict := &pdfSignDictionary{
|
||||
PdfObjectDictionary: core.MakeDict(),
|
||||
handler: &sig.Handler,
|
||||
signature: sig,
|
||||
}
|
||||
sig.container = core.MakeIndirectObject(sigDict)
|
||||
return sig
|
||||
func NewPdfSignature(handler SignatureHandler) *PdfSignature {
|
||||
sig := &PdfSignature{
|
||||
Type: core.MakeName("Sig"),
|
||||
Handler: handler,
|
||||
}
|
||||
|
||||
// PdfSignatureReference represents a signature reference dictionary.
|
||||
// (Table 253 - p. 477 in PDF32000_2008).
|
||||
type PdfSignatureReference struct {
|
||||
// Type: SigRef
|
||||
TransformMethod *core.PdfObjectName
|
||||
TransformParams *core.PdfObjectDictionary
|
||||
Data core.PdfObject
|
||||
DigestMethod *core.PdfObjectName
|
||||
dict := &pdfSignDictionary{
|
||||
PdfObjectDictionary: core.MakeDict(),
|
||||
handler: &handler,
|
||||
signature: sig,
|
||||
}
|
||||
|
||||
sig.container = core.MakeIndirectObject(dict)
|
||||
return sig
|
||||
}
|
||||
|
||||
// GetContainingPdfObject implements interface PdfModel.
|
||||
@ -128,6 +126,34 @@ func (sig *PdfSignature) GetContainingPdfObject() core.PdfObject {
|
||||
return sig.container
|
||||
}
|
||||
|
||||
// SetName sets the `Name` field of the signature.
|
||||
func (sig *PdfSignature) SetName(name string) {
|
||||
sig.Name = core.MakeString(name)
|
||||
}
|
||||
|
||||
// SetDate sets the `M` field of the signature.
|
||||
func (sig *PdfSignature) SetDate(date time.Time, format string) {
|
||||
if format == "" {
|
||||
format = "D:20060102150405-07'00'"
|
||||
}
|
||||
|
||||
sig.M = core.MakeString(date.Format(format))
|
||||
}
|
||||
|
||||
// SetReason sets the `Reason` field of the signature.
|
||||
func (sig *PdfSignature) SetReason(reason string) {
|
||||
sig.Reason = core.MakeString(reason)
|
||||
}
|
||||
|
||||
// Initialize initializes the PdfSignature.
|
||||
func (sig *PdfSignature) Initialize() error {
|
||||
if sig.Handler == nil {
|
||||
return errors.New("signature handler cannot be nil")
|
||||
}
|
||||
|
||||
return sig.Handler.InitSignature(sig)
|
||||
}
|
||||
|
||||
// ToPdfObject implements interface PdfModel.
|
||||
func (sig *PdfSignature) ToPdfObject() core.PdfObject {
|
||||
container := sig.container
|
||||
@ -213,146 +239,3 @@ func (r *PdfReader) newPdfSignatureFromIndirect(container *core.PdfIndirectObjec
|
||||
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
// PdfSignatureField represents a form field that contains a digital signature.
|
||||
// (12.7.4.5 - Signature Fields p. 454 in PDF32000_2008).
|
||||
//
|
||||
// The signature form field serves two primary purposes. 1. Define the form field that will provide the
|
||||
// visual signing properties for display but may also hold information needed when the actual signing
|
||||
// takes place such as signature method. This carries information from the author of the document to the
|
||||
// software that later does signing.
|
||||
//
|
||||
// Filling in (signing) the signature field entails updating at least the V entry and usually the AP entry of the
|
||||
// associated widget annotation. (Exporting a signature field exports the T, V, AP entries)
|
||||
//
|
||||
// The annotation rectangle (Rect) in such a dictionary shall give the position of the field on its page. Signature
|
||||
// fields that are not intended to be visible shall have an annotation rectangle that has zero height and width. PDF
|
||||
// processors shall treat such signatures as not visible. PDF processors shall also treat signatures as not
|
||||
// visible if either the Hidden bit or the NoView bit of the F entry is true
|
||||
//
|
||||
// The location of a signature within a document can have a bearing on its legal meaning. For this reason,
|
||||
// signature fields shall never refer to more than one annotation.
|
||||
type PdfSignatureField struct {
|
||||
container *core.PdfIndirectObject
|
||||
|
||||
V *PdfSignature
|
||||
Lock *core.PdfIndirectObject
|
||||
SV *core.PdfIndirectObject
|
||||
Kids *core.PdfObjectArray
|
||||
}
|
||||
|
||||
// NewPdfSignatureField prepares a PdfSignatureField from a PdfSignature.
|
||||
func NewPdfSignatureField(signature *PdfSignature) *PdfSignatureField {
|
||||
return &PdfSignatureField{
|
||||
V: signature,
|
||||
container: core.MakeIndirectObject(core.MakeDict()),
|
||||
}
|
||||
}
|
||||
|
||||
// ToPdfObject implements interface PdfModel.
|
||||
func (sf *PdfSignatureField) ToPdfObject() core.PdfObject {
|
||||
container := sf.container
|
||||
dict := container.PdfObject.(*core.PdfObjectDictionary)
|
||||
|
||||
// Set fields.
|
||||
if sf.V != nil {
|
||||
dict.Set("V", sf.V.ToPdfObject())
|
||||
}
|
||||
|
||||
dict.Set("FT", core.MakeName("Sig"))
|
||||
dict.SetIfNotNil("Lock", sf.Lock)
|
||||
dict.SetIfNotNil("SV", sf.SV)
|
||||
dict.SetIfNotNil("Kids", sf.Kids)
|
||||
// Other standard fields...
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
// PdfSignatureFieldLock represents signature field lock dictionary.
|
||||
// (Table 233 - p. 455 in PDF32000_2008).
|
||||
type PdfSignatureFieldLock struct {
|
||||
Type core.PdfObject
|
||||
Action *core.PdfObjectName
|
||||
Fields *core.PdfObjectArray
|
||||
P *core.PdfObjectInteger
|
||||
}
|
||||
|
||||
// PdfSignatureFieldSeed represents signature field seed value dictionary.
|
||||
// (Table 234 - p. 455 in PDF32000_2008).
|
||||
type PdfSignatureFieldSeed struct {
|
||||
container *core.PdfIndirectObject
|
||||
|
||||
Ff *core.PdfObjectInteger
|
||||
Filter *core.PdfObjectName
|
||||
SubFilter *core.PdfObjectArray
|
||||
DigestMethod *core.PdfObjectArray
|
||||
V *core.PdfObjectInteger
|
||||
Cert core.PdfObject
|
||||
Reasons *core.PdfObjectArray
|
||||
MDP *core.PdfObjectDictionary
|
||||
TimeStamp *core.PdfObjectDictionary
|
||||
LegalAttestation *core.PdfObjectArray
|
||||
AddRevInfo *core.PdfObjectBool
|
||||
LockDocument *core.PdfObjectName
|
||||
AppearanceFilter *core.PdfObjectString
|
||||
}
|
||||
|
||||
func (pss *PdfSignatureFieldSeed) ToPdfObject() core.PdfObject {
|
||||
container := pss.container
|
||||
dict := container.PdfObject.(*core.PdfObjectDictionary)
|
||||
|
||||
dict.SetIfNotNil("Ff", pss.Ff)
|
||||
dict.SetIfNotNil("Filter", pss.Filter)
|
||||
dict.SetIfNotNil("SubFilter", pss.SubFilter)
|
||||
dict.SetIfNotNil("DigestMethod", pss.DigestMethod)
|
||||
dict.SetIfNotNil("V", pss.V)
|
||||
dict.SetIfNotNil("Cert", pss.Cert)
|
||||
dict.SetIfNotNil("Reasons", pss.Reasons)
|
||||
dict.SetIfNotNil("MDP", pss.MDP)
|
||||
dict.SetIfNotNil("TimeStamp", pss.TimeStamp)
|
||||
dict.SetIfNotNil("LegalAttestation", pss.LegalAttestation)
|
||||
dict.SetIfNotNil("AddRevInfo", pss.AddRevInfo)
|
||||
dict.SetIfNotNil("LockDocument", pss.LockDocument)
|
||||
dict.SetIfNotNil("AppearanceFilter", pss.AppearanceFilter)
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
// PdfCertificateSeed represents certificate seed value dictionary.
|
||||
// (Table 235 - p. 457 in PDF32000_2008).
|
||||
type PdfCertificateSeed struct {
|
||||
container *core.PdfIndirectObject
|
||||
// Type
|
||||
Ff *core.PdfObjectInteger
|
||||
Subject *core.PdfObjectArray
|
||||
SignaturePolicyOID *core.PdfObjectString
|
||||
SignaturePolicyHashValue *core.PdfObjectString
|
||||
SignaturePolicyHashAlgorithm *core.PdfObjectName
|
||||
SignaturePolicyCommitmentType *core.PdfObjectArray
|
||||
SubjectDN *core.PdfObjectArray
|
||||
KeyUsage *core.PdfObjectArray
|
||||
Issuer *core.PdfObjectArray
|
||||
OID *core.PdfObjectArray
|
||||
URL *core.PdfObjectString
|
||||
URLType *core.PdfObjectName
|
||||
}
|
||||
|
||||
func (pcs *PdfCertificateSeed) ToPdfObject() core.PdfObject {
|
||||
container := pcs.container
|
||||
dict := container.PdfObject.(*core.PdfObjectDictionary)
|
||||
|
||||
dict.SetIfNotNil("Ff", pcs.Ff)
|
||||
dict.SetIfNotNil("Subject", pcs.Subject)
|
||||
dict.SetIfNotNil("SignaturePolicyOID", pcs.SignaturePolicyOID)
|
||||
dict.SetIfNotNil("SignaturePolicyHashValue", pcs.SignaturePolicyHashValue)
|
||||
dict.SetIfNotNil("SignaturePolicyHashAlgorithm", pcs.SignaturePolicyHashAlgorithm)
|
||||
dict.SetIfNotNil("SignaturePolicyCommitmentType", pcs.SignaturePolicyCommitmentType)
|
||||
dict.SetIfNotNil("SubjectDN", pcs.SubjectDN)
|
||||
dict.SetIfNotNil("KeyUsage", pcs.KeyUsage)
|
||||
dict.SetIfNotNil("Issuer", pcs.Issuer)
|
||||
dict.SetIfNotNil("OID", pcs.OID)
|
||||
dict.SetIfNotNil("URL", pcs.URL)
|
||||
dict.SetIfNotNil("URLType", pcs.URLType)
|
||||
|
||||
return container
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user