1package gofpdf
2
3import (
4	"bytes"
5	"crypto/sha1"
6	"encoding/gob"
7	"errors"
8	"fmt"
9)
10
11/*
12 * Copyright (c) 2015 Kurt Jung (Gmail: kurt.w.jung),
13 *   Marcus Downing, Jan Slabon (Setasign)
14 *
15 * Permission to use, copy, modify, and distribute this software for any
16 * purpose with or without fee is hereby granted, provided that the above
17 * copyright notice and this permission notice appear in all copies.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
20 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
22 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
23 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
24 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
25 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 */
27
28// newTpl creates a template, copying graphics settings from a template if one is given
29func newTpl(corner PointType, size SizeType, orientationStr, unitStr, fontDirStr string, fn func(*Tpl), copyFrom *Fpdf) Template {
30	sizeStr := ""
31
32	fpdf := fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr, size)
33	tpl := Tpl{*fpdf}
34	if copyFrom != nil {
35		tpl.loadParamsFromFpdf(copyFrom)
36	}
37	tpl.Fpdf.AddPage()
38	fn(&tpl)
39
40	bytes := make([][]byte, len(tpl.Fpdf.pages))
41	// skip the first page as it will always be empty
42	for x := 1; x < len(bytes); x++ {
43		bytes[x] = tpl.Fpdf.pages[x].Bytes()
44	}
45
46	templates := make([]Template, 0, len(tpl.Fpdf.templates))
47	for _, key := range templateKeyList(tpl.Fpdf.templates, true) {
48		templates = append(templates, tpl.Fpdf.templates[key])
49	}
50	images := tpl.Fpdf.images
51
52	template := FpdfTpl{corner, size, bytes, images, templates, tpl.Fpdf.page}
53	return &template
54}
55
56// FpdfTpl is a concrete implementation of the Template interface.
57type FpdfTpl struct {
58	corner    PointType
59	size      SizeType
60	bytes     [][]byte
61	images    map[string]*ImageInfoType
62	templates []Template
63	page      int
64}
65
66// ID returns the global template identifier
67func (t *FpdfTpl) ID() string {
68	return fmt.Sprintf("%x", sha1.Sum(t.Bytes()))
69}
70
71// Size gives the bounding dimensions of this template
72func (t *FpdfTpl) Size() (corner PointType, size SizeType) {
73	return t.corner, t.size
74}
75
76// Bytes returns the actual template data, not including resources
77func (t *FpdfTpl) Bytes() []byte {
78	return t.bytes[t.page]
79}
80
81// FromPage creates a new template from a specific Page
82func (t *FpdfTpl) FromPage(page int) (Template, error) {
83	// pages start at 1
84	if page == 0 {
85		return nil, errors.New("Pages start at 1 No template will have a page 0")
86	}
87
88	if page > t.NumPages() {
89		return nil, fmt.Errorf("The template does not have a page %d", page)
90	}
91	// if it is already pointing to the correct page
92	// there is no need to create a new template
93	if t.page == page {
94		return t, nil
95	}
96
97	t2 := *t
98	t2.page = page
99	return &t2, nil
100}
101
102// FromPages creates a template slice with all the pages within a template.
103func (t *FpdfTpl) FromPages() []Template {
104	p := make([]Template, t.NumPages())
105	for x := 1; x <= t.NumPages(); x++ {
106		// the only error is when accessing a
107		// non existing template... that can't happen
108		// here
109		p[x-1], _ = t.FromPage(x)
110	}
111
112	return p
113}
114
115// Images returns a list of the images used in this template
116func (t *FpdfTpl) Images() map[string]*ImageInfoType {
117	return t.images
118}
119
120// Templates returns a list of templates used in this template
121func (t *FpdfTpl) Templates() []Template {
122	return t.templates
123}
124
125// NumPages returns the number of available pages within the template. Look at FromPage and FromPages on access to that content.
126func (t *FpdfTpl) NumPages() int {
127	// the first page is empty to
128	// make the pages begin at one
129	return len(t.bytes) - 1
130}
131
132// Serialize turns a template into a byte string for later deserialization
133func (t *FpdfTpl) Serialize() ([]byte, error) {
134	b := new(bytes.Buffer)
135	enc := gob.NewEncoder(b)
136	err := enc.Encode(t)
137
138	return b.Bytes(), err
139}
140
141// DeserializeTemplate creaties a template from a previously serialized
142// template
143func DeserializeTemplate(b []byte) (Template, error) {
144	tpl := new(FpdfTpl)
145	dec := gob.NewDecoder(bytes.NewBuffer(b))
146	err := dec.Decode(tpl)
147	return tpl, err
148}
149
150// childrenImages returns the next layer of children images, it doesn't dig into
151// children of children. Applies template namespace to keys to ensure
152// no collisions. See UseTemplateScaled
153func (t *FpdfTpl) childrenImages() map[string]*ImageInfoType {
154	childrenImgs := make(map[string]*ImageInfoType)
155
156	for x := 0; x < len(t.templates); x++ {
157		imgs := t.templates[x].Images()
158		for key, val := range imgs {
159			name := sprintf("t%s-%s", t.templates[x].ID(), key)
160			childrenImgs[name] = val
161		}
162	}
163
164	return childrenImgs
165}
166
167// childrensTemplates returns the next layer of children templates, it doesn't dig into
168// children of children.
169func (t *FpdfTpl) childrensTemplates() []Template {
170	childrenTmpls := make([]Template, 0)
171
172	for x := 0; x < len(t.templates); x++ {
173		tmpls := t.templates[x].Templates()
174		childrenTmpls = append(childrenTmpls, tmpls...)
175	}
176
177	return childrenTmpls
178}
179
180// GobEncode encodes the receiving template into a byte buffer. Use GobDecode
181// to decode the byte buffer back to a template.
182func (t *FpdfTpl) GobEncode() ([]byte, error) {
183	w := new(bytes.Buffer)
184	encoder := gob.NewEncoder(w)
185
186	childrensTemplates := t.childrensTemplates()
187	firstClassTemplates := make([]Template, 0)
188
189found_continue:
190	for x := 0; x < len(t.templates); x++ {
191		for y := 0; y < len(childrensTemplates); y++ {
192			if childrensTemplates[y].ID() == t.templates[x].ID() {
193				continue found_continue
194			}
195		}
196
197		firstClassTemplates = append(firstClassTemplates, t.templates[x])
198	}
199	err := encoder.Encode(firstClassTemplates)
200
201	childrenImgs := t.childrenImages()
202	firstClassImgs := make(map[string]*ImageInfoType)
203
204	for key, img := range t.images {
205		if _, ok := childrenImgs[key]; !ok {
206			firstClassImgs[key] = img
207		}
208	}
209
210	if err == nil {
211		err = encoder.Encode(firstClassImgs)
212	}
213	if err == nil {
214		err = encoder.Encode(t.corner)
215	}
216	if err == nil {
217		err = encoder.Encode(t.size)
218	}
219	if err == nil {
220		err = encoder.Encode(t.bytes)
221	}
222	if err == nil {
223		err = encoder.Encode(t.page)
224	}
225
226	return w.Bytes(), err
227}
228
229// GobDecode decodes the specified byte buffer into the receiving template.
230func (t *FpdfTpl) GobDecode(buf []byte) error {
231	r := bytes.NewBuffer(buf)
232	decoder := gob.NewDecoder(r)
233
234	firstClassTemplates := make([]*FpdfTpl, 0)
235	err := decoder.Decode(&firstClassTemplates)
236	t.templates = make([]Template, len(firstClassTemplates))
237
238	for x := 0; x < len(t.templates); x++ {
239		t.templates[x] = Template(firstClassTemplates[x])
240	}
241
242	firstClassImages := t.childrenImages()
243
244	t.templates = append(t.childrensTemplates(), t.templates...)
245
246	t.images = make(map[string]*ImageInfoType)
247	if err == nil {
248		err = decoder.Decode(&t.images)
249	}
250
251	for k, v := range firstClassImages {
252		t.images[k] = v
253	}
254
255	if err == nil {
256		err = decoder.Decode(&t.corner)
257	}
258	if err == nil {
259		err = decoder.Decode(&t.size)
260	}
261	if err == nil {
262		err = decoder.Decode(&t.bytes)
263	}
264	if err == nil {
265		err = decoder.Decode(&t.page)
266	}
267
268	return err
269}
270
271// Tpl is an Fpdf used for writing a template. It has most of the facilities of
272// an Fpdf, but cannot add more pages. Tpl is used directly only during the
273// limited time a template is writable.
274type Tpl struct {
275	Fpdf
276}
277
278func (t *Tpl) loadParamsFromFpdf(f *Fpdf) {
279	t.Fpdf.compress = false
280
281	t.Fpdf.k = f.k
282	t.Fpdf.x = f.x
283	t.Fpdf.y = f.y
284	t.Fpdf.lineWidth = f.lineWidth
285	t.Fpdf.capStyle = f.capStyle
286	t.Fpdf.joinStyle = f.joinStyle
287
288	t.Fpdf.color.draw = f.color.draw
289	t.Fpdf.color.fill = f.color.fill
290	t.Fpdf.color.text = f.color.text
291
292	t.Fpdf.fonts = f.fonts
293	t.Fpdf.currentFont = f.currentFont
294	t.Fpdf.fontFamily = f.fontFamily
295	t.Fpdf.fontSize = f.fontSize
296	t.Fpdf.fontSizePt = f.fontSizePt
297	t.Fpdf.fontStyle = f.fontStyle
298	t.Fpdf.ws = f.ws
299
300	for key, value := range f.images {
301		t.Fpdf.images[key] = value
302	}
303}
304