github.com/phpdave11/gofpdf@v1.4.2/template.go (about) 1 package 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 20 import ( 21 "encoding/gob" 22 "sort" 23 ) 24 25 // CreateTemplate defines a new template using the current page size. 26 func (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. 31 func (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. 42 func 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 52 func 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. 58 func (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. 69 func (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. 115 type 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 129 func (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 148 func (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 218 func 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 236 func 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)) 256 chain: 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 270 func 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|