1// Copyright (c) Kurt Jung (Gmail: kurt.w.jung)
2//
3// Permission to use, copy, modify, and distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15// Adapted from http://www.fpdf.org/en/script/script89.php by Olivier PLATHEY
16
17package gofpdf
18
19import (
20	"fmt"
21	"strings"
22)
23
24func byteBound(v byte) byte {
25	if v > 100 {
26		return 100
27	}
28	return v
29}
30
31// AddSpotColor adds an ink-based CMYK color to the gofpdf instance and
32// associates it with the specified name. The individual components specify
33// percentages ranging from 0 to 100. Values above this are quietly capped to
34// 100. An error occurs if the specified name is already associated with a
35// color.
36func (f *Fpdf) AddSpotColor(nameStr string, c, m, y, k byte) {
37	if f.err == nil {
38		_, ok := f.spotColorMap[nameStr]
39		if !ok {
40			id := len(f.spotColorMap) + 1
41			f.spotColorMap[nameStr] = spotColorType{
42				id: id,
43				val: cmykColorType{
44					c: byteBound(c),
45					m: byteBound(m),
46					y: byteBound(y),
47					k: byteBound(k),
48				},
49			}
50		} else {
51			f.err = fmt.Errorf("name \"%s\" is already associated with a spot color", nameStr)
52		}
53	}
54}
55
56func (f *Fpdf) getSpotColor(nameStr string) (clr spotColorType, ok bool) {
57	if f.err == nil {
58		clr, ok = f.spotColorMap[nameStr]
59		if !ok {
60			f.err = fmt.Errorf("spot color name \"%s\" is not registered", nameStr)
61		}
62	}
63	return
64}
65
66// SetDrawSpotColor sets the current draw color to the spot color associated
67// with nameStr. An error occurs if the name is not associated with a color.
68// The value for tint ranges from 0 (no intensity) to 100 (full intensity). It
69// is quietly bounded to this range.
70func (f *Fpdf) SetDrawSpotColor(nameStr string, tint byte) {
71	var clr spotColorType
72	var ok bool
73
74	clr, ok = f.getSpotColor(nameStr)
75	if ok {
76		f.color.draw.mode = colorModeSpot
77		f.color.draw.spotStr = nameStr
78		f.color.draw.str = sprintf("/CS%d CS %.3f SCN", clr.id, float64(byteBound(tint))/100)
79		if f.page > 0 {
80			f.out(f.color.draw.str)
81		}
82	}
83}
84
85// SetFillSpotColor sets the current fill color to the spot color associated
86// with nameStr. An error occurs if the name is not associated with a color.
87// The value for tint ranges from 0 (no intensity) to 100 (full intensity). It
88// is quietly bounded to this range.
89func (f *Fpdf) SetFillSpotColor(nameStr string, tint byte) {
90	var clr spotColorType
91	var ok bool
92
93	clr, ok = f.getSpotColor(nameStr)
94	if ok {
95		f.color.fill.mode = colorModeSpot
96		f.color.fill.spotStr = nameStr
97		f.color.fill.str = sprintf("/CS%d cs %.3f scn", clr.id, float64(byteBound(tint))/100)
98		f.colorFlag = f.color.fill.str != f.color.text.str
99		if f.page > 0 {
100			f.out(f.color.fill.str)
101		}
102	}
103}
104
105// SetTextSpotColor sets the current text color to the spot color associated
106// with nameStr. An error occurs if the name is not associated with a color.
107// The value for tint ranges from 0 (no intensity) to 100 (full intensity). It
108// is quietly bounded to this range.
109func (f *Fpdf) SetTextSpotColor(nameStr string, tint byte) {
110	var clr spotColorType
111	var ok bool
112
113	clr, ok = f.getSpotColor(nameStr)
114	if ok {
115		f.color.text.mode = colorModeSpot
116		f.color.text.spotStr = nameStr
117		f.color.text.str = sprintf("/CS%d cs %.3f scn", clr.id, float64(byteBound(tint))/100)
118		f.colorFlag = f.color.text.str != f.color.text.str
119	}
120}
121
122func (f *Fpdf) returnSpotColor(clr colorType) (name string, c, m, y, k byte) {
123	var spotClr spotColorType
124	var ok bool
125
126	name = clr.spotStr
127	if name != "" {
128		spotClr, ok = f.getSpotColor(name)
129		if ok {
130			c = spotClr.val.c
131			m = spotClr.val.m
132			y = spotClr.val.y
133			k = spotClr.val.k
134		}
135	}
136	return
137}
138
139// GetDrawSpotColor returns the most recently used spot color information for
140// drawing. This will not be the current drawing color if some other color type
141// such as RGB is active. If no spot color has been set for drawing, zero
142// values are returned.
143func (f *Fpdf) GetDrawSpotColor() (name string, c, m, y, k byte) {
144	return f.returnSpotColor(f.color.draw)
145}
146
147// GetTextSpotColor returns the most recently used spot color information for
148// text output. This will not be the current text color if some other color
149// type such as RGB is active. If no spot color has been set for text, zero
150// values are returned.
151func (f *Fpdf) GetTextSpotColor() (name string, c, m, y, k byte) {
152	return f.returnSpotColor(f.color.text)
153}
154
155// GetFillSpotColor returns the most recently used spot color information for
156// fill output. This will not be the current fill color if some other color
157// type such as RGB is active. If no fill spot color has been set, zero values
158// are returned.
159func (f *Fpdf) GetFillSpotColor() (name string, c, m, y, k byte) {
160	return f.returnSpotColor(f.color.fill)
161}
162
163func (f *Fpdf) putSpotColors() {
164	for k, v := range f.spotColorMap {
165		f.newobj()
166		f.outf("[/Separation /%s", strings.Replace(k, " ", "#20", -1))
167		f.out("/DeviceCMYK <<")
168		f.out("/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ")
169		f.outf("/C1 [%.3f %.3f %.3f %.3f] ", float64(v.val.c)/100, float64(v.val.m)/100,
170			float64(v.val.y)/100, float64(v.val.k)/100)
171		f.out("/FunctionType 2 /Domain [0 1] /N 1>>]")
172		f.out("endobj")
173		v.objID = f.n
174		f.spotColorMap[k] = v
175	}
176}
177
178func (f *Fpdf) spotColorPutResourceDict() {
179	f.out("/ColorSpace <<")
180	for _, clr := range f.spotColorMap {
181		f.outf("/CS%d %d 0 R", clr.id, clr.objID)
182	}
183	f.out(">>")
184}
185