From 0d1bf91d603c56140ca9caee9ad073842319fcb0 Mon Sep 17 00:00:00 2001 From: Ben Rush Date: Tue, 11 May 2021 13:31:46 -0700 Subject: [PATCH 1/2] Overhaul Annotations Add support for highlight annotation and implement indirect references for all annotations --- annotations.go | 53 +++++++++++++++++++++++++++++++++ attachments.go | 16 ---------- def.go | 1 + fpdf.go | 80 +++++++++++++++++++++++++++++++++++--------------- fpdf_test.go | 29 ++++++++++++++++++ go.sum | 14 +-------- 6 files changed, 141 insertions(+), 52 deletions(-) create mode 100644 annotations.go diff --git a/annotations.go b/annotations.go new file mode 100644 index 00000000..94580692 --- /dev/null +++ b/annotations.go @@ -0,0 +1,53 @@ +package gofpdf + +import ( + "fmt" +) + +type Highlight struct { + Contents string + Rect []float32 + QuadPoints []float32 + Color []float32 + Opacity float32 + Author string +} + +func (h *Highlight) String() string { + if h.Rect == nil || len(h.Rect) != 4 { + return "" + } + + hlstr := "<= 8 { + hlstr += fmt.Sprintf(" /QuadPoints %.4f ", h.QuadPoints) + } + + if h.Contents != "" { + hlstr += fmt.Sprintf("/Contents (%s)", h.Contents) + } + + if h.Author != "" { + hlstr += fmt.Sprintf("/T (%s)", h.Author) + } + + hlstr += fmt.Sprintf("/Rect %.4f", h.Rect) + hlstr += "]>>" + return hlstr +} + +func (f *Fpdf) AddHighlightAnnotation(highlight Highlight) { + if f.page >= len(f.highlights) { + f.highlights = append(f.highlights, []Highlight{highlight}) + } else { + f.highlights[f.page] = append(f.highlights[f.page], highlight) + } +} diff --git a/attachments.go b/attachments.go index 6e397b73..5b9ca27b 100644 --- a/attachments.go +++ b/attachments.go @@ -139,19 +139,3 @@ func (f *Fpdf) putAnnotationsAttachments() { } } } - -func (f *Fpdf) putAttachmentAnnotationLinks(out *fmtBuffer, page int) { - for _, an := range f.pageAttachments[page] { - x1, y1, x2, y2 := an.x, an.y, an.x+an.w, an.y-an.h - as := fmt.Sprintf("<< /Type /XObject /Subtype /Form /BBox [%.2f %.2f %.2f %.2f] /Length 0 >>", - x1, y1, x2, y2) - as += "\nstream\nendstream" - - out.printf("<< /Type /Annot /Subtype /FileAttachment /Rect [%.2f %.2f %.2f %.2f] /Border [0 0 0]\n", - x1, y1, x2, y2) - out.printf("/Contents %s ", f.textstring(utf8toutf16(an.Description))) - out.printf("/T %s ", f.textstring(utf8toutf16(an.Filename))) - out.printf("/AP << /N %s>>", as) - out.printf("/FS %d 0 R >>\n", an.objectNumber) - } -} diff --git a/def.go b/def.go index 6a7030f9..f1df8515 100644 --- a/def.go +++ b/def.go @@ -553,6 +553,7 @@ type Fpdf struct { links []intLinkType // array of internal links attachments []Attachment // slice of content to embed globally pageAttachments [][]annotationAttach // 1-based array of annotation for file attachments (per page) + highlights [][]Highlight // 1-based array of array of highlight annotations (per page) outlines []outlineType // array of outlines outlineRoot int // root of outlines autoPageBreak bool // automatic page breaking diff --git a/fpdf.go b/fpdf.go index 3be1cdc2..38214529 100644 --- a/fpdf.go +++ b/fpdf.go @@ -97,6 +97,8 @@ func fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr string, size SizeType) f.links = append(f.links, intLinkType{}) // links[0] is unused (1-based) f.pageAttachments = make([][]annotationAttach, 0, 8) f.pageAttachments = append(f.pageAttachments, []annotationAttach{}) // + f.highlights = make([][]Highlight, 0, 8) + f.highlights = append(f.highlights, []Highlight{}) f.aliasMap = make(map[string]string) f.inHeader = false f.inFooter = false @@ -3519,6 +3521,7 @@ func (f *Fpdf) beginpage(orientationStr string, size SizeType) { f.pages = append(f.pages, bytes.NewBufferString("")) f.pageLinks = append(f.pageLinks, make([]linkType, 0, 0)) f.pageAttachments = append(f.pageAttachments, []annotationAttach{}) + f.highlights = append(f.highlights, make([]Highlight, 0)) f.state = 2 f.x = f.lMargin f.y = f.tMargin @@ -3893,28 +3896,25 @@ func (f *Fpdf) putpages() { } pagesObjectNumbers := make([]int, nb+1) // 1-based for n := 1; n <= nb; n++ { - // Page - f.newobj() - pagesObjectNumbers[n] = f.n // save for /Kids - f.out("< 0 { - var annots fmtBuffer - annots.printf("/Annots [") + // Annotations + annots := make([]string, len(f.pageLinks[n])+len(f.pageAttachments[n])+len(f.highlights[n])) + if len(annots) > 0 { + if len(f.highlights) >= n { + for _, hl := range f.highlights[n] { + f.newobj() + f.out(hl.String()) + annots = append(annots, fmt.Sprintf("%d 0 R ", f.n)) + f.out("endobj") + } + } + for _, pl := range f.pageLinks[n] { - annots.printf("<>>>", f.textstring(pl.linkStr)) + f.outf("/A <>>>", f.textstring(pl.linkStr)) } else { l := f.links[pl.link] var sz SizeType @@ -3926,13 +3926,47 @@ func (f *Fpdf) putpages() { h = hPt } // dbg("h [%.2f], l.y [%.2f] f.k [%.2f]\n", h, l.y, f.k) - annots.printf("/Dest [%d 0 R /XYZ 0 %.2f null]>>", 1+2*l.page, h-l.y*f.k) + f.outf("/Dest [%d 0 R /XYZ 0 %.2f null]>>", 1+2*l.page, h-l.y*f.k) } } - f.putAttachmentAnnotationLinks(&annots, n) - annots.printf("]") - f.out(annots.String()) + + for _, an := range f.pageAttachments[n] { + f.newobj() + annots = append(annots, fmt.Sprintf("%d 0 R ", f.n)) + x1, y1, x2, y2 := an.x, an.y, an.x+an.w, an.y-an.h + as := fmt.Sprintf("<< /Type /XObject /Subtype /Form /BBox [%.2f %.2f %.2f %.2f] /Length 0 >>", + x1, y1, x2, y2) + as += "\nstream\nendstream" + + f.outf("<< /Type /Annot /Subtype /FileAttachment /Rect [%.2f %.2f %.2f %.2f] /Border [0 0 0]\n", + x1, y1, x2, y2) + f.outf("/Contents %s ", f.textstring(utf8toutf16(an.Description))) + f.outf("/T %s ", f.textstring(utf8toutf16(an.Filename))) + f.outf("/AP << /N %s>>", as) + f.outf("/FS %d 0 R >>\n", an.objectNumber) + } + } + + // Page + f.newobj() + pagesObjectNumbers[n] = f.n // save for /Kids + f.out("< 0 { + f.out("/Annots [ ") + for _, ref := range annots { + f.out(ref) + } + f.out("] ") } + f.out("/Resources 2 0 R") if f.pdfVersion > "1.3" { f.out("/Group <>") } diff --git a/fpdf_test.go b/fpdf_test.go index a8d969eb..4248a877 100644 --- a/fpdf_test.go +++ b/fpdf_test.go @@ -182,6 +182,35 @@ func TestIssue0209SplitLinesEqualMultiCell(t *testing.T) { } } +func TestHighlightAnnotations(t *testing.T) { + + var pdf *gofpdf.Fpdf + + pdf = gofpdf.New("P", "mm", "A4", "") + + pdf.AddPage() + pdf.SetFont("Times", "", 12) + ht := 4.0 //mm + pdf.Write(ht, lorem()) + + hl := gofpdf.Highlight{ + Rect: []float32{25.0, 750.0, 570.0, 820.0}, + QuadPoints: []float32{25, 820, 570, 820, 25, 750, 570, 750}, + Color: []float32{1.0, 0.941, 0.4}, + Opacity: 0.3, + Author: "Fpdf", + Contents: "Test highlight", + } + + pdf.AddHighlightAnnotation(hl) + + fileStr := example.Filename("Fpdf_HighlightAnnotations") + err := pdf.OutputFileAndClose(fileStr) + example.Summary(err, fileStr) + // Output: + // Successfully generated pdf/Fpdf_HighlightAnnotations.pdf +} + // TestFooterFuncLpi tests to make sure the footer is not call twice and SetFooterFuncLpi can work // without SetFooterFunc. func TestFooterFuncLpi(t *testing.T) { diff --git a/go.sum b/go.sum index 0f70e932..47a31a0b 100644 --- a/go.sum +++ b/go.sum @@ -3,17 +3,7 @@ github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/phpdave11/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/phpdave11/gofpdi v1.0.7 h1:k2oy4yhkQopCK+qW8KjCla0iU2RpDow+QUDmH9DDt44= -github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.9 h1:MpzgK5tRbY7mfiGcLcV3CUk3sFBa61xbBXDYW3LlmBM= -github.com/phpdave11/gofpdi v1.0.9/go.mod h1:r/fO8a9KSCrpwwTaqEx3amFJ6IHjfvAq7w1GP0XYRcg= -github.com/phpdave11/gofpdi v1.0.10 h1:hghWGyJV8uSyIYvzCasOtqevbV/4FCL8fef9y8ttJTU= -github.com/phpdave11/gofpdi v1.0.10/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.11 h1:wsBNx+3S0wy1dEp6fzv281S74ogZGgIdYWV2PugWgho= -github.com/phpdave11/gofpdi v1.0.11/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.12 h1:RZb9NG62cw/RW0rHAduVRo+98R8o/G1krcg2ns7DakQ= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13 h1:o61duiW8M9sMlkVXWlvP92sZJtGKENvW3VExs6dZukQ= github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -25,8 +15,6 @@ github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58 h1:nlG4Wa5+min github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/image v0.0.0-20190902063713-cb417be4ba39 h1:4dQcAORh9oYBwVSBVIkP489LUPC+f1HBkTYXgmqfR+o= -golang.org/x/image v0.0.0-20190902063713-cb417be4ba39/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a h1:gHevYm0pO4QUbwy8Dmdr01R5r1BuKtfYqRqF0h/Cbh0= golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 535d8164373f22599e1fea0d27d0dfc7eb4536f0 Mon Sep 17 00:00:00 2001 From: Ben Rush Date: Fri, 14 May 2021 15:57:37 -0700 Subject: [PATCH 2/2] Clean up a few formatting bugs --- annotations.go | 2 +- fpdf.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/annotations.go b/annotations.go index 94580692..a7383d8e 100644 --- a/annotations.go +++ b/annotations.go @@ -40,7 +40,7 @@ func (h *Highlight) String() string { } hlstr += fmt.Sprintf("/Rect %.4f", h.Rect) - hlstr += "]>>" + hlstr += ">>" return hlstr } diff --git a/fpdf.go b/fpdf.go index 38214529..658597a4 100644 --- a/fpdf.go +++ b/fpdf.go @@ -3962,7 +3962,9 @@ func (f *Fpdf) putpages() { if len(annots) > 0 { f.out("/Annots [ ") for _, ref := range annots { - f.out(ref) + if ref != "" { + f.out(ref) + } } f.out("] ") }