summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml3
-rw-r--r--LICENSE (renamed from license.txt)4
-rw-r--r--README.md20
-rw-r--r--compare.go16
-rw-r--r--def.go37
-rw-r--r--doc.go20
-rw-r--r--fpdf.go70
-rw-r--r--fpdf_test.go117
-rw-r--r--internal/example/example.go18
-rw-r--r--spotcolor.go184
-rw-r--r--template.go4
-rw-r--r--util.go36
12 files changed, 445 insertions, 84 deletions
diff --git a/.travis.yml b/.travis.yml
index 1a76ca1..32bd6fd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,11 +3,10 @@ language: go
sudo: false
go:
- - 1.6.2
+ - master
os:
- linux
- - osx
script: go test -v
diff --git a/license.txt b/LICENSE
index eb1abac..946bb79 100644
--- a/license.txt
+++ b/LICENSE
@@ -1,8 +1,6 @@
MIT License
-Copyright (c) 2013-2016 Kurt Jung (Gmail: kurt.w.jung)
-
-Portions copyright by the contributors acknowledged in the documentation.
+Copyright (c) 2017 Kurt Jung and contributors acknowledged in the documentation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index d74aa70..d0cd77c 100644
--- a/README.md
+++ b/README.md
@@ -116,15 +116,17 @@ 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.
+directory and if the third argument to ComparePDFFiles() in
+internal/example/example.go is true. (By default it is false.) 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
index 416e545..f0c286e 100644
--- a/compare.go
+++ b/compare.go
@@ -75,9 +75,9 @@ func writeBytes(leadStr string, startPos int, sl []byte) {
fmt.Printf("|\n")
}
-func checkBytes(pos int, sl1, sl2 []byte) (eq bool) {
+func checkBytes(pos int, sl1, sl2 []byte, printDiff bool) (eq bool) {
eq = bytes.Equal(sl1, sl2)
- if !eq {
+ if !eq && printDiff {
writeBytes("<", pos, sl1)
writeBytes(">", pos, sl2)
}
@@ -86,7 +86,7 @@ func checkBytes(pos int, sl1, sl2 []byte) (eq bool) {
// 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) {
+func CompareBytes(sl1, sl2 []byte, printDiff bool) (err error) {
var posStart, posEnd, len1, len2, length int
var diffs bool
@@ -101,7 +101,7 @@ func CompareBytes(sl1, sl2 []byte) (err error) {
if posEnd > length {
posEnd = length
}
- if !checkBytes(posStart, sl1[posStart:posEnd], sl2[posStart:posEnd]) {
+ if !checkBytes(posStart, sl1[posStart:posEnd], sl2[posStart:posEnd], printDiff) {
diffs = true
}
posStart = posEnd
@@ -115,13 +115,13 @@ func CompareBytes(sl1, sl2 []byte) (err error) {
// ComparePDFs reads and compares the full contents of the two specified
// readers byte-for-byte. Nil is returned if the buffers are equal, otherwise
// an error.
-func ComparePDFs(rdr1, rdr2 io.Reader) (err error) {
+func ComparePDFs(rdr1, rdr2 io.Reader, printDiff bool) (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())
+ err = CompareBytes(b1.Bytes(), b2.Bytes(), printDiff)
}
}
return
@@ -130,13 +130,13 @@ func ComparePDFs(rdr1, rdr2 io.Reader) (err error) {
// ComparePDFFiles reads and compares the full contents of the two specified
// files byte-for-byte. 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) {
+func ComparePDFFiles(file1Str, file2Str string, printDiff bool) (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)
+ err = CompareBytes(sl1, sl2, printDiff)
} else {
// Second file is missing; treat this as success
err = nil
diff --git a/def.go b/def.go
index 552415f..52e6474 100644
--- a/def.go
+++ b/def.go
@@ -39,6 +39,39 @@ type gradientType struct {
objNum int
}
+const (
+ OrientationPortrait = "portrait"
+ OrientationLandscape = "landscape"
+)
+
+type colorMode int
+
+const (
+ colorModeRGB colorMode = iota
+ colorModeSpot
+ colorModeCMYK
+)
+
+type colorType struct {
+ r, g, b float64
+ ir, ig, ib int
+ mode colorMode
+ spotStr string // name of current spot color
+ gray bool
+ str string
+}
+
+// SpotColorType specifies a named spot color value
+type spotColorType struct {
+ id, objID int
+ val cmykColorType
+}
+
+// CMYKColorType specifies an ink-based CMYK color value
+type cmykColorType struct {
+ c, m, y, k byte // 0% to 100%
+}
+
// SizeType fields Wd and Ht specify the horizontal and vertical extents of a
// document element such as a page.
type SizeType struct {
@@ -224,6 +257,7 @@ type Fpdf struct {
footerFncLpi func(bool) // function provided by app and called to write footer with last page flag
zoomMode string // zoom display mode
layoutMode string // layout display mode
+ xmp []byte // XMP metadata
title string // title
subject string // subject
author string // author
@@ -253,8 +287,9 @@ type Fpdf struct {
colorFlag bool // indicates whether fill and text colors are different
color struct {
// Composite values of colors
- draw, fill, text clrType
+ draw, fill, text colorType
}
+ spotColorMap map[string]spotColorType // Map of named ink-based colors
}
type encType struct {
diff --git a/doc.go b/doc.go
index 00bbf21..4c63289 100644
--- a/doc.go
+++ b/doc.go
@@ -130,15 +130,17 @@ 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.
+directory and if the third argument to ComparePDFFiles() in
+internal/example/example.go is true. (By default it is false.) 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 27e0c98..567311d 100644
--- a/fpdf.go
+++ b/fpdf.go
@@ -59,7 +59,9 @@ func (b *fmtBuffer) printf(fmtStr string, args ...interface{}) {
func fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr string, size SizeType) (f *Fpdf) {
f = new(Fpdf)
if orientationStr == "" {
- orientationStr = "P"
+ orientationStr = "p"
+ } else {
+ orientationStr = strings.ToLower(orientationStr)
}
if unitStr == "" {
unitStr = "mm"
@@ -132,6 +134,7 @@ func fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr string, size SizeType)
f.stdPageSizes["a1"] = SizeType{1683.78, 2383.94}
f.stdPageSizes["letter"] = SizeType{612, 792}
f.stdPageSizes["legal"] = SizeType{612, 1008}
+ f.stdPageSizes["tabloid"] = SizeType{792, 1224}
if size.Wd > 0 && size.Ht > 0 {
f.defPageSize = size
} else {
@@ -142,7 +145,6 @@ func fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr string, size SizeType)
}
f.curPageSize = f.defPageSize
// Page orientation
- orientationStr = strings.ToLower(orientationStr)
switch orientationStr {
case "p", "portrait":
f.defOrientation = "P"
@@ -179,6 +181,7 @@ func fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr string, size SizeType)
}
// Enable compression
f.SetCompression(!gl.noCompress)
+ f.spotColorMap = make(map[string]spotColorType)
f.blendList = make([]blendModeType, 0, 8)
f.blendList = append(f.blendList, blendModeType{}) // blendList[0] is unused (1-based)
f.blendMap = make(map[string]int)
@@ -215,7 +218,7 @@ func NewCustom(init *InitType) (f *Fpdf) {
// string will be replaced with "mm".
//
// sizeStr specifies the page size. Acceptable values are "A3", "A4", "A5",
-// "Letter", or "Legal". An empty string will be replaced with "A4".
+// "Letter", "Legal", or "Tabloid". An empty string will be replaced with "A4".
//
// fontDirStr specifies the file system location in which font resources will
// be found. An empty string is replaced with ".". This argument only needs to
@@ -510,6 +513,11 @@ func (f *Fpdf) SetCreator(creatorStr string, isUTF8 bool) {
f.creator = creatorStr
}
+// SetXmpMetadata defines XMP metadata that will be embedded with the document.
+func (f *Fpdf) SetXmpMetadata(xmpStream []byte) {
+ f.xmp = xmpStream
+}
+
// AliasNbPages defines an alias for the total number of pages. It will be
// substituted as the document is closed. An empty string is replaced with the
// string "{nb}".
@@ -715,13 +723,6 @@ func (f *Fpdf) PageNo() int {
return f.page
}
-type clrType struct {
- r, g, b float64
- ir, ig, ib int
- gray bool
- str string
-}
-
func colorComp(v int) (int, float64) {
if v < 0 {
v = 0
@@ -731,10 +732,11 @@ func colorComp(v int) (int, float64) {
return v, float64(v) / 255.0
}
-func colorValue(r, g, b int, grayStr, fullStr string) (clr clrType) {
+func rgbColorValue(r, g, b int, grayStr, fullStr string) (clr colorType) {
clr.ir, clr.r = colorComp(r)
clr.ig, clr.g = colorComp(g)
clr.ib, clr.b = colorComp(b)
+ clr.mode = colorModeRGB
clr.gray = clr.ir == clr.ig && clr.r == clr.b
if len(grayStr) > 0 {
if clr.gray {
@@ -753,13 +755,15 @@ func colorValue(r, g, b int, grayStr, fullStr string) (clr clrType) {
// The method can be called before the first page is created. The value is
// retained from page to page.
func (f *Fpdf) SetDrawColor(r, g, b int) {
- f.color.draw = colorValue(r, g, b, "G", "RG")
+ f.color.draw = rgbColorValue(r, g, b, "G", "RG")
if f.page > 0 {
f.out(f.color.draw.str)
}
}
-// GetDrawColor returns the current draw color as RGB components (0 - 255).
+// GetDrawColor returns the most recently set draw color as RGB components (0 -
+// 255). This will not be the current value if a draw color of some other type
+// (for example, spot) has been more recently set.
func (f *Fpdf) GetDrawColor() (int, int, int) {
return f.color.draw.ir, f.color.draw.ig, f.color.draw.ib
}
@@ -769,14 +773,16 @@ func (f *Fpdf) GetDrawColor() (int, int, int) {
// -255). The method can be called before the first page is created and the
// value is retained from page to page.
func (f *Fpdf) SetFillColor(r, g, b int) {
- f.color.fill = colorValue(r, g, b, "g", "rg")
+ f.color.fill = rgbColorValue(r, g, b, "g", "rg")
f.colorFlag = f.color.fill.str != f.color.text.str
if f.page > 0 {
f.out(f.color.fill.str)
}
}
-// GetFillColor returns the current fill color as RGB components (0 - 255).
+// GetFillColor returns the most recently set fill color as RGB components (0 -
+// 255). This will not be the current value if a fill color of some other type
+// (for example, spot) has been more recently set.
func (f *Fpdf) GetFillColor() (int, int, int) {
return f.color.fill.ir, f.color.fill.ig, f.color.fill.ib
}
@@ -785,11 +791,13 @@ func (f *Fpdf) GetFillColor() (int, int, int) {
// components (0 - 255). The method can be called before the first page is
// created. The value is retained from page to page.
func (f *Fpdf) SetTextColor(r, g, b int) {
- f.color.text = colorValue(r, g, b, "g", "rg")
+ f.color.text = rgbColorValue(r, g, b, "g", "rg")
f.colorFlag = f.color.fill.str != f.color.text.str
}
-// GetTextColor returns the current text color as RGB components (0 - 255).
+// GetTextColor returns the most recently set text color as RGB components (0 -
+// 255). This will not be the current value if a text color of some other type
+// (for example, spot) has been more recently set.
func (f *Fpdf) GetTextColor() (int, int, int) {
return f.color.text.ir, f.color.text.ig, f.color.text.ib
}
@@ -1177,8 +1185,8 @@ func (f *Fpdf) gradientClipEnd() {
func (f *Fpdf) gradient(tp int, r1, g1, b1 int, r2, g2, b2 int, x1, y1 float64, x2, y2 float64, r float64) {
pos := len(f.gradientList)
- clr1 := colorValue(r1, g1, b1, "", "")
- clr2 := colorValue(r2, g2, b2, "", "")
+ clr1 := rgbColorValue(r1, g1, b1, "", "")
+ clr2 := rgbColorValue(r2, g2, b2, "", "")
f.gradientList = append(f.gradientList, gradientType{tp, clr1.str, clr2.str,
x1, y1, x2, y2, r, 0})
f.outf("/Sh%d sh", pos)
@@ -1924,19 +1932,19 @@ func (f *Fpdf) CellFormat(w, h float64, txtStr string, borderStr string, ln int,
if len(txtStr) > 0 {
var dx, dy float64
// Horizontal alignment
- if strings.Index(alignStr, "R") != -1 {
+ if strings.Contains(alignStr, "R") {
dx = w - f.cMargin - f.GetStringWidth(txtStr)
- } else if strings.Index(alignStr, "C") != -1 {
+ } else if strings.Contains(alignStr, "C") {
dx = (w - f.GetStringWidth(txtStr)) / 2
} else {
dx = f.cMargin
}
// Vertical alignment
- if strings.Index(alignStr, "T") != -1 {
+ if strings.Contains(alignStr, "T") {
dy = (f.fontSize - h) / 2.0
- } else if strings.Index(alignStr, "B") != -1 {
+ } else if strings.Contains(alignStr, "B") {
dy = (h - f.fontSize) / 2.0
- } else if strings.Index(alignStr, "A") != -1 {
+ } else if strings.Contains(alignStr, "A") {
var descent float64
d := f.currentFont.Desc
if d.Descent == 0 {
@@ -3471,6 +3479,7 @@ func (f *Fpdf) putresourcedict() {
}
// Layers
f.layerPutResourceDict()
+ f.spotColorPutResourceDict()
}
func (f *Fpdf) putBlendModes() {
@@ -3536,6 +3545,7 @@ func (f *Fpdf) putresources() {
f.layerPutLayers()
f.putBlendModes()
f.putGradients()
+ f.putSpotColors()
f.putfonts()
if f.err != nil {
return
@@ -3650,6 +3660,16 @@ func (f *Fpdf) puttrailer() {
}
}
+func (f *Fpdf) putxmp() {
+ if len(f.xmp) == 0 {
+ return
+ }
+ f.newobj()
+ f.outf("<< /Type /Metadata /Subtype /XML /Length %d >>", len(f.xmp))
+ f.putstream(f.xmp)
+ f.out("endobj")
+}
+
func (f *Fpdf) putbookmarks() {
nb := len(f.outlines)
if nb > 0 {
@@ -3716,6 +3736,8 @@ func (f *Fpdf) enddoc() {
}
// Bookmarks
f.putbookmarks()
+ // Metadata
+ f.putxmp()
// Info
f.newobj()
f.out("<<")
diff --git a/fpdf_test.go b/fpdf_test.go
index 9b45486..ff41fd2 100644
--- a/fpdf_test.go
+++ b/fpdf_test.go
@@ -164,13 +164,21 @@ func strDelimit(str string, sepstr string, sepcount int) string {
return str
}
+func loremList() []string {
+ return []string{
+ "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod " +
+ "tempor incididunt ut labore et dolore magna aliqua.",
+ "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
+ "aliquip ex ea commodo consequat.",
+ "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum " +
+ "dolore eu fugiat nulla pariatur.",
+ "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui " +
+ "officia deserunt mollit anim id est laborum.",
+ }
+}
+
func lorem() string {
- return "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod " +
- "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis " +
- "nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis " +
- "aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat " +
- "nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui " +
- "officia deserunt mollit anim id est laborum."
+ return strings.Join(loremList(), " ")
}
// This example demonstrates the generation of a simple PDF document. Note that
@@ -183,7 +191,7 @@ func lorem() string {
// finally retrieved with the output call where it can be handled by the
// application.
func Example() {
- pdf := gofpdf.New("P", "mm", "A4", "")
+ pdf := gofpdf.New(gofpdf.OrientationPortrait, "mm", "A4", "")
pdf.AddPage()
pdf.SetFont("Arial", "B", 16)
pdf.Cell(40, 10, "Hello World!")
@@ -407,6 +415,87 @@ func ExampleFpdf_SetLeftMargin() {
// Successfully generated pdf/Fpdf_SetLeftMargin_multicolumn.pdf
}
+// This example demonstrates word-wrapped table cells
+func ExampleFpdf_SplitLines_tables() {
+ const (
+ colCount = 3
+ colWd = 60.0
+ marginH = 15.0
+ lineHt = 5.5
+ cellGap = 2.0
+ )
+ // var colStrList [colCount]string
+ type cellType struct {
+ str string
+ list [][]byte
+ ht float64
+ }
+ var (
+ cellList [colCount]cellType
+ cell cellType
+ )
+
+ pdf := gofpdf.New("P", "mm", "A4", "") // 210 x 297
+ header := [colCount]string{"Column A", "Column B", "Column C"}
+ alignList := [colCount]string{"L", "C", "R"}
+ strList := loremList()
+ pdf.SetMargins(marginH, 15, marginH)
+ pdf.SetFont("Arial", "", 14)
+ pdf.AddPage()
+
+ // Headers
+ pdf.SetTextColor(224, 224, 224)
+ pdf.SetFillColor(64, 64, 64)
+ for colJ := 0; colJ < colCount; colJ++ {
+ pdf.CellFormat(colWd, 10, header[colJ], "1", 0, "CM", true, 0, "")
+ }
+ pdf.Ln(-1)
+ pdf.SetTextColor(24, 24, 24)
+ pdf.SetFillColor(255, 255, 255)
+
+ // Rows
+ y := pdf.GetY()
+ count := 0
+ for rowJ := 0; rowJ < 2; rowJ++ {
+ maxHt := lineHt
+ // Cell height calculation loop
+ for colJ := 0; colJ < colCount; colJ++ {
+ count++
+ if count > len(strList) {
+ count = 1
+ }
+ cell.str = strings.Join(strList[0:count], " ")
+ cell.list = pdf.SplitLines([]byte(cell.str), colWd-cellGap-cellGap)
+ cell.ht = float64(len(cell.list)) * lineHt
+ if cell.ht > maxHt {
+ maxHt = cell.ht
+ }
+ cellList[colJ] = cell
+ }
+ // Cell render loop
+ x := marginH
+ for colJ := 0; colJ < colCount; colJ++ {
+ pdf.Rect(x, y, colWd, maxHt+cellGap+cellGap, "D")
+ cell = cellList[colJ]
+ cellY := y + cellGap + (maxHt-cell.ht)/2
+ for splitJ := 0; splitJ < len(cell.list); splitJ++ {
+ pdf.SetXY(x+cellGap, cellY)
+ pdf.CellFormat(colWd-cellGap-cellGap, lineHt, string(cell.list[splitJ]), "", 0,
+ alignList[colJ], false, 0, "")
+ cellY += lineHt
+ }
+ x += colWd
+ }
+ y += maxHt + cellGap + cellGap
+ }
+
+ fileStr := example.Filename("Fpdf_SplitLines_tables")
+ err := pdf.OutputFileAndClose(fileStr)
+ example.Summary(err, fileStr)
+ // Output:
+ // Successfully generated pdf/Fpdf_SplitLines_tables.pdf
+}
+
// This example demonstrates various table styles.
func ExampleFpdf_CellFormat_tables() {
pdf := gofpdf.New("P", "mm", "A4", "")
@@ -1957,3 +2046,17 @@ func ExampleFpdf_SetJavascript() {
// Output:
// Successfully generated pdf/Fpdf_SetJavascript.pdf
}
+
+// This example demonstrates spot color use
+func ExampleFpdf_AddSpotColor() {
+ pdf := gofpdf.New("P", "mm", "A4", "")
+ pdf.AddSpotColor("PANTONE 145 CVC", 0, 42, 100, 25)
+ pdf.AddPage()
+ pdf.SetFillSpotColor("PANTONE 145 CVC", 90)
+ pdf.Rect(80, 40, 50, 50, "F")
+ fileStr := example.Filename("Fpdf_AddSpotColor")
+ err := pdf.OutputFileAndClose(fileStr)
+ example.Summary(err, fileStr)
+ // Output:
+ // Successfully generated pdf/Fpdf_AddSpotColor.pdf
+}
diff --git a/internal/example/example.go b/internal/example/example.go
index edf2388..90f861a 100644
--- a/internal/example/example.go
+++ b/internal/example/example.go
@@ -104,7 +104,7 @@ func referenceCompare(fileStr string) (err error) {
err = os.MkdirAll(refDirStr, 0755)
if err == nil {
refFileStr = filepath.Join(refDirStr, baseFileStr)
- err = gofpdf.ComparePDFFiles(fileStr, refFileStr)
+ err = gofpdf.ComparePDFFiles(fileStr, refFileStr, false)
}
return
}
@@ -115,6 +115,22 @@ func referenceCompare(fileStr string) (err error) {
// error is not nil, its String() value is printed to standard output.
func Summary(err error, fileStr string) {
if err == nil {
+ fileStr = filepath.ToSlash(fileStr)
+ fmt.Printf("Successfully generated %s\n", fileStr)
+ } else {
+ fmt.Println(err)
+ }
+}
+
+// SummaryCompare generates a predictable report for use by test examples. If
+// the specified error is nil, the generated file is compared with a reference
+// copy for byte-for-byte equality. If the files match, then the filename
+// delimiters are normalized and the filename printed to standard output with a
+// success message. If the files do not match, this condition is reported on
+// standard output. If the specified error is not nil, its String() value is
+// printed to standard output.
+func SummaryCompare(err error, fileStr string) {
+ if err == nil {
err = referenceCompare(fileStr)
}
if err == nil {
diff --git a/spotcolor.go b/spotcolor.go
new file mode 100644
index 0000000..33aca82
--- /dev/null
+++ b/spotcolor.go
@@ -0,0 +1,184 @@
+// Copyright (c) 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.
+
+// Adapted from http://www.fpdf.org/en/script/script89.php by Olivier PLATHEY
+
+package gofpdf
+
+import (
+ "fmt"
+ "strings"
+)
+
+func byteBound(v byte) byte {
+ if v > 100 {
+ return 100
+ }
+ return v
+}
+
+// AddSpotColor adds an ink-based CMYK color to the gofpdf instance and
+// associates it with the specified name. The individual components specify
+// percentages ranging from 0 to 100. Values above this are quietly capped to
+// 100. An error occurs if the specified name is already associated with a
+// color.
+func (f *Fpdf) AddSpotColor(nameStr string, c, m, y, k byte) {
+ if f.err == nil {
+ _, ok := f.spotColorMap[nameStr]
+ if !ok {
+ id := len(f.spotColorMap) + 1
+ f.spotColorMap[nameStr] = spotColorType{
+ id: id,
+ val: cmykColorType{
+ c: byteBound(c),
+ m: byteBound(m),
+ y: byteBound(y),
+ k: byteBound(k),
+ },
+ }
+ } else {
+ f.err = fmt.Errorf("name \"%s\" is already associated with a spot color", nameStr)
+ }
+ }
+}
+
+func (f *Fpdf) getSpotColor(nameStr string) (clr spotColorType, ok bool) {
+ if f.err == nil {
+ clr, ok = f.spotColorMap[nameStr]
+ if !ok {
+ f.err = fmt.Errorf("spot color name \"%s\" is not registered", nameStr)
+ }
+ }
+ return
+}
+
+// SetDrawSpotColor sets the current draw color to the spot color associated
+// with nameStr. An error occurs if the name is not associated with a color.
+// The value for tint ranges from 0 (no intensity) to 100 (full intensity). It
+// is quietly bounded to this range.
+func (f *Fpdf) SetDrawSpotColor(nameStr string, tint byte) {
+ var clr spotColorType
+ var ok bool
+
+ clr, ok = f.getSpotColor(nameStr)
+ if ok {
+ f.color.draw.mode = colorModeSpot
+ f.color.draw.spotStr = nameStr
+ f.color.draw.str = sprintf("/CS%d CS %.3f SCN", clr.id, float64(byteBound(tint))/100)
+ if f.page > 0 {
+ f.out(f.color.draw.str)
+ }
+ }
+}
+
+// SetFillSpotColor sets the current fill color to the spot color associated
+// with nameStr. An error occurs if the name is not associated with a color.
+// The value for tint ranges from 0 (no intensity) to 100 (full intensity). It
+// is quietly bounded to this range.
+func (f *Fpdf) SetFillSpotColor(nameStr string, tint byte) {
+ var clr spotColorType
+ var ok bool
+
+ clr, ok = f.getSpotColor(nameStr)
+ if ok {
+ f.color.fill.mode = colorModeSpot
+ f.color.fill.spotStr = nameStr
+ f.color.fill.str = sprintf("/CS%d cs %.3f scn", clr.id, float64(byteBound(tint))/100)
+ f.colorFlag = f.color.fill.str != f.color.text.str
+ if f.page > 0 {
+ f.out(f.color.fill.str)
+ }
+ }
+}
+
+// SetTextSpotColor sets the current text color to the spot color associated
+// with nameStr. An error occurs if the name is not associated with a color.
+// The value for tint ranges from 0 (no intensity) to 100 (full intensity). It
+// is quietly bounded to this range.
+func (f *Fpdf) SetTextSpotColor(nameStr string, tint byte) {
+ var clr spotColorType
+ var ok bool
+
+ clr, ok = f.getSpotColor(nameStr)
+ if ok {
+ f.color.text.mode = colorModeSpot
+ f.color.text.spotStr = nameStr
+ f.color.text.str = sprintf("/CS%d cs %.3f scn", clr.id, float64(byteBound(tint))/100)
+ f.colorFlag = f.color.text.str != f.color.text.str
+ }
+}
+
+func (f *Fpdf) returnSpotColor(clr colorType) (name string, c, m, y, k byte) {
+ var spotClr spotColorType
+ var ok bool
+
+ name = clr.spotStr
+ if name != "" {
+ spotClr, ok = f.getSpotColor(name)
+ if ok {
+ c = spotClr.val.c
+ m = spotClr.val.m
+ y = spotClr.val.y
+ k = spotClr.val.k
+ }
+ }
+ return
+}
+
+// GetDrawSpotColor returns the most recently used spot color information for
+// drawing. This will not be the current drawing color if some other color type
+// such as RGB is active. If no spot color has been set for drawing, zero
+// values are returned.
+func (f *Fpdf) GetDrawSpotColor() (name string, c, m, y, k byte) {
+ return f.returnSpotColor(f.color.draw)
+}
+
+// GetTextSpotColor returns the most recently used spot color information for
+// text output. This will not be the current text color if some other color
+// type such as RGB is active. If no spot color has been set for text, zero
+// values are returned.
+func (f *Fpdf) GetTextSpotColor() (name string, c, m, y, k byte) {
+ return f.returnSpotColor(f.color.text)
+}
+
+// GetFillSpotColor returns the most recently used spot color information for
+// fill output. This will not be the current fill color if some other color
+// type such as RGB is active. If no fill spot color has been set, zero values
+// are returned.
+func (f *Fpdf) GetFillSpotColor() (name string, c, m, y, k byte) {
+ return f.returnSpotColor(f.color.fill)
+}
+
+func (f *Fpdf) putSpotColors() {
+ for k, v := range f.spotColorMap {
+ f.newobj()
+ f.outf("[/Separation /%s", strings.Replace(k, " ", "#20", -1))
+ f.out("/DeviceCMYK <<")
+ f.out("/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ")
+ f.outf("/C1 [%.3f %.3f %.3f %.3f] ", float64(v.val.c)/100, float64(v.val.m)/100,
+ float64(v.val.y)/100, float64(v.val.k)/100)
+ f.out("/FunctionType 2 /Domain [0 1] /N 1>>]")
+ f.out("endobj")
+ v.objID = f.n
+ f.spotColorMap[k] = v
+ }
+}
+
+func (f *Fpdf) spotColorPutResourceDict() {
+ f.out("/ColorSpace <<")
+ for _, clr := range f.spotColorMap {
+ f.outf("/CS%d %d 0 R", clr.id, clr.objID)
+ }
+ f.out(">>")
+}
diff --git a/template.go b/template.go
index 1826fd7..60b076b 100644
--- a/template.go
+++ b/template.go
@@ -254,9 +254,7 @@ func templateChainDependencies(template Template) []Template {
requires := template.Templates()
chain := make([]Template, len(requires)*2)
for _, req := range requires {
- for _, sub := range templateChainDependencies(req) {
- chain = append(chain, sub)
- }
+ chain = append(chain, templateChainDependencies(req)...)
}
chain = append(chain, template)
return chain
diff --git a/util.go b/util.go
index 2c8c5bb..dbdaa7c 100644
--- a/util.go
+++ b/util.go
@@ -153,6 +153,11 @@ func strIf(cnd bool, aStr, bStr string) string {
return bStr
}
+// doNothing returns the passed string with no translation.
+func doNothing(s string) string {
+ return s
+}
+
// Dump the internals of the specified values
// func dump(fileStr string, a ...interface{}) {
// fl, err := os.OpenFile(fileStr, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
@@ -219,9 +224,7 @@ func UnicodeTranslator(r io.Reader) (f func(string) string, err error) {
if err == nil {
f = repClosure(m)
} else {
- f = func(s string) string {
- return s
- }
+ f = doNothing
}
return
}
@@ -241,9 +244,7 @@ func UnicodeTranslatorFromFile(fileStr string) (f func(string) string, err error
f, err = UnicodeTranslator(fl)
fl.Close()
} else {
- f = func(s string) string {
- return s
- }
+ f = doNothing
}
return
}
@@ -260,21 +261,22 @@ func UnicodeTranslatorFromFile(fileStr string) (f func(string) string, err error
// If an error occurs reading the descriptor, the returned function is valid
// but does not perform any rune translation.
//
-// The CellFormat (4) example demonstrates this method.
+// The CellFormat_codepage example demonstrates this method.
func (f *Fpdf) UnicodeTranslatorFromDescriptor(cpStr string) (rep func(string) string) {
var str string
var ok bool
- if f.err != nil {
- return
- }
- if len(cpStr) == 0 {
- cpStr = "cp1252"
- }
- str, ok = embeddedMapList[cpStr]
- if ok {
- rep, f.err = UnicodeTranslator(strings.NewReader(str))
+ if f.err == nil {
+ if len(cpStr) == 0 {
+ cpStr = "cp1252"
+ }
+ str, ok = embeddedMapList[cpStr]
+ if ok {
+ rep, f.err = UnicodeTranslator(strings.NewReader(str))
+ } else {
+ rep, f.err = UnicodeTranslatorFromFile(filepath.Join(f.fontpath, cpStr) + ".map")
+ }
} else {
- rep, f.err = UnicodeTranslatorFromFile(filepath.Join(f.fontpath, cpStr) + ".map")
+ rep = doNothing
}
return
}