mirror of
https://github.com/unidoc/unipdf.git
synced 2025-04-27 13:48:51 +08:00
893 lines
20 KiB
Go
893 lines
20 KiB
Go
/*
|
|
* This file is subject to the terms and conditions defined in
|
|
* file 'LICENSE.md', which is part of this source code package.
|
|
*/
|
|
|
|
package model_test
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/rsa"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/pkcs12"
|
|
|
|
"github.com/unidoc/unidoc/common"
|
|
"github.com/unidoc/unidoc/pdf/annotator"
|
|
"github.com/unidoc/unidoc/pdf/core"
|
|
"github.com/unidoc/unidoc/pdf/model"
|
|
"github.com/unidoc/unidoc/pdf/model/sighandler"
|
|
)
|
|
|
|
// This test file contains multiple tests to generate PDFs from existing Pdf files. The outputs are written
|
|
// into TMPDIR as files. The files themselves need to be observed to check for correctness as we don't have
|
|
// a good way to automatically check if every detail is correct.
|
|
|
|
func init() {
|
|
common.SetLogger(common.NewConsoleLogger(common.LogLevelDebug))
|
|
}
|
|
|
|
const testPdfFile1 = "./testdata/minimal.pdf"
|
|
const testPdfLoremIpsumFile = "./testdata/lorem.pdf"
|
|
const testPdf3pages = "./testdata/pages3.pdf"
|
|
|
|
const imgPdfFile1 = "./testdata/img1-1.pdf"
|
|
const imgPdfFile2 = "./testdata/img1-2.pdf"
|
|
|
|
// source http://foersom.com/net/HowTo/data/OoPdfFormExample.pdf
|
|
const testPdfAcroFormFile1 = "./testdata/OoPdfFormExample.pdf"
|
|
|
|
const testPdfSignedPDFDocument = "./testdata/SampleSignedPDFDocument.pdf"
|
|
|
|
const testPKS12Key = "./testdata/certificate.p12"
|
|
const testPKS12KeyPassword = "password"
|
|
const testSampleSignatureFile = "./testdata/sample_signature"
|
|
|
|
func tempFile(name string) string {
|
|
return filepath.Join(os.TempDir(), name)
|
|
}
|
|
|
|
func TestAppenderAddPage(t *testing.T) {
|
|
f1, err := os.Open(testPdfLoremIpsumFile)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f1.Close()
|
|
pdf1, err := model.NewPdfReader(f1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
f2, err := os.Open(testPdfFile1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f2.Close()
|
|
pdf2, err := model.NewPdfReader(f2)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender, err := model.NewPdfAppender(pdf1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender.AddPages(pdf1.PageList...)
|
|
|
|
appender.AddPages(pdf2.PageList...)
|
|
appender.AddPages(pdf2.PageList...)
|
|
|
|
err = appender.WriteToFile(tempFile("appender_add_page_1.pdf"))
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestAppenderAddPage2(t *testing.T) {
|
|
f1, err := os.Open(testPdfLoremIpsumFile)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f1.Close()
|
|
pdf1, err := model.NewPdfReader(f1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
f2, err := os.Open(testPdfAcroFormFile1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f2.Close()
|
|
pdf2, err := model.NewPdfReader(f2)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender, err := model.NewPdfAppender(pdf1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender.AddPages(pdf2.PageList...)
|
|
|
|
appender.ReplaceAcroForm(pdf2.AcroForm)
|
|
|
|
err = appender.WriteToFile(tempFile("appender_add_page_2.pdf"))
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestAppenderRemovePage(t *testing.T) {
|
|
f1, err := os.Open(testPdf3pages)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f1.Close()
|
|
pdf1, err := model.NewPdfReader(f1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender, err := model.NewPdfAppender(pdf1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender.RemovePage(1)
|
|
appender.RemovePage(2)
|
|
|
|
err = appender.WriteToFile(tempFile("appender_remove_page_1.pdf"))
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestAppenderReplacePage(t *testing.T) {
|
|
f1, err := os.Open(testPdf3pages)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f1.Close()
|
|
pdf1, err := model.NewPdfReader(f1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
f2, err := os.Open(testPdfFile1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f2.Close()
|
|
pdf2, err := model.NewPdfReader(f2)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender, err := model.NewPdfAppender(pdf1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender.ReplacePage(1, pdf1.PageList[1])
|
|
appender.ReplacePage(3, pdf2.PageList[0])
|
|
|
|
err = appender.WriteToFile(tempFile("appender_replace_page_1.pdf"))
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestAppenderAddAnnotation(t *testing.T) {
|
|
f1, err := os.Open(testPdf3pages)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f1.Close()
|
|
pdf1, err := model.NewPdfReader(f1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender, err := model.NewPdfAppender(pdf1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
page := pdf1.PageList[0]
|
|
annotation := model.NewPdfAnnotationSquare()
|
|
rect := model.PdfRectangle{Ury: 250.0, Urx: 150.0, Lly: 50.0, Llx: 50.0}
|
|
annotation.Rect = rect.ToPdfObject()
|
|
annotation.IC = core.MakeArrayFromFloats([]float64{4.0, 0.0, 0.3})
|
|
annotation.CA = core.MakeFloat(0.5)
|
|
|
|
page.Annotations = append(page.Annotations, annotation.PdfAnnotation)
|
|
|
|
appender.ReplacePage(1, page)
|
|
|
|
err = appender.WriteToFile(tempFile("appender_add_annotation_1.pdf"))
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestAppenderMergePage(t *testing.T) {
|
|
f1, err := os.Open(testPdf3pages)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f1.Close()
|
|
pdf1, err := model.NewPdfReader(f1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
f2, err := os.Open(testPdfFile1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f2.Close()
|
|
pdf2, err := model.NewPdfReader(f2)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender, err := model.NewPdfAppender(pdf1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
err = appender.MergePageWith(1, pdf2.PageList[0])
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
err = appender.WriteToFile(tempFile("appender_merge_page_1.pdf"))
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestAppenderMergePage2(t *testing.T) {
|
|
f1, err := os.Open(imgPdfFile1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f1.Close()
|
|
|
|
pdf1, err := model.NewPdfReader(f1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
f2, err := os.Open(imgPdfFile2)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f2.Close()
|
|
|
|
pdf2, err := model.NewPdfReader(f2)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender, err := model.NewPdfAppender(pdf1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
err = appender.MergePageWith(1, pdf2.PageList[0])
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender.AddPages(pdf2.PageList...)
|
|
|
|
err = appender.WriteToFile(tempFile("appender_merge_page_2.pdf"))
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestAppenderMergePage3(t *testing.T) {
|
|
f1, err := os.Open(testPdfFile1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f1.Close()
|
|
pdf1, err := model.NewPdfReader(f1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
f2, err := os.Open(testPdfLoremIpsumFile)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f2.Close()
|
|
|
|
pdf2, err := model.NewPdfReader(f2)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender, err := model.NewPdfAppender(pdf1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
err = appender.MergePageWith(1, pdf2.PageList[0])
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
err = appender.WriteToFile(tempFile("appender_merge_page_3.pdf"))
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func validateFile(t *testing.T, fileName string) {
|
|
t.Logf("Validating %s", fileName)
|
|
data, err := ioutil.ReadFile(fileName)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
reader, err := model.NewPdfReader(bytes.NewReader(data))
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
handler, _ := sighandler.NewAdobeX509RSASHA1(nil, nil)
|
|
handler2, _ := sighandler.NewAdobePKCS7Detached(nil, nil)
|
|
handlers := []model.SignatureHandler{handler, handler2}
|
|
|
|
res, err := reader.ValidateSignatures(handlers)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
if len(res) == 0 {
|
|
t.Errorf("Fail: signature fields not found")
|
|
return
|
|
}
|
|
|
|
if !res[0].IsSigned || !res[0].IsVerified {
|
|
t.Errorf("Fail: validation failed")
|
|
return
|
|
}
|
|
|
|
for i, item := range res {
|
|
t.Logf("== Signature %d", i+1)
|
|
t.Logf("%s", item.String())
|
|
}
|
|
}
|
|
|
|
func parseByteRange(byteRange *core.PdfObjectArray) ([]int64, error) {
|
|
if byteRange == nil {
|
|
return nil, errors.New("byte range cannot be nil")
|
|
}
|
|
if byteRange.Len() != 4 {
|
|
return nil, errors.New("invalid byte range length")
|
|
}
|
|
|
|
s1, err := core.GetNumberAsInt64(byteRange.Get(0))
|
|
if err != nil {
|
|
return nil, errors.New("invalid byte range value")
|
|
}
|
|
l1, err := core.GetNumberAsInt64(byteRange.Get(1))
|
|
if err != nil {
|
|
return nil, errors.New("invalid byte range value")
|
|
}
|
|
|
|
s2, err := core.GetNumberAsInt64(byteRange.Get(2))
|
|
if err != nil {
|
|
return nil, errors.New("invalid byte range value")
|
|
}
|
|
l2, err := core.GetNumberAsInt64(byteRange.Get(3))
|
|
if err != nil {
|
|
return nil, errors.New("invalid byte range value")
|
|
}
|
|
|
|
return []int64{s1, s1 + l1, s2, s2 + l2}, nil
|
|
}
|
|
|
|
func TestAppenderSignPage4(t *testing.T) {
|
|
// TODO move to reader_test.go
|
|
validateFile(t, testPdfSignedPDFDocument)
|
|
|
|
f1, err := os.Open(testPdfFile1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f1.Close()
|
|
pdf1, err := model.NewPdfReader(f1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender, err := model.NewPdfAppender(pdf1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
f, _ := ioutil.ReadFile(testPKS12Key)
|
|
privateKey, cert, err := pkcs12.Decode(f, testPKS12KeyPassword)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
handler, err := sighandler.NewAdobePKCS7Detached(privateKey.(*rsa.PrivateKey), cert)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// Create signature field and appearance.
|
|
signature := model.NewPdfSignature(handler)
|
|
signature.SetName("Test Appender")
|
|
signature.SetReason("TestAppenderSignPage4")
|
|
signature.SetDate(time.Now(), "")
|
|
|
|
if err := signature.Initialize(); err != nil {
|
|
return
|
|
}
|
|
|
|
sigField := model.NewPdfFieldSignature(signature)
|
|
sigField.T = core.MakeString("Signature1")
|
|
sigField.Rect = core.MakeArray(
|
|
core.MakeInteger(0),
|
|
core.MakeInteger(0),
|
|
core.MakeInteger(0),
|
|
core.MakeInteger(0),
|
|
)
|
|
|
|
if err = appender.Sign(1, sigField); err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
err = appender.WriteToFile(tempFile("appender_sign_page_4.pdf"))
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
validateFile(t, tempFile("appender_sign_page_4.pdf"))
|
|
}
|
|
|
|
func TestAppenderSignMultiple(t *testing.T) {
|
|
inputPath := testPdfFile1
|
|
|
|
for i := 0; i < 3; i++ {
|
|
f, err := os.Open(inputPath)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
pdfReader, err := model.NewPdfReader(f)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
f.Close()
|
|
return
|
|
}
|
|
|
|
t.Logf("Fields: %d", len(pdfReader.AcroForm.AllFields()))
|
|
|
|
if len(pdfReader.AcroForm.AllFields()) != i {
|
|
t.Fatalf("fields != %d (got %d)", i, len(pdfReader.AcroForm.AllFields()))
|
|
}
|
|
|
|
t.Logf("Annotations: %d", len(pdfReader.PageList[0].Annotations))
|
|
if len(pdfReader.PageList[0].Annotations) != i {
|
|
t.Fatalf("page annotations != %d (got %d)", i, len(pdfReader.PageList[0].Annotations))
|
|
}
|
|
|
|
appender, err := model.NewPdfAppender(pdfReader)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
f.Close()
|
|
return
|
|
}
|
|
|
|
pfxData, _ := ioutil.ReadFile(testPKS12Key)
|
|
privateKey, cert, err := pkcs12.Decode(pfxData, testPKS12KeyPassword)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
f.Close()
|
|
return
|
|
}
|
|
|
|
handler, err := sighandler.NewAdobePKCS7Detached(privateKey.(*rsa.PrivateKey), cert)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
f.Close()
|
|
return
|
|
}
|
|
|
|
// Create signature field and appearance.
|
|
signature := model.NewPdfSignature(handler)
|
|
signature.SetName(fmt.Sprintf("Test Appender - Round %d", i+1))
|
|
signature.SetReason(fmt.Sprintf("Test Appender - Round %d", i+1))
|
|
signature.SetDate(time.Now(), "")
|
|
|
|
if err := signature.Initialize(); err != nil {
|
|
return
|
|
}
|
|
|
|
sigField := model.NewPdfFieldSignature(signature)
|
|
sigField.T = core.MakeString(fmt.Sprintf("Signature %d", i+1))
|
|
sigField.Rect = core.MakeArray(
|
|
core.MakeInteger(0),
|
|
core.MakeInteger(0),
|
|
core.MakeInteger(0),
|
|
core.MakeInteger(0),
|
|
)
|
|
|
|
if err = appender.Sign(1, sigField); err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
f.Close()
|
|
return
|
|
}
|
|
|
|
outPath := tempFile(fmt.Sprintf("appender_sign_multiple_%d.pdf", i+1))
|
|
|
|
err = appender.WriteToFile(outPath)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
f.Close()
|
|
return
|
|
}
|
|
|
|
validateFile(t, outPath)
|
|
inputPath = outPath
|
|
|
|
f.Close()
|
|
}
|
|
}
|
|
|
|
func TestSignatureAppearance(t *testing.T) {
|
|
f, err := os.Open(testPdf3pages)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f.Close()
|
|
|
|
pdfReader, err := model.NewPdfReader(f)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
t.Logf("Fields: %d", len(pdfReader.AcroForm.AllFields()))
|
|
|
|
appender, err := model.NewPdfAppender(pdfReader)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
pfxData, _ := ioutil.ReadFile(testPKS12Key)
|
|
privateKey, cert, err := pkcs12.Decode(pfxData, testPKS12KeyPassword)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
handler, err := sighandler.NewAdobePKCS7Detached(privateKey.(*rsa.PrivateKey), cert)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// Create signature.
|
|
signature := model.NewPdfSignature(handler)
|
|
signature.SetName("Test Signature Appearance Name")
|
|
signature.SetReason("TestSignatureAppearance Reason")
|
|
signature.SetDate(time.Now(), "")
|
|
|
|
if err := signature.Initialize(); err != nil {
|
|
return
|
|
}
|
|
|
|
numPages, err := pdfReader.GetNumPages()
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
for i := 0; i < numPages; i++ {
|
|
pageNum := i + 1
|
|
|
|
// Annot1
|
|
opts := annotator.NewSignatureFieldOpts()
|
|
opts.FontSize = 10
|
|
opts.Rect = []float64{10, 25, 75, 60}
|
|
|
|
sigField, err := annotator.NewSignatureField(
|
|
signature,
|
|
[]*annotator.SignatureLine{
|
|
annotator.NewSignatureLine("Name", "Jane Doe"),
|
|
annotator.NewSignatureLine("Date", "2019.01.03"),
|
|
annotator.NewSignatureLine("Reason", "Some reason"),
|
|
annotator.NewSignatureLine("Location", "New York"),
|
|
annotator.NewSignatureLine("DN", "authority1:name1"),
|
|
},
|
|
opts,
|
|
)
|
|
sigField.T = core.MakeString(fmt.Sprintf("Signature %d", pageNum))
|
|
|
|
if err = appender.Sign(pageNum, sigField); err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// Annot2
|
|
opts = annotator.NewSignatureFieldOpts()
|
|
opts.FontSize = 8
|
|
opts.Rect = []float64{250, 25, 325, 70}
|
|
opts.TextColor = model.NewPdfColorDeviceRGB(255, 0, 0)
|
|
|
|
sigField, err = annotator.NewSignatureField(
|
|
signature,
|
|
[]*annotator.SignatureLine{
|
|
annotator.NewSignatureLine("Name", "John Doe"),
|
|
annotator.NewSignatureLine("Date", "2019.03.14"),
|
|
annotator.NewSignatureLine("Reason", "No reason"),
|
|
annotator.NewSignatureLine("Location", "London"),
|
|
annotator.NewSignatureLine("DN", "authority2:name2"),
|
|
},
|
|
opts,
|
|
)
|
|
sigField.T = core.MakeString(fmt.Sprintf("Signature2 %d", pageNum))
|
|
|
|
if err = appender.Sign(pageNum, sigField); err != nil {
|
|
log.Fatalf("Fail: %v\n", err)
|
|
}
|
|
|
|
// Annot3
|
|
opts = annotator.NewSignatureFieldOpts()
|
|
opts.BorderSize = 1
|
|
opts.FontSize = 10
|
|
opts.Rect = []float64{475, 25, 590, 80}
|
|
opts.FillColor = model.NewPdfColorDeviceRGB(255, 255, 0)
|
|
opts.TextColor = model.NewPdfColorDeviceRGB(0, 0, 200)
|
|
|
|
sigField, err = annotator.NewSignatureField(
|
|
signature,
|
|
[]*annotator.SignatureLine{
|
|
annotator.NewSignatureLine("Name", "John Smith"),
|
|
annotator.NewSignatureLine("Date", "2019.02.19"),
|
|
annotator.NewSignatureLine("Reason", "Another reason"),
|
|
annotator.NewSignatureLine("Location", "Paris"),
|
|
annotator.NewSignatureLine("DN", "authority3:name3"),
|
|
},
|
|
opts,
|
|
)
|
|
sigField.T = core.MakeString(fmt.Sprintf("Signature3 %d", pageNum))
|
|
|
|
if err = appender.Sign(pageNum, sigField); err != nil {
|
|
log.Fatalf("Fail: %v\n", err)
|
|
}
|
|
}
|
|
|
|
outPath := tempFile("appender_signature_appearance.pdf")
|
|
if err = appender.WriteToFile(outPath); err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
validateFile(t, outPath)
|
|
}
|
|
|
|
func TestAppenderExternalSignature(t *testing.T) {
|
|
validateFile(t, testPdfSignedPDFDocument)
|
|
|
|
f1, err := os.Open(testPdfFile1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f1.Close()
|
|
|
|
reader, err := model.NewPdfReader(f1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender, err := model.NewPdfAppender(reader)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
handler, err := sighandler.NewEmptyAdobePKCS7Detached(8192)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// Create signature.
|
|
signature := model.NewPdfSignature(handler)
|
|
signature.SetName("Test External Signature")
|
|
signature.SetReason("TestAppenderExternalSignature")
|
|
signature.SetDate(time.Date(2019, 3, 15, 4, 25, 24, 0, time.UTC), "")
|
|
|
|
if err := signature.Initialize(); err != nil {
|
|
return
|
|
}
|
|
|
|
// Create signature field and appearance.
|
|
opts := annotator.NewSignatureFieldOpts()
|
|
opts.FontSize = 10
|
|
opts.Rect = []float64{10, 25, 75, 60}
|
|
|
|
sigField, err := annotator.NewSignatureField(
|
|
signature,
|
|
[]*annotator.SignatureLine{
|
|
annotator.NewSignatureLine("Name", "John Doe"),
|
|
annotator.NewSignatureLine("Date", "2019.15.03"),
|
|
annotator.NewSignatureLine("Reason", "External signature test"),
|
|
},
|
|
opts,
|
|
)
|
|
sigField.T = core.MakeString("External signature")
|
|
|
|
if err = appender.Sign(1, sigField); err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// Write PDF file to buffer.
|
|
pdfBuf := bytes.NewBuffer(nil)
|
|
if err = appender.Write(pdfBuf); err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
pdfData := pdfBuf.Bytes()
|
|
|
|
// Parse signature byte range.
|
|
byteRange, err := parseByteRange(signature.ByteRange)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// This would be the time to send the PDF buffer to a signing device or
|
|
// signing web service and get back the signature. We will simulate this by
|
|
// reading the signature from a sample signature file.
|
|
|
|
// Read external signature data.
|
|
signatureData, err := ioutil.ReadFile(testSampleSignatureFile)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// Apply external signature to the PDF data buffer.
|
|
sigBytes := make([]byte, 8192)
|
|
copy(sigBytes, signatureData)
|
|
|
|
sig := core.MakeHexString(string(sigBytes)).WriteString()
|
|
copy(pdfData[byteRange[1]:byteRange[2]], []byte(sig))
|
|
|
|
// Write output file.
|
|
outputPath := tempFile("appender_sign_external_signature.pdf")
|
|
if err := ioutil.WriteFile(outputPath, pdfData, os.ModePerm); err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// Validate output file.
|
|
validateFile(t, outputPath)
|
|
}
|
|
|
|
// Each Appender can only be written out once, further invokations of Write should result in an error.
|
|
func TestAppenderAttemptMultiWrite(t *testing.T) {
|
|
f1, err := os.Open(testPdfLoremIpsumFile)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f1.Close()
|
|
pdf1, err := model.NewPdfReader(f1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
f2, err := os.Open(testPdfFile1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
defer f2.Close()
|
|
pdf2, err := model.NewPdfReader(f2)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender, err := model.NewPdfAppender(pdf1)
|
|
if err != nil {
|
|
t.Errorf("Fail: %v\n", err)
|
|
return
|
|
}
|
|
|
|
appender.AddPages(pdf1.PageList...)
|
|
appender.AddPages(pdf2.PageList...)
|
|
appender.AddPages(pdf2.PageList...)
|
|
|
|
// Write twice to buffer and compare results.
|
|
var buf1, buf2 bytes.Buffer
|
|
err = appender.Write(&buf1)
|
|
if err != nil {
|
|
t.Fatalf("Error: %v", err)
|
|
}
|
|
err = appender.Write(&buf2)
|
|
if err == nil {
|
|
t.Fatalf("Second invokation of appender.Write should yield an error")
|
|
}
|
|
}
|