123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- 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, fn func(*Tpl), copyFrom *Fpdf) Template {
- sizeStr := ""
- fpdf := fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr, size)
- 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
- }
- }
|