package gofpdf import ( "bytes" "crypto/sha1" "encoding/gob" "errors" "fmt" ) /* * Copyright (c) 2015 Kurt Jung (Gmail: kurt.w.jung), * Marcus Downing, Jan Slabon (Setasign) * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // newTpl creates a template, copying graphics settings from a template if one is given func newTpl(corner PointType, size SizeType, orientationStr, unitStr, fontDirStr string, density float64, fn func(*Tpl), copyFrom *Fpdf) Template { sizeStr := "" fpdf := fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr, size, density) tpl := Tpl{*fpdf} if copyFrom != nil { tpl.loadParamsFromFpdf(copyFrom) } tpl.Fpdf.AddPage() fn(&tpl) bytes := make([][]byte, len(tpl.Fpdf.pages)) // skip the first page as it will always be empty for x := 1; x < len(bytes); x++ { bytes[x] = tpl.Fpdf.pages[x].Bytes() } templates := make([]Template, 0, len(tpl.Fpdf.templates)) for _, key := range templateKeyList(tpl.Fpdf.templates, true) { templates = append(templates, tpl.Fpdf.templates[key]) } images := tpl.Fpdf.images template := FpdfTpl{corner, size, bytes, images, templates, tpl.Fpdf.page} return &template } // FpdfTpl is a concrete implementation of the Template interface. type FpdfTpl struct { corner PointType size SizeType bytes [][]byte images map[string]*ImageInfoType templates []Template page int } // ID returns the global template identifier func (t *FpdfTpl) ID() string { return fmt.Sprintf("%x", sha1.Sum(t.Bytes())) } // Size gives the bounding dimensions of this template func (t *FpdfTpl) Size() (corner PointType, size SizeType) { return t.corner, t.size } // Bytes returns the actual template data, not including resources func (t *FpdfTpl) Bytes() []byte { return t.bytes[t.page] } // FromPage creates a new template from a specific Page func (t *FpdfTpl) FromPage(page int) (Template, error) { // pages start at 1 if page == 0 { return nil, errors.New("Pages start at 1 No template will have a page 0") } if page > t.NumPages() { return nil, fmt.Errorf("The template does not have a page %d", page) } // if it is already pointing to the correct page // there is no need to create a new template if t.page == page { return t, nil } t2 := *t t2.page = page return &t2, nil } // FromPages creates a template slice with all the pages within a template. func (t *FpdfTpl) FromPages() []Template { p := make([]Template, t.NumPages()) for x := 1; x <= t.NumPages(); x++ { // the only error is when accessing a // non existing template... that can't happen // here p[x-1], _ = t.FromPage(x) } return p } // Images returns a list of the images used in this template func (t *FpdfTpl) Images() map[string]*ImageInfoType { return t.images } // Templates returns a list of templates used in this template func (t *FpdfTpl) Templates() []Template { return t.templates } // NumPages returns the number of available pages within the template. Look at FromPage and FromPages on access to that content. func (t *FpdfTpl) NumPages() int { // the first page is empty to // make the pages begin at one return len(t.bytes) - 1 } // Serialize turns a template into a byte string for later deserialization func (t *FpdfTpl) Serialize() ([]byte, error) { b := new(bytes.Buffer) enc := gob.NewEncoder(b) err := enc.Encode(t) return b.Bytes(), err } // DeserializeTemplate creaties a template from a previously serialized // template func DeserializeTemplate(b []byte) (Template, error) { tpl := new(FpdfTpl) dec := gob.NewDecoder(bytes.NewBuffer(b)) err := dec.Decode(tpl) return tpl, err } // childrenImages returns the next layer of children images, it doesn't dig into // children of children. Applies template namespace to keys to ensure // no collisions. See UseTemplateScaled func (t *FpdfTpl) childrenImages() map[string]*ImageInfoType { childrenImgs := make(map[string]*ImageInfoType) for x := 0; x < len(t.templates); x++ { imgs := t.templates[x].Images() for key, val := range imgs { name := sprintf("t%s-%s", t.templates[x].ID(), key) childrenImgs[name] = val } } return childrenImgs } // childrensTemplates returns the next layer of children templates, it doesn't dig into // children of children. func (t *FpdfTpl) childrensTemplates() []Template { childrenTmpls := make([]Template, 0) for x := 0; x < len(t.templates); x++ { tmpls := t.templates[x].Templates() childrenTmpls = append(childrenTmpls, tmpls...) } return childrenTmpls } // GobEncode encodes the receiving template into a byte buffer. Use GobDecode // to decode the byte buffer back to a template. func (t *FpdfTpl) GobEncode() ([]byte, error) { w := new(bytes.Buffer) encoder := gob.NewEncoder(w) childrensTemplates := t.childrensTemplates() firstClassTemplates := make([]Template, 0) found_continue: for x := 0; x < len(t.templates); x++ { for y := 0; y < len(childrensTemplates); y++ { if childrensTemplates[y].ID() == t.templates[x].ID() { continue found_continue } } firstClassTemplates = append(firstClassTemplates, t.templates[x]) } err := encoder.Encode(firstClassTemplates) childrenImgs := t.childrenImages() firstClassImgs := make(map[string]*ImageInfoType) for key, img := range t.images { if _, ok := childrenImgs[key]; !ok { firstClassImgs[key] = img } } if err == nil { err = encoder.Encode(firstClassImgs) } if err == nil { err = encoder.Encode(t.corner) } if err == nil { err = encoder.Encode(t.size) } if err == nil { err = encoder.Encode(t.bytes) } if err == nil { err = encoder.Encode(t.page) } return w.Bytes(), err } // GobDecode decodes the specified byte buffer into the receiving template. func (t *FpdfTpl) GobDecode(buf []byte) error { r := bytes.NewBuffer(buf) decoder := gob.NewDecoder(r) firstClassTemplates := make([]*FpdfTpl, 0) err := decoder.Decode(&firstClassTemplates) t.templates = make([]Template, len(firstClassTemplates)) for x := 0; x < len(t.templates); x++ { t.templates[x] = Template(firstClassTemplates[x]) } firstClassImages := t.childrenImages() t.templates = append(t.childrensTemplates(), t.templates...) t.images = make(map[string]*ImageInfoType) if err == nil { err = decoder.Decode(&t.images) } for k, v := range firstClassImages { t.images[k] = v } if err == nil { err = decoder.Decode(&t.corner) } if err == nil { err = decoder.Decode(&t.size) } if err == nil { err = decoder.Decode(&t.bytes) } if err == nil { err = decoder.Decode(&t.page) } return err } // Tpl is an Fpdf used for writing a template. It has most of the facilities of // an Fpdf, but cannot add more pages. Tpl is used directly only during the // limited time a template is writable. type Tpl struct { Fpdf } func (t *Tpl) loadParamsFromFpdf(f *Fpdf) { t.Fpdf.compress = false t.Fpdf.k = f.k t.Fpdf.x = f.x t.Fpdf.y = f.y t.Fpdf.lineWidth = f.lineWidth t.Fpdf.capStyle = f.capStyle t.Fpdf.joinStyle = f.joinStyle t.Fpdf.color.draw = f.color.draw t.Fpdf.color.fill = f.color.fill t.Fpdf.color.text = f.color.text t.Fpdf.fonts = f.fonts t.Fpdf.currentFont = f.currentFont t.Fpdf.fontFamily = f.fontFamily t.Fpdf.fontSize = f.fontSize t.Fpdf.fontSizePt = f.fontSizePt t.Fpdf.fontStyle = f.fontStyle t.Fpdf.ws = f.ws for key, value := range f.images { t.Fpdf.images[key] = value } }