From 0fb5126fefeb1a7be7ed1c4dac02539af931a237 Mon Sep 17 00:00:00 2001 From: Dave Barnes Date: Wed, 15 May 2019 10:45:22 -0500 Subject: Add support for imported objects and templates to version 1 of gofpdf. --- contrib/gofpdi/gofpdi.go | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 contrib/gofpdi/gofpdi.go (limited to 'contrib/gofpdi') diff --git a/contrib/gofpdi/gofpdi.go b/contrib/gofpdi/gofpdi.go new file mode 100644 index 0000000..c1f3df8 --- /dev/null +++ b/contrib/gofpdi/gofpdi.go @@ -0,0 +1,62 @@ +package gofpdi + +import ( + realgofpdi "github.com/phpdave11/gofpdi" +) + +// Create new gofpdi instance +var fpdi = realgofpdi.NewImporter() + +// 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) +} + +// 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 ImportPage(f gofpdiPdf, sourceFile string, pageno int, box string) int { + // Set source file for fpdi + fpdi.SetSourceFile(sourceFile) + + // Import page + tpl := 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 := 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 := 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 := fpdi.GetImportedObjHashPos() + + // Import gofpdi object hashes and their positions into gopdf + f.ImportObjPos(importedObjPos) + + return tpl +} + +// Draw 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 UseImportedTemplate(f gofpdiPdf, tplid int, x float64, y float64, w float64, h float64) { + // Get values from fpdi + tplName, scaleX, scaleY, tX, tY := fpdi.UseTemplate(tplid, x, y, w, h) + + f.UseImportedTemplate(tplName, scaleX, scaleY, tX, tY) +} -- cgit v1.2.1-24-ge1ad From ce8a12ff5655c57989b925abc0b8a3a1e6535d59 Mon Sep 17 00:00:00 2001 From: Dave Barnes Date: Wed, 15 May 2019 11:25:39 -0500 Subject: Clean up code based on jung-kurt's feedback. --- contrib/gofpdi/gofpdi.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib/gofpdi') diff --git a/contrib/gofpdi/gofpdi.go b/contrib/gofpdi/gofpdi.go index c1f3df8..d1b98e9 100644 --- a/contrib/gofpdi/gofpdi.go +++ b/contrib/gofpdi/gofpdi.go @@ -29,10 +29,10 @@ func ImportPage(f gofpdiPdf, sourceFile string, pageno int, box string) int { // 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 := fpdi.PutFormXobjectsUnordered() + tplObjIDs := fpdi.PutFormXobjectsUnordered() // Set template names and ids (hashes) in gofpdf - f.ImportTemplates(tplObjIds) + f.ImportTemplates(tplObjIDs) // Get a map[string]string of the imported objects. // The map keys will be the ID of each object. @@ -41,7 +41,7 @@ func ImportPage(f gofpdiPdf, sourceFile string, pageno int, box string) int { // 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, + // 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 := fpdi.GetImportedObjHashPos() -- cgit v1.2.1-24-ge1ad From 5f4e2f65402b9aa611f93b2fa14ea69bf7b26d2d Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 18 May 2019 08:08:21 -0400 Subject: Generalize awk script that produces doc.go --- contrib/gofpdi/gofpdi.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'contrib/gofpdi') diff --git a/contrib/gofpdi/gofpdi.go b/contrib/gofpdi/gofpdi.go index d1b98e9..b951ea3 100644 --- a/contrib/gofpdi/gofpdi.go +++ b/contrib/gofpdi/gofpdi.go @@ -17,8 +17,9 @@ type gofpdiPdf interface { SetError(err error) } -// 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. +// 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 ImportPage(f gofpdiPdf, sourceFile string, pageno int, box string) int { // Set source file for fpdi fpdi.SetSourceFile(sourceFile) @@ -51,9 +52,9 @@ func ImportPage(f gofpdiPdf, sourceFile string, pageno int, box string) int { return tpl } -// Draw 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 +// 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 UseImportedTemplate(f gofpdiPdf, tplid int, x float64, y float64, w float64, h float64) { // Get values from fpdi tplName, scaleX, scaleY, tX, tY := fpdi.UseTemplate(tplid, x, y, w, h) -- cgit v1.2.1-24-ge1ad From f3ee9b63d7affa5386053fe7c2b44c06ae4159d4 Mon Sep 17 00:00:00 2001 From: matthias Date: Fri, 9 Aug 2019 14:31:01 +0200 Subject: update contrib/gofpdi from v1.0.3 to v1.0.7 and expose new functionality * imports pdf documents from any io.ReadSeeker * exposes page sizes of imported pdf documents --- contrib/gofpdi/gofpdi.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'contrib/gofpdi') diff --git a/contrib/gofpdi/gofpdi.go b/contrib/gofpdi/gofpdi.go index b951ea3..58f8fae 100644 --- a/contrib/gofpdi/gofpdi.go +++ b/contrib/gofpdi/gofpdi.go @@ -2,6 +2,7 @@ package gofpdi import ( realgofpdi "github.com/phpdave11/gofpdi" + "io" ) // Create new gofpdi instance @@ -23,7 +24,21 @@ type gofpdiPdf interface { func ImportPage(f gofpdiPdf, sourceFile string, pageno int, box string) int { // Set source file for fpdi fpdi.SetSourceFile(sourceFile) + // return template id + return getTemplateID(f, pageno, box) +} +// ImportPage 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 ImportPageFromStream(f gofpdiPdf, rs *io.ReadSeeker, pageno int, box string) int { + // Set source stream for fpdi + fpdi.SetSourceStream(rs) + // return template id + return getTemplateID(f, pageno, box) +} + +func getTemplateID(f gofpdiPdf, pageno int, box string) int { // Import page tpl := fpdi.ImportPage(pageno, box) @@ -61,3 +76,12 @@ func UseImportedTemplate(f gofpdiPdf, tplid int, x float64, y float64, w float64 f.UseImportedTemplate(tplName, scaleX, scaleY, tX, tY) } + +// GetPageSizes returns page dimensions for all pages of the imported pdf. +// Result consists of map[]map[]map[]. +// : page number, note that page numbers start at 1 +// : box identifier, e.g. "/MediaBox" +// : dimension string, either "w" or "h" +func GetPageSizes() map[int]map[string]map[string]float64 { + return fpdi.GetPageSizes() +} -- cgit v1.2.1-24-ge1ad From fdac35522f22a910970fc07bd6922f4f53ba6642 Mon Sep 17 00:00:00 2001 From: Kurt Date: Mon, 12 Aug 2019 10:52:03 -0400 Subject: Corrected comment to match function name --- contrib/gofpdi/gofpdi.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'contrib/gofpdi') diff --git a/contrib/gofpdi/gofpdi.go b/contrib/gofpdi/gofpdi.go index 58f8fae..e600fb1 100644 --- a/contrib/gofpdi/gofpdi.go +++ b/contrib/gofpdi/gofpdi.go @@ -28,9 +28,10 @@ func ImportPage(f gofpdiPdf, sourceFile string, pageno int, box string) int { return getTemplateID(f, pageno, box) } -// ImportPage 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. +// 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 ImportPageFromStream(f gofpdiPdf, rs *io.ReadSeeker, pageno int, box string) int { // Set source stream for fpdi fpdi.SetSourceStream(rs) -- cgit v1.2.1-24-ge1ad From e697c5fb90d8afbc84be98bfec2154653e03382b Mon Sep 17 00:00:00 2001 From: matthias Date: Tue, 13 Aug 2019 14:03:28 +0200 Subject: wrap contrib/gofpdi:realgofpdi.Importer * allow for multiple independent Importer instances * keep a default Importer for backwards compatibility * add package level comment --- contrib/gofpdi/gofpdi.go | 87 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 16 deletions(-) (limited to 'contrib/gofpdi') diff --git a/contrib/gofpdi/gofpdi.go b/contrib/gofpdi/gofpdi.go index e600fb1..7b9c822 100644 --- a/contrib/gofpdi/gofpdi.go +++ b/contrib/gofpdi/gofpdi.go @@ -1,3 +1,11 @@ +/* +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 ( @@ -5,9 +13,6 @@ import ( "io" ) -// Create new gofpdi instance -var fpdi = realgofpdi.NewImporter() - // 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 { @@ -18,49 +23,61 @@ type gofpdiPdf interface { 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 ImportPage(f gofpdiPdf, sourceFile string, pageno int, box string) int { +func (i *Importer) ImportPage(f gofpdiPdf, sourceFile string, pageno int, box string) int { // Set source file for fpdi - fpdi.SetSourceFile(sourceFile) + i.fpdi.SetSourceFile(sourceFile) // return template id - return getTemplateID(f, pageno, box) + 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 ImportPageFromStream(f gofpdiPdf, rs *io.ReadSeeker, pageno int, box string) int { +func (i *Importer) ImportPageFromStream(f gofpdiPdf, rs *io.ReadSeeker, pageno int, box string) int { // Set source stream for fpdi - fpdi.SetSourceStream(rs) + i.fpdi.SetSourceStream(rs) // return template id - return getTemplateID(f, pageno, box) + return i.getTemplateID(f, pageno, box) } -func getTemplateID(f gofpdiPdf, pageno int, box string) int { +func (i *Importer) getTemplateID(f gofpdiPdf, pageno int, box string) int { // Import page - tpl := fpdi.ImportPage(pageno, box) + 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 := fpdi.PutFormXobjectsUnordered() + 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 := fpdi.GetImportedObjectsUnordered() + 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 := fpdi.GetImportedObjHashPos() + importedObjPos := i.fpdi.GetImportedObjHashPos() // Import gofpdi object hashes and their positions into gopdf f.ImportObjPos(importedObjPos) @@ -71,9 +88,9 @@ func getTemplateID(f gofpdiPdf, pageno int, box string) int { // 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 UseImportedTemplate(f gofpdiPdf, tplid int, x float64, y float64, w float64, h float64) { +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 := fpdi.UseTemplate(tplid, x, y, w, h) + tplName, scaleX, scaleY, tX, tY := i.fpdi.UseTemplate(tplid, x, y, w, h) f.UseImportedTemplate(tplName, scaleX, scaleY, tX, tY) } @@ -83,6 +100,44 @@ func UseImportedTemplate(f gofpdiPdf, tplid int, x float64, y float64, w float64 // : page number, note that page numbers start at 1 // : box identifier, e.g. "/MediaBox" // : 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[]map[]map[]. +// : page number, note that page numbers start at 1 +// : box identifier, e.g. "/MediaBox" +// : 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() } -- cgit v1.2.1-24-ge1ad From 03b1cc4baaf3f7c9ae441b711e18fdd5671a5dd4 Mon Sep 17 00:00:00 2001 From: matthias Date: Thu, 15 Aug 2019 17:27:56 +0200 Subject: add simple example --- contrib/gofpdi/gofpdi_test.go | 52 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 contrib/gofpdi/gofpdi_test.go (limited to 'contrib/gofpdi') diff --git a/contrib/gofpdi/gofpdi_test.go b/contrib/gofpdi/gofpdi_test.go new file mode 100644 index 0000000..1ecc5ca --- /dev/null +++ b/contrib/gofpdi/gofpdi_test.go @@ -0,0 +1,52 @@ +package gofpdi + +import ( + "bytes" + "github.com/jung-kurt/gofpdf" + "github.com/jung-kurt/gofpdf/internal/example" + "io" +) + +func ExampleGofpdiImporter() { + // 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 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 +} -- cgit v1.2.1-24-ge1ad From afaa377e877515c31a941b4c88953e20ead17255 Mon Sep 17 00:00:00 2001 From: matthias Date: Thu, 15 Aug 2019 17:33:26 +0200 Subject: add simple test for data race detection --- contrib/gofpdi/gofpdi_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'contrib/gofpdi') diff --git a/contrib/gofpdi/gofpdi_test.go b/contrib/gofpdi/gofpdi_test.go index 1ecc5ca..77db338 100644 --- a/contrib/gofpdi/gofpdi_test.go +++ b/contrib/gofpdi/gofpdi_test.go @@ -5,6 +5,8 @@ import ( "github.com/jung-kurt/gofpdf" "github.com/jung-kurt/gofpdf/internal/example" "io" + "sync" + "testing" ) func ExampleGofpdiImporter() { @@ -39,6 +41,29 @@ func ExampleGofpdiImporter() { // 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", "") + rs, _ := getTemplatePdf() + imp := NewImporter() + tpl := imp.ImportPageFromStream(pdf, &rs, 1, "/MediaBox") + pdf.AddPage() + 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() -- cgit v1.2.1-24-ge1ad From dab6c0551d4d91973e1bb91341619885e48797a9 Mon Sep 17 00:00:00 2001 From: matthias Date: Thu, 15 Aug 2019 17:55:42 +0200 Subject: fix checks --- contrib/gofpdi/gofpdi_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'contrib/gofpdi') diff --git a/contrib/gofpdi/gofpdi_test.go b/contrib/gofpdi/gofpdi_test.go index 77db338..70ef6cc 100644 --- a/contrib/gofpdi/gofpdi_test.go +++ b/contrib/gofpdi/gofpdi_test.go @@ -9,7 +9,7 @@ import ( "testing" ) -func ExampleGofpdiImporter() { +func ExampleNewImporter() { // create new pdf pdf := gofpdf.New("P", "pt", "A4", "") @@ -48,11 +48,10 @@ func TestGofpdiConcurrent(t *testing.T) { go func() { defer wg.Done() pdf := gofpdf.New("P", "mm", "A4", "") + pdf.AddPage() rs, _ := getTemplatePdf() imp := NewImporter() tpl := imp.ImportPageFromStream(pdf, &rs, 1, "/MediaBox") - pdf.AddPage() - tpl = imp.ImportPageFromStream(pdf, &rs, 1, "/MediaBox") imp.UseImportedTemplate(pdf, tpl, 0, 0, 210.0, 297.0) // write to bytes buffer buf := bytes.Buffer{} -- cgit v1.2.1-24-ge1ad