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