template.go 8.0 KB

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