285 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			285 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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, f.density, 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, f.density, 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, density float64, fn func(*Tpl)) Template {
 | |
| 	orientationStr := "p"
 | |
| 	if size.Wd > size.Ht {
 | |
| 		orientationStr = "l"
 | |
| 	}
 | |
| 
 | |
| 	return CreateTpl(corner, size, orientationStr, unitStr, fontDirStr, density, fn)
 | |
| }
 | |
| 
 | |
| // CreateTpl creates a template not attached to any document
 | |
| func CreateTpl(corner PointType, size SizeType, orientationStr, unitStr, fontDirStr string, density float64, fn func(*Tpl)) Template {
 | |
| 	return newTpl(corner, size, orientationStr, unitStr, fontDirStr, density, 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|
 |