123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- package gofpdf
- /*
- * 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.
- */
- import (
- "encoding/gob"
- "sort"
- )
- // CreateTemplate defines a new template using the current page size.
- func (f *Fpdf) CreateTemplate(fn func(*Tpl)) Template {
- return newTpl(PointType{0, 0}, f.curPageSize, f.defOrientation, f.unitStr, f.fontDirStr, fn, f)
- }
- // CreateTemplateCustom starts a template, using the given bounds.
- func (f *Fpdf) CreateTemplateCustom(corner PointType, size SizeType, fn func(*Tpl)) Template {
- return newTpl(corner, size, f.defOrientation, f.unitStr, f.fontDirStr, fn, f)
- }
- // CreateTemplate creates a template that is not attached to any document.
- //
- // This function is deprecated; it incorrectly assumes that a page with a width
- // smaller than its height is oriented in portrait mode, otherwise it assumes
- // landscape mode. This causes problems when placing the template in a master
- // document where this condition does not apply. CreateTpl() is a similar
- // function that lets you specify the orientation to avoid this problem.
- func CreateTemplate(corner PointType, size SizeType, unitStr, fontDirStr string, fn func(*Tpl)) Template {
- orientationStr := "p"
- if size.Wd > size.Ht {
- orientationStr = "l"
- }
- return CreateTpl(corner, size, orientationStr, unitStr, fontDirStr, fn)
- }
- // CreateTpl creates a template not attached to any document
- func CreateTpl(corner PointType, size SizeType, orientationStr, unitStr, fontDirStr string, fn func(*Tpl)) Template {
- return newTpl(corner, size, orientationStr, unitStr, fontDirStr, fn, nil)
- }
- // UseTemplate adds a template to the current page or another template,
- // using the size and position at which it was originally written.
- func (f *Fpdf) UseTemplate(t Template) {
- if t == nil {
- f.SetErrorf("template is nil")
- return
- }
- corner, size := t.Size()
- f.UseTemplateScaled(t, corner, size)
- }
- // UseTemplateScaled adds a template to the current page or another template,
- // using the given page coordinates.
- func (f *Fpdf) UseTemplateScaled(t Template, corner PointType, size SizeType) {
- if t == nil {
- f.SetErrorf("template is nil")
- return
- }
- // You have to add at least a page first
- if f.page <= 0 {
- f.SetErrorf("cannot use a template without first adding a page")
- return
- }
- // make a note of the fact that we actually use this template, as well as any other templates,
- // images or fonts it uses
- f.templates[t.ID()] = t
- for _, tt := range t.Templates() {
- f.templates[tt.ID()] = tt
- }
- // Create a list of existing image SHA-1 hashes.
- existingImages := map[string]bool{}
- for _, image := range f.images {
- existingImages[image.i] = true
- }
- // Add each template image to $f, unless already present.
- for name, ti := range t.Images() {
- if _, found := existingImages[ti.i]; found {
- continue
- }
- name = sprintf("t%s-%s", t.ID(), name)
- f.images[name] = ti
- }
- // template data
- _, templateSize := t.Size()
- scaleX := size.Wd / templateSize.Wd
- scaleY := size.Ht / templateSize.Ht
- tx := corner.X * f.k
- ty := (f.curPageSize.Ht - corner.Y - size.Ht) * f.k
- f.outf("q %.4f 0 0 %.4f %.4f %.4f cm", scaleX, scaleY, tx, ty) // Translate
- f.outf("/TPL%s Do Q", t.ID())
- }
- // Template is an object that can be written to, then used and re-used any number of times within a document.
- type Template interface {
- ID() string
- Size() (PointType, SizeType)
- Bytes() []byte
- Images() map[string]*ImageInfoType
- Templates() []Template
- NumPages() int
- FromPage(int) (Template, error)
- FromPages() []Template
- Serialize() ([]byte, error)
- gob.GobDecoder
- gob.GobEncoder
- }
- func (f *Fpdf) templateFontCatalog() {
- var keyList []string
- var font fontDefType
- var key string
- f.out("/Font <<")
- for key = range f.fonts {
- keyList = append(keyList, key)
- }
- if f.catalogSort {
- sort.Strings(keyList)
- }
- for _, key = range keyList {
- font = f.fonts[key]
- f.outf("/F%s %d 0 R", font.i, font.N)
- }
- f.out(">>")
- }
- // putTemplates writes the templates to the PDF
- func (f *Fpdf) putTemplates() {
- filter := ""
- if f.compress {
- filter = "/Filter /FlateDecode "
- }
- templates := sortTemplates(f.templates, f.catalogSort)
- var t Template
- for _, t = range templates {
- corner, size := t.Size()
- f.newobj()
- f.templateObjects[t.ID()] = f.n
- f.outf("<<%s/Type /XObject", filter)
- f.out("/Subtype /Form")
- f.out("/Formtype 1")
- 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)
- if corner.X != 0 || corner.Y != 0 {
- f.outf("/Matrix [1 0 0 1 %.5f %.5f]", -corner.X*f.k*2, corner.Y*f.k*2)
- }
- // Template's resource dictionary
- f.out("/Resources ")
- f.out("<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]")
- f.templateFontCatalog()
- tImages := t.Images()
- tTemplates := t.Templates()
- if len(tImages) > 0 || len(tTemplates) > 0 {
- f.out("/XObject <<")
- {
- var key string
- var keyList []string
- var ti *ImageInfoType
- for key = range tImages {
- keyList = append(keyList, key)
- }
- if gl.catalogSort {
- sort.Strings(keyList)
- }
- for _, key = range keyList {
- // for _, ti := range tImages {
- ti = tImages[key]
- f.outf("/I%s %d 0 R", ti.i, ti.n)
- }
- }
- for _, tt := range tTemplates {
- id := tt.ID()
- if objID, ok := f.templateObjects[id]; ok {
- f.outf("/TPL%s %d 0 R", id, objID)
- }
- }
- f.out(">>")
- }
- f.out(">>")
- // Write the template's byte stream
- buffer := t.Bytes()
- // fmt.Println("Put template bytes", string(buffer[:]))
- if f.compress {
- buffer = sliceCompress(buffer)
- }
- f.outf("/Length %d >>", len(buffer))
- f.putstream(buffer)
- f.out("endobj")
- }
- }
- func templateKeyList(mp map[string]Template, sort bool) (keyList []string) {
- var key string
- for key = range mp {
- keyList = append(keyList, key)
- }
- if sort {
- gensort(len(keyList),
- func(a, b int) bool {
- return keyList[a] < keyList[b]
- },
- func(a, b int) {
- keyList[a], keyList[b] = keyList[b], keyList[a]
- })
- }
- return
- }
- // sortTemplates puts templates in a suitable order based on dependices
- func sortTemplates(templates map[string]Template, catalogSort bool) []Template {
- chain := make([]Template, 0, len(templates)*2)
- // build a full set of dependency chains
- var keyList []string
- var key string
- var t Template
- keyList = templateKeyList(templates, catalogSort)
- for _, key = range keyList {
- t = templates[key]
- tlist := templateChainDependencies(t)
- for _, tt := range tlist {
- if tt != nil {
- chain = append(chain, tt)
- }
- }
- }
- // reduce that to make a simple list
- sorted := make([]Template, 0, len(templates))
- chain:
- for _, t := range chain {
- for _, already := range sorted {
- if t == already {
- continue chain
- }
- }
- sorted = append(sorted, t)
- }
- return sorted
- }
- // templateChainDependencies is a recursive function for determining the full chain of template dependencies
- func templateChainDependencies(template Template) []Template {
- requires := template.Templates()
- chain := make([]Template, len(requires)*2)
- for _, req := range requires {
- chain = append(chain, templateChainDependencies(req)...)
- }
- chain = append(chain, template)
- return chain
- }
- // < 0002640 31 20 31 32 20 30 20 52 0a 2f 54 50 4c 32 20 31 |1 12 0 R./TPL2 1|
- // < 0002650 35 20 30 20 52 0a 2f 54 50 4c 31 20 31 34 20 30 |5 0 R./TPL1 14 0|
- // > 0002640 31 20 31 32 20 30 20 52 0a 2f 54 50 4c 31 20 31 |1 12 0 R./TPL1 1|
- // > 0002650 34 20 30 20 52 0a 2f 54 50 4c 32 20 31 35 20 30 |4 0 R./TPL2 15 0|
|