github.com/phpdave11/gofpdf@v1.4.2/template.go (about)

     1  package gofpdf
     2  
     3  /*
     4   * Copyright (c) 2015 Kurt Jung (Gmail: kurt.w.jung),
     5   *   Marcus Downing, Jan Slabon (Setasign)
     6   *
     7   * Permission to use, copy, modify, and distribute this software for any
     8   * purpose with or without fee is hereby granted, provided that the above
     9   * copyright notice and this permission notice appear in all copies.
    10   *
    11   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    12   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    13   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    14   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    15   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    16   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    17   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    18   */
    19  
    20  import (
    21  	"encoding/gob"
    22  	"sort"
    23  )
    24  
    25  // CreateTemplate defines a new template using the current page size.
    26  func (f *Fpdf) CreateTemplate(fn func(*Tpl)) Template {
    27  	return newTpl(PointType{0, 0}, f.curPageSize, f.defOrientation, f.unitStr, f.fontDirStr, fn, f)
    28  }
    29  
    30  // CreateTemplateCustom starts a template, using the given bounds.
    31  func (f *Fpdf) CreateTemplateCustom(corner PointType, size SizeType, fn func(*Tpl)) Template {
    32  	return newTpl(corner, size, f.defOrientation, f.unitStr, f.fontDirStr, fn, f)
    33  }
    34  
    35  // CreateTemplate creates a template that is not attached to any document.
    36  //
    37  // This function is deprecated; it incorrectly assumes that a page with a width
    38  // smaller than its height is oriented in portrait mode, otherwise it assumes
    39  // landscape mode. This causes problems when placing the template in a master
    40  // document where this condition does not apply. CreateTpl() is a similar
    41  // function that lets you specify the orientation to avoid this problem.
    42  func CreateTemplate(corner PointType, size SizeType, unitStr, fontDirStr string, fn func(*Tpl)) Template {
    43  	orientationStr := "p"
    44  	if size.Wd > size.Ht {
    45  		orientationStr = "l"
    46  	}
    47  
    48  	return CreateTpl(corner, size, orientationStr, unitStr, fontDirStr, fn)
    49  }
    50  
    51  // CreateTpl creates a template not attached to any document
    52  func CreateTpl(corner PointType, size SizeType, orientationStr, unitStr, fontDirStr string, fn func(*Tpl)) Template {
    53  	return newTpl(corner, size, orientationStr, unitStr, fontDirStr, fn, nil)
    54  }
    55  
    56  // UseTemplate adds a template to the current page or another template,
    57  // using the size and position at which it was originally written.
    58  func (f *Fpdf) UseTemplate(t Template) {
    59  	if t == nil {
    60  		f.SetErrorf("template is nil")
    61  		return
    62  	}
    63  	corner, size := t.Size()
    64  	f.UseTemplateScaled(t, corner, size)
    65  }
    66  
    67  // UseTemplateScaled adds a template to the current page or another template,
    68  // using the given page coordinates.
    69  func (f *Fpdf) UseTemplateScaled(t Template, corner PointType, size SizeType) {
    70  	if t == nil {
    71  		f.SetErrorf("template is nil")
    72  		return
    73  	}
    74  
    75  	// You have to add at least a page first
    76  	if f.page <= 0 {
    77  		f.SetErrorf("cannot use a template without first adding a page")
    78  		return
    79  	}
    80  
    81  	// make a note of the fact that we actually use this template, as well as any other templates,
    82  	// images or fonts it uses
    83  	f.templates[t.ID()] = t
    84  	for _, tt := range t.Templates() {
    85  		f.templates[tt.ID()] = tt
    86  	}
    87  
    88  	// Create a list of existing image SHA-1 hashes.
    89  	existingImages := map[string]bool{}
    90  	for _, image := range f.images {
    91  		existingImages[image.i] = true
    92  	}
    93  
    94  	// Add each template image to $f, unless already present.
    95  	for name, ti := range t.Images() {
    96  		if _, found := existingImages[ti.i]; found {
    97  			continue
    98  		}
    99  		name = sprintf("t%s-%s", t.ID(), name)
   100  		f.images[name] = ti
   101  	}
   102  
   103  	// template data
   104  	_, templateSize := t.Size()
   105  	scaleX := size.Wd / templateSize.Wd
   106  	scaleY := size.Ht / templateSize.Ht
   107  	tx := corner.X * f.k
   108  	ty := (f.curPageSize.Ht - corner.Y - size.Ht) * f.k
   109  
   110  	f.outf("q %.4f 0 0 %.4f %.4f %.4f cm", scaleX, scaleY, tx, ty) // Translate
   111  	f.outf("/TPL%s Do Q", t.ID())
   112  }
   113  
   114  // Template is an object that can be written to, then used and re-used any number of times within a document.
   115  type Template interface {
   116  	ID() string
   117  	Size() (PointType, SizeType)
   118  	Bytes() []byte
   119  	Images() map[string]*ImageInfoType
   120  	Templates() []Template
   121  	NumPages() int
   122  	FromPage(int) (Template, error)
   123  	FromPages() []Template
   124  	Serialize() ([]byte, error)
   125  	gob.GobDecoder
   126  	gob.GobEncoder
   127  }
   128  
   129  func (f *Fpdf) templateFontCatalog() {
   130  	var keyList []string
   131  	var font fontDefType
   132  	var key string
   133  	f.out("/Font <<")
   134  	for key = range f.fonts {
   135  		keyList = append(keyList, key)
   136  	}
   137  	if f.catalogSort {
   138  		sort.Strings(keyList)
   139  	}
   140  	for _, key = range keyList {
   141  		font = f.fonts[key]
   142  		f.outf("/F%s %d 0 R", font.i, font.N)
   143  	}
   144  	f.out(">>")
   145  }
   146  
   147  // putTemplates writes the templates to the PDF
   148  func (f *Fpdf) putTemplates() {
   149  	filter := ""
   150  	if f.compress {
   151  		filter = "/Filter /FlateDecode "
   152  	}
   153  
   154  	templates := sortTemplates(f.templates, f.catalogSort)
   155  	var t Template
   156  	for _, t = range templates {
   157  		corner, size := t.Size()
   158  
   159  		f.newobj()
   160  		f.templateObjects[t.ID()] = f.n
   161  		f.outf("<<%s/Type /XObject", filter)
   162  		f.out("/Subtype /Form")
   163  		f.out("/Formtype 1")
   164  		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)
   165  		if corner.X != 0 || corner.Y != 0 {
   166  			f.outf("/Matrix [1 0 0 1 %.5f %.5f]", -corner.X*f.k*2, corner.Y*f.k*2)
   167  		}
   168  
   169  		// Template's resource dictionary
   170  		f.out("/Resources ")
   171  		f.out("<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]")
   172  
   173  		f.templateFontCatalog()
   174  
   175  		tImages := t.Images()
   176  		tTemplates := t.Templates()
   177  		if len(tImages) > 0 || len(tTemplates) > 0 {
   178  			f.out("/XObject <<")
   179  			{
   180  				var key string
   181  				var keyList []string
   182  				var ti *ImageInfoType
   183  				for key = range tImages {
   184  					keyList = append(keyList, key)
   185  				}
   186  				if gl.catalogSort {
   187  					sort.Strings(keyList)
   188  				}
   189  				for _, key = range keyList {
   190  					// for _, ti := range tImages {
   191  					ti = tImages[key]
   192  					f.outf("/I%s %d 0 R", ti.i, ti.n)
   193  				}
   194  			}
   195  			for _, tt := range tTemplates {
   196  				id := tt.ID()
   197  				if objID, ok := f.templateObjects[id]; ok {
   198  					f.outf("/TPL%s %d 0 R", id, objID)
   199  				}
   200  			}
   201  			f.out(">>")
   202  		}
   203  
   204  		f.out(">>")
   205  
   206  		//  Write the template's byte stream
   207  		buffer := t.Bytes()
   208  		// fmt.Println("Put template bytes", string(buffer[:]))
   209  		if f.compress {
   210  			buffer = sliceCompress(buffer)
   211  		}
   212  		f.outf("/Length %d >>", len(buffer))
   213  		f.putstream(buffer)
   214  		f.out("endobj")
   215  	}
   216  }
   217  
   218  func templateKeyList(mp map[string]Template, sort bool) (keyList []string) {
   219  	var key string
   220  	for key = range mp {
   221  		keyList = append(keyList, key)
   222  	}
   223  	if sort {
   224  		gensort(len(keyList),
   225  			func(a, b int) bool {
   226  				return keyList[a] < keyList[b]
   227  			},
   228  			func(a, b int) {
   229  				keyList[a], keyList[b] = keyList[b], keyList[a]
   230  			})
   231  	}
   232  	return
   233  }
   234  
   235  // sortTemplates puts templates in a suitable order based on dependices
   236  func sortTemplates(templates map[string]Template, catalogSort bool) []Template {
   237  	chain := make([]Template, 0, len(templates)*2)
   238  
   239  	// build a full set of dependency chains
   240  	var keyList []string
   241  	var key string
   242  	var t Template
   243  	keyList = templateKeyList(templates, catalogSort)
   244  	for _, key = range keyList {
   245  		t = templates[key]
   246  		tlist := templateChainDependencies(t)
   247  		for _, tt := range tlist {
   248  			if tt != nil {
   249  				chain = append(chain, tt)
   250  			}
   251  		}
   252  	}
   253  
   254  	// reduce that to make a simple list
   255  	sorted := make([]Template, 0, len(templates))
   256  chain:
   257  	for _, t := range chain {
   258  		for _, already := range sorted {
   259  			if t == already {
   260  				continue chain
   261  			}
   262  		}
   263  		sorted = append(sorted, t)
   264  	}
   265  
   266  	return sorted
   267  }
   268  
   269  //  templateChainDependencies is a recursive function for determining the full chain of template dependencies
   270  func templateChainDependencies(template Template) []Template {
   271  	requires := template.Templates()
   272  	chain := make([]Template, len(requires)*2)
   273  	for _, req := range requires {
   274  		chain = append(chain, templateChainDependencies(req)...)
   275  	}
   276  	chain = append(chain, template)
   277  	return chain
   278  }
   279  
   280  // < 0002640  31 20 31 32 20 30 20 52  0a 2f 54 50 4c 32 20 31  |1 12 0 R./TPL2 1|
   281  // < 0002650  35 20 30 20 52 0a 2f 54  50 4c 31 20 31 34 20 30  |5 0 R./TPL1 14 0|
   282  
   283  // > 0002640  31 20 31 32 20 30 20 52  0a 2f 54 50 4c 31 20 31  |1 12 0 R./TPL1 1|
   284  // > 0002650  34 20 30 20 52 0a 2f 54  50 4c 32 20 31 35 20 30  |4 0 R./TPL2 15 0|