summaryrefslogtreecommitdiff
path: root/contrib/gofpdi
diff options
context:
space:
mode:
authorDarkFreedman <misterdark@mail.ru>2019-10-04 13:25:04 +0300
committerDarkFreedman <misterdark@mail.ru>2019-10-04 13:25:04 +0300
commit6b0f0dee0dd42aebd05ef612bc8069361d06e487 (patch)
tree6ab44fb49bf3359e3803181dcd3ab9e1df6a65e4 /contrib/gofpdi
parent2e803db13067e702208818c79d70bd0ad1b13370 (diff)
parent291a9758a2b6dad177c43ee435a68dcd46959496 (diff)
Merge remote-tracking branch 'upstream/master'
Update to latest version
Diffstat (limited to 'contrib/gofpdi')
-rw-r--r--contrib/gofpdi/gofpdi.go143
-rw-r--r--contrib/gofpdi/gofpdi_test.go76
2 files changed, 219 insertions, 0 deletions
diff --git a/contrib/gofpdi/gofpdi.go b/contrib/gofpdi/gofpdi.go
new file mode 100644
index 0000000..7b9c822
--- /dev/null
+++ b/contrib/gofpdi/gofpdi.go
@@ -0,0 +1,143 @@
+/*
+Package gofpdi wraps the gofpdi PDF library to import existing PDFs as templates. See github.com/phpdave11/gofpdi
+for further information and examples.
+
+Users should call NewImporter() to obtain their own Importer instance to work with.
+To retain backwards compatibility, the package offers a default Importer that may be used via global functions. Note
+however that use of the default Importer is not thread safe.
+*/
+package gofpdi
+
+import (
+ realgofpdi "github.com/phpdave11/gofpdi"
+ "io"
+)
+
+// gofpdiPdf is a partial interface that only implements the functions we need
+// from the PDF generator to put the imported PDF templates on the PDF.
+type gofpdiPdf interface {
+ ImportObjects(objs map[string][]byte)
+ ImportObjPos(objs map[string]map[int]string)
+ ImportTemplates(tpls map[string]string)
+ UseImportedTemplate(tplName string, x float64, y float64, w float64, h float64)
+ SetError(err error)
+}
+
+// Importer wraps an Importer from the gofpdi library.
+type Importer struct {
+ fpdi *realgofpdi.Importer
+}
+
+// NewImporter creates a new Importer wrapping functionality from the gofpdi library.
+func NewImporter() *Importer {
+ return &Importer{
+ fpdi: realgofpdi.NewImporter(),
+ }
+}
+
+// ImportPage imports a page of a PDF file with the specified box (/MediaBox,
+// /TrimBox, /ArtBox, /CropBox, or /BleedBox). Returns a template id that can
+// be used with UseImportedTemplate to draw the template onto the page.
+func (i *Importer) ImportPage(f gofpdiPdf, sourceFile string, pageno int, box string) int {
+ // Set source file for fpdi
+ i.fpdi.SetSourceFile(sourceFile)
+ // return template id
+ return i.getTemplateID(f, pageno, box)
+}
+
+// ImportPageFromStream imports a page of a PDF with the specified box
+// (/MediaBox, TrimBox, /ArtBox, /CropBox, or /BleedBox). Returns a template id
+// that can be used with UseImportedTemplate to draw the template onto the
+// page.
+func (i *Importer) ImportPageFromStream(f gofpdiPdf, rs *io.ReadSeeker, pageno int, box string) int {
+ // Set source stream for fpdi
+ i.fpdi.SetSourceStream(rs)
+ // return template id
+ return i.getTemplateID(f, pageno, box)
+}
+
+func (i *Importer) getTemplateID(f gofpdiPdf, pageno int, box string) int {
+ // Import page
+ tpl := i.fpdi.ImportPage(pageno, box)
+
+ // Import objects into current pdf document
+ // Unordered means that the objects will be returned with a sha1 hash instead of an integer
+ // The objects themselves may have references to other hashes which will be replaced in ImportObjects()
+ tplObjIDs := i.fpdi.PutFormXobjectsUnordered()
+
+ // Set template names and ids (hashes) in gofpdf
+ f.ImportTemplates(tplObjIDs)
+
+ // Get a map[string]string of the imported objects.
+ // The map keys will be the ID of each object.
+ imported := i.fpdi.GetImportedObjectsUnordered()
+
+ // Import gofpdi objects into gofpdf
+ f.ImportObjects(imported)
+
+ // Get a map[string]map[int]string of the object hashes and their positions within each object,
+ // to be replaced with object ids (integers).
+ importedObjPos := i.fpdi.GetImportedObjHashPos()
+
+ // Import gofpdi object hashes and their positions into gopdf
+ f.ImportObjPos(importedObjPos)
+
+ return tpl
+}
+
+// UseImportedTemplate draws the template onto the page at x,y. If w is 0, the
+// template will be scaled to fit based on h. If h is 0, the template will be
+// scaled to fit based on w.
+func (i *Importer) UseImportedTemplate(f gofpdiPdf, tplid int, x float64, y float64, w float64, h float64) {
+ // Get values from fpdi
+ tplName, scaleX, scaleY, tX, tY := i.fpdi.UseTemplate(tplid, x, y, w, h)
+
+ f.UseImportedTemplate(tplName, scaleX, scaleY, tX, tY)
+}
+
+// GetPageSizes returns page dimensions for all pages of the imported pdf.
+// Result consists of map[<page number>]map[<box>]map[<dimension>]<value>.
+// <page number>: page number, note that page numbers start at 1
+// <box>: box identifier, e.g. "/MediaBox"
+// <dimension>: dimension string, either "w" or "h"
+func (i *Importer) GetPageSizes() map[int]map[string]map[string]float64 {
+ return i.fpdi.GetPageSizes()
+}
+
+// Default Importer used by global functions
+var fpdi = NewImporter()
+
+// ImportPage imports a page of a PDF file with the specified box (/MediaBox,
+// /TrimBox, /ArtBox, /CropBox, or /BleedBox). Returns a template id that can
+// be used with UseImportedTemplate to draw the template onto the page.
+// Note: This uses the default Importer. Call NewImporter() to obtain a custom Importer.
+func ImportPage(f gofpdiPdf, sourceFile string, pageno int, box string) int {
+ return fpdi.ImportPage(f, sourceFile, pageno, box)
+}
+
+// ImportPageFromStream imports a page of a PDF with the specified box
+// (/MediaBox, TrimBox, /ArtBox, /CropBox, or /BleedBox). Returns a template id
+// that can be used with UseImportedTemplate to draw the template onto the
+// page.
+// Note: This uses the default Importer. Call NewImporter() to obtain a custom Importer.
+func ImportPageFromStream(f gofpdiPdf, rs *io.ReadSeeker, pageno int, box string) int {
+ return fpdi.ImportPageFromStream(f, rs, pageno, box)
+}
+
+// UseImportedTemplate draws the template onto the page at x,y. If w is 0, the
+// template will be scaled to fit based on h. If h is 0, the template will be
+// scaled to fit based on w.
+// Note: This uses the default Importer. Call NewImporter() to obtain a custom Importer.
+func UseImportedTemplate(f gofpdiPdf, tplid int, x float64, y float64, w float64, h float64) {
+ fpdi.UseImportedTemplate(f, tplid, x, y, w, h)
+}
+
+// GetPageSizes returns page dimensions for all pages of the imported pdf.
+// Result consists of map[<page number>]map[<box>]map[<dimension>]<value>.
+// <page number>: page number, note that page numbers start at 1
+// <box>: box identifier, e.g. "/MediaBox"
+// <dimension>: dimension string, either "w" or "h"
+// Note: This uses the default Importer. Call NewImporter() to obtain a custom Importer.
+func GetPageSizes() map[int]map[string]map[string]float64 {
+ return fpdi.GetPageSizes()
+}
diff --git a/contrib/gofpdi/gofpdi_test.go b/contrib/gofpdi/gofpdi_test.go
new file mode 100644
index 0000000..70ef6cc
--- /dev/null
+++ b/contrib/gofpdi/gofpdi_test.go
@@ -0,0 +1,76 @@
+package gofpdi
+
+import (
+ "bytes"
+ "github.com/jung-kurt/gofpdf"
+ "github.com/jung-kurt/gofpdf/internal/example"
+ "io"
+ "sync"
+ "testing"
+)
+
+func ExampleNewImporter() {
+ // create new pdf
+ pdf := gofpdf.New("P", "pt", "A4", "")
+
+ // for testing purposes, get an arbitrary template pdf as stream
+ rs, _ := getTemplatePdf()
+
+ // create a new Importer instance
+ imp := NewImporter()
+
+ // import first page and determine page sizes
+ tpl := imp.ImportPageFromStream(pdf, &rs, 1, "/MediaBox")
+ pageSizes := imp.GetPageSizes()
+ nrPages := len(imp.GetPageSizes())
+
+ // add all pages from template pdf
+ for i := 1; i <= nrPages; i++ {
+ pdf.AddPage()
+ if i > 1 {
+ tpl = imp.ImportPageFromStream(pdf, &rs, i, "/MediaBox")
+ }
+ imp.UseImportedTemplate(pdf, tpl, 0, 0, pageSizes[i]["/MediaBox"]["w"], pageSizes[i]["/MediaBox"]["h"])
+ }
+
+ // output
+ fileStr := example.Filename("contrib_gofpdi_Importer")
+ err := pdf.OutputFileAndClose(fileStr)
+ example.Summary(err, fileStr)
+ // Output:
+ // Successfully generated ../../pdf/contrib_gofpdi_Importer.pdf
+}
+
+func TestGofpdiConcurrent(t *testing.T) {
+ wg := sync.WaitGroup{}
+ for i := 0; i < 100; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ pdf := gofpdf.New("P", "mm", "A4", "")
+ pdf.AddPage()
+ rs, _ := getTemplatePdf()
+ imp := NewImporter()
+ tpl := imp.ImportPageFromStream(pdf, &rs, 1, "/MediaBox")
+ imp.UseImportedTemplate(pdf, tpl, 0, 0, 210.0, 297.0)
+ // write to bytes buffer
+ buf := bytes.Buffer{}
+ if err := pdf.Output(&buf); err != nil {
+ t.Fail()
+ }
+ }()
+ }
+ wg.Wait()
+}
+
+func getTemplatePdf() (io.ReadSeeker, error) {
+ tpdf := gofpdf.New("P", "pt", "A4", "")
+ tpdf.AddPage()
+ tpdf.SetFont("Arial", "", 12)
+ tpdf.Text(20, 20, "Example Page 1")
+ tpdf.AddPage()
+ tpdf.Text(20, 20, "Example Page 2")
+ tbuf := bytes.Buffer{}
+ err := tpdf.Output(&tbuf)
+ return bytes.NewReader(tbuf.Bytes()), err
+}