summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKurt <kurt.w.jung@gmail.com>2017-11-12 14:04:27 -0500
committerKurt <kurt.w.jung@gmail.com>2017-11-12 14:04:27 -0500
commit7751c17f106d9d7d4c6c290b969ece1c8dab4371 (patch)
treefc51f6814469f13161138cda99442c95c91c78ec
parent5e5bb8c2a2d6d41c3d0ef93f5d6181e7f1070a33 (diff)
Add partial support for spot colors. This does not yet include gradients, etc
-rw-r--r--def.go31
-rw-r--r--fpdf.go35
-rw-r--r--fpdf_test.go14
-rw-r--r--spotcolor.go184
4 files changed, 247 insertions, 17 deletions
diff --git a/def.go b/def.go
index fd6ef81..88e4dea 100644
--- a/def.go
+++ b/def.go
@@ -39,6 +39,34 @@ type gradientType struct {
objNum int
}
+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 {
@@ -254,8 +282,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/fpdf.go b/fpdf.go
index 3b2d488..a590581 100644
--- a/fpdf.go
+++ b/fpdf.go
@@ -180,6 +180,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)
@@ -721,13 +722,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
@@ -737,10 +731,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 {
@@ -759,13 +754,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
}
@@ -775,14 +772,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
}
@@ -791,11 +790,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
}
@@ -1183,8 +1184,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)
@@ -3477,6 +3478,7 @@ func (f *Fpdf) putresourcedict() {
}
// Layers
f.layerPutResourceDict()
+ f.spotColorPutResourceDict()
}
func (f *Fpdf) putBlendModes() {
@@ -3542,6 +3544,7 @@ func (f *Fpdf) putresources() {
f.layerPutLayers()
f.putBlendModes()
f.putGradients()
+ f.putSpotColors()
f.putfonts()
if f.err != nil {
return
diff --git a/fpdf_test.go b/fpdf_test.go
index db46528..76ef558 100644
--- a/fpdf_test.go
+++ b/fpdf_test.go
@@ -2046,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/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(">>")
+}