summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKurt Jung <kurt.w.jung@gmail.com>2015-10-20 12:26:10 -0400
committerKurt Jung <kurt.w.jung@gmail.com>2015-10-20 12:26:10 -0400
commite2d935b7215daa9476f1ca385a15227f5f5a7011 (patch)
tree56b837702a1d3b036e081506513d79913389875f
parent26baf7d305543d6afe39d38f0b2c30054c9fa4e3 (diff)
parentf45e5e9196bb313883f36e0ccc72f052f46aa37e (diff)
Merge pull request #49 from jung-kurt/compare-reference-pdf
Compare example PDFs with reference copies
-rw-r--r--.gitattribute1
-rw-r--r--.gitignore1
-rw-r--r--README.md15
-rw-r--r--compare.go149
-rw-r--r--def.go7
-rw-r--r--doc.go15
-rw-r--r--fpdf.go318
-rw-r--r--fpdf_test.go15
-rw-r--r--internal/example/example.go28
-rw-r--r--list/list.go59
-rwxr-xr-xmkdoc6
-rw-r--r--pdf/reference/Fpdf_AddFont.pdfbin0 -> 27815 bytes
-rw-r--r--pdf/reference/Fpdf_AddLayer.pdfbin0 -> 1318 bytes
-rw-r--r--pdf/reference/Fpdf_AddPage.pdfbin0 -> 4690 bytes
-rw-r--r--pdf/reference/Fpdf_Beziergon.pdfbin0 -> 1272 bytes
-rw-r--r--pdf/reference/Fpdf_Bookmark.pdfbin0 -> 1992 bytes
-rw-r--r--pdf/reference/Fpdf_CellFormat_1_tables.pdfbin0 -> 5863 bytes
-rw-r--r--pdf/reference/Fpdf_CellFormat_2_align.pdfbin0 -> 31946 bytes
-rw-r--r--pdf/reference/Fpdf_CellFormat_3_codepageescape.pdfbin0 -> 1461 bytes
-rw-r--r--pdf/reference/Fpdf_CellFormat_4_codepage.pdfbin0 -> 315587 bytes
-rw-r--r--pdf/reference/Fpdf_Circle_figures.pdfbin0 -> 2732 bytes
-rw-r--r--pdf/reference/Fpdf_ClipText.pdfbin0 -> 9018 bytes
-rw-r--r--pdf/reference/Fpdf_CreateTemplate.pdfbin0 -> 10763 bytes
-rw-r--r--pdf/reference/Fpdf_DrawPath_fill.pdfbin0 -> 2150 bytes
-rw-r--r--pdf/reference/Fpdf_HTMLBasicNew.pdfbin0 -> 5093 bytes
-rw-r--r--pdf/reference/Fpdf_Image.pdfbin0 -> 23469 bytes
-rw-r--r--pdf/reference/Fpdf_LinearGradient_gradient.pdfbin0 -> 2545 bytes
-rw-r--r--pdf/reference/Fpdf_MoveTo_path.pdfbin0 -> 855 bytes
-rw-r--r--pdf/reference/Fpdf_MultiCell.pdfbin0 -> 11693 bytes
-rw-r--r--pdf/reference/Fpdf_PageSize.pdfbin0 -> 1520 bytes
-rw-r--r--pdf/reference/Fpdf_Polygon.pdfbin0 -> 3229 bytes
-rw-r--r--pdf/reference/Fpdf_RegisterImage.pdfbin0 -> 27788 bytes
-rw-r--r--pdf/reference/Fpdf_RegisterImageReader_url.pdfbin0 -> 10998 bytes
-rw-r--r--pdf/reference/Fpdf_SVGBasicWrite.pdfbin0 -> 7375 bytes
-rw-r--r--pdf/reference/Fpdf_SetAcceptPageBreakFunc_landscape.pdfbin0 -> 139383 bytes
-rw-r--r--pdf/reference/Fpdf_SetAlpha_transparency.pdfbin0 -> 130413 bytes
-rw-r--r--pdf/reference/Fpdf_SetFontLoader.pdfbin0 -> 27815 bytes
-rw-r--r--pdf/reference/Fpdf_SetKeywords.pdfbin0 -> 44528 bytes
-rw-r--r--pdf/reference/Fpdf_SetLeftMargin_multicolumn.pdfbin0 -> 14966 bytes
-rw-r--r--pdf/reference/Fpdf_SetLineJoinStyle_caps.pdfbin0 -> 822 bytes
-rw-r--r--pdf/reference/Fpdf_SetProtection.pdfbin0 -> 1077 bytes
-rw-r--r--pdf/reference/Fpdf_Splitlines.pdfbin0 -> 1325 bytes
-rw-r--r--pdf/reference/Fpdf_TransformBegin.pdfbin0 -> 1835 bytes
-rw-r--r--pdf/reference/Fpdf_WriteAligned.pdfbin0 -> 967 bytes
-rw-r--r--pdf/reference/basic.pdfbin0 -> 895 bytes
-rw-r--r--pdf/reference/contrib_barcode_Register.pdfbin0 -> 4954 bytes
-rw-r--r--pdf/reference/contrib_barcode_RegisterCodabar.pdfbin0 -> 5249 bytes
-rw-r--r--pdf/reference/contrib_barcode_RegisterCode128.pdfbin0 -> 5421 bytes
-rw-r--r--pdf/reference/contrib_barcode_RegisterCode39.pdfbin0 -> 5252 bytes
-rw-r--r--pdf/reference/contrib_barcode_RegisterDataMatrix.pdfbin0 -> 4592 bytes
-rw-r--r--pdf/reference/contrib_barcode_RegisterEAN.pdfbin0 -> 4747 bytes
-rw-r--r--pdf/reference/contrib_barcode_RegisterQR.pdfbin0 -> 4411 bytes
-rw-r--r--pdf/reference/contrib_barcode_RegisterTwoOfFive.pdfbin0 -> 5599 bytes
-rw-r--r--pdf/reference/contrib_httpimg_Register.pdfbin0 -> 82994 bytes
-rw-r--r--template.go67
-rw-r--r--template_impl.go4
56 files changed, 551 insertions, 134 deletions
diff --git a/.gitattribute b/.gitattribute
new file mode 100644
index 0000000..d72fd52
--- /dev/null
+++ b/.gitattribute
@@ -0,0 +1 @@
+*.pdf binary
diff --git a/.gitignore b/.gitignore
index 8203c3c..e8f0fe4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,5 @@
makefont/makefont
pdf/*.pdf
-pdf/reference
look
open
pdf.txt
diff --git a/README.md b/README.md
index 95fe304..fdcbac4 100644
--- a/README.md
+++ b/README.md
@@ -99,7 +99,20 @@ complete.
Please note that these examples run in the context of a test. In order run an
example as a standalone application, you'll need to examine [fpdf_test.go](https://github.com/jung-kurt/gofpdf/blob/master/fpdf_test.go) for
-some helper routines, for example exampleFilename and summary.
+some helper routines, for example exampleFilename() and summary().
+
+Example PDFs can be compared with reference copies in order to verify that they
+have been generated as expected. This comparison will be performed if a PDF
+with the same name as the example PDF is placed in the gofpdf/pdf/reference
+directory. The routine that summarizes an example will look for this file and,
+if found, will call ComparePDFFiles() to check the example PDF for equality
+with its reference PDF. If differences exist between the two files they will be
+printed to standard output and the test will fail. If the reference file is
+missing, the comparison is considered to succeed. In order to successfully
+compare two PDFs, the placement of internal resources must be consistent and
+the internal creation timestamps must be the same. To do this, the methods
+SetCatalogSort() and SetCreationDate() need to be called for both files. This
+is done automatically for all examples.
##Nonstandard Fonts
diff --git a/compare.go b/compare.go
new file mode 100644
index 0000000..64cad1f
--- /dev/null
+++ b/compare.go
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2015 Kurt Jung (Gmail: kurt.w.jung)
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package gofpdf
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "sort"
+)
+
+type sortType struct {
+ length int
+ less func(int, int) bool
+ swap func(int, int)
+}
+
+func (s *sortType) Len() int {
+ return s.length
+}
+
+func (s *sortType) Less(i, j int) bool {
+ return s.less(i, j)
+}
+
+func (s *sortType) Swap(i, j int) {
+ s.swap(i, j)
+}
+
+func gensort(Len int, Less func(int, int) bool, Swap func(int, int)) {
+ sort.Sort(&sortType{length: Len, less: Less, swap: Swap})
+}
+
+func writeBytes(leadStr string, startPos int, sl []byte) {
+ var pos, max int
+ var b byte
+ fmt.Printf("%s %07x", leadStr, startPos)
+ max = len(sl)
+ for pos < max {
+ fmt.Printf(" ")
+ for k := 0; k < 8; k++ {
+ if pos < max {
+ fmt.Printf(" %02x", sl[pos])
+ } else {
+ fmt.Printf(" ")
+ }
+ pos++
+ }
+ }
+ fmt.Printf(" |")
+ pos = 0
+ for pos < max {
+ b = sl[pos]
+ if b < 32 || b >= 128 {
+ b = '.'
+ }
+ fmt.Printf("%c", b)
+ pos++
+ }
+ fmt.Printf("|\n")
+}
+
+func checkBytes(pos int, sl1, sl2 []byte) (eq bool) {
+ eq = bytes.Equal(sl1, sl2)
+ if !eq {
+ writeBytes("<", pos, sl1)
+ writeBytes(">", pos, sl2)
+ }
+ return
+}
+
+// compareBytes compares the bytes referred to by sl1 with those referred to by
+// sl2. Nil is returned if the buffers are equal, otherwise an error.
+func compareBytes(sl1, sl2 []byte) (err error) {
+ var posStart, posEnd, len1, len2, length int
+ var diffs bool
+
+ len1 = len(sl1)
+ len2 = len(sl2)
+ length = len1
+ if length > len2 {
+ length = len2
+ }
+ for posStart < length-1 {
+ posEnd = posStart + 16
+ if posEnd > length {
+ posEnd = length
+ }
+ if !checkBytes(posStart, sl1[posStart:posEnd], sl2[posStart:posEnd]) {
+ diffs = true
+ }
+ posStart = posEnd
+ }
+ if diffs {
+ err = fmt.Errorf("documents are different")
+ }
+ return
+}
+
+// ComparePDFs reads and compares the full contents of the two specified
+// readers. The comparison is done byte-for-byte with the exception of the
+// CreationDate fields which are effectively ignored. Nil is returned if the
+// buffers are equal, otherwise an error.
+func ComparePDFs(rdr1, rdr2 io.Reader) (err error) {
+ var b1, b2 *bytes.Buffer
+ _, err = b1.ReadFrom(rdr1)
+ if err == nil {
+ _, err = b2.ReadFrom(rdr2)
+ if err == nil {
+ err = compareBytes(b1.Bytes(), b2.Bytes())
+ }
+ }
+ return
+}
+
+// ComparePDFFiles reads and compares the full contents of the two specified
+// files. The comparison is done byte-for-byte with the exception of the
+// CreationDate fields which are effectively ignored. Nil is returned if the
+// file contents are equal, or if the second file is missing, otherwise an
+// error.
+func ComparePDFFiles(file1Str, file2Str string) (err error) {
+ var sl1, sl2 []byte
+ sl1, err = ioutil.ReadFile(file1Str)
+ if err == nil {
+ sl2, err = ioutil.ReadFile(file2Str)
+ if err == nil {
+ err = compareBytes(sl1, sl2)
+ } else {
+ // Second file is missing; treat this as success
+ err = nil
+ }
+ }
+ return
+}
diff --git a/def.go b/def.go
index f1c8d7b..4c99733 100644
--- a/def.go
+++ b/def.go
@@ -19,6 +19,7 @@ package gofpdf
import (
"bytes"
"io"
+ "time"
)
// Version of FPDF from which this package is derived
@@ -150,7 +151,7 @@ type InitType struct {
// from arbitrary locations (e.g. files, zip files, embedded font resources).
//
// Open provides an io.Reader for the specified font file (.json or .z). The file name
-// does never include a path. Open returns an error if the specified file cannot be opened.
+// never includes a path. Open returns an error if the specified file cannot be opened.
type FontLoader interface {
Open(name string) (io.Reader, error)
}
@@ -161,7 +162,7 @@ type Fpdf struct {
n int // current object number
offsets []int // array of object offsets
templates map[int64]Template // templates used in this document
- templateObjects map[int64]int //template object IDs within this document
+ templateObjects map[int64]int // template object IDs within this document
buffer fmtBuffer // buffer holding in-memory PDF
pages []*bytes.Buffer // slice[page] of page content; 1-based
state int // current document state
@@ -216,6 +217,7 @@ type Fpdf struct {
author string // author
keywords string // keywords
creator string // creator
+ creationDate time.Time // override for dcoument CreationDate value
aliasNbPagesStr string // alias for total number of pages
pdfVersion string // PDF version number
fontDirStr string // location of font definition files
@@ -233,6 +235,7 @@ type Fpdf struct {
err error // Set if error occurs during life cycle of instance
protect protectType // document protection structure
layer layerRecType // manages optional layers in document
+ catalogSort bool // sort resource catalogs in document
colorFlag bool // indicates whether fill and text colors are different
color struct { // Composite values of colors
draw, fill, text clrType
diff --git a/doc.go b/doc.go
index 3dd09ef..15bf40e 100644
--- a/doc.go
+++ b/doc.go
@@ -118,7 +118,20 @@ complete.
Please note that these examples run in the context of a test. In order run an
example as a standalone application, you'll need to examine fpdf_test.go for
-some helper routines, for example exampleFilename and summary.
+some helper routines, for example exampleFilename() and summary().
+
+Example PDFs can be compared with reference copies in order to verify that they
+have been generated as expected. This comparison will be performed if a PDF
+with the same name as the example PDF is placed in the gofpdf/pdf/reference
+directory. The routine that summarizes an example will look for this file and,
+if found, will call ComparePDFFiles() to check the example PDF for equality
+with its reference PDF. If differences exist between the two files they will be
+printed to standard output and the test will fail. If the reference file is
+missing, the comparison is considered to succeed. In order to successfully
+compare two PDFs, the placement of internal resources must be consistent and
+the internal creation timestamps must be the same. To do this, the methods
+SetCatalogSort() and SetCreationDate() need to be called for both files. This
+is done automatically for all examples.
Nonstandard Fonts
diff --git a/fpdf.go b/fpdf.go
index f1c6b58..bc23674 100644
--- a/fpdf.go
+++ b/fpdf.go
@@ -36,11 +36,17 @@ import (
"math"
"os"
"path"
+ "sort"
"strconv"
"strings"
"time"
)
+var gl struct {
+ catalogSort bool
+ creationDate time.Time
+}
+
type fmtBuffer struct {
bytes.Buffer
}
@@ -179,6 +185,8 @@ func fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr string, size SizeType)
// Set default PDF version number
f.pdfVersion = "1.3"
f.layerInit()
+ f.catalogSort = gl.catalogSort
+ f.creationDate = gl.creationDate
return
}
@@ -2986,6 +2994,35 @@ func (f *Fpdf) outf(fmtStr string, args ...interface{}) {
f.out(sprintf(fmtStr, args...))
}
+// SetDefaultCatalogSort sets the default value of the catalog sort flag that
+// will be used when initializing a new Fpdf instance. See SetCatalogSort() for
+// more details.
+func SetDefaultCatalogSort(flag bool) {
+ gl.catalogSort = flag
+}
+
+// SetCatalogSort sets a flag that will be used, if true, to consistently order
+// the document's internal resource catalogs. This method is typically only
+// used for test purposes to facilitate PDF comparison.
+func (f *Fpdf) SetCatalogSort(flag bool) {
+ f.catalogSort = flag
+}
+
+// SetDefaultCreationDate sets the default value of the document creation date
+// that will be used when initializing a new Fpdf instance. See
+// SetCreationDate() for more details.
+func SetDefaultCreationDate(tm time.Time) {
+ gl.creationDate = tm
+}
+
+// SetCreationDate fixes the document's internal CreationDate value. By
+// default, the time when the document is generated is used for this value.
+// This method is typically only used for testing purposes to facilitate PDF
+// comparsion. Specify a zero-value time to revert to the default behavior.
+func (f *Fpdf) SetCreationDate(tm time.Time) {
+ f.creationDate = tm
+}
+
func (f *Fpdf) putpages() {
var wPt, hPt float64
var pageSize SizeType
@@ -3092,107 +3129,130 @@ func (f *Fpdf) putfonts() {
f.outf("<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [%s]>>", diff)
f.out("endobj")
}
- for file, info := range f.fontFiles {
- // foreach($this->fontFiles as $file=>$info)
- // Font file embedding
- f.newobj()
- info.n = f.n
- f.fontFiles[file] = info
- font, err := f.loadFontFile(file)
- if err != nil {
- f.err = err
- return
- }
- // dbg("font file [%s], ext [%s]", file, file[len(file)-2:])
- compressed := file[len(file)-2:] == ".z"
- if !compressed && info.length2 > 0 {
- buf := font[6:info.length1]
- buf = append(buf, font[6+info.length1+6:info.length2]...)
- font = buf
+ {
+ var fileList []string
+ var info fontFileType
+ var file string
+ for file = range f.fontFiles {
+ fileList = append(fileList, file)
}
- f.outf("<</Length %d", len(font))
- if compressed {
- f.out("/Filter /FlateDecode")
+ if f.catalogSort {
+ sort.Strings(fileList)
}
- f.outf("/Length1 %d", info.length1)
- if info.length2 > 0 {
- f.outf("/Length2 %d /Length3 0", info.length2)
- }
- f.out(">>")
- f.putstream(font)
- f.out("endobj")
- }
- for k, font := range f.fonts {
- // Font objects
- font.N = f.n + 1
- f.fonts[k] = font
- tp := font.Tp
- name := font.Name
- if tp == "Core" {
- // Core font
+ for _, file = range fileList {
+ info = f.fontFiles[file]
+ // Font file embedding
f.newobj()
- f.out("<</Type /Font")
- f.outf("/BaseFont /%s", name)
- f.out("/Subtype /Type1")
- if name != "Symbol" && name != "ZapfDingbats" {
- f.out("/Encoding /WinAnsiEncoding")
+ info.n = f.n
+ f.fontFiles[file] = info
+ font, err := f.loadFontFile(file)
+ if err != nil {
+ f.err = err
+ return
}
- f.out(">>")
- f.out("endobj")
- } else if tp == "Type1" || tp == "TrueType" {
- // Additional Type1 or TrueType/OpenType font
- f.newobj()
- f.out("<</Type /Font")
- f.outf("/BaseFont /%s", name)
- f.outf("/Subtype /%s", tp)
- f.out("/FirstChar 32 /LastChar 255")
- f.outf("/Widths %d 0 R", f.n+1)
- f.outf("/FontDescriptor %d 0 R", f.n+2)
- if font.DiffN > 0 {
- f.outf("/Encoding %d 0 R", nf+font.DiffN)
- } else {
- f.out("/Encoding /WinAnsiEncoding")
+ // dbg("font file [%s], ext [%s]", file, file[len(file)-2:])
+ compressed := file[len(file)-2:] == ".z"
+ if !compressed && info.length2 > 0 {
+ buf := font[6:info.length1]
+ buf = append(buf, font[6+info.length1+6:info.length2]...)
+ font = buf
}
- f.out(">>")
- f.out("endobj")
- // Widths
- f.newobj()
- var s fmtBuffer
- s.WriteString("[")
- for j := 32; j < 256; j++ {
- s.printf("%d ", font.Cw[j])
+ f.outf("<</Length %d", len(font))
+ if compressed {
+ f.out("/Filter /FlateDecode")
}
- s.WriteString("]")
- f.out(s.String())
- f.out("endobj")
- // Descriptor
- f.newobj()
- s.Truncate(0)
- s.printf("<</Type /FontDescriptor /FontName /%s ", name)
- s.printf("/Ascent %d ", font.Desc.Ascent)
- s.printf("/Descent %d ", font.Desc.Descent)
- s.printf("/CapHeight %d ", font.Desc.CapHeight)
- s.printf("/Flags %d ", font.Desc.Flags)
- s.printf("/FontBBox [%d %d %d %d] ", font.Desc.FontBBox.Xmin, font.Desc.FontBBox.Ymin,
- font.Desc.FontBBox.Xmax, font.Desc.FontBBox.Ymax)
- s.printf("/ItalicAngle %d ", font.Desc.ItalicAngle)
- s.printf("/StemV %d ", font.Desc.StemV)
- s.printf("/MissingWidth %d ", font.Desc.MissingWidth)
- var suffix string
- if tp != "Type1" {
- suffix = "2"
+ f.outf("/Length1 %d", info.length1)
+ if info.length2 > 0 {
+ f.outf("/Length2 %d /Length3 0", info.length2)
}
- s.printf("/FontFile%s %d 0 R>>", suffix, f.fontFiles[font.File].n)
- f.out(s.String())
+ f.out(">>")
+ f.putstream(font)
f.out("endobj")
- } else {
- f.err = fmt.Errorf("unsupported font type: %s", tp)
- return
- // Allow for additional types
- // $mtd = 'put'.strtolower($type);
- // if(!method_exists($this,$mtd))
- // $this->Error('Unsupported font type: '.$type);
- // $this->$mtd($font);
+ }
+ }
+ {
+ var keyList []string
+ var font fontDefType
+ var key string
+ for key = range f.fonts {
+ keyList = append(keyList, key)
+ }
+ if f.catalogSort {
+ sort.Strings(keyList)
+ }
+ for _, key = range keyList {
+ font = f.fonts[key]
+ // Font objects
+ font.N = f.n + 1
+ f.fonts[key] = font
+ tp := font.Tp
+ name := font.Name
+ if tp == "Core" {
+ // Core font
+ f.newobj()
+ f.out("<</Type /Font")
+ f.outf("/BaseFont /%s", name)
+ f.out("/Subtype /Type1")
+ if name != "Symbol" && name != "ZapfDingbats" {
+ f.out("/Encoding /WinAnsiEncoding")
+ }
+ f.out(">>")
+ f.out("endobj")
+ } else if tp == "Type1" || tp == "TrueType" {
+ // Additional Type1 or TrueType/OpenType font
+ f.newobj()
+ f.out("<</Type /Font")
+ f.outf("/BaseFont /%s", name)
+ f.outf("/Subtype /%s", tp)
+ f.out("/FirstChar 32 /LastChar 255")
+ f.outf("/Widths %d 0 R", f.n+1)
+ f.outf("/FontDescriptor %d 0 R", f.n+2)
+ if font.DiffN > 0 {
+ f.outf("/Encoding %d 0 R", nf+font.DiffN)
+ } else {
+ f.out("/Encoding /WinAnsiEncoding")
+ }
+ f.out(">>")
+ f.out("endobj")
+ // Widths
+ f.newobj()
+ var s fmtBuffer
+ s.WriteString("[")
+ for j := 32; j < 256; j++ {
+ s.printf("%d ", font.Cw[j])
+ }
+ s.WriteString("]")
+ f.out(s.String())
+ f.out("endobj")
+ // Descriptor
+ f.newobj()
+ s.Truncate(0)
+ s.printf("<</Type /FontDescriptor /FontName /%s ", name)
+ s.printf("/Ascent %d ", font.Desc.Ascent)
+ s.printf("/Descent %d ", font.Desc.Descent)
+ s.printf("/CapHeight %d ", font.Desc.CapHeight)
+ s.printf("/Flags %d ", font.Desc.Flags)
+ s.printf("/FontBBox [%d %d %d %d] ", font.Desc.FontBBox.Xmin, font.Desc.FontBBox.Ymin,
+ font.Desc.FontBBox.Xmax, font.Desc.FontBBox.Ymax)
+ s.printf("/ItalicAngle %d ", font.Desc.ItalicAngle)
+ s.printf("/StemV %d ", font.Desc.StemV)
+ s.printf("/MissingWidth %d ", font.Desc.MissingWidth)
+ var suffix string
+ if tp != "Type1" {
+ suffix = "2"
+ }
+ s.printf("/FontFile%s %d 0 R>>", suffix, f.fontFiles[font.File].n)
+ f.out(s.String())
+ f.out("endobj")
+ } else {
+ f.err = fmt.Errorf("unsupported font type: %s", tp)
+ return
+ // Allow for additional types
+ // $mtd = 'put'.strtolower($type);
+ // if(!method_exists($this,$mtd))
+ // $this->Error('Unsupported font type: '.$type);
+ // $this->$mtd($font);
+ }
}
}
return
@@ -3213,8 +3273,16 @@ func (f *Fpdf) loadFontFile(name string) ([]byte, error) {
}
func (f *Fpdf) putimages() {
- for _, img := range f.images {
- f.putimage(img)
+ var keyList []string
+ var key string
+ for key = range f.images {
+ keyList = append(keyList, key)
+ }
+ if f.catalogSort {
+ sort.Strings(keyList)
+ }
+ for _, key = range keyList {
+ f.putimage(f.images[key])
}
}
@@ -3283,14 +3351,33 @@ func (f *Fpdf) putimage(info *ImageInfoType) {
}
func (f *Fpdf) putxobjectdict() {
- for _, image := range f.images {
- // foreach($this->images as $image)
- f.outf("/I%d %d 0 R", image.i, image.n)
- }
- for _, tpl := range f.templates {
- id := tpl.ID()
- if objID, ok := f.templateObjects[id]; ok {
- f.outf("/TPL%d %d 0 R", id, objID)
+ {
+ var image *ImageInfoType
+ var key string
+ var keyList []string
+ for key = range f.images {
+ keyList = append(keyList, key)
+ }
+ if f.catalogSort {
+ sort.Strings(keyList)
+ }
+ for _, key = range keyList {
+ image = f.images[key]
+ f.outf("/I%d %d 0 R", image.i, image.n)
+ }
+ }
+ {
+ var keyList []int64
+ var key int64
+ var tpl Template
+ keyList = templateKeyList(f.templates, f.catalogSort)
+ for _, key = range keyList {
+ tpl = f.templates[key]
+ // for _, tpl := range f.templates {
+ id := tpl.ID()
+ if objID, ok := f.templateObjects[id]; ok {
+ f.outf("/TPL%d %d 0 R", id, objID)
+ }
}
}
}
@@ -3298,9 +3385,20 @@ func (f *Fpdf) putxobjectdict() {
func (f *Fpdf) putresourcedict() {
f.out("/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]")
f.out("/Font <<")
- for _, font := range f.fonts {
- // foreach($this->fonts as $font)
- f.outf("/F%d %d 0 R", font.I, font.N)
+ {
+ var keyList []string
+ var font fontDefType
+ var key string
+ for key = range f.fonts {
+ keyList = append(keyList, key)
+ }
+ if f.catalogSort {
+ sort.Strings(keyList)
+ }
+ for _, key = range keyList {
+ font = f.fonts[key]
+ f.outf("/F%d %d 0 R", font.I, font.N)
+ }
}
f.out(">>")
f.out("/XObject <<")
@@ -3400,6 +3498,7 @@ func (f *Fpdf) putresources() {
}
func (f *Fpdf) putinfo() {
+ var tm time.Time
f.outf("/Producer %s", f.textstring("FPDF "+cnFpdfVersion))
if len(f.title) > 0 {
f.outf("/Title %s", f.textstring(f.title))
@@ -3416,7 +3515,12 @@ func (f *Fpdf) putinfo() {
if len(f.creator) > 0 {
f.outf("/Creator %s", f.textstring(f.creator))
}
- f.outf("/CreationDate %s", f.textstring("D:"+time.Now().Format("20060102150405")))
+ if f.creationDate.IsZero() {
+ tm = time.Now()
+ } else {
+ tm = f.creationDate
+ }
+ f.outf("/CreationDate %s", f.textstring("D:"+tm.Format("20060102150405")))
}
func (f *Fpdf) putcatalog() {
diff --git a/fpdf_test.go b/fpdf_test.go
index 966767f..596816c 100644
--- a/fpdf_test.go
+++ b/fpdf_test.go
@@ -40,9 +40,14 @@ func init() {
func cleanup() {
filepath.Walk(example.PdfDir(),
func(path string, info os.FileInfo, err error) (reterr error) {
- if len(path) > 3 {
- if path[len(path)-4:] == ".pdf" {
- os.Remove(path)
+ if info.Mode().IsRegular() {
+ dir, _ := filepath.Split(path)
+ if "reference" != filepath.Base(dir) {
+ if len(path) > 3 {
+ if path[len(path)-4:] == ".pdf" {
+ os.Remove(path)
+ }
+ }
}
}
return
@@ -1728,8 +1733,8 @@ func ExampleFpdf_CreateTemplate() {
pdf.AddPage()
pdf.UseTemplate(template)
- pdf.UseTemplateScaled(template, gofpdf.PointType{0, 30}, tplSize)
- pdf.UseTemplateScaled(template, gofpdf.PointType{0, 60}, tplSize.ScaleBy(1.4))
+ pdf.UseTemplateScaled(template, gofpdf.PointType{X: 0, Y: 30}, tplSize)
+ pdf.UseTemplateScaled(template, gofpdf.PointType{X: 0, Y: 60}, tplSize.ScaleBy(1.4))
pdf.Line(40, 210, 60, 210)
pdf.Text(40, 200, "Template example page 1")
diff --git a/internal/example/example.go b/internal/example/example.go
index d6a114c..56ffb23 100644
--- a/internal/example/example.go
+++ b/internal/example/example.go
@@ -22,12 +22,17 @@ import (
"os"
"path/filepath"
"strings"
+ "time"
+
+ "github.com/jung-kurt/gofpdf"
)
var gofpdfDir string
func init() {
setRoot()
+ gofpdf.SetDefaultCatalogSort(true)
+ gofpdf.SetDefaultCreationDate(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC))
}
// Assign the relative path to the gofpdfDir directory based on current working
@@ -36,8 +41,7 @@ func setRoot() {
wdStr, err := os.Getwd()
if err == nil {
gofpdfDir = ""
- sepStr := string(os.PathSeparator)
- list := strings.Split(wdStr, sepStr)
+ list := strings.Split(filepath.ToSlash(wdStr), "/")
for j := len(list) - 1; j >= 0 && list[j] != "gofpdf"; j-- {
gofpdfDir = filepath.Join(gofpdfDir, "..")
}
@@ -87,12 +91,32 @@ func Filename(baseStr string) string {
return PdfFile(baseStr + ".pdf")
}
+// referenceCompare compares the specified file with the file's reference copy
+// located in the 'reference' subdirectory. All bytes of the two files are
+// compared except for the value of the /CreationDate field in the PDF. This
+// function succeeds if both files are equivalent except for their
+// /CreationDate values or if the reference file does not exist.
+func referenceCompare(fileStr string) (err error) {
+ var refFileStr, refDirStr, dirStr, baseFileStr string
+ dirStr, baseFileStr = filepath.Split(fileStr)
+ refDirStr = filepath.Join(dirStr, "reference")
+ err = os.MkdirAll(refDirStr, 0755)
+ if err == nil {
+ refFileStr = filepath.Join(refDirStr, baseFileStr)
+ err = gofpdf.ComparePDFFiles(fileStr, refFileStr)
+ }
+ return
+}
+
// Summary generates a predictable report for use by test examples. If the
// specified error is nil, the filename delimiters are normalized and the
// filename printed to standard output with a success message. If the specified
// error is not nil, its String() value is printed to standard output.
func Summary(err error, fileStr string) {
if err == nil {
+ err = referenceCompare(fileStr)
+ }
+ if err == nil {
fileStr = filepath.ToSlash(fileStr)
fmt.Printf("Successfully generated %s\n", fileStr)
} else {
diff --git a/list/list.go b/list/list.go
new file mode 100644
index 0000000..8099404
--- /dev/null
+++ b/list/list.go
@@ -0,0 +1,59 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+func matchTail(str, tailStr string) (match bool, headStr string) {
+ sln := len(str)
+ ln := len(tailStr)
+ if sln > ln {
+ match = str[sln-ln:] == tailStr
+ if match {
+ headStr = str[:sln-ln]
+ }
+ }
+ return
+}
+
+func matchHead(str, headStr string) (match bool, tailStr string) {
+ ln := len(headStr)
+ if len(str) > ln {
+ match = str[:ln] == headStr
+ if match {
+ tailStr = str[ln:]
+ }
+ }
+ return
+}
+
+func main() {
+ var err error
+ var ok bool
+ var showStr, name string
+ err = filepath.Walk("pdf/reference", func(path string, info os.FileInfo, err error) error {
+ if info.Mode().IsRegular() {
+ name = filepath.Base(path)
+ ok, name = matchTail(name, ".pdf")
+ if ok {
+ name = strings.Replace(name, "_", " ", -1)
+ ok, showStr = matchHead(name, "Fpdf ")
+ if ok {
+ fmt.Printf("[%s](%s)\n", showStr, path)
+ } else {
+ ok, showStr = matchHead(name, "contrib ")
+ if ok {
+ fmt.Printf("[%s](%s)\n", showStr, path)
+ }
+ }
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ fmt.Println(err)
+ }
+}
diff --git a/mkdoc b/mkdoc
index 301c39f..71294d7 100755
--- a/mkdoc
+++ b/mkdoc
@@ -1,3 +1,5 @@
+#!/bin/bash
+
# https://github.com/jimmyfrasche/autoreadme
autoreadme -f -template README.md.template
# Improve the appearance of the markdown document with features unavailable in godoc
@@ -16,5 +18,5 @@ cat README.md | tr '\n' '\v' | sed \
-e 's/test.coverage.(\(https:\/\/blog\.golang\.org\/cover\))/[test coverage](\1)/g' \
-e 's/Pull.requests.(\(https:\/\/help\.github\.com\/articles\/using\-pull\-requests\/\))/[Pull requests](\1)/g' \
-e 's/Your change should\v/Your change should\v\v/g' \
- | tr '\v' '\n' > 0
-mv 0 README.md
+ | tr '\v' '\n' > _0
+mv _0 README.md
diff --git a/pdf/reference/Fpdf_AddFont.pdf b/pdf/reference/Fpdf_AddFont.pdf
new file mode 100644
index 0000000..e5c8266
--- /dev/null
+++ b/pdf/reference/Fpdf_AddFont.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_AddLayer.pdf b/pdf/reference/Fpdf_AddLayer.pdf
new file mode 100644
index 0000000..16b4b5b
--- /dev/null
+++ b/pdf/reference/Fpdf_AddLayer.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_AddPage.pdf b/pdf/reference/Fpdf_AddPage.pdf
new file mode 100644
index 0000000..72c59eb
--- /dev/null
+++ b/pdf/reference/Fpdf_AddPage.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_Beziergon.pdf b/pdf/reference/Fpdf_Beziergon.pdf
new file mode 100644
index 0000000..b33e2ee
--- /dev/null
+++ b/pdf/reference/Fpdf_Beziergon.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_Bookmark.pdf b/pdf/reference/Fpdf_Bookmark.pdf
new file mode 100644
index 0000000..463d49e
--- /dev/null
+++ b/pdf/reference/Fpdf_Bookmark.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_CellFormat_1_tables.pdf b/pdf/reference/Fpdf_CellFormat_1_tables.pdf
new file mode 100644
index 0000000..ef0a880
--- /dev/null
+++ b/pdf/reference/Fpdf_CellFormat_1_tables.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_CellFormat_2_align.pdf b/pdf/reference/Fpdf_CellFormat_2_align.pdf
new file mode 100644
index 0000000..5074357
--- /dev/null
+++ b/pdf/reference/Fpdf_CellFormat_2_align.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_CellFormat_3_codepageescape.pdf b/pdf/reference/Fpdf_CellFormat_3_codepageescape.pdf
new file mode 100644
index 0000000..1d31153
--- /dev/null
+++ b/pdf/reference/Fpdf_CellFormat_3_codepageescape.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_CellFormat_4_codepage.pdf b/pdf/reference/Fpdf_CellFormat_4_codepage.pdf
new file mode 100644
index 0000000..6c11be0
--- /dev/null
+++ b/pdf/reference/Fpdf_CellFormat_4_codepage.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_Circle_figures.pdf b/pdf/reference/Fpdf_Circle_figures.pdf
new file mode 100644
index 0000000..097d53c
--- /dev/null
+++ b/pdf/reference/Fpdf_Circle_figures.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_ClipText.pdf b/pdf/reference/Fpdf_ClipText.pdf
new file mode 100644
index 0000000..258ea1f
--- /dev/null
+++ b/pdf/reference/Fpdf_ClipText.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_CreateTemplate.pdf b/pdf/reference/Fpdf_CreateTemplate.pdf
new file mode 100644
index 0000000..0223da9
--- /dev/null
+++ b/pdf/reference/Fpdf_CreateTemplate.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_DrawPath_fill.pdf b/pdf/reference/Fpdf_DrawPath_fill.pdf
new file mode 100644
index 0000000..fdde12c
--- /dev/null
+++ b/pdf/reference/Fpdf_DrawPath_fill.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_HTMLBasicNew.pdf b/pdf/reference/Fpdf_HTMLBasicNew.pdf
new file mode 100644
index 0000000..fb5cbf4
--- /dev/null
+++ b/pdf/reference/Fpdf_HTMLBasicNew.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_Image.pdf b/pdf/reference/Fpdf_Image.pdf
new file mode 100644
index 0000000..838dd4b
--- /dev/null
+++ b/pdf/reference/Fpdf_Image.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_LinearGradient_gradient.pdf b/pdf/reference/Fpdf_LinearGradient_gradient.pdf
new file mode 100644
index 0000000..cb1e3bd
--- /dev/null
+++ b/pdf/reference/Fpdf_LinearGradient_gradient.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_MoveTo_path.pdf b/pdf/reference/Fpdf_MoveTo_path.pdf
new file mode 100644
index 0000000..b5dcca0
--- /dev/null
+++ b/pdf/reference/Fpdf_MoveTo_path.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_MultiCell.pdf b/pdf/reference/Fpdf_MultiCell.pdf
new file mode 100644
index 0000000..a073d75
--- /dev/null
+++ b/pdf/reference/Fpdf_MultiCell.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_PageSize.pdf b/pdf/reference/Fpdf_PageSize.pdf
new file mode 100644
index 0000000..f523782
--- /dev/null
+++ b/pdf/reference/Fpdf_PageSize.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_Polygon.pdf b/pdf/reference/Fpdf_Polygon.pdf
new file mode 100644
index 0000000..a3c04ed
--- /dev/null
+++ b/pdf/reference/Fpdf_Polygon.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_RegisterImage.pdf b/pdf/reference/Fpdf_RegisterImage.pdf
new file mode 100644
index 0000000..80a14d9
--- /dev/null
+++ b/pdf/reference/Fpdf_RegisterImage.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_RegisterImageReader_url.pdf b/pdf/reference/Fpdf_RegisterImageReader_url.pdf
new file mode 100644
index 0000000..171ad32
--- /dev/null
+++ b/pdf/reference/Fpdf_RegisterImageReader_url.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_SVGBasicWrite.pdf b/pdf/reference/Fpdf_SVGBasicWrite.pdf
new file mode 100644
index 0000000..bdefa65
--- /dev/null
+++ b/pdf/reference/Fpdf_SVGBasicWrite.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_SetAcceptPageBreakFunc_landscape.pdf b/pdf/reference/Fpdf_SetAcceptPageBreakFunc_landscape.pdf
new file mode 100644
index 0000000..7997df9
--- /dev/null
+++ b/pdf/reference/Fpdf_SetAcceptPageBreakFunc_landscape.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_SetAlpha_transparency.pdf b/pdf/reference/Fpdf_SetAlpha_transparency.pdf
new file mode 100644
index 0000000..49dde6a
--- /dev/null
+++ b/pdf/reference/Fpdf_SetAlpha_transparency.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_SetFontLoader.pdf b/pdf/reference/Fpdf_SetFontLoader.pdf
new file mode 100644
index 0000000..1aa2f6f
--- /dev/null
+++ b/pdf/reference/Fpdf_SetFontLoader.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_SetKeywords.pdf b/pdf/reference/Fpdf_SetKeywords.pdf
new file mode 100644
index 0000000..3d3d3a5
--- /dev/null
+++ b/pdf/reference/Fpdf_SetKeywords.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_SetLeftMargin_multicolumn.pdf b/pdf/reference/Fpdf_SetLeftMargin_multicolumn.pdf
new file mode 100644
index 0000000..91b6d59
--- /dev/null
+++ b/pdf/reference/Fpdf_SetLeftMargin_multicolumn.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_SetLineJoinStyle_caps.pdf b/pdf/reference/Fpdf_SetLineJoinStyle_caps.pdf
new file mode 100644
index 0000000..9b9badb
--- /dev/null
+++ b/pdf/reference/Fpdf_SetLineJoinStyle_caps.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_SetProtection.pdf b/pdf/reference/Fpdf_SetProtection.pdf
new file mode 100644
index 0000000..8d007c7
--- /dev/null
+++ b/pdf/reference/Fpdf_SetProtection.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_Splitlines.pdf b/pdf/reference/Fpdf_Splitlines.pdf
new file mode 100644
index 0000000..d8064be
--- /dev/null
+++ b/pdf/reference/Fpdf_Splitlines.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_TransformBegin.pdf b/pdf/reference/Fpdf_TransformBegin.pdf
new file mode 100644
index 0000000..d1ffe34
--- /dev/null
+++ b/pdf/reference/Fpdf_TransformBegin.pdf
Binary files differ
diff --git a/pdf/reference/Fpdf_WriteAligned.pdf b/pdf/reference/Fpdf_WriteAligned.pdf
new file mode 100644
index 0000000..c173146
--- /dev/null
+++ b/pdf/reference/Fpdf_WriteAligned.pdf
Binary files differ
diff --git a/pdf/reference/basic.pdf b/pdf/reference/basic.pdf
new file mode 100644
index 0000000..80a7fbf
--- /dev/null
+++ b/pdf/reference/basic.pdf
Binary files differ
diff --git a/pdf/reference/contrib_barcode_Register.pdf b/pdf/reference/contrib_barcode_Register.pdf
new file mode 100644
index 0000000..5ad12e7
--- /dev/null
+++ b/pdf/reference/contrib_barcode_Register.pdf
Binary files differ
diff --git a/pdf/reference/contrib_barcode_RegisterCodabar.pdf b/pdf/reference/contrib_barcode_RegisterCodabar.pdf
new file mode 100644
index 0000000..4ed247b
--- /dev/null
+++ b/pdf/reference/contrib_barcode_RegisterCodabar.pdf
Binary files differ
diff --git a/pdf/reference/contrib_barcode_RegisterCode128.pdf b/pdf/reference/contrib_barcode_RegisterCode128.pdf
new file mode 100644
index 0000000..f76ae86
--- /dev/null
+++ b/pdf/reference/contrib_barcode_RegisterCode128.pdf
Binary files differ
diff --git a/pdf/reference/contrib_barcode_RegisterCode39.pdf b/pdf/reference/contrib_barcode_RegisterCode39.pdf
new file mode 100644
index 0000000..fee2c99
--- /dev/null
+++ b/pdf/reference/contrib_barcode_RegisterCode39.pdf
Binary files differ
diff --git a/pdf/reference/contrib_barcode_RegisterDataMatrix.pdf b/pdf/reference/contrib_barcode_RegisterDataMatrix.pdf
new file mode 100644
index 0000000..5ea011d
--- /dev/null
+++ b/pdf/reference/contrib_barcode_RegisterDataMatrix.pdf
Binary files differ
diff --git a/pdf/reference/contrib_barcode_RegisterEAN.pdf b/pdf/reference/contrib_barcode_RegisterEAN.pdf
new file mode 100644
index 0000000..166090d
--- /dev/null
+++ b/pdf/reference/contrib_barcode_RegisterEAN.pdf
Binary files differ
diff --git a/pdf/reference/contrib_barcode_RegisterQR.pdf b/pdf/reference/contrib_barcode_RegisterQR.pdf
new file mode 100644
index 0000000..6e989e3
--- /dev/null
+++ b/pdf/reference/contrib_barcode_RegisterQR.pdf
Binary files differ
diff --git a/pdf/reference/contrib_barcode_RegisterTwoOfFive.pdf b/pdf/reference/contrib_barcode_RegisterTwoOfFive.pdf
new file mode 100644
index 0000000..0078dc1
--- /dev/null
+++ b/pdf/reference/contrib_barcode_RegisterTwoOfFive.pdf
Binary files differ
diff --git a/pdf/reference/contrib_httpimg_Register.pdf b/pdf/reference/contrib_httpimg_Register.pdf
new file mode 100644
index 0000000..212ca93
--- /dev/null
+++ b/pdf/reference/contrib_httpimg_Register.pdf
Binary files differ
diff --git a/template.go b/template.go
index 7561ae1..c9a33d5 100644
--- a/template.go
+++ b/template.go
@@ -17,6 +17,10 @@ package gofpdf
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+import (
+ "sort"
+)
+
// CreateTemplate defines a new template using the current page size.
func (f *Fpdf) CreateTemplate(fn func(*Tpl)) Template {
return newTpl(PointType{0, 0}, f.curPageSize, f.unitStr, f.fontDirStr, fn, f)
@@ -36,7 +40,7 @@ func CreateTemplate(corner PointType, size SizeType, unitStr, fontDirStr string,
// using the size and position at which it was originally written.
func (f *Fpdf) UseTemplate(t Template) {
if t == nil {
- f.SetErrorf("Template is nil")
+ f.SetErrorf("template is nil")
return
}
corner, size := t.Size()
@@ -47,13 +51,13 @@ func (f *Fpdf) UseTemplate(t Template) {
// using the given page coordinates.
func (f *Fpdf) UseTemplateScaled(t Template, corner PointType, size SizeType) {
if t == nil {
- f.SetErrorf("Template is nil")
+ f.SetErrorf("template is nil")
return
}
// You have to add at least a page first
if f.page <= 0 {
- f.SetErrorf("Cannot use a template without first adding a page")
+ f.SetErrorf("cannot use a template without first adding a page")
return
}
@@ -75,7 +79,7 @@ func (f *Fpdf) UseTemplateScaled(t Template, corner PointType, size SizeType) {
tx := corner.X * f.k
ty := (f.curPageSize.Ht - corner.Y - size.Ht) * f.k
- f.outf("q %.4F 0 0 %.4F %.4F %.4F cm", scaleX, scaleY, tx, ty) // Translate
+ f.outf("q %.4f 0 0 %.4f %.4f %.4f cm", scaleX, scaleY, tx, ty) // Translate
f.outf("/TPL%d Do Q", t.ID())
}
@@ -112,7 +116,7 @@ func (f *Fpdf) putTemplates() {
filter = "/Filter /FlateDecode "
}
- templates := sortTemplates(f.templates)
+ templates := sortTemplates(f.templates, f.catalogSort)
var t Template
for _, t = range templates {
corner, size := t.Size()
@@ -122,9 +126,9 @@ func (f *Fpdf) putTemplates() {
f.outf("<<%s/Type /XObject", filter)
f.out("/Subtype /Form")
f.out("/Formtype 1")
- f.outf("/BBox [%.2F %.2F %.2F %.2F]", corner.X*f.k, corner.Y*f.k, (corner.X+size.Wd)*f.k, (corner.Y+size.Ht)*f.k)
+ f.outf("/BBox [%.2f %.2f %.2f %.2f]", corner.X*f.k, corner.Y*f.k, (corner.X+size.Wd)*f.k, (corner.Y+size.Ht)*f.k)
if corner.X != 0 || corner.Y != 0 {
- f.outf("/Matrix [1 0 0 1 %.5F %.5F]", -corner.X*f.k*2, corner.Y*f.k*2)
+ f.outf("/Matrix [1 0 0 1 %.5f %.5f]", -corner.X*f.k*2, corner.Y*f.k*2)
}
// Template's resource dictionary
@@ -135,8 +139,21 @@ func (f *Fpdf) putTemplates() {
tTemplates := t.Templates()
if len(tImages) > 0 || len(tTemplates) > 0 {
f.out("/XObject <<")
- for _, ti := range tImages {
- f.outf("/I%d %d 0 R", ti.i, ti.n)
+ {
+ var key string
+ var keyList []string
+ var ti *ImageInfoType
+ for key = range tImages {
+ keyList = append(keyList, key)
+ }
+ if gl.catalogSort {
+ sort.Strings(keyList)
+ }
+ for _, key = range keyList {
+ // for _, ti := range tImages {
+ ti = tImages[key]
+ f.outf("/I%d %d 0 R", ti.i, ti.n)
+ }
}
for _, tt := range tTemplates {
id := tt.ID()
@@ -161,12 +178,34 @@ func (f *Fpdf) putTemplates() {
}
}
+func templateKeyList(mp map[int64]Template, sort bool) (keyList []int64) {
+ var key int64
+ for key = range mp {
+ keyList = append(keyList, key)
+ }
+ if sort {
+ gensort(len(keyList),
+ func(a, b int) bool {
+ return keyList[a] < keyList[b]
+ },
+ func(a, b int) {
+ keyList[a], keyList[b] = keyList[b], keyList[a]
+ })
+ }
+ return
+}
+
// sortTemplates puts templates in a suitable order based on dependices
-func sortTemplates(templates map[int64]Template) []Template {
+func sortTemplates(templates map[int64]Template, catalogSort bool) []Template {
chain := make([]Template, 0, len(templates)*2)
// build a full set of dependency chains
- for _, t := range templates {
+ var keyList []int64
+ var key int64
+ var t Template
+ keyList = templateKeyList(templates, catalogSort)
+ for _, key = range keyList {
+ t = templates[key]
tlist := templateChainDependencies(t)
for _, tt := range tlist {
if tt != nil {
@@ -202,3 +241,9 @@ func templateChainDependencies(template Template) []Template {
chain = append(chain, template)
return chain
}
+
+// < 0002640 31 20 31 32 20 30 20 52 0a 2f 54 50 4c 32 20 31 |1 12 0 R./TPL2 1|
+// < 0002650 35 20 30 20 52 0a 2f 54 50 4c 31 20 31 34 20 30 |5 0 R./TPL1 14 0|
+
+// > 0002640 31 20 31 32 20 30 20 52 0a 2f 54 50 4c 31 20 31 |1 12 0 R./TPL1 1|
+// > 0002650 34 20 30 20 52 0a 2f 54 50 4c 32 20 31 35 20 30 |4 0 R./TPL2 15 0|
diff --git a/template_impl.go b/template_impl.go
index 093e1fd..c94f880 100644
--- a/template_impl.go
+++ b/template_impl.go
@@ -35,8 +35,8 @@ func newTpl(corner PointType, size SizeType, unitStr, fontDirStr string, fn func
fn(&tpl)
bytes := tpl.Fpdf.pages[tpl.Fpdf.page].Bytes()
templates := make([]Template, 0, len(tpl.Fpdf.templates))
- for _, t := range tpl.Fpdf.templates {
- templates = append(templates, t)
+ for _, key := range templateKeyList(tpl.Fpdf.templates, true) {
+ templates = append(templates, tpl.Fpdf.templates[key])
}
images := tpl.Fpdf.images