diff options
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | contrib/gofpdi/gofpdi.go | 62 | ||||
-rw-r--r-- | def.go | 4 | ||||
-rw-r--r-- | doc.go | 5 | ||||
-rw-r--r-- | doc/document.md | 3 | ||||
-rw-r--r-- | fpdf.go | 91 |
6 files changed, 166 insertions, 3 deletions
@@ -30,6 +30,7 @@ support for text, drawing and images. - Templates - Barcodes - Charting facility + - Import PDFs as templates gofpdf has no dependencies other than the Go standard library. All tests pass on Linux, Mac and Windows platforms. @@ -246,7 +247,8 @@ encoding and decoding functionality for templates, including images that are embedded in templates; this allows templates to be stored independently of gofpdf. Paul also added support for page boxes used in printing PDF documents. Wojciech Matusiak added supported for word -spacing. Artem Korotkiy added support of UTF-8 fonts. +spacing. Artem Korotkiy added support of UTF-8 fonts. Dave Barnes added +support for imported objects and templates. ## Roadmap diff --git a/contrib/gofpdi/gofpdi.go b/contrib/gofpdi/gofpdi.go new file mode 100644 index 0000000..d1b98e9 --- /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) +} @@ -504,6 +504,10 @@ type Fpdf struct { offsets []int // array of object offsets templates map[string]Template // templates used in this document templateObjects map[string]int // template object IDs within this document + importedObjs map[string][]byte // imported template objects (gofpdi) + importedObjPos map[string]map[int]string // imported template objects hashes and their positions (gofpdi) + importedTplObjs map[string]string // imported template names and IDs (hashed) (gofpdi) + importedTplIDs map[string]int // imported template ids hash to object id int (gofpdi) buffer fmtBuffer // buffer holding in-memory PDF pages []*bytes.Buffer // slice[page] of page content; 1-based state int // current document state @@ -42,6 +42,8 @@ Features - Charting facility +- Import PDFs as templates + gofpdf has no dependencies other than the Go standard library. All tests pass on Linux, Mac and Windows platforms. @@ -253,7 +255,8 @@ encoding and decoding functionality for templates, including images that are embedded in templates; this allows templates to be stored independently of gofpdf. Paul also added support for page boxes used in printing PDF documents. Wojciech Matusiak added supported for word -spacing. Artem Korotkiy added support of UTF-8 fonts. +spacing. Artem Korotkiy added support of UTF-8 fonts. Dave Barnes added +support for imported objects and templates. Roadmap diff --git a/doc/document.md b/doc/document.md index 33f5dac..fa544af 100644 --- a/doc/document.md +++ b/doc/document.md @@ -29,6 +29,7 @@ text, drawing and images. * Templates * Barcodes * Charting facility +* Import PDFs as templates gofpdf has no dependencies other than the Go standard library. All tests pass on Linux, Mac and Windows platforms. @@ -226,7 +227,7 @@ decoding functionality for templates, including images that are embedded in templates; this allows templates to be stored independently of gofpdf. Paul also added support for page boxes used in printing PDF documents. Wojciech Matusiak added supported for word spacing. Artem Korotkiy added support of -UTF-8 fonts. +UTF-8 fonts. Dave Barnes added support for imported objects and templates. ## Roadmap @@ -85,6 +85,10 @@ func fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr string, size SizeType) f.diffs = make([]string, 0, 8) f.templates = make(map[string]Template) f.templateObjects = make(map[string]int) + f.importedObjs = make(map[string][]byte, 0) + f.importedObjPos = make(map[string]map[int]string, 0) + f.importedTplObjs = make(map[string]string) + f.importedTplIDs = make(map[string]int, 0) f.images = make(map[string]*ImageInfoType) f.pageLinks = make([][]linkType, 0, 8) f.pageLinks = append(f.pageLinks, make([]linkType, 0, 0)) // pageLinks[0] is unused (1-based) @@ -3102,6 +3106,86 @@ func (f *Fpdf) GetImageInfo(imageStr string) (info *ImageInfoType) { return f.images[imageStr] } +// ImportObjects imports objects from gofpdi into current document +func (f *Fpdf) ImportObjects(objs map[string][]byte) { + for k, v := range objs { + f.importedObjs[k] = v + } +} + +// ImportObjPos imports object hash positions from gofpdi +func (f *Fpdf) ImportObjPos(objPos map[string]map[int]string) { + for k, v := range objPos { + f.importedObjPos[k] = v + } +} + +// putImportedTemplates writes the imported template objects to the PDF +func (f *Fpdf) putImportedTemplates() { + nOffset := f.n + 1 + + // keep track of list of sha1 hashes (to be replaced with integers) + objsIDHash := make([]string, len(f.importedObjs)) + + // actual object data with new id + objsIDData := make([][]byte, len(f.importedObjs)) + + // Populate hash slice and data slice + i := 0 + for k, v := range f.importedObjs { + objsIDHash[i] = k + objsIDData[i] = v + + i++ + } + + // Populate a lookup table to get an object id from a hash + hashToObjID := make(map[string]int, len(f.importedObjs)) + for i = 0; i < len(objsIDHash); i++ { + hashToObjID[objsIDHash[i]] = i + nOffset + } + + // Now, replace hashes inside data with %040d object id + for i = 0; i < len(objsIDData); i++ { + // get hash + hash := objsIDHash[i] + + for pos, h := range f.importedObjPos[hash] { + // Convert object id into a 40 character string padded with spaces + objIDPadded := fmt.Sprintf("%40s", fmt.Sprintf("%d", hashToObjID[h])) + + // Convert objIDPadded into []byte + objIDBytes := []byte(objIDPadded) + + // Replace sha1 hash with object id padded + for j := pos; j < pos+40; j++ { + objsIDData[i][j] = objIDBytes[j-pos] + } + } + + // Save objsIDHash so that procset dictionary has the correct object ids + f.importedTplIDs[hash] = i + nOffset + } + + // Now, put objects + for i = 0; i < len(objsIDData); i++ { + f.newobj() + f.out(string(objsIDData[i])) + } +} + +// UseImportedTemplate uses imported template from gofpdi - draws imported PDF page onto page +func (f *Fpdf) UseImportedTemplate(tplName string, scaleX float64, scaleY float64, tX float64, tY float64) { + f.outf("q 0 J 1 w 0 j 0 G 0 g q %.4F 0 0 %.4F %.4F %.4F cm %s Do Q Q\n", scaleX*f.k, scaleY*f.k, tX*f.k, (tY+f.h)*f.k, tplName) +} + +// ImportTemplates imports gofpdi template names into importedTplObjs - to be included in the procset dictionary +func (f *Fpdf) ImportTemplates(tpls map[string]string) { + for tplName, tplID := range tpls { + f.importedTplObjs[tplName] = tplID + } +} + // GetConversionRatio returns the conversion ratio based on the unit given when // creating the PDF. func (f *Fpdf) GetConversionRatio() float64 { @@ -4174,6 +4258,12 @@ func (f *Fpdf) putxobjectdict() { } } } + { + for tplName, objID := range f.importedTplObjs { + // here replace obj id hash with n + f.outf("%s %d 0 R", tplName, f.importedTplIDs[objID]) + } + } } func (f *Fpdf) putresourcedict() { @@ -4289,6 +4379,7 @@ func (f *Fpdf) putresources() { } f.putimages() f.putTemplates() + f.putImportedTemplates() // gofpdi // Resource dictionary f.offsets[2] = f.buffer.Len() f.out("2 0 obj") |