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 } for name, ti := range t.Images() { name = sprintf("t%d-%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%d Do Q", t.ID()) } var nextTemplateIDChannel = func() chan int64 { ch := make(chan int64) go func() { var nextID int64 = 1 for { ch <- nextID nextID++ } }() return ch }() // GenerateTemplateID gives the next template ID. These numbers are global so that they can never clash. func GenerateTemplateID() int64 { return <-nextTemplateIDChannel } // 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() int64 Size() (PointType, SizeType) Bytes() []byte Images() map[string]*ImageInfoType Templates() []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%d %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("< 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%d %d 0 R", ti.i, ti.n) } } for _, tt := range tTemplates { id := tt.ID() if objID, ok := f.templateObjects[id]; ok { f.outf("/TPL%d %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[int64]Template, sort bool) (keyList []int64) { var key int64 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[int64]Template, catalogSort bool) []Template { chain := make([]Template, 0, len(templates)*2) // build a full set of dependency chains var keyList []int64 var key int64 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|