From 6e1af9ed3b76f0306a469ed50e64e737c9f752f7 Mon Sep 17 00:00:00 2001 From: Horst Rutter Date: Sun, 24 Feb 2019 21:40:31 +0100 Subject: [PATCH] Fix stamp transform calc --- pkg/pdfcpu/context.go | 12 ++-- pkg/pdfcpu/crypto.go | 2 +- pkg/pdfcpu/read.go | 109 ++++++++++++++++++++++++++++++++---- pkg/pdfcpu/stamp.go | 18 ++++-- pkg/pdfcpu/validate/info.go | 5 ++ pkg/pdfcpu/writeObjects.go | 22 ++++++-- pkg/pdfcpu/writePages.go | 5 ++ 7 files changed, 149 insertions(+), 24 deletions(-) diff --git a/pkg/pdfcpu/context.go b/pkg/pdfcpu/context.go index 6a1a8fac..3cd469b9 100644 --- a/pkg/pdfcpu/context.go +++ b/pkg/pdfcpu/context.go @@ -30,9 +30,11 @@ import ( type Context struct { *Configuration *XRefTable - Read *ReadContext - Optimize *OptimizationContext - Write *WriteContext + Read *ReadContext + Optimize *OptimizationContext + Write *WriteContext + writingPages bool // true, when writing page dicts. + dest bool // true when writing a destination within a page. } // NewContext initializes a new Context. @@ -48,6 +50,8 @@ func NewContext(rs io.ReadSeeker, fileName string, fileSize int64, config *Confi newReadContext(rs, fileName, fileSize), newOptimizationContext(), NewWriteContext(config.Eol), + false, + false, } return ctx, nil @@ -572,7 +576,7 @@ func (wc *WriteContext) HasWriteOffset(objNumber int) bool { return found } -// ReducedFeatureSet returns true for Split,Trim,Merge,ExtractPages. +// ReducedFeatureSet returns true for some operations. // Don't confuse with pdfcpu commands, these are internal triggers. func (wc *WriteContext) ReducedFeatureSet() bool { switch wc.Command { diff --git a/pkg/pdfcpu/crypto.go b/pkg/pdfcpu/crypto.go index 0014b4d9..77f7ba45 100644 --- a/pkg/pdfcpu/crypto.go +++ b/pkg/pdfcpu/crypto.go @@ -340,7 +340,7 @@ func validateOwnerPassword(ctx *Context) (ok bool, k []byte, err error) { ownerpw := ctx.OwnerPW userpw := ctx.UserPW - //fmt.Printf("ValidateOwnerPassword: ownerpw=ctx.OwnerPW=%s userpw=ctx.UserPW=%s\n", ownerpw, userpw) + //fmt.Printf("ValidateOwnerPassword: ownerpw=ctx.OwnerPW:<%s> userpw=ctx.UserPW:<%s>\n", ownerpw, userpw) e := ctx.E diff --git a/pkg/pdfcpu/read.go b/pkg/pdfcpu/read.go index b2667b78..765be18a 100644 --- a/pkg/pdfcpu/read.go +++ b/pkg/pdfcpu/read.go @@ -19,6 +19,7 @@ package pdfcpu import ( "bufio" "bytes" + "fmt" "io" "os" "sort" @@ -780,6 +781,98 @@ func scanLine(s *bufio.Scanner) (string, error) { return s.Text(), nil } +func scanTrailer(s *bufio.Scanner, line string) (string, error) { + + var buf bytes.Buffer + var err error + var i, j, k int + + log.Read.Printf("line: <%s>\n", line) + + // Scan for dict start tag "<<". + for { + i = strings.Index(line, "<<") + if i >= 0 { + break + } + line, err = scanLine(s) + log.Read.Printf("line: <%s>\n", line) + if err != nil { + return "", err + } + } + + line = line[i:] + buf.WriteString(line) + buf.WriteString(" ") + log.Read.Printf("scanTrailer dictBuf after start tag: <%s>\n", line) + + // Scan for dict end tag ">>" but account for inner dicts. + line = line[2:] + + for { + + if len(line) == 0 { + line, err = scanLine(s) + if err != nil { + return "", err + } + buf.WriteString(line) + buf.WriteString(" ") + log.Read.Printf("scanTrailer dictBuf next line: <%s>\n", line) + } + + i = strings.Index(line, "<<") + if i < 0 { + // No << + j = strings.Index(line, ">>") + if j >= 0 { + // Yes >> + if k == 0 { + return buf.String(), nil + } + k-- + println(k) + line = line[j+2:] + continue + } + // No >> + line, err = scanLine(s) + if err != nil { + return "", err + } + buf.WriteString(line) + buf.WriteString(" ") + log.Read.Printf("scanTrailer dictBuf next line: <%s>\n", line) + } else { + // Yes << + j = strings.Index(line, ">>") + if j < 0 { + // No >> + k++ + println(k) + line = line[i+2:] + } else { + // Yes >> + if i < j { + // handle << + k++ + println(k) + line = line[i+2:] + } else { + // handle >> + if k == 0 { + return buf.String(), nil + } + k-- + println(k) + line = line[j+2:] + } + } + } + } +} + func scanTrailerDict(s *bufio.Scanner, startTag bool) (string, error) { var buf bytes.Buffer @@ -864,16 +957,9 @@ func parseXRefSection(s *bufio.Scanner, ctx *Context) (*int64, error) { log.Read.Printf("line (len %d) <%s>\n", len(line), line) } - // Unless trailerDict already scanned into trailerString - if strings.Index(trailerString, ">>") == -1 { - - // scan lines until we have the complete trailer dict: << ... >> - trailerDictString, err := scanTrailerDict(s, strings.Index(trailerString, "<<") > 0) - if err != nil { - return nil, err - } - - trailerString += trailerDictString + trailerString, err = scanTrailer(s, trailerString) + if err != nil { + return nil, err } log.Read.Printf("parseXRefSection: trailerString: (len:%d) <%s>\n", len(trailerString), trailerString) @@ -2179,6 +2265,7 @@ func setupEncryptionKey(ctx *Context, encryptDictObjNr int) error { if err != nil { return err } + fmt.Printf("read: id = %0X\n", enc.ID) var ok bool @@ -2189,7 +2276,7 @@ func setupEncryptionKey(ctx *Context, encryptDictObjNr int) error { } // If the owner password does not match we generally move on if the user password is correct - // unless we need to insist on a correct owner password. + // unless we need to insist on a correct owner password due to the specific command in progress. if !ok && needsOwnerAndUserPassword(ctx.Mode) { return errors.New("owner password authentication error") } diff --git a/pkg/pdfcpu/stamp.go b/pkg/pdfcpu/stamp.go index 1a5c51dd..fb1a85fc 100644 --- a/pkg/pdfcpu/stamp.go +++ b/pkg/pdfcpu/stamp.go @@ -150,6 +150,16 @@ func (wm Watermark) String() string { sc = "absolute" } + bbox := "" + if wm.bb != nil { + bbox = (*wm.bb).String() + } + + vp := "" + if wm.vp != nil { + vp = (*wm.vp).String() + } + return fmt.Sprintf("Watermark: <%s> is %son top, typ:%s\n"+ "%s %d points\n"+ "PDFpage#: %d\n"+ @@ -171,8 +181,8 @@ func (wm Watermark) String() string { wm.diagonal, wm.opacity, wm.renderMode, - *wm.bb, - *wm.vp, + bbox, + vp, wm.pageRot, ) } @@ -287,8 +297,8 @@ func (wm *Watermark) calcTransformMatrix() *matrix { dy = wm.bb.LL.Y } - m2[2][0] = wm.vp.Width()/2 + sin*(wm.bb.Height()/2+dy) - cos*wm.bb.Width()/2 - m2[2][1] = wm.vp.Height()/2 - cos*(wm.bb.Height()/2+dy) - sin*wm.bb.Width()/2 + m2[2][0] = wm.vp.LL.X + wm.vp.Width()/2 + sin*(wm.bb.Height()/2+dy) - cos*wm.bb.Width()/2 + m2[2][1] = wm.vp.LL.Y + wm.vp.Height()/2 - cos*(wm.bb.Height()/2+dy) - sin*wm.bb.Width()/2 m := m1.multiply(m2) return &m diff --git a/pkg/pdfcpu/validate/info.go b/pkg/pdfcpu/validate/info.go index 60fcb6e1..037f3509 100644 --- a/pkg/pdfcpu/validate/info.go +++ b/pkg/pdfcpu/validate/info.go @@ -93,6 +93,11 @@ func validateDocumentInfoDict(xRefTable *pdf.XRefTable, obj pdf.Object) (hasModD // name, optional, since V1.3 case "Trapped": validate := func(s string) bool { return pdf.MemberOf(s, []string{"True", "False", "Unknown"}) } + if xRefTable.ValidationMode == pdf.ValidationRelaxed { + validate = func(s string) bool { + return pdf.MemberOf(s, []string{"True", "False", "Unknown", "true", "false", "unknown"}) + } + } _, err = xRefTable.DereferenceName(v, pdf.V13, validate) // text string, optional diff --git a/pkg/pdfcpu/writeObjects.go b/pkg/pdfcpu/writeObjects.go index debf0675..f5893647 100644 --- a/pkg/pdfcpu/writeObjects.go +++ b/pkg/pdfcpu/writeObjects.go @@ -523,16 +523,23 @@ func writeDirectObject(ctx *Context, o Object) error { switch o := o.(type) { case Dict: - for _, v := range o { + for k, v := range o { + if ctx.writingPages && (k == "Dest" || k == "D") { + ctx.dest = true + } _, _, err := writeDeepObject(ctx, v) if err != nil { return err } + ctx.dest = false } log.Write.Printf("writeDirectObject: end offset=%d\n", ctx.Write.Offset) case Array: - for _, v := range o { + for i, v := range o { + if ctx.dest && i == 0 { + continue + } _, _, err := writeDeepObject(ctx, v) if err != nil { return err @@ -568,11 +575,15 @@ func writeDeepDict(ctx *Context, d Dict, objNr, genNr int) error { return err } - for _, v := range d { + for k, v := range d { + if ctx.writingPages && (k == "Dest" || k == "D") { + ctx.dest = true + } _, _, err = writeDeepObject(ctx, v) if err != nil { return err } + ctx.dest = false } return nil @@ -609,7 +620,10 @@ func writeDeepArray(ctx *Context, a Array, objNr, genNr int) error { return err } - for _, v := range a { + for i, v := range a { + if ctx.dest && i == 0 { + continue + } _, _, err = writeDeepObject(ctx, v) if err != nil { return err diff --git a/pkg/pdfcpu/writePages.go b/pkg/pdfcpu/writePages.go index 775ee88a..22b54af1 100644 --- a/pkg/pdfcpu/writePages.go +++ b/pkg/pdfcpu/writePages.go @@ -61,6 +61,8 @@ func writePageDict(ctx *Context, ir *IndirectRef, pageDict Dict, pageNr int) err return errors.New("writePageDict: missing parent") } + ctx.writingPages = true + for _, e := range []struct { entryName string statsAttr int @@ -100,6 +102,8 @@ func writePageDict(ctx *Context, ir *IndirectRef, pageDict Dict, pageNr int) err } } + ctx.writingPages = false + log.Write.Printf("*** writePageDict end: obj#%d offset=%d ***\n", objNr, ctx.Write.Offset) return nil @@ -253,6 +257,7 @@ func writePagesDict(ctx *Context, ir *IndirectRef, pageNr *int) (skip bool, writ d.Update("Kids", kidsNew) d.Update("Count", Integer(countNew)) log.Write.Printf("writePagesDict: writing pageDict for obj=%d page=%d\n%s", objNr, *pageNr, d) + err = writeDictObject(ctx, objNr, genNr, d) if err != nil { return false, 0, err