1package gofpdf
2
3/*
4 * Copyright (c) 2015 Kurt Jung (Gmail: kurt.w.jung),
5 *   Marcus Downing, Jan Slabon (Setasign)
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20import (
21	"encoding/gob"
22	"sort"
23)
24
25// CreateTemplate defines a new template using the current page size.
26func (f *Fpdf) CreateTemplate(fn func(*Tpl)) Template {
27	return newTpl(PointType{0, 0}, f.curPageSize, f.defOrientation, f.unitStr, f.fontDirStr, fn, f)
28}
29
30// CreateTemplateCustom starts a template, using the given bounds.
31func (f *Fpdf) CreateTemplateCustom(corner PointType, size SizeType, fn func(*Tpl)) Template {
32	return newTpl(corner, size, f.defOrientation, f.unitStr, f.fontDirStr, fn, f)
33}
34
35// CreateTemplate creates a template that is not attached to any document.
36//
37// This function is deprecated; it incorrectly assumes that a page with a width
38// smaller than its height is oriented in portrait mode, otherwise it assumes
39// landscape mode. This causes problems when placing the template in a master
40// document where this condition does not apply. CreateTpl() is a similar
41// function that lets you specify the orientation to avoid this problem.
42func CreateTemplate(corner PointType, size SizeType, unitStr, fontDirStr string, fn func(*Tpl)) Template {
43	orientationStr := "p"
44	if size.Wd > size.Ht {
45		orientationStr = "l"
46	}
47
48	return CreateTpl(corner, size, orientationStr, unitStr, fontDirStr, fn)
49}
50
51// CreateTpl creates a template not attached to any document
52func CreateTpl(corner PointType, size SizeType, orientationStr, unitStr, fontDirStr string, fn func(*Tpl)) Template {
53	return newTpl(corner, size, orientationStr, unitStr, fontDirStr, fn, nil)
54}
55
56// UseTemplate adds a template to the current page or another template,
57// using the size and position at which it was originally written.
58func (f *Fpdf) UseTemplate(t Template) {
59	if t == nil {
60		f.SetErrorf("template is nil")
61		return
62	}
63	corner, size := t.Size()
64	f.UseTemplateScaled(t, corner, size)
65}
66
67// UseTemplateScaled adds a template to the current page or another template,
68// using the given page coordinates.
69func (f *Fpdf) UseTemplateScaled(t Template, corner PointType, size SizeType) {
70	if t == nil {
71		f.SetErrorf("template is nil")
72		return
73	}
74
75	// You have to add at least a page first
76	if f.page <= 0 {
77		f.SetErrorf("cannot use a template without first adding a page")
78		return
79	}
80
81	// make a note of the fact that we actually use this template, as well as any other templates,
82	// images or fonts it uses
83	f.templates[t.ID()] = t
84	for _, tt := range t.Templates() {
85		f.templates[tt.ID()] = tt
86	}
87
88	// Create a list of existing image SHA-1 hashes.
89	existingImages := map[string]bool{}
90	for _, image := range f.images {
91		existingImages[image.i] = true
92	}
93
94	// Add each template image to $f, unless already present.
95	for name, ti := range t.Images() {
96		if _, found := existingImages[ti.i]; found {
97			continue
98		}
99		name = sprintf("t%s-%s", t.ID(), name)
100		f.images[name] = ti
101	}
102
103	// template data
104	_, templateSize := t.Size()
105	scaleX := size.Wd / templateSize.Wd
106	scaleY := size.Ht / templateSize.Ht
107	tx := corner.X * f.k
108	ty := (f.curPageSize.Ht - corner.Y - size.Ht) * f.k
109
110	f.outf("q %.4f 0 0 %.4f %.4f %.4f cm", scaleX, scaleY, tx, ty) // Translate
111	f.outf("/TPL%s Do Q", t.ID())
112}
113
114// Template is an object that can be written to, then used and re-used any number of times within a document.
115type Template interface {
116	ID() string
117	Size() (PointType, SizeType)
118	Bytes() []byte
119	Images() map[string]*ImageInfoType
120	Templates() []Template
121	NumPages() int
122	FromPage(int) (Template, error)
123	FromPages() []Template
124	Serialize() ([]byte, error)
125	gob.GobDecoder
126	gob.GobEncoder
127}
128
129func (f *Fpdf) templateFontCatalog() {
130	var keyList []string
131	var font fontDefType
132	var key string
133	f.out("/Font <<")
134	for key = range f.fonts {
135		keyList = append(keyList, key)
136	}
137	if f.catalogSort {
138		sort.Strings(keyList)
139	}
140	for _, key = range keyList {
141		font = f.fonts[key]
142		f.outf("/F%s %d 0 R", font.i, font.N)
143	}
144	f.out(">>")
145}
146
147// putTemplates writes the templates to the PDF
148func (f *Fpdf) putTemplates() {
149	filter := ""
150	if f.compress {
151		filter = "/Filter /FlateDecode "
152	}
153
154	templates := sortTemplates(f.templates, f.catalogSort)
155	var t Template
156	for _, t = range templates {
157		corner, size := t.Size()
158
159		f.newobj()
160		f.templateObjects[t.ID()] = f.n
161		f.outf("<<%s/Type /XObject", filter)
162		f.out("/Subtype /Form")
163		f.out("/Formtype 1")
164		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)
165		if corner.X != 0 || corner.Y != 0 {
166			f.outf("/Matrix [1 0 0 1 %.5f %.5f]", -corner.X*f.k*2, corner.Y*f.k*2)
167		}
168
169		// Template's resource dictionary
170		f.out("/Resources ")
171		f.out("<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]")
172
173		f.templateFontCatalog()
174
175		tImages := t.Images()
176		tTemplates := t.Templates()
177		if len(tImages) > 0 || len(tTemplates) > 0 {
178			f.out("/XObject <<")
179			{
180				var key string
181				var keyList []string
182				var ti *ImageInfoType
183				for key = range tImages {
184					keyList = append(keyList, key)
185				}
186				if gl.catalogSort {
187					sort.Strings(keyList)
188				}
189				for _, key = range keyList {
190					// for _, ti := range tImages {
191					ti = tImages[key]
192					f.outf("/I%s %d 0 R", ti.i, ti.n)
193				}
194			}
195			for _, tt := range tTemplates {
196				id := tt.ID()
197				if objID, ok := f.templateObjects[id]; ok {
198					f.outf("/TPL%s %d 0 R", id, objID)
199				}
200			}
201			f.out(">>")
202		}
203
204		f.out(">>")
205
206		//  Write the template's byte stream
207		buffer := t.Bytes()
208		// fmt.Println("Put template bytes", string(buffer[:]))
209		if f.compress {
210			buffer = sliceCompress(buffer)
211		}
212		f.outf("/Length %d >>", len(buffer))
213		f.putstream(buffer)
214		f.out("endobj")
215	}
216}
217
218func templateKeyList(mp map[string]Template, sort bool) (keyList []string) {
219	var key string
220	for key = range mp {
221		keyList = append(keyList, key)
222	}
223	if sort {
224		gensort(len(keyList),
225			func(a, b int) bool {
226				return keyList[a] < keyList[b]
227			},
228			func(a, b int) {
229				keyList[a], keyList[b] = keyList[b], keyList[a]
230			})
231	}
232	return
233}
234
235// sortTemplates puts templates in a suitable order based on dependices
236func sortTemplates(templates map[string]Template, catalogSort bool) []Template {
237	chain := make([]Template, 0, len(templates)*2)
238
239	// build a full set of dependency chains
240	var keyList []string
241	var key string
242	var t Template
243	keyList = templateKeyList(templates, catalogSort)
244	for _, key = range keyList {
245		t = templates[key]
246		tlist := templateChainDependencies(t)
247		for _, tt := range tlist {
248			if tt != nil {
249				chain = append(chain, tt)
250			}
251		}
252	}
253
254	// reduce that to make a simple list
255	sorted := make([]Template, 0, len(templates))
256chain:
257	for _, t := range chain {
258		for _, already := range sorted {
259			if t == already {
260				continue chain
261			}
262		}
263		sorted = append(sorted, t)
264	}
265
266	return sorted
267}
268
269//  templateChainDependencies is a recursive function for determining the full chain of template dependencies
270func templateChainDependencies(template Template) []Template {
271	requires := template.Templates()
272	chain := make([]Template, len(requires)*2)
273	for _, req := range requires {
274		chain = append(chain, templateChainDependencies(req)...)
275	}
276	chain = append(chain, template)
277	return chain
278}
279
280// < 0002640  31 20 31 32 20 30 20 52  0a 2f 54 50 4c 32 20 31  |1 12 0 R./TPL2 1|
281// < 0002650  35 20 30 20 52 0a 2f 54  50 4c 31 20 31 34 20 30  |5 0 R./TPL1 14 0|
282
283// > 0002640  31 20 31 32 20 30 20 52  0a 2f 54 50 4c 31 20 31  |1 12 0 R./TPL1 1|
284// > 0002650  34 20 30 20 52 0a 2f 54  50 4c 32 20 31 35 20 30  |4 0 R./TPL2 15 0|
285