diff options
Diffstat (limited to 'contrib')
| -rw-r--r-- | contrib/gofpdi/gofpdi.go | 143 | ||||
| -rw-r--r-- | contrib/gofpdi/gofpdi_test.go | 76 | 
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 +} | 
