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  }