1/*
2 * Copyright (c) 2013-2014 Kurt Jung (Gmail: kurt.w.jung)
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17package gofpdf
18
19// Version: 1.7
20// Date:    2011-06-18
21// Author:  Olivier PLATHEY
22// Port to Go: Kurt Jung, 2013-07-15
23
24import (
25	"bytes"
26	"encoding/binary"
27	"encoding/json"
28	"fmt"
29	"image"
30	"image/color"
31	"image/gif"
32	"image/jpeg"
33	"image/png"
34	"io"
35	"io/ioutil"
36	"math"
37	"os"
38	"path"
39	"sort"
40	"strconv"
41	"strings"
42	"time"
43)
44
45var gl struct {
46	catalogSort  bool
47	noCompress   bool // Initial zero value indicates compression
48	creationDate time.Time
49	modDate      time.Time
50}
51
52type fmtBuffer struct {
53	bytes.Buffer
54}
55
56func (b *fmtBuffer) printf(fmtStr string, args ...interface{}) {
57	b.Buffer.WriteString(fmt.Sprintf(fmtStr, args...))
58}
59
60func fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr string, size SizeType) (f *Fpdf) {
61	f = new(Fpdf)
62	if orientationStr == "" {
63		orientationStr = "p"
64	} else {
65		orientationStr = strings.ToLower(orientationStr)
66	}
67	if unitStr == "" {
68		unitStr = "mm"
69	}
70	if sizeStr == "" {
71		sizeStr = "A4"
72	}
73	if fontDirStr == "" {
74		fontDirStr = "."
75	}
76	f.page = 0
77	f.n = 2
78	f.pages = make([]*bytes.Buffer, 0, 8)
79	f.pages = append(f.pages, bytes.NewBufferString("")) // pages[0] is unused (1-based)
80	f.pageSizes = make(map[int]SizeType)
81	f.pageBoxes = make(map[int]map[string]PageBox)
82	f.defPageBoxes = make(map[string]PageBox)
83	f.state = 0
84	f.fonts = make(map[string]fontDefType)
85	f.fontFiles = make(map[string]fontFileType)
86	f.diffs = make([]string, 0, 8)
87	f.templates = make(map[string]Template)
88	f.templateObjects = make(map[string]int)
89	f.importedObjs = make(map[string][]byte, 0)
90	f.importedObjPos = make(map[string]map[int]string, 0)
91	f.importedTplObjs = make(map[string]string)
92	f.importedTplIDs = make(map[string]int, 0)
93	f.images = make(map[string]*ImageInfoType)
94	f.pageLinks = make([][]linkType, 0, 8)
95	f.pageLinks = append(f.pageLinks, make([]linkType, 0, 0)) // pageLinks[0] is unused (1-based)
96	f.links = make([]intLinkType, 0, 8)
97	f.links = append(f.links, intLinkType{}) // links[0] is unused (1-based)
98	f.pageAttachments = make([][]annotationAttach, 0, 8)
99	f.pageAttachments = append(f.pageAttachments, []annotationAttach{}) //
100	f.aliasMap = make(map[string]string)
101	f.inHeader = false
102	f.inFooter = false
103	f.lasth = 0
104	f.fontFamily = ""
105	f.fontStyle = ""
106	f.SetFontSize(12)
107	f.underline = false
108	f.strikeout = false
109	f.setDrawColor(0, 0, 0)
110	f.setFillColor(0, 0, 0)
111	f.setTextColor(0, 0, 0)
112	f.colorFlag = false
113	f.ws = 0
114	f.fontpath = fontDirStr
115	// Core fonts
116	f.coreFonts = map[string]bool{
117		"courier":      true,
118		"helvetica":    true,
119		"times":        true,
120		"symbol":       true,
121		"zapfdingbats": true,
122	}
123	// Scale factor
124	switch unitStr {
125	case "pt", "point":
126		f.k = 1.0
127	case "mm":
128		f.k = 72.0 / 25.4
129	case "cm":
130		f.k = 72.0 / 2.54
131	case "in", "inch":
132		f.k = 72.0
133	default:
134		f.err = fmt.Errorf("incorrect unit %s", unitStr)
135		return
136	}
137	f.unitStr = unitStr
138	// Page sizes
139	f.stdPageSizes = make(map[string]SizeType)
140	f.stdPageSizes["a3"] = SizeType{841.89, 1190.55}
141	f.stdPageSizes["a4"] = SizeType{595.28, 841.89}
142	f.stdPageSizes["a5"] = SizeType{420.94, 595.28}
143	f.stdPageSizes["a6"] = SizeType{297.64, 420.94}
144	f.stdPageSizes["a2"] = SizeType{1190.55, 1683.78}
145	f.stdPageSizes["a1"] = SizeType{1683.78, 2383.94}
146	f.stdPageSizes["letter"] = SizeType{612, 792}
147	f.stdPageSizes["legal"] = SizeType{612, 1008}
148	f.stdPageSizes["tabloid"] = SizeType{792, 1224}
149	if size.Wd > 0 && size.Ht > 0 {
150		f.defPageSize = size
151	} else {
152		f.defPageSize = f.getpagesizestr(sizeStr)
153		if f.err != nil {
154			return
155		}
156	}
157	f.curPageSize = f.defPageSize
158	// Page orientation
159	switch orientationStr {
160	case "p", "portrait":
161		f.defOrientation = "P"
162		f.w = f.defPageSize.Wd
163		f.h = f.defPageSize.Ht
164		// dbg("Assign h: %8.2f", f.h)
165	case "l", "landscape":
166		f.defOrientation = "L"
167		f.w = f.defPageSize.Ht
168		f.h = f.defPageSize.Wd
169	default:
170		f.err = fmt.Errorf("incorrect orientation: %s", orientationStr)
171		return
172	}
173	f.curOrientation = f.defOrientation
174	f.wPt = f.w * f.k
175	f.hPt = f.h * f.k
176	// Page margins (1 cm)
177	margin := 28.35 / f.k
178	f.SetMargins(margin, margin, margin)
179	// Interior cell margin (1 mm)
180	f.cMargin = margin / 10
181	// Line width (0.2 mm)
182	f.lineWidth = 0.567 / f.k
183	// 	Automatic page break
184	f.SetAutoPageBreak(true, 2*margin)
185	// Default display mode
186	f.SetDisplayMode("default", "default")
187	if f.err != nil {
188		return
189	}
190	f.acceptPageBreak = func() bool {
191		return f.autoPageBreak
192	}
193	// Enable compression
194	f.SetCompression(!gl.noCompress)
195	f.spotColorMap = make(map[string]spotColorType)
196	f.blendList = make([]blendModeType, 0, 8)
197	f.blendList = append(f.blendList, blendModeType{}) // blendList[0] is unused (1-based)
198	f.blendMap = make(map[string]int)
199	f.blendMode = "Normal"
200	f.alpha = 1
201	f.gradientList = make([]gradientType, 0, 8)
202	f.gradientList = append(f.gradientList, gradientType{}) // gradientList[0] is unused
203	// Set default PDF version number
204	f.pdfVersion = "1.3"
205	f.SetProducer("FPDF "+cnFpdfVersion, true)
206	f.layerInit()
207	f.catalogSort = gl.catalogSort
208	f.creationDate = gl.creationDate
209	f.modDate = gl.modDate
210	f.userUnderlineThickness = 1
211	return
212}
213
214// NewCustom returns a pointer to a new Fpdf instance. Its methods are
215// subsequently called to produce a single PDF document. NewCustom() is an
216// alternative to New() that provides additional customization. The PageSize()
217// example demonstrates this method.
218func NewCustom(init *InitType) (f *Fpdf) {
219	return fpdfNew(init.OrientationStr, init.UnitStr, init.SizeStr, init.FontDirStr, init.Size)
220}
221
222// New returns a pointer to a new Fpdf instance. Its methods are subsequently
223// called to produce a single PDF document.
224//
225// orientationStr specifies the default page orientation. For portrait mode,
226// specify "P" or "Portrait". For landscape mode, specify "L" or "Landscape".
227// An empty string will be replaced with "P".
228//
229// unitStr specifies the unit of length used in size parameters for elements
230// other than fonts, which are always measured in points. Specify "pt" for
231// point, "mm" for millimeter, "cm" for centimeter, or "in" for inch. An empty
232// string will be replaced with "mm".
233//
234// sizeStr specifies the page size. Acceptable values are "A3", "A4", "A5",
235// "Letter", "Legal", or "Tabloid". An empty string will be replaced with "A4".
236//
237// fontDirStr specifies the file system location in which font resources will
238// be found. An empty string is replaced with ".". This argument only needs to
239// reference an actual directory if a font other than one of the core
240// fonts is used. The core fonts are "courier", "helvetica" (also called
241// "arial"), "times", and "zapfdingbats" (also called "symbol").
242func New(orientationStr, unitStr, sizeStr, fontDirStr string) (f *Fpdf) {
243	return fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr, SizeType{0, 0})
244}
245
246// Ok returns true if no processing errors have occurred.
247func (f *Fpdf) Ok() bool {
248	return f.err == nil
249}
250
251// Err returns true if a processing error has occurred.
252func (f *Fpdf) Err() bool {
253	return f.err != nil
254}
255
256// ClearError unsets the internal Fpdf error. This method should be used with
257// care, as an internal error condition usually indicates an unrecoverable
258// problem with the generation of a document. It is intended to deal with cases
259// in which an error is used to select an alternate form of the document.
260func (f *Fpdf) ClearError() {
261	f.err = nil
262}
263
264// SetErrorf sets the internal Fpdf error with formatted text to halt PDF
265// generation; this may facilitate error handling by application. If an error
266// condition is already set, this call is ignored.
267//
268// See the documentation for printing in the standard fmt package for details
269// about fmtStr and args.
270func (f *Fpdf) SetErrorf(fmtStr string, args ...interface{}) {
271	if f.err == nil {
272		f.err = fmt.Errorf(fmtStr, args...)
273	}
274}
275
276// String satisfies the fmt.Stringer interface and summarizes the Fpdf
277// instance.
278func (f *Fpdf) String() string {
279	return "Fpdf " + cnFpdfVersion
280}
281
282// SetError sets an error to halt PDF generation. This may facilitate error
283// handling by application. See also Ok(), Err() and Error().
284func (f *Fpdf) SetError(err error) {
285	if f.err == nil && err != nil {
286		f.err = err
287	}
288}
289
290// Error returns the internal Fpdf error; this will be nil if no error has occurred.
291func (f *Fpdf) Error() error {
292	return f.err
293}
294
295// GetPageSize returns the current page's width and height. This is the paper's
296// size. To compute the size of the area being used, subtract the margins (see
297// GetMargins()).
298func (f *Fpdf) GetPageSize() (width, height float64) {
299	width = f.w
300	height = f.h
301	return
302}
303
304// GetMargins returns the left, top, right, and bottom margins. The first three
305// are set with the SetMargins() method. The bottom margin is set with the
306// SetAutoPageBreak() method.
307func (f *Fpdf) GetMargins() (left, top, right, bottom float64) {
308	left = f.lMargin
309	top = f.tMargin
310	right = f.rMargin
311	bottom = f.bMargin
312	return
313}
314
315// SetMargins defines the left, top and right margins. By default, they equal 1
316// cm. Call this method to change them. If the value of the right margin is
317// less than zero, it is set to the same as the left margin.
318func (f *Fpdf) SetMargins(left, top, right float64) {
319	f.lMargin = left
320	f.tMargin = top
321	if right < 0 {
322		right = left
323	}
324	f.rMargin = right
325}
326
327// SetLeftMargin defines the left margin. The method can be called before
328// creating the first page. If the current abscissa gets out of page, it is
329// brought back to the margin.
330func (f *Fpdf) SetLeftMargin(margin float64) {
331	f.lMargin = margin
332	if f.page > 0 && f.x < margin {
333		f.x = margin
334	}
335}
336
337// GetCellMargin returns the cell margin. This is the amount of space before
338// and after the text within a cell that's left blank, and is in units passed
339// to New(). It defaults to 1mm.
340func (f *Fpdf) GetCellMargin() float64 {
341	return f.cMargin
342}
343
344// SetCellMargin sets the cell margin. This is the amount of space before and
345// after the text within a cell that's left blank, and is in units passed to
346// New().
347func (f *Fpdf) SetCellMargin(margin float64) {
348	f.cMargin = margin
349}
350
351// SetPageBoxRec sets the page box for the current page, and any following
352// pages. Allowable types are trim, trimbox, crop, cropbox, bleed, bleedbox,
353// art and artbox box types are case insensitive. See SetPageBox() for a method
354// that specifies the coordinates and extent of the page box individually.
355func (f *Fpdf) SetPageBoxRec(t string, pb PageBox) {
356	switch strings.ToLower(t) {
357	case "trim":
358		fallthrough
359	case "trimbox":
360		t = "TrimBox"
361	case "crop":
362		fallthrough
363	case "cropbox":
364		t = "CropBox"
365	case "bleed":
366		fallthrough
367	case "bleedbox":
368		t = "BleedBox"
369	case "art":
370		fallthrough
371	case "artbox":
372		t = "ArtBox"
373	default:
374		f.err = fmt.Errorf("%s is not a valid page box type", t)
375		return
376	}
377
378	pb.X = pb.X * f.k
379	pb.Y = pb.Y * f.k
380	pb.Wd = (pb.Wd * f.k) + pb.X
381	pb.Ht = (pb.Ht * f.k) + pb.Y
382
383	if f.page > 0 {
384		f.pageBoxes[f.page][t] = pb
385	}
386
387	// always override. page defaults are supplied in addPage function
388	f.defPageBoxes[t] = pb
389}
390
391// SetPageBox sets the page box for the current page, and any following pages.
392// Allowable types are trim, trimbox, crop, cropbox, bleed, bleedbox, art and
393// artbox box types are case insensitive.
394func (f *Fpdf) SetPageBox(t string, x, y, wd, ht float64) {
395	f.SetPageBoxRec(t, PageBox{SizeType{Wd: wd, Ht: ht}, PointType{X: x, Y: y}})
396}
397
398// SetPage sets the current page to that of a valid page in the PDF document.
399// pageNum is one-based. The SetPage() example demonstrates this method.
400func (f *Fpdf) SetPage(pageNum int) {
401	if (pageNum > 0) && (pageNum < len(f.pages)) {
402		f.page = pageNum
403	}
404}
405
406// PageCount returns the number of pages currently in the document. Since page
407// numbers in gofpdf are one-based, the page count is the same as the page
408// number of the current last page.
409func (f *Fpdf) PageCount() int {
410	return len(f.pages) - 1
411}
412
413// SetFontLocation sets the location in the file system of the font and font
414// definition files.
415func (f *Fpdf) SetFontLocation(fontDirStr string) {
416	f.fontpath = fontDirStr
417}
418
419// SetFontLoader sets a loader used to read font files (.json and .z) from an
420// arbitrary source. If a font loader has been specified, it is used to load
421// the named font resources when AddFont() is called. If this operation fails,
422// an attempt is made to load the resources from the configured font directory
423// (see SetFontLocation()).
424func (f *Fpdf) SetFontLoader(loader FontLoader) {
425	f.fontLoader = loader
426}
427
428// SetHeaderFuncMode sets the function that lets the application render the
429// page header. See SetHeaderFunc() for more details. The value for homeMode
430// should be set to true to have the current position set to the left and top
431// margin after the header function is called.
432func (f *Fpdf) SetHeaderFuncMode(fnc func(), homeMode bool) {
433	f.headerFnc = fnc
434	f.headerHomeMode = homeMode
435}
436
437// SetHeaderFunc sets the function that lets the application render the page
438// header. The specified function is automatically called by AddPage() and
439// should not be called directly by the application. The implementation in Fpdf
440// is empty, so you have to provide an appropriate function if you want page
441// headers. fnc will typically be a closure that has access to the Fpdf
442// instance and other document generation variables.
443//
444// A header is a convenient place to put background content that repeats on
445// each page such as a watermark. When this is done, remember to reset the X
446// and Y values so the normal content begins where expected. Including a
447// watermark on each page is demonstrated in the example for TransformRotate.
448//
449// This method is demonstrated in the example for AddPage().
450func (f *Fpdf) SetHeaderFunc(fnc func()) {
451	f.headerFnc = fnc
452}
453
454// SetFooterFunc sets the function that lets the application render the page
455// footer. The specified function is automatically called by AddPage() and
456// Close() and should not be called directly by the application. The
457// implementation in Fpdf is empty, so you have to provide an appropriate
458// function if you want page footers. fnc will typically be a closure that has
459// access to the Fpdf instance and other document generation variables. See
460// SetFooterFuncLpi for a similar function that passes a last page indicator.
461//
462// This method is demonstrated in the example for AddPage().
463func (f *Fpdf) SetFooterFunc(fnc func()) {
464	f.footerFnc = fnc
465	f.footerFncLpi = nil
466}
467
468// SetFooterFuncLpi sets the function that lets the application render the page
469// footer. The specified function is automatically called by AddPage() and
470// Close() and should not be called directly by the application. It is passed a
471// boolean that is true if the last page of the document is being rendered. The
472// implementation in Fpdf is empty, so you have to provide an appropriate
473// function if you want page footers. fnc will typically be a closure that has
474// access to the Fpdf instance and other document generation variables.
475func (f *Fpdf) SetFooterFuncLpi(fnc func(lastPage bool)) {
476	f.footerFncLpi = fnc
477	f.footerFnc = nil
478}
479
480// SetTopMargin defines the top margin. The method can be called before
481// creating the first page.
482func (f *Fpdf) SetTopMargin(margin float64) {
483	f.tMargin = margin
484}
485
486// SetRightMargin defines the right margin. The method can be called before
487// creating the first page.
488func (f *Fpdf) SetRightMargin(margin float64) {
489	f.rMargin = margin
490}
491
492// GetAutoPageBreak returns true if automatic pages breaks are enabled, false
493// otherwise. This is followed by the triggering limit from the bottom of the
494// page. This value applies only if automatic page breaks are enabled.
495func (f *Fpdf) GetAutoPageBreak() (auto bool, margin float64) {
496	auto = f.autoPageBreak
497	margin = f.bMargin
498	return
499}
500
501// SetAutoPageBreak enables or disables the automatic page breaking mode. When
502// enabling, the second parameter is the distance from the bottom of the page
503// that defines the triggering limit. By default, the mode is on and the margin
504// is 2 cm.
505func (f *Fpdf) SetAutoPageBreak(auto bool, margin float64) {
506	f.autoPageBreak = auto
507	f.bMargin = margin
508	f.pageBreakTrigger = f.h - margin
509}
510
511// SetDisplayMode sets advisory display directives for the document viewer.
512// Pages can be displayed entirely on screen, occupy the full width of the
513// window, use real size, be scaled by a specific zooming factor or use viewer
514// default (configured in the Preferences menu of Adobe Reader). The page
515// layout can be specified so that pages are displayed individually or in
516// pairs.
517//
518// zoomStr can be "fullpage" to display the entire page on screen, "fullwidth"
519// to use maximum width of window, "real" to use real size (equivalent to 100%
520// zoom) or "default" to use viewer default mode.
521//
522// layoutStr can be "single" (or "SinglePage") to display one page at once,
523// "continuous" (or "OneColumn") to display pages continuously, "two" (or
524// "TwoColumnLeft") to display two pages on two columns with odd-numbered pages
525// on the left, or "TwoColumnRight" to display two pages on two columns with
526// odd-numbered pages on the right, or "TwoPageLeft" to display pages two at a
527// time with odd-numbered pages on the left, or "TwoPageRight" to display pages
528// two at a time with odd-numbered pages on the right, or "default" to use
529// viewer default mode.
530func (f *Fpdf) SetDisplayMode(zoomStr, layoutStr string) {
531	if f.err != nil {
532		return
533	}
534	if layoutStr == "" {
535		layoutStr = "default"
536	}
537	switch zoomStr {
538	case "fullpage", "fullwidth", "real", "default":
539		f.zoomMode = zoomStr
540	default:
541		f.err = fmt.Errorf("incorrect zoom display mode: %s", zoomStr)
542		return
543	}
544	switch layoutStr {
545	case "single", "continuous", "two", "default", "SinglePage", "OneColumn",
546		"TwoColumnLeft", "TwoColumnRight", "TwoPageLeft", "TwoPageRight":
547		f.layoutMode = layoutStr
548	default:
549		f.err = fmt.Errorf("incorrect layout display mode: %s", layoutStr)
550		return
551	}
552}
553
554// SetDefaultCompression controls the default setting of the internal
555// compression flag. See SetCompression() for more details. Compression is on
556// by default.
557func SetDefaultCompression(compress bool) {
558	gl.noCompress = !compress
559}
560
561// SetCompression activates or deactivates page compression with zlib. When
562// activated, the internal representation of each page is compressed, which
563// leads to a compression ratio of about 2 for the resulting document.
564// Compression is on by default.
565func (f *Fpdf) SetCompression(compress bool) {
566	f.compress = compress
567}
568
569// SetProducer defines the producer of the document. isUTF8 indicates if the string
570// is encoded in ISO-8859-1 (false) or UTF-8 (true).
571func (f *Fpdf) SetProducer(producerStr string, isUTF8 bool) {
572	if isUTF8 {
573		producerStr = utf8toutf16(producerStr)
574	}
575	f.producer = producerStr
576}
577
578// SetTitle defines the title of the document. isUTF8 indicates if the string
579// is encoded in ISO-8859-1 (false) or UTF-8 (true).
580func (f *Fpdf) SetTitle(titleStr string, isUTF8 bool) {
581	if isUTF8 {
582		titleStr = utf8toutf16(titleStr)
583	}
584	f.title = titleStr
585}
586
587// SetSubject defines the subject of the document. isUTF8 indicates if the
588// string is encoded in ISO-8859-1 (false) or UTF-8 (true).
589func (f *Fpdf) SetSubject(subjectStr string, isUTF8 bool) {
590	if isUTF8 {
591		subjectStr = utf8toutf16(subjectStr)
592	}
593	f.subject = subjectStr
594}
595
596// SetAuthor defines the author of the document. isUTF8 indicates if the string
597// is encoded in ISO-8859-1 (false) or UTF-8 (true).
598func (f *Fpdf) SetAuthor(authorStr string, isUTF8 bool) {
599	if isUTF8 {
600		authorStr = utf8toutf16(authorStr)
601	}
602	f.author = authorStr
603}
604
605// SetKeywords defines the keywords of the document. keywordStr is a
606// space-delimited string, for example "invoice August". isUTF8 indicates if
607// the string is encoded
608func (f *Fpdf) SetKeywords(keywordsStr string, isUTF8 bool) {
609	if isUTF8 {
610		keywordsStr = utf8toutf16(keywordsStr)
611	}
612	f.keywords = keywordsStr
613}
614
615// SetCreator defines the creator of the document. isUTF8 indicates if the
616// string is encoded in ISO-8859-1 (false) or UTF-8 (true).
617func (f *Fpdf) SetCreator(creatorStr string, isUTF8 bool) {
618	if isUTF8 {
619		creatorStr = utf8toutf16(creatorStr)
620	}
621	f.creator = creatorStr
622}
623
624// SetXmpMetadata defines XMP metadata that will be embedded with the document.
625func (f *Fpdf) SetXmpMetadata(xmpStream []byte) {
626	f.xmp = xmpStream
627}
628
629// AliasNbPages defines an alias for the total number of pages. It will be
630// substituted as the document is closed. An empty string is replaced with the
631// string "{nb}".
632//
633// See the example for AddPage() for a demonstration of this method.
634func (f *Fpdf) AliasNbPages(aliasStr string) {
635	if aliasStr == "" {
636		aliasStr = "{nb}"
637	}
638	f.aliasNbPagesStr = aliasStr
639}
640
641// RTL enables right-to-left mode
642func (f *Fpdf) RTL() {
643	f.isRTL = true
644}
645
646// LTR disables right-to-left mode
647func (f *Fpdf) LTR() {
648	f.isRTL = false
649}
650
651// open begins a document
652func (f *Fpdf) open() {
653	f.state = 1
654}
655
656// Close terminates the PDF document. It is not necessary to call this method
657// explicitly because Output(), OutputAndClose() and OutputFileAndClose() do it
658// automatically. If the document contains no page, AddPage() is called to
659// prevent the generation of an invalid document.
660func (f *Fpdf) Close() {
661	if f.err == nil {
662		if f.clipNest > 0 {
663			f.err = fmt.Errorf("clip procedure must be explicitly ended")
664		} else if f.transformNest > 0 {
665			f.err = fmt.Errorf("transformation procedure must be explicitly ended")
666		}
667	}
668	if f.err != nil {
669		return
670	}
671	if f.state == 3 {
672		return
673	}
674	if f.page == 0 {
675		f.AddPage()
676		if f.err != nil {
677			return
678		}
679	}
680	// Page footer
681	f.inFooter = true
682	if f.footerFnc != nil {
683		f.footerFnc()
684	} else if f.footerFncLpi != nil {
685		f.footerFncLpi(true)
686	}
687	f.inFooter = false
688
689	// Close page
690	f.endpage()
691	// Close document
692	f.enddoc()
693	return
694}
695
696// PageSize returns the width and height of the specified page in the units
697// established in New(). These return values are followed by the unit of
698// measure itself. If pageNum is zero or otherwise out of bounds, it returns
699// the default page size, that is, the size of the page that would be added by
700// AddPage().
701func (f *Fpdf) PageSize(pageNum int) (wd, ht float64, unitStr string) {
702	sz, ok := f.pageSizes[pageNum]
703	if ok {
704		sz.Wd, sz.Ht = sz.Wd/f.k, sz.Ht/f.k
705	} else {
706		sz = f.defPageSize // user units
707	}
708	return sz.Wd, sz.Ht, f.unitStr
709}
710
711// AddPageFormat adds a new page with non-default orientation or size. See
712// AddPage() for more details.
713//
714// See New() for a description of orientationStr.
715//
716// size specifies the size of the new page in the units established in New().
717//
718// The PageSize() example demonstrates this method.
719func (f *Fpdf) AddPageFormat(orientationStr string, size SizeType) {
720	if f.err != nil {
721		return
722	}
723	if f.page != len(f.pages)-1 {
724		f.page = len(f.pages) - 1
725	}
726	if f.state == 0 {
727		f.open()
728	}
729	familyStr := f.fontFamily
730	style := f.fontStyle
731	if f.underline {
732		style += "U"
733	}
734	if f.strikeout {
735		style += "S"
736	}
737	fontsize := f.fontSizePt
738	lw := f.lineWidth
739	dc := f.color.draw
740	fc := f.color.fill
741	tc := f.color.text
742	cf := f.colorFlag
743
744	if f.page > 0 {
745		f.inFooter = true
746		// Page footer avoid double call on footer.
747		if f.footerFnc != nil {
748			f.footerFnc()
749
750		} else if f.footerFncLpi != nil {
751			f.footerFncLpi(false) // not last page.
752		}
753		f.inFooter = false
754		// Close page
755		f.endpage()
756	}
757	// Start new page
758	f.beginpage(orientationStr, size)
759	// 	Set line cap style to current value
760	// f.out("2 J")
761	f.outf("%d J", f.capStyle)
762	// 	Set line join style to current value
763	f.outf("%d j", f.joinStyle)
764	// Set line width
765	f.lineWidth = lw
766	f.outf("%.2f w", lw*f.k)
767	// Set dash pattern
768	if len(f.dashArray) > 0 {
769		f.outputDashPattern()
770	}
771	// 	Set font
772	if familyStr != "" {
773		f.SetFont(familyStr, style, fontsize)
774		if f.err != nil {
775			return
776		}
777	}
778	// 	Set colors
779	f.color.draw = dc
780	if dc.str != "0 G" {
781		f.out(dc.str)
782	}
783	f.color.fill = fc
784	if fc.str != "0 g" {
785		f.out(fc.str)
786	}
787	f.color.text = tc
788	f.colorFlag = cf
789	// 	Page header
790	if f.headerFnc != nil {
791		f.inHeader = true
792		f.headerFnc()
793		f.inHeader = false
794		if f.headerHomeMode {
795			f.SetHomeXY()
796		}
797	}
798	// 	Restore line width
799	if f.lineWidth != lw {
800		f.lineWidth = lw
801		f.outf("%.2f w", lw*f.k)
802	}
803	// Restore font
804	if familyStr != "" {
805		f.SetFont(familyStr, style, fontsize)
806		if f.err != nil {
807			return
808		}
809	}
810	// Restore colors
811	if f.color.draw.str != dc.str {
812		f.color.draw = dc
813		f.out(dc.str)
814	}
815	if f.color.fill.str != fc.str {
816		f.color.fill = fc
817		f.out(fc.str)
818	}
819	f.color.text = tc
820	f.colorFlag = cf
821	return
822}
823
824// AddPage adds a new page to the document. If a page is already present, the
825// Footer() method is called first to output the footer. Then the page is
826// added, the current position set to the top-left corner according to the left
827// and top margins, and Header() is called to display the header.
828//
829// The font which was set before calling is automatically restored. There is no
830// need to call SetFont() again if you want to continue with the same font. The
831// same is true for colors and line width.
832//
833// The origin of the coordinate system is at the top-left corner and increasing
834// ordinates go downwards.
835//
836// See AddPageFormat() for a version of this method that allows the page size
837// and orientation to be different than the default.
838func (f *Fpdf) AddPage() {
839	if f.err != nil {
840		return
841	}
842	// dbg("AddPage")
843	f.AddPageFormat(f.defOrientation, f.defPageSize)
844	return
845}
846
847// PageNo returns the current page number.
848//
849// See the example for AddPage() for a demonstration of this method.
850func (f *Fpdf) PageNo() int {
851	return f.page
852}
853
854func colorComp(v int) (int, float64) {
855	if v < 0 {
856		v = 0
857	} else if v > 255 {
858		v = 255
859	}
860	return v, float64(v) / 255.0
861}
862
863func rgbColorValue(r, g, b int, grayStr, fullStr string) (clr colorType) {
864	clr.ir, clr.r = colorComp(r)
865	clr.ig, clr.g = colorComp(g)
866	clr.ib, clr.b = colorComp(b)
867	clr.mode = colorModeRGB
868	clr.gray = clr.ir == clr.ig && clr.r == clr.b
869	if len(grayStr) > 0 {
870		if clr.gray {
871			clr.str = sprintf("%.3f %s", clr.r, grayStr)
872		} else {
873			clr.str = sprintf("%.3f %.3f %.3f %s", clr.r, clr.g, clr.b, fullStr)
874		}
875	} else {
876		clr.str = sprintf("%.3f %.3f %.3f", clr.r, clr.g, clr.b)
877	}
878	return
879}
880
881// SetDrawColor defines the color used for all drawing operations (lines,
882// rectangles and cell borders). It is expressed in RGB components (0 - 255).
883// The method can be called before the first page is created. The value is
884// retained from page to page.
885func (f *Fpdf) SetDrawColor(r, g, b int) {
886	f.setDrawColor(r, g, b)
887}
888
889func (f *Fpdf) setDrawColor(r, g, b int) {
890	f.color.draw = rgbColorValue(r, g, b, "G", "RG")
891	if f.page > 0 {
892		f.out(f.color.draw.str)
893	}
894}
895
896// GetDrawColor returns the most recently set draw color as RGB components (0 -
897// 255). This will not be the current value if a draw color of some other type
898// (for example, spot) has been more recently set.
899func (f *Fpdf) GetDrawColor() (int, int, int) {
900	return f.color.draw.ir, f.color.draw.ig, f.color.draw.ib
901}
902
903// SetFillColor defines the color used for all filling operations (filled
904// rectangles and cell backgrounds). It is expressed in RGB components (0
905// -255). The method can be called before the first page is created and the
906// value is retained from page to page.
907func (f *Fpdf) SetFillColor(r, g, b int) {
908	f.setFillColor(r, g, b)
909}
910
911func (f *Fpdf) setFillColor(r, g, b int) {
912	f.color.fill = rgbColorValue(r, g, b, "g", "rg")
913	f.colorFlag = f.color.fill.str != f.color.text.str
914	if f.page > 0 {
915		f.out(f.color.fill.str)
916	}
917}
918
919// GetFillColor returns the most recently set fill color as RGB components (0 -
920// 255). This will not be the current value if a fill color of some other type
921// (for example, spot) has been more recently set.
922func (f *Fpdf) GetFillColor() (int, int, int) {
923	return f.color.fill.ir, f.color.fill.ig, f.color.fill.ib
924}
925
926// SetTextColor defines the color used for text. It is expressed in RGB
927// components (0 - 255). The method can be called before the first page is
928// created. The value is retained from page to page.
929func (f *Fpdf) SetTextColor(r, g, b int) {
930	f.setTextColor(r, g, b)
931}
932
933func (f *Fpdf) setTextColor(r, g, b int) {
934	f.color.text = rgbColorValue(r, g, b, "g", "rg")
935	f.colorFlag = f.color.fill.str != f.color.text.str
936}
937
938// GetTextColor returns the most recently set text color as RGB components (0 -
939// 255). This will not be the current value if a text color of some other type
940// (for example, spot) has been more recently set.
941func (f *Fpdf) GetTextColor() (int, int, int) {
942	return f.color.text.ir, f.color.text.ig, f.color.text.ib
943}
944
945// GetStringWidth returns the length of a string in user units. A font must be
946// currently selected.
947func (f *Fpdf) GetStringWidth(s string) float64 {
948	if f.err != nil {
949		return 0
950	}
951	w := f.GetStringSymbolWidth(s)
952	return float64(w) * f.fontSize / 1000
953}
954
955// GetStringSymbolWidth returns the length of a string in glyf units. A font must be
956// currently selected.
957func (f *Fpdf) GetStringSymbolWidth(s string) int {
958	if f.err != nil {
959		return 0
960	}
961	w := 0
962	if f.isCurrentUTF8 {
963		unicode := []rune(s)
964		for _, char := range unicode {
965			intChar := int(char)
966			if len(f.currentFont.Cw) >= intChar && f.currentFont.Cw[intChar] > 0 {
967				if f.currentFont.Cw[intChar] != 65535 {
968					w += f.currentFont.Cw[intChar]
969				}
970			} else if f.currentFont.Desc.MissingWidth != 0 {
971				w += f.currentFont.Desc.MissingWidth
972			} else {
973				w += 500
974			}
975		}
976	} else {
977		for _, ch := range []byte(s) {
978			if ch == 0 {
979				break
980			}
981			w += f.currentFont.Cw[ch]
982		}
983	}
984	return w
985}
986
987// SetLineWidth defines the line width. By default, the value equals 0.2 mm.
988// The method can be called before the first page is created. The value is
989// retained from page to page.
990func (f *Fpdf) SetLineWidth(width float64) {
991	f.setLineWidth(width)
992}
993
994func (f *Fpdf) setLineWidth(width float64) {
995	f.lineWidth = width
996	if f.page > 0 {
997		f.outf("%.2f w", width*f.k)
998	}
999}
1000
1001// GetLineWidth returns the current line thickness.
1002func (f *Fpdf) GetLineWidth() float64 {
1003	return f.lineWidth
1004}
1005
1006// SetLineCapStyle defines the line cap style. styleStr should be "butt",
1007// "round" or "square". A square style projects from the end of the line. The
1008// method can be called before the first page is created. The value is
1009// retained from page to page.
1010func (f *Fpdf) SetLineCapStyle(styleStr string) {
1011	var capStyle int
1012	switch styleStr {
1013	case "round":
1014		capStyle = 1
1015	case "square":
1016		capStyle = 2
1017	default:
1018		capStyle = 0
1019	}
1020	f.capStyle = capStyle
1021	if f.page > 0 {
1022		f.outf("%d J", f.capStyle)
1023	}
1024}
1025
1026// SetLineJoinStyle defines the line cap style. styleStr should be "miter",
1027// "round" or "bevel". The method can be called before the first page
1028// is created. The value is retained from page to page.
1029func (f *Fpdf) SetLineJoinStyle(styleStr string) {
1030	var joinStyle int
1031	switch styleStr {
1032	case "round":
1033		joinStyle = 1
1034	case "bevel":
1035		joinStyle = 2
1036	default:
1037		joinStyle = 0
1038	}
1039	f.joinStyle = joinStyle
1040	if f.page > 0 {
1041		f.outf("%d j", f.joinStyle)
1042	}
1043}
1044
1045// SetDashPattern sets the dash pattern that is used to draw lines. The
1046// dashArray elements are numbers that specify the lengths, in units
1047// established in New(), of alternating dashes and gaps. The dash phase
1048// specifies the distance into the dash pattern at which to start the dash. The
1049// dash pattern is retained from page to page. Call this method with an empty
1050// array to restore solid line drawing.
1051//
1052// The Beziergon() example demonstrates this method.
1053func (f *Fpdf) SetDashPattern(dashArray []float64, dashPhase float64) {
1054	scaled := make([]float64, len(dashArray))
1055	for i, value := range dashArray {
1056		scaled[i] = value * f.k
1057	}
1058	dashPhase *= f.k
1059
1060	f.dashArray = scaled
1061	f.dashPhase = dashPhase
1062	if f.page > 0 {
1063		f.outputDashPattern()
1064	}
1065
1066}
1067
1068func (f *Fpdf) outputDashPattern() {
1069	var buf bytes.Buffer
1070	buf.WriteByte('[')
1071	for i, value := range f.dashArray {
1072		if i > 0 {
1073			buf.WriteByte(' ')
1074		}
1075		buf.WriteString(strconv.FormatFloat(value, 'f', 2, 64))
1076	}
1077	buf.WriteString("] ")
1078	buf.WriteString(strconv.FormatFloat(f.dashPhase, 'f', 2, 64))
1079	buf.WriteString(" d")
1080	f.outbuf(&buf)
1081}
1082
1083// Line draws a line between points (x1, y1) and (x2, y2) using the current
1084// draw color, line width and cap style.
1085func (f *Fpdf) Line(x1, y1, x2, y2 float64) {
1086	f.outf("%.2f %.2f m %.2f %.2f l S", x1*f.k, (f.h-y1)*f.k, x2*f.k, (f.h-y2)*f.k)
1087}
1088
1089// fillDrawOp corrects path painting operators
1090func fillDrawOp(styleStr string) (opStr string) {
1091	switch strings.ToUpper(styleStr) {
1092	case "", "D":
1093		// Stroke the path.
1094		opStr = "S"
1095	case "F":
1096		// fill the path, using the nonzero winding number rule
1097		opStr = "f"
1098	case "F*":
1099		// fill the path, using the even-odd rule
1100		opStr = "f*"
1101	case "FD", "DF":
1102		// fill and then stroke the path, using the nonzero winding number rule
1103		opStr = "B"
1104	case "FD*", "DF*":
1105		// fill and then stroke the path, using the even-odd rule
1106		opStr = "B*"
1107	default:
1108		opStr = styleStr
1109	}
1110	return
1111}
1112
1113// Rect outputs a rectangle of width w and height h with the upper left corner
1114// positioned at point (x, y).
1115//
1116// It can be drawn (border only), filled (with no border) or both. styleStr can
1117// be "F" for filled, "D" for outlined only, or "DF" or "FD" for outlined and
1118// filled. An empty string will be replaced with "D". Drawing uses the current
1119// draw color and line width centered on the rectangle's perimeter. Filling
1120// uses the current fill color.
1121func (f *Fpdf) Rect(x, y, w, h float64, styleStr string) {
1122	f.outf("%.2f %.2f %.2f %.2f re %s", x*f.k, (f.h-y)*f.k, w*f.k, -h*f.k, fillDrawOp(styleStr))
1123}
1124
1125// RoundedRect outputs a rectangle of width w and height h with the upper left
1126// corner positioned at point (x, y). It can be drawn (border only), filled
1127// (with no border) or both. styleStr can be "F" for filled, "D" for outlined
1128// only, or "DF" or "FD" for outlined and filled. An empty string will be
1129// replaced with "D". Drawing uses the current draw color and line width
1130// centered on the rectangle's perimeter. Filling uses the current fill color.
1131// The rounded corners of the rectangle are specified by radius r. corners is a
1132// string that includes "1" to round the upper left corner, "2" to round the
1133// upper right corner, "3" to round the lower right corner, and "4" to round
1134// the lower left corner. The RoundedRect example demonstrates this method.
1135func (f *Fpdf) RoundedRect(x, y, w, h, r float64, corners string, stylestr string) {
1136	// This routine was adapted by Brigham Thompson from a script by Christophe Prugnaud
1137	var rTL, rTR, rBR, rBL float64 // zero means no rounded corner
1138	if strings.Contains(corners, "1") {
1139		rTL = r
1140	}
1141	if strings.Contains(corners, "2") {
1142		rTR = r
1143	}
1144	if strings.Contains(corners, "3") {
1145		rBR = r
1146	}
1147	if strings.Contains(corners, "4") {
1148		rBL = r
1149	}
1150	f.RoundedRectExt(x, y, w, h, rTL, rTR, rBR, rBL, stylestr)
1151}
1152
1153// RoundedRectExt behaves the same as RoundedRect() but supports a different
1154// radius for each corner. A zero radius means squared corner. See
1155// RoundedRect() for more details. This method is demonstrated in the
1156// RoundedRect() example.
1157func (f *Fpdf) RoundedRectExt(x, y, w, h, rTL, rTR, rBR, rBL float64, stylestr string) {
1158	f.roundedRectPath(x, y, w, h, rTL, rTR, rBR, rBL)
1159	f.out(fillDrawOp(stylestr))
1160}
1161
1162// Circle draws a circle centered on point (x, y) with radius r.
1163//
1164// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
1165// outlined and filled. An empty string will be replaced with "D". Drawing uses
1166// the current draw color and line width centered on the circle's perimeter.
1167// Filling uses the current fill color.
1168func (f *Fpdf) Circle(x, y, r float64, styleStr string) {
1169	f.Ellipse(x, y, r, r, 0, styleStr)
1170}
1171
1172// Ellipse draws an ellipse centered at point (x, y). rx and ry specify its
1173// horizontal and vertical radii.
1174//
1175// degRotate specifies the counter-clockwise angle in degrees that the ellipse
1176// will be rotated.
1177//
1178// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
1179// outlined and filled. An empty string will be replaced with "D". Drawing uses
1180// the current draw color and line width centered on the ellipse's perimeter.
1181// Filling uses the current fill color.
1182//
1183// The Circle() example demonstrates this method.
1184func (f *Fpdf) Ellipse(x, y, rx, ry, degRotate float64, styleStr string) {
1185	f.arc(x, y, rx, ry, degRotate, 0, 360, styleStr, false)
1186}
1187
1188// Polygon draws a closed figure defined by a series of vertices specified by
1189// points. The x and y fields of the points use the units established in New().
1190// The last point in the slice will be implicitly joined to the first to close
1191// the polygon.
1192//
1193// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
1194// outlined and filled. An empty string will be replaced with "D". Drawing uses
1195// the current draw color and line width centered on the ellipse's perimeter.
1196// Filling uses the current fill color.
1197func (f *Fpdf) Polygon(points []PointType, styleStr string) {
1198	if len(points) > 2 {
1199		for j, pt := range points {
1200			if j == 0 {
1201				f.point(pt.X, pt.Y)
1202			} else {
1203				f.outf("%.5f %.5f l ", pt.X*f.k, (f.h-pt.Y)*f.k)
1204			}
1205		}
1206		f.outf("%.5f %.5f l ", points[0].X*f.k, (f.h-points[0].Y)*f.k)
1207		f.DrawPath(styleStr)
1208	}
1209}
1210
1211// Beziergon draws a closed figure defined by a series of cubic Bézier curve
1212// segments. The first point in the slice defines the starting point of the
1213// figure. Each three following points p1, p2, p3 represent a curve segment to
1214// the point p3 using p1 and p2 as the Bézier control points.
1215//
1216// The x and y fields of the points use the units established in New().
1217//
1218// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
1219// outlined and filled. An empty string will be replaced with "D". Drawing uses
1220// the current draw color and line width centered on the ellipse's perimeter.
1221// Filling uses the current fill color.
1222func (f *Fpdf) Beziergon(points []PointType, styleStr string) {
1223
1224	// Thanks, Robert Lillack, for contributing this function.
1225
1226	if len(points) < 4 {
1227		return
1228	}
1229	f.point(points[0].XY())
1230
1231	points = points[1:]
1232	for len(points) >= 3 {
1233		cx0, cy0 := points[0].XY()
1234		cx1, cy1 := points[1].XY()
1235		x1, y1 := points[2].XY()
1236		f.curve(cx0, cy0, cx1, cy1, x1, y1)
1237		points = points[3:]
1238	}
1239
1240	f.DrawPath(styleStr)
1241}
1242
1243// point outputs current point
1244func (f *Fpdf) point(x, y float64) {
1245	f.outf("%.2f %.2f m", x*f.k, (f.h-y)*f.k)
1246}
1247
1248// curve outputs a single cubic Bézier curve segment from current point
1249func (f *Fpdf) curve(cx0, cy0, cx1, cy1, x, y float64) {
1250	// Thanks, Robert Lillack, for straightening this out
1251	f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c", cx0*f.k, (f.h-cy0)*f.k, cx1*f.k,
1252		(f.h-cy1)*f.k, x*f.k, (f.h-y)*f.k)
1253}
1254
1255// Curve draws a single-segment quadratic Bézier curve. The curve starts at
1256// the point (x0, y0) and ends at the point (x1, y1). The control point (cx,
1257// cy) specifies the curvature. At the start point, the curve is tangent to the
1258// straight line between the start point and the control point. At the end
1259// point, the curve is tangent to the straight line between the end point and
1260// the control point.
1261//
1262// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
1263// outlined and filled. An empty string will be replaced with "D". Drawing uses
1264// the current draw color, line width, and cap style centered on the curve's
1265// path. Filling uses the current fill color.
1266//
1267// The Circle() example demonstrates this method.
1268func (f *Fpdf) Curve(x0, y0, cx, cy, x1, y1 float64, styleStr string) {
1269	f.point(x0, y0)
1270	f.outf("%.5f %.5f %.5f %.5f v %s", cx*f.k, (f.h-cy)*f.k, x1*f.k, (f.h-y1)*f.k,
1271		fillDrawOp(styleStr))
1272}
1273
1274// CurveCubic draws a single-segment cubic Bézier curve. This routine performs
1275// the same function as CurveBezierCubic() but has a nonstandard argument order.
1276// It is retained to preserve backward compatibility.
1277func (f *Fpdf) CurveCubic(x0, y0, cx0, cy0, x1, y1, cx1, cy1 float64, styleStr string) {
1278	// f.point(x0, y0)
1279	// f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c %s", cx0*f.k, (f.h-cy0)*f.k,
1280	// cx1*f.k, (f.h-cy1)*f.k, x1*f.k, (f.h-y1)*f.k, fillDrawOp(styleStr))
1281	f.CurveBezierCubic(x0, y0, cx0, cy0, cx1, cy1, x1, y1, styleStr)
1282}
1283
1284// CurveBezierCubic draws a single-segment cubic Bézier curve. The curve starts at
1285// the point (x0, y0) and ends at the point (x1, y1). The control points (cx0,
1286// cy0) and (cx1, cy1) specify the curvature. At the start point, the curve is
1287// tangent to the straight line between the start point and the control point
1288// (cx0, cy0). At the end point, the curve is tangent to the straight line
1289// between the end point and the control point (cx1, cy1).
1290//
1291// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
1292// outlined and filled. An empty string will be replaced with "D". Drawing uses
1293// the current draw color, line width, and cap style centered on the curve's
1294// path. Filling uses the current fill color.
1295//
1296// This routine performs the same function as CurveCubic() but uses standard
1297// argument order.
1298//
1299// The Circle() example demonstrates this method.
1300func (f *Fpdf) CurveBezierCubic(x0, y0, cx0, cy0, cx1, cy1, x1, y1 float64, styleStr string) {
1301	f.point(x0, y0)
1302	f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c %s", cx0*f.k, (f.h-cy0)*f.k,
1303		cx1*f.k, (f.h-cy1)*f.k, x1*f.k, (f.h-y1)*f.k, fillDrawOp(styleStr))
1304}
1305
1306// Arc draws an elliptical arc centered at point (x, y). rx and ry specify its
1307// horizontal and vertical radii.
1308//
1309// degRotate specifies the angle that the arc will be rotated. degStart and
1310// degEnd specify the starting and ending angle of the arc. All angles are
1311// specified in degrees and measured counter-clockwise from the 3 o'clock
1312// position.
1313//
1314// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
1315// outlined and filled. An empty string will be replaced with "D". Drawing uses
1316// the current draw color, line width, and cap style centered on the arc's
1317// path. Filling uses the current fill color.
1318//
1319// The Circle() example demonstrates this method.
1320func (f *Fpdf) Arc(x, y, rx, ry, degRotate, degStart, degEnd float64, styleStr string) {
1321	f.arc(x, y, rx, ry, degRotate, degStart, degEnd, styleStr, false)
1322}
1323
1324// GetAlpha returns the alpha blending channel, which consists of the
1325// alpha transparency value and the blend mode. See SetAlpha for more
1326// details.
1327func (f *Fpdf) GetAlpha() (alpha float64, blendModeStr string) {
1328	return f.alpha, f.blendMode
1329}
1330
1331// SetAlpha sets the alpha blending channel. The blending effect applies to
1332// text, drawings and images.
1333//
1334// alpha must be a value between 0.0 (fully transparent) to 1.0 (fully opaque).
1335// Values outside of this range result in an error.
1336//
1337// blendModeStr must be one of "Normal", "Multiply", "Screen", "Overlay",
1338// "Darken", "Lighten", "ColorDodge", "ColorBurn","HardLight", "SoftLight",
1339// "Difference", "Exclusion", "Hue", "Saturation", "Color", or "Luminosity". An
1340// empty string is replaced with "Normal".
1341//
1342// To reset normal rendering after applying a blending mode, call this method
1343// with alpha set to 1.0 and blendModeStr set to "Normal".
1344func (f *Fpdf) SetAlpha(alpha float64, blendModeStr string) {
1345	if f.err != nil {
1346		return
1347	}
1348	var bl blendModeType
1349	switch blendModeStr {
1350	case "Normal", "Multiply", "Screen", "Overlay",
1351		"Darken", "Lighten", "ColorDodge", "ColorBurn", "HardLight", "SoftLight",
1352		"Difference", "Exclusion", "Hue", "Saturation", "Color", "Luminosity":
1353		bl.modeStr = blendModeStr
1354	case "":
1355		bl.modeStr = "Normal"
1356	default:
1357		f.err = fmt.Errorf("unrecognized blend mode \"%s\"", blendModeStr)
1358		return
1359	}
1360	if alpha < 0.0 || alpha > 1.0 {
1361		f.err = fmt.Errorf("alpha value (0.0 - 1.0) is out of range: %.3f", alpha)
1362		return
1363	}
1364	f.alpha = alpha
1365	f.blendMode = blendModeStr
1366	alphaStr := sprintf("%.3f", alpha)
1367	keyStr := sprintf("%s %s", alphaStr, blendModeStr)
1368	pos, ok := f.blendMap[keyStr]
1369	if !ok {
1370		pos = len(f.blendList) // at least 1
1371		f.blendList = append(f.blendList, blendModeType{alphaStr, alphaStr, blendModeStr, 0})
1372		f.blendMap[keyStr] = pos
1373	}
1374	f.outf("/GS%d gs", pos)
1375}
1376
1377func (f *Fpdf) gradientClipStart(x, y, w, h float64) {
1378	// Save current graphic state and set clipping area
1379	f.outf("q %.2f %.2f %.2f %.2f re W n", x*f.k, (f.h-y)*f.k, w*f.k, -h*f.k)
1380	// Set up transformation matrix for gradient
1381	f.outf("%.5f 0 0 %.5f %.5f %.5f cm", w*f.k, h*f.k, x*f.k, (f.h-(y+h))*f.k)
1382}
1383
1384func (f *Fpdf) gradientClipEnd() {
1385	// Restore previous graphic state
1386	f.out("Q")
1387}
1388
1389func (f *Fpdf) gradient(tp, r1, g1, b1, r2, g2, b2 int, x1, y1, x2, y2, r float64) {
1390	pos := len(f.gradientList)
1391	clr1 := rgbColorValue(r1, g1, b1, "", "")
1392	clr2 := rgbColorValue(r2, g2, b2, "", "")
1393	f.gradientList = append(f.gradientList, gradientType{tp, clr1.str, clr2.str,
1394		x1, y1, x2, y2, r, 0})
1395	f.outf("/Sh%d sh", pos)
1396}
1397
1398// LinearGradient draws a rectangular area with a blending of one color to
1399// another. The rectangle is of width w and height h. Its upper left corner is
1400// positioned at point (x, y).
1401//
1402// Each color is specified with three component values, one each for red, green
1403// and blue. The values range from 0 to 255. The first color is specified by
1404// (r1, g1, b1) and the second color by (r2, g2, b2).
1405//
1406// The blending is controlled with a gradient vector that uses normalized
1407// coordinates in which the lower left corner is position (0, 0) and the upper
1408// right corner is (1, 1). The vector's origin and destination are specified by
1409// the points (x1, y1) and (x2, y2). In a linear gradient, blending occurs
1410// perpendicularly to the vector. The vector does not necessarily need to be
1411// anchored on the rectangle edge. Color 1 is used up to the origin of the
1412// vector and color 2 is used beyond the vector's end point. Between the points
1413// the colors are gradually blended.
1414func (f *Fpdf) LinearGradient(x, y, w, h float64, r1, g1, b1, r2, g2, b2 int, x1, y1, x2, y2 float64) {
1415	f.gradientClipStart(x, y, w, h)
1416	f.gradient(2, r1, g1, b1, r2, g2, b2, x1, y1, x2, y2, 0)
1417	f.gradientClipEnd()
1418}
1419
1420// RadialGradient draws a rectangular area with a blending of one color to
1421// another. The rectangle is of width w and height h. Its upper left corner is
1422// positioned at point (x, y).
1423//
1424// Each color is specified with three component values, one each for red, green
1425// and blue. The values range from 0 to 255. The first color is specified by
1426// (r1, g1, b1) and the second color by (r2, g2, b2).
1427//
1428// The blending is controlled with a point and a circle, both specified with
1429// normalized coordinates in which the lower left corner of the rendered
1430// rectangle is position (0, 0) and the upper right corner is (1, 1). Color 1
1431// begins at the origin point specified by (x1, y1). Color 2 begins at the
1432// circle specified by the center point (x2, y2) and radius r. Colors are
1433// gradually blended from the origin to the circle. The origin and the circle's
1434// center do not necessarily have to coincide, but the origin must be within
1435// the circle to avoid rendering problems.
1436//
1437// The LinearGradient() example demonstrates this method.
1438func (f *Fpdf) RadialGradient(x, y, w, h float64, r1, g1, b1, r2, g2, b2 int, x1, y1, x2, y2, r float64) {
1439	f.gradientClipStart(x, y, w, h)
1440	f.gradient(3, r1, g1, b1, r2, g2, b2, x1, y1, x2, y2, r)
1441	f.gradientClipEnd()
1442}
1443
1444// ClipRect begins a rectangular clipping operation. The rectangle is of width
1445// w and height h. Its upper left corner is positioned at point (x, y). outline
1446// is true to draw a border with the current draw color and line width centered
1447// on the rectangle's perimeter. Only the outer half of the border will be
1448// shown. After calling this method, all rendering operations (for example,
1449// Image(), LinearGradient(), etc) will be clipped by the specified rectangle.
1450// Call ClipEnd() to restore unclipped operations.
1451//
1452// This ClipText() example demonstrates this method.
1453func (f *Fpdf) ClipRect(x, y, w, h float64, outline bool) {
1454	f.clipNest++
1455	f.outf("q %.2f %.2f %.2f %.2f re W %s", x*f.k, (f.h-y)*f.k, w*f.k, -h*f.k, strIf(outline, "S", "n"))
1456}
1457
1458// ClipText begins a clipping operation in which rendering is confined to the
1459// character string specified by txtStr. The origin (x, y) is on the left of
1460// the first character at the baseline. The current font is used. outline is
1461// true to draw a border with the current draw color and line width centered on
1462// the perimeters of the text characters. Only the outer half of the border
1463// will be shown. After calling this method, all rendering operations (for
1464// example, Image(), LinearGradient(), etc) will be clipped. Call ClipEnd() to
1465// restore unclipped operations.
1466func (f *Fpdf) ClipText(x, y float64, txtStr string, outline bool) {
1467	f.clipNest++
1468	f.outf("q BT %.5f %.5f Td %d Tr (%s) Tj ET", x*f.k, (f.h-y)*f.k, intIf(outline, 5, 7), f.escape(txtStr))
1469}
1470
1471func (f *Fpdf) clipArc(x1, y1, x2, y2, x3, y3 float64) {
1472	h := f.h
1473	f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c ", x1*f.k, (h-y1)*f.k,
1474		x2*f.k, (h-y2)*f.k, x3*f.k, (h-y3)*f.k)
1475}
1476
1477// ClipRoundedRect begins a rectangular clipping operation. The rectangle is of
1478// width w and height h. Its upper left corner is positioned at point (x, y).
1479// The rounded corners of the rectangle are specified by radius r. outline is
1480// true to draw a border with the current draw color and line width centered on
1481// the rectangle's perimeter. Only the outer half of the border will be shown.
1482// After calling this method, all rendering operations (for example, Image(),
1483// LinearGradient(), etc) will be clipped by the specified rectangle. Call
1484// ClipEnd() to restore unclipped operations.
1485//
1486// This ClipText() example demonstrates this method.
1487func (f *Fpdf) ClipRoundedRect(x, y, w, h, r float64, outline bool) {
1488	f.ClipRoundedRectExt(x, y, w, h, r, r, r, r, outline)
1489}
1490
1491// ClipRoundedRectExt behaves the same as ClipRoundedRect() but supports a
1492// different radius for each corner, given by rTL (top-left), rTR (top-right)
1493// rBR (bottom-right), rBL (bottom-left). See ClipRoundedRect() for more
1494// details. This method is demonstrated in the ClipText() example.
1495func (f *Fpdf) ClipRoundedRectExt(x, y, w, h, rTL, rTR, rBR, rBL float64, outline bool) {
1496	f.clipNest++
1497	f.roundedRectPath(x, y, w, h, rTL, rTR, rBR, rBL)
1498	f.outf(" W %s", strIf(outline, "S", "n"))
1499}
1500
1501// add a rectangle path with rounded corners.
1502// routine shared by RoundedRect() and ClipRoundedRect(), which add the
1503// drawing operation
1504func (f *Fpdf) roundedRectPath(x, y, w, h, rTL, rTR, rBR, rBL float64) {
1505	k := f.k
1506	hp := f.h
1507	myArc := (4.0 / 3.0) * (math.Sqrt2 - 1.0)
1508	f.outf("q %.5f %.5f m", (x+rTL)*k, (hp-y)*k)
1509	xc := x + w - rTR
1510	yc := y + rTR
1511	f.outf("%.5f %.5f l", xc*k, (hp-y)*k)
1512	if rTR != 0 {
1513		f.clipArc(xc+rTR*myArc, yc-rTR, xc+rTR, yc-rTR*myArc, xc+rTR, yc)
1514	}
1515	xc = x + w - rBR
1516	yc = y + h - rBR
1517	f.outf("%.5f %.5f l", (x+w)*k, (hp-yc)*k)
1518	if rBR != 0 {
1519		f.clipArc(xc+rBR, yc+rBR*myArc, xc+rBR*myArc, yc+rBR, xc, yc+rBR)
1520	}
1521	xc = x + rBL
1522	yc = y + h - rBL
1523	f.outf("%.5f %.5f l", xc*k, (hp-(y+h))*k)
1524	if rBL != 0 {
1525		f.clipArc(xc-rBL*myArc, yc+rBL, xc-rBL, yc+rBL*myArc, xc-rBL, yc)
1526	}
1527	xc = x + rTL
1528	yc = y + rTL
1529	f.outf("%.5f %.5f l", x*k, (hp-yc)*k)
1530	if rTL != 0 {
1531		f.clipArc(xc-rTL, yc-rTL*myArc, xc-rTL*myArc, yc-rTL, xc, yc-rTL)
1532	}
1533}
1534
1535// ClipEllipse begins an elliptical clipping operation. The ellipse is centered
1536// at (x, y). Its horizontal and vertical radii are specified by rx and ry.
1537// outline is true to draw a border with the current draw color and line width
1538// centered on the ellipse's perimeter. Only the outer half of the border will
1539// be shown. After calling this method, all rendering operations (for example,
1540// Image(), LinearGradient(), etc) will be clipped by the specified ellipse.
1541// Call ClipEnd() to restore unclipped operations.
1542//
1543// This ClipText() example demonstrates this method.
1544func (f *Fpdf) ClipEllipse(x, y, rx, ry float64, outline bool) {
1545	f.clipNest++
1546	lx := (4.0 / 3.0) * rx * (math.Sqrt2 - 1)
1547	ly := (4.0 / 3.0) * ry * (math.Sqrt2 - 1)
1548	k := f.k
1549	h := f.h
1550	f.outf("q %.5f %.5f m %.5f %.5f %.5f %.5f %.5f %.5f c",
1551		(x+rx)*k, (h-y)*k,
1552		(x+rx)*k, (h-(y-ly))*k,
1553		(x+lx)*k, (h-(y-ry))*k,
1554		x*k, (h-(y-ry))*k)
1555	f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c",
1556		(x-lx)*k, (h-(y-ry))*k,
1557		(x-rx)*k, (h-(y-ly))*k,
1558		(x-rx)*k, (h-y)*k)
1559	f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c",
1560		(x-rx)*k, (h-(y+ly))*k,
1561		(x-lx)*k, (h-(y+ry))*k,
1562		x*k, (h-(y+ry))*k)
1563	f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c W %s",
1564		(x+lx)*k, (h-(y+ry))*k,
1565		(x+rx)*k, (h-(y+ly))*k,
1566		(x+rx)*k, (h-y)*k,
1567		strIf(outline, "S", "n"))
1568}
1569
1570// ClipCircle begins a circular clipping operation. The circle is centered at
1571// (x, y) and has radius r. outline is true to draw a border with the current
1572// draw color and line width centered on the circle's perimeter. Only the outer
1573// half of the border will be shown. After calling this method, all rendering
1574// operations (for example, Image(), LinearGradient(), etc) will be clipped by
1575// the specified circle. Call ClipEnd() to restore unclipped operations.
1576//
1577// The ClipText() example demonstrates this method.
1578func (f *Fpdf) ClipCircle(x, y, r float64, outline bool) {
1579	f.ClipEllipse(x, y, r, r, outline)
1580}
1581
1582// ClipPolygon begins a clipping operation within a polygon. The figure is
1583// defined by a series of vertices specified by points. The x and y fields of
1584// the points use the units established in New(). The last point in the slice
1585// will be implicitly joined to the first to close the polygon. outline is true
1586// to draw a border with the current draw color and line width centered on the
1587// polygon's perimeter. Only the outer half of the border will be shown. After
1588// calling this method, all rendering operations (for example, Image(),
1589// LinearGradient(), etc) will be clipped by the specified polygon. Call
1590// ClipEnd() to restore unclipped operations.
1591//
1592// The ClipText() example demonstrates this method.
1593func (f *Fpdf) ClipPolygon(points []PointType, outline bool) {
1594	f.clipNest++
1595	var s fmtBuffer
1596	h := f.h
1597	k := f.k
1598	s.printf("q ")
1599	for j, pt := range points {
1600		s.printf("%.5f %.5f %s ", pt.X*k, (h-pt.Y)*k, strIf(j == 0, "m", "l"))
1601	}
1602	s.printf("h W %s", strIf(outline, "S", "n"))
1603	f.out(s.String())
1604}
1605
1606// ClipEnd ends a clipping operation that was started with a call to
1607// ClipRect(), ClipRoundedRect(), ClipText(), ClipEllipse(), ClipCircle() or
1608// ClipPolygon(). Clipping operations can be nested. The document cannot be
1609// successfully output while a clipping operation is active.
1610//
1611// The ClipText() example demonstrates this method.
1612func (f *Fpdf) ClipEnd() {
1613	if f.err == nil {
1614		if f.clipNest > 0 {
1615			f.clipNest--
1616			f.out("Q")
1617		} else {
1618			f.err = fmt.Errorf("error attempting to end clip operation out of sequence")
1619		}
1620	}
1621}
1622
1623// AddFont imports a TrueType, OpenType or Type1 font and makes it available.
1624// It is necessary to generate a font definition file first with the makefont
1625// utility. It is not necessary to call this function for the core PDF fonts
1626// (courier, helvetica, times, zapfdingbats).
1627//
1628// The JSON definition file (and the font file itself when embedding) must be
1629// present in the font directory. If it is not found, the error "Could not
1630// include font definition file" is set.
1631//
1632// family specifies the font family. The name can be chosen arbitrarily. If it
1633// is a standard family name, it will override the corresponding font. This
1634// string is used to subsequently set the font with the SetFont method.
1635//
1636// style specifies the font style. Acceptable values are (case insensitive) the
1637// empty string for regular style, "B" for bold, "I" for italic, or "BI" or
1638// "IB" for bold and italic combined.
1639//
1640// fileStr specifies the base name with ".json" extension of the font
1641// definition file to be added. The file will be loaded from the font directory
1642// specified in the call to New() or SetFontLocation().
1643func (f *Fpdf) AddFont(familyStr, styleStr, fileStr string) {
1644	f.addFont(fontFamilyEscape(familyStr), styleStr, fileStr, false)
1645}
1646
1647// AddUTF8Font imports a TrueType font with utf-8 symbols and makes it available.
1648// It is necessary to generate a font definition file first with the makefont
1649// utility. It is not necessary to call this function for the core PDF fonts
1650// (courier, helvetica, times, zapfdingbats).
1651//
1652// The JSON definition file (and the font file itself when embedding) must be
1653// present in the font directory. If it is not found, the error "Could not
1654// include font definition file" is set.
1655//
1656// family specifies the font family. The name can be chosen arbitrarily. If it
1657// is a standard family name, it will override the corresponding font. This
1658// string is used to subsequently set the font with the SetFont method.
1659//
1660// style specifies the font style. Acceptable values are (case insensitive) the
1661// empty string for regular style, "B" for bold, "I" for italic, or "BI" or
1662// "IB" for bold and italic combined.
1663//
1664// fileStr specifies the base name with ".json" extension of the font
1665// definition file to be added. The file will be loaded from the font directory
1666// specified in the call to New() or SetFontLocation().
1667func (f *Fpdf) AddUTF8Font(familyStr, styleStr, fileStr string) {
1668	f.addFont(fontFamilyEscape(familyStr), styleStr, fileStr, true)
1669}
1670
1671func (f *Fpdf) addFont(familyStr, styleStr, fileStr string, isUTF8 bool) {
1672	if fileStr == "" {
1673		if isUTF8 {
1674			fileStr = strings.Replace(familyStr, " ", "", -1) + strings.ToLower(styleStr) + ".ttf"
1675		} else {
1676			fileStr = strings.Replace(familyStr, " ", "", -1) + strings.ToLower(styleStr) + ".json"
1677		}
1678	}
1679	if isUTF8 {
1680		fontKey := getFontKey(familyStr, styleStr)
1681		_, ok := f.fonts[fontKey]
1682		if ok {
1683			return
1684		}
1685		var ttfStat os.FileInfo
1686		var err error
1687		fileStr = path.Join(f.fontpath, fileStr)
1688		ttfStat, err = os.Stat(fileStr)
1689		if err != nil {
1690			f.SetError(err)
1691			return
1692		}
1693		originalSize := ttfStat.Size()
1694		Type := "UTF8"
1695		var utf8Bytes []byte
1696		utf8Bytes, err = ioutil.ReadFile(fileStr)
1697		if err != nil {
1698			f.SetError(err)
1699			return
1700		}
1701		reader := fileReader{readerPosition: 0, array: utf8Bytes}
1702		utf8File := newUTF8Font(&reader)
1703		err = utf8File.parseFile()
1704		if err != nil {
1705			f.SetError(err)
1706			return
1707		}
1708
1709		desc := FontDescType{
1710			Ascent:       int(utf8File.Ascent),
1711			Descent:      int(utf8File.Descent),
1712			CapHeight:    utf8File.CapHeight,
1713			Flags:        utf8File.Flags,
1714			FontBBox:     utf8File.Bbox,
1715			ItalicAngle:  utf8File.ItalicAngle,
1716			StemV:        utf8File.StemV,
1717			MissingWidth: round(utf8File.DefaultWidth),
1718		}
1719
1720		var sbarr map[int]int
1721		if f.aliasNbPagesStr == "" {
1722			sbarr = makeSubsetRange(57)
1723		} else {
1724			sbarr = makeSubsetRange(32)
1725		}
1726		def := fontDefType{
1727			Tp:        Type,
1728			Name:      fontKey,
1729			Desc:      desc,
1730			Up:        int(round(utf8File.UnderlinePosition)),
1731			Ut:        round(utf8File.UnderlineThickness),
1732			Cw:        utf8File.CharWidths,
1733			usedRunes: sbarr,
1734			File:      fileStr,
1735			utf8File:  utf8File,
1736		}
1737		def.i, _ = generateFontID(def)
1738		f.fonts[fontKey] = def
1739		f.fontFiles[fontKey] = fontFileType{
1740			length1:  originalSize,
1741			fontType: "UTF8",
1742		}
1743		f.fontFiles[fileStr] = fontFileType{
1744			fontType: "UTF8",
1745		}
1746	} else {
1747		if f.fontLoader != nil {
1748			reader, err := f.fontLoader.Open(fileStr)
1749			if err == nil {
1750				f.AddFontFromReader(familyStr, styleStr, reader)
1751				if closer, ok := reader.(io.Closer); ok {
1752					closer.Close()
1753				}
1754				return
1755			}
1756		}
1757
1758		fileStr = path.Join(f.fontpath, fileStr)
1759		file, err := os.Open(fileStr)
1760		if err != nil {
1761			f.err = err
1762			return
1763		}
1764		defer file.Close()
1765
1766		f.AddFontFromReader(familyStr, styleStr, file)
1767	}
1768}
1769
1770func makeSubsetRange(end int) map[int]int {
1771	answer := make(map[int]int)
1772	for i := 0; i < end; i++ {
1773		answer[i] = 0
1774	}
1775	return answer
1776}
1777
1778// AddFontFromBytes imports a TrueType, OpenType or Type1 font from static
1779// bytes within the executable and makes it available for use in the generated
1780// document.
1781//
1782// family specifies the font family. The name can be chosen arbitrarily. If it
1783// is a standard family name, it will override the corresponding font. This
1784// string is used to subsequently set the font with the SetFont method.
1785//
1786// style specifies the font style. Acceptable values are (case insensitive) the
1787// empty string for regular style, "B" for bold, "I" for italic, or "BI" or
1788// "IB" for bold and italic combined.
1789//
1790// jsonFileBytes contain all bytes of JSON file.
1791//
1792// zFileBytes contain all bytes of Z file.
1793func (f *Fpdf) AddFontFromBytes(familyStr, styleStr string, jsonFileBytes, zFileBytes []byte) {
1794	f.addFontFromBytes(fontFamilyEscape(familyStr), styleStr, jsonFileBytes, zFileBytes, nil)
1795}
1796
1797// AddUTF8FontFromBytes  imports a TrueType font with utf-8 symbols from static
1798// bytes within the executable and makes it available for use in the generated
1799// document.
1800//
1801// family specifies the font family. The name can be chosen arbitrarily. If it
1802// is a standard family name, it will override the corresponding font. This
1803// string is used to subsequently set the font with the SetFont method.
1804//
1805// style specifies the font style. Acceptable values are (case insensitive) the
1806// empty string for regular style, "B" for bold, "I" for italic, or "BI" or
1807// "IB" for bold and italic combined.
1808//
1809// jsonFileBytes contain all bytes of JSON file.
1810//
1811// zFileBytes contain all bytes of Z file.
1812func (f *Fpdf) AddUTF8FontFromBytes(familyStr, styleStr string, utf8Bytes []byte) {
1813	f.addFontFromBytes(fontFamilyEscape(familyStr), styleStr, nil, nil, utf8Bytes)
1814}
1815
1816func (f *Fpdf) addFontFromBytes(familyStr, styleStr string, jsonFileBytes, zFileBytes, utf8Bytes []byte) {
1817	if f.err != nil {
1818		return
1819	}
1820
1821	// load font key
1822	var ok bool
1823	fontkey := getFontKey(familyStr, styleStr)
1824	_, ok = f.fonts[fontkey]
1825
1826	if ok {
1827		return
1828	}
1829
1830	if utf8Bytes != nil {
1831
1832		// if styleStr == "IB" {
1833		// 	styleStr = "BI"
1834		// }
1835
1836		Type := "UTF8"
1837		reader := fileReader{readerPosition: 0, array: utf8Bytes}
1838
1839		utf8File := newUTF8Font(&reader)
1840
1841		err := utf8File.parseFile()
1842		if err != nil {
1843			fmt.Printf("get metrics Error: %e\n", err)
1844			return
1845		}
1846		desc := FontDescType{
1847			Ascent:       int(utf8File.Ascent),
1848			Descent:      int(utf8File.Descent),
1849			CapHeight:    utf8File.CapHeight,
1850			Flags:        utf8File.Flags,
1851			FontBBox:     utf8File.Bbox,
1852			ItalicAngle:  utf8File.ItalicAngle,
1853			StemV:        utf8File.StemV,
1854			MissingWidth: round(utf8File.DefaultWidth),
1855		}
1856
1857		var sbarr map[int]int
1858		if f.aliasNbPagesStr == "" {
1859			sbarr = makeSubsetRange(57)
1860		} else {
1861			sbarr = makeSubsetRange(32)
1862		}
1863		def := fontDefType{
1864			Tp:        Type,
1865			Name:      fontkey,
1866			Desc:      desc,
1867			Up:        int(round(utf8File.UnderlinePosition)),
1868			Ut:        round(utf8File.UnderlineThickness),
1869			Cw:        utf8File.CharWidths,
1870			utf8File:  utf8File,
1871			usedRunes: sbarr,
1872		}
1873		def.i, _ = generateFontID(def)
1874		f.fonts[fontkey] = def
1875	} else {
1876		// load font definitions
1877		var info fontDefType
1878		err := json.Unmarshal(jsonFileBytes, &info)
1879
1880		if err != nil {
1881			f.err = err
1882		}
1883
1884		if f.err != nil {
1885			return
1886		}
1887
1888		if info.i, err = generateFontID(info); err != nil {
1889			f.err = err
1890			return
1891		}
1892
1893		// search existing encodings
1894		if len(info.Diff) > 0 {
1895			n := -1
1896
1897			for j, str := range f.diffs {
1898				if str == info.Diff {
1899					n = j + 1
1900					break
1901				}
1902			}
1903
1904			if n < 0 {
1905				f.diffs = append(f.diffs, info.Diff)
1906				n = len(f.diffs)
1907			}
1908
1909			info.DiffN = n
1910		}
1911
1912		// embed font
1913		if len(info.File) > 0 {
1914			if info.Tp == "TrueType" {
1915				f.fontFiles[info.File] = fontFileType{
1916					length1:  int64(info.OriginalSize),
1917					embedded: true,
1918					content:  zFileBytes,
1919				}
1920			} else {
1921				f.fontFiles[info.File] = fontFileType{
1922					length1:  int64(info.Size1),
1923					length2:  int64(info.Size2),
1924					embedded: true,
1925					content:  zFileBytes,
1926				}
1927			}
1928		}
1929
1930		f.fonts[fontkey] = info
1931	}
1932}
1933
1934// getFontKey is used by AddFontFromReader and GetFontDesc
1935func getFontKey(familyStr, styleStr string) string {
1936	familyStr = strings.ToLower(familyStr)
1937	styleStr = strings.ToUpper(styleStr)
1938	if styleStr == "IB" {
1939		styleStr = "BI"
1940	}
1941	return familyStr + styleStr
1942}
1943
1944// AddFontFromReader imports a TrueType, OpenType or Type1 font and makes it
1945// available using a reader that satisifies the io.Reader interface. See
1946// AddFont for details about familyStr and styleStr.
1947func (f *Fpdf) AddFontFromReader(familyStr, styleStr string, r io.Reader) {
1948	if f.err != nil {
1949		return
1950	}
1951	// dbg("Adding family [%s], style [%s]", familyStr, styleStr)
1952	familyStr = fontFamilyEscape(familyStr)
1953	var ok bool
1954	fontkey := getFontKey(familyStr, styleStr)
1955	_, ok = f.fonts[fontkey]
1956	if ok {
1957		return
1958	}
1959	var info fontDefType
1960	info = f.loadfont(r)
1961	if f.err != nil {
1962		return
1963	}
1964	if len(info.Diff) > 0 {
1965		// Search existing encodings
1966		n := -1
1967		for j, str := range f.diffs {
1968			if str == info.Diff {
1969				n = j + 1
1970				break
1971			}
1972		}
1973		if n < 0 {
1974			f.diffs = append(f.diffs, info.Diff)
1975			n = len(f.diffs)
1976		}
1977		info.DiffN = n
1978	}
1979	// dbg("font [%s], type [%s]", info.File, info.Tp)
1980	if len(info.File) > 0 {
1981		// Embedded font
1982		if info.Tp == "TrueType" {
1983			f.fontFiles[info.File] = fontFileType{length1: int64(info.OriginalSize)}
1984		} else {
1985			f.fontFiles[info.File] = fontFileType{length1: int64(info.Size1), length2: int64(info.Size2)}
1986		}
1987	}
1988	f.fonts[fontkey] = info
1989	return
1990}
1991
1992// GetFontDesc returns the font descriptor, which can be used for
1993// example to find the baseline of a font. If familyStr is empty
1994// current font descriptor will be returned.
1995// See FontDescType for documentation about the font descriptor.
1996// See AddFont for details about familyStr and styleStr.
1997func (f *Fpdf) GetFontDesc(familyStr, styleStr string) FontDescType {
1998	if familyStr == "" {
1999		return f.currentFont.Desc
2000	}
2001	return f.fonts[getFontKey(fontFamilyEscape(familyStr), styleStr)].Desc
2002}
2003
2004// SetFont sets the font used to print character strings. It is mandatory to
2005// call this method at least once before printing text or the resulting
2006// document will not be valid.
2007//
2008// The font can be either a standard one or a font added via the AddFont()
2009// method or AddFontFromReader() method. Standard fonts use the Windows
2010// encoding cp1252 (Western Europe).
2011//
2012// The method can be called before the first page is created and the font is
2013// kept from page to page. If you just wish to change the current font size, it
2014// is simpler to call SetFontSize().
2015//
2016// Note: the font definition file must be accessible. An error is set if the
2017// file cannot be read.
2018//
2019// familyStr specifies the font family. It can be either a name defined by
2020// AddFont(), AddFontFromReader() or one of the standard families (case
2021// insensitive): "Courier" for fixed-width, "Helvetica" or "Arial" for sans
2022// serif, "Times" for serif, "Symbol" or "ZapfDingbats" for symbolic.
2023//
2024// styleStr can be "B" (bold), "I" (italic), "U" (underscore), "S" (strike-out)
2025// or any combination. The default value (specified with an empty string) is
2026// regular. Bold and italic styles do not apply to Symbol and ZapfDingbats.
2027//
2028// size is the font size measured in points. The default value is the current
2029// size. If no size has been specified since the beginning of the document, the
2030// value taken is 12.
2031func (f *Fpdf) SetFont(familyStr, styleStr string, size float64) {
2032	// dbg("SetFont x %.2f, lMargin %.2f", f.x, f.lMargin)
2033
2034	if f.err != nil {
2035		return
2036	}
2037	// dbg("SetFont")
2038	familyStr = fontFamilyEscape(familyStr)
2039	var ok bool
2040	if familyStr == "" {
2041		familyStr = f.fontFamily
2042	} else {
2043		familyStr = strings.ToLower(familyStr)
2044	}
2045	styleStr = strings.ToUpper(styleStr)
2046	f.underline = strings.Contains(styleStr, "U")
2047	if f.underline {
2048		styleStr = strings.Replace(styleStr, "U", "", -1)
2049	}
2050	f.strikeout = strings.Contains(styleStr, "S")
2051	if f.strikeout {
2052		styleStr = strings.Replace(styleStr, "S", "", -1)
2053	}
2054	if styleStr == "IB" {
2055		styleStr = "BI"
2056	}
2057	if size == 0.0 {
2058		size = f.fontSizePt
2059	}
2060
2061	// Test if font is already loaded
2062	fontKey := familyStr + styleStr
2063	_, ok = f.fonts[fontKey]
2064	if !ok {
2065		// Test if one of the core fonts
2066		if familyStr == "arial" {
2067			familyStr = "helvetica"
2068		}
2069		_, ok = f.coreFonts[familyStr]
2070		if ok {
2071			if familyStr == "symbol" {
2072				familyStr = "zapfdingbats"
2073			}
2074			if familyStr == "zapfdingbats" {
2075				styleStr = ""
2076			}
2077			fontKey = familyStr + styleStr
2078			_, ok = f.fonts[fontKey]
2079			if !ok {
2080				rdr := f.coreFontReader(familyStr, styleStr)
2081				if f.err == nil {
2082					f.AddFontFromReader(familyStr, styleStr, rdr)
2083				}
2084				if f.err != nil {
2085					return
2086				}
2087			}
2088		} else {
2089			f.err = fmt.Errorf("undefined font: %s %s", familyStr, styleStr)
2090			return
2091		}
2092	}
2093	// Select it
2094	f.fontFamily = familyStr
2095	f.fontStyle = styleStr
2096	f.fontSizePt = size
2097	f.fontSize = size / f.k
2098	f.currentFont = f.fonts[fontKey]
2099	if f.currentFont.Tp == "UTF8" {
2100		f.isCurrentUTF8 = true
2101	} else {
2102		f.isCurrentUTF8 = false
2103	}
2104	if f.page > 0 {
2105		f.outf("BT /F%s %.2f Tf ET", f.currentFont.i, f.fontSizePt)
2106	}
2107	return
2108}
2109
2110// SetFontStyle sets the style of the current font. See also SetFont()
2111func (f *Fpdf) SetFontStyle(styleStr string) {
2112	f.SetFont(f.fontFamily, styleStr, f.fontSizePt)
2113}
2114
2115// SetFontSize defines the size of the current font. Size is specified in
2116// points (1/ 72 inch). See also SetFontUnitSize().
2117func (f *Fpdf) SetFontSize(size float64) {
2118	f.fontSizePt = size
2119	f.fontSize = size / f.k
2120	if f.page > 0 {
2121		f.outf("BT /F%s %.2f Tf ET", f.currentFont.i, f.fontSizePt)
2122	}
2123}
2124
2125// SetFontUnitSize defines the size of the current font. Size is specified in
2126// the unit of measure specified in New(). See also SetFontSize().
2127func (f *Fpdf) SetFontUnitSize(size float64) {
2128	f.fontSizePt = size * f.k
2129	f.fontSize = size
2130	if f.page > 0 {
2131		f.outf("BT /F%s %.2f Tf ET", f.currentFont.i, f.fontSizePt)
2132	}
2133}
2134
2135// GetFontSize returns the size of the current font in points followed by the
2136// size in the unit of measure specified in New(). The second value can be used
2137// as a line height value in drawing operations.
2138func (f *Fpdf) GetFontSize() (ptSize, unitSize float64) {
2139	return f.fontSizePt, f.fontSize
2140}
2141
2142// AddLink creates a new internal link and returns its identifier. An internal
2143// link is a clickable area which directs to another place within the document.
2144// The identifier can then be passed to Cell(), Write(), Image() or Link(). The
2145// destination is defined with SetLink().
2146func (f *Fpdf) AddLink() int {
2147	f.links = append(f.links, intLinkType{})
2148	return len(f.links) - 1
2149}
2150
2151// SetLink defines the page and position a link points to. See AddLink().
2152func (f *Fpdf) SetLink(link int, y float64, page int) {
2153	if y == -1 {
2154		y = f.y
2155	}
2156	if page == -1 {
2157		page = f.page
2158	}
2159	f.links[link] = intLinkType{page, y}
2160}
2161
2162// newLink adds a new clickable link on current page
2163func (f *Fpdf) newLink(x, y, w, h float64, link int, linkStr string) {
2164	// linkList, ok := f.pageLinks[f.page]
2165	// if !ok {
2166	// linkList = make([]linkType, 0, 8)
2167	// f.pageLinks[f.page] = linkList
2168	// }
2169	f.pageLinks[f.page] = append(f.pageLinks[f.page],
2170		linkType{x * f.k, f.hPt - y*f.k, w * f.k, h * f.k, link, linkStr})
2171}
2172
2173// Link puts a link on a rectangular area of the page. Text or image links are
2174// generally put via Cell(), Write() or Image(), but this method can be useful
2175// for instance to define a clickable area inside an image. link is the value
2176// returned by AddLink().
2177func (f *Fpdf) Link(x, y, w, h float64, link int) {
2178	f.newLink(x, y, w, h, link, "")
2179}
2180
2181// LinkString puts a link on a rectangular area of the page. Text or image
2182// links are generally put via Cell(), Write() or Image(), but this method can
2183// be useful for instance to define a clickable area inside an image. linkStr
2184// is the target URL.
2185func (f *Fpdf) LinkString(x, y, w, h float64, linkStr string) {
2186	f.newLink(x, y, w, h, 0, linkStr)
2187}
2188
2189// Bookmark sets a bookmark that will be displayed in a sidebar outline. txtStr
2190// is the title of the bookmark. level specifies the level of the bookmark in
2191// the outline; 0 is the top level, 1 is just below, and so on. y specifies the
2192// vertical position of the bookmark destination in the current page; -1
2193// indicates the current position.
2194func (f *Fpdf) Bookmark(txtStr string, level int, y float64) {
2195	if y == -1 {
2196		y = f.y
2197	}
2198	if f.isCurrentUTF8 {
2199		txtStr = utf8toutf16(txtStr)
2200	}
2201	f.outlines = append(f.outlines, outlineType{text: txtStr, level: level, y: y, p: f.PageNo(), prev: -1, last: -1, next: -1, first: -1})
2202}
2203
2204// Text prints a character string. The origin (x, y) is on the left of the
2205// first character at the baseline. This method permits a string to be placed
2206// precisely on the page, but it is usually easier to use Cell(), MultiCell()
2207// or Write() which are the standard methods to print text.
2208func (f *Fpdf) Text(x, y float64, txtStr string) {
2209	var txt2 string
2210	if f.isCurrentUTF8 {
2211		if f.isRTL {
2212			txtStr = reverseText(txtStr)
2213			x -= f.GetStringWidth(txtStr)
2214		}
2215		txt2 = f.escape(utf8toutf16(txtStr, false))
2216		for _, uni := range []rune(txtStr) {
2217			f.currentFont.usedRunes[int(uni)] = int(uni)
2218		}
2219	} else {
2220		txt2 = f.escape(txtStr)
2221	}
2222	s := sprintf("BT %.2f %.2f Td (%s) Tj ET", x*f.k, (f.h-y)*f.k, txt2)
2223	if f.underline && txtStr != "" {
2224		s += " " + f.dounderline(x, y, txtStr)
2225	}
2226	if f.strikeout && txtStr != "" {
2227		s += " " + f.dostrikeout(x, y, txtStr)
2228	}
2229	if f.colorFlag {
2230		s = sprintf("q %s %s Q", f.color.text.str, s)
2231	}
2232	f.out(s)
2233}
2234
2235// SetWordSpacing sets spacing between words of following text. See the
2236// WriteAligned() example for a demonstration of its use.
2237func (f *Fpdf) SetWordSpacing(space float64) {
2238	f.out(sprintf("%.5f Tw", space*f.k))
2239}
2240
2241// SetTextRenderingMode sets the rendering mode of following text.
2242// The mode can be as follows:
2243// 0: Fill text
2244// 1: Stroke text
2245// 2: Fill, then stroke text
2246// 3: Neither fill nor stroke text (invisible)
2247// 4: Fill text and add to path for clipping
2248// 5: Stroke text and add to path for clipping
2249// 6: Fills then stroke text and add to path for clipping
2250// 7: Add text to path for clipping
2251// This method is demonstrated in the SetTextRenderingMode example.
2252func (f *Fpdf) SetTextRenderingMode(mode int) {
2253	if mode >= 0 && mode <= 7 {
2254		f.out(sprintf("%d Tr", mode))
2255	}
2256}
2257
2258// SetAcceptPageBreakFunc allows the application to control where page breaks
2259// occur.
2260//
2261// fnc is an application function (typically a closure) that is called by the
2262// library whenever a page break condition is met. The break is issued if true
2263// is returned. The default implementation returns a value according to the
2264// mode selected by SetAutoPageBreak. The function provided should not be
2265// called by the application.
2266//
2267// See the example for SetLeftMargin() to see how this function can be used to
2268// manage multiple columns.
2269func (f *Fpdf) SetAcceptPageBreakFunc(fnc func() bool) {
2270	f.acceptPageBreak = fnc
2271}
2272
2273// CellFormat prints a rectangular cell with optional borders, background color
2274// and character string. The upper-left corner of the cell corresponds to the
2275// current position. The text can be aligned or centered. After the call, the
2276// current position moves to the right or to the next line. It is possible to
2277// put a link on the text.
2278//
2279// An error will be returned if a call to SetFont() has not already taken
2280// place before this method is called.
2281//
2282// If automatic page breaking is enabled and the cell goes beyond the limit, a
2283// page break is done before outputting.
2284//
2285// w and h specify the width and height of the cell. If w is 0, the cell
2286// extends up to the right margin. Specifying 0 for h will result in no output,
2287// but the current position will be advanced by w.
2288//
2289// txtStr specifies the text to display.
2290//
2291// borderStr specifies how the cell border will be drawn. An empty string
2292// indicates no border, "1" indicates a full border, and one or more of "L",
2293// "T", "R" and "B" indicate the left, top, right and bottom sides of the
2294// border.
2295//
2296// ln indicates where the current position should go after the call. Possible
2297// values are 0 (to the right), 1 (to the beginning of the next line), and 2
2298// (below). Putting 1 is equivalent to putting 0 and calling Ln() just after.
2299//
2300// alignStr specifies how the text is to be positioned within the cell.
2301// Horizontal alignment is controlled by including "L", "C" or "R" (left,
2302// center, right) in alignStr. Vertical alignment is controlled by including
2303// "T", "M", "B" or "A" (top, middle, bottom, baseline) in alignStr. The default
2304// alignment is left middle.
2305//
2306// fill is true to paint the cell background or false to leave it transparent.
2307//
2308// link is the identifier returned by AddLink() or 0 for no internal link.
2309//
2310// linkStr is a target URL or empty for no external link. A non--zero value for
2311// link takes precedence over linkStr.
2312func (f *Fpdf) CellFormat(w, h float64, txtStr, borderStr string, ln int,
2313	alignStr string, fill bool, link int, linkStr string) {
2314	// dbg("CellFormat. h = %.2f, borderStr = %s", h, borderStr)
2315	if f.err != nil {
2316		return
2317	}
2318
2319	if f.currentFont.Name == "" {
2320		f.err = fmt.Errorf("font has not been set; unable to render text")
2321		return
2322	}
2323
2324	borderStr = strings.ToUpper(borderStr)
2325	k := f.k
2326	if f.y+h > f.pageBreakTrigger && !f.inHeader && !f.inFooter && f.acceptPageBreak() {
2327		// Automatic page break
2328		x := f.x
2329		ws := f.ws
2330		// dbg("auto page break, x %.2f, ws %.2f", x, ws)
2331		if ws > 0 {
2332			f.ws = 0
2333			f.out("0 Tw")
2334		}
2335		f.AddPageFormat(f.curOrientation, f.curPageSize)
2336		if f.err != nil {
2337			return
2338		}
2339		f.x = x
2340		if ws > 0 {
2341			f.ws = ws
2342			f.outf("%.3f Tw", ws*k)
2343		}
2344	}
2345	if w == 0 {
2346		w = f.w - f.rMargin - f.x
2347	}
2348	var s fmtBuffer
2349	if fill || borderStr == "1" {
2350		var op string
2351		if fill {
2352			if borderStr == "1" {
2353				op = "B"
2354				// dbg("border is '1', fill")
2355			} else {
2356				op = "f"
2357				// dbg("border is empty, fill")
2358			}
2359		} else {
2360			// dbg("border is '1', no fill")
2361			op = "S"
2362		}
2363		/// dbg("(CellFormat) f.x %.2f f.k %.2f", f.x, f.k)
2364		s.printf("%.2f %.2f %.2f %.2f re %s ", f.x*k, (f.h-f.y)*k, w*k, -h*k, op)
2365	}
2366	if len(borderStr) > 0 && borderStr != "1" {
2367		// fmt.Printf("border is '%s', no fill\n", borderStr)
2368		x := f.x
2369		y := f.y
2370		left := x * k
2371		top := (f.h - y) * k
2372		right := (x + w) * k
2373		bottom := (f.h - (y + h)) * k
2374		if strings.Contains(borderStr, "L") {
2375			s.printf("%.2f %.2f m %.2f %.2f l S ", left, top, left, bottom)
2376		}
2377		if strings.Contains(borderStr, "T") {
2378			s.printf("%.2f %.2f m %.2f %.2f l S ", left, top, right, top)
2379		}
2380		if strings.Contains(borderStr, "R") {
2381			s.printf("%.2f %.2f m %.2f %.2f l S ", right, top, right, bottom)
2382		}
2383		if strings.Contains(borderStr, "B") {
2384			s.printf("%.2f %.2f m %.2f %.2f l S ", left, bottom, right, bottom)
2385		}
2386	}
2387	if len(txtStr) > 0 {
2388		var dx, dy float64
2389		// Horizontal alignment
2390		switch {
2391		case strings.Contains(alignStr, "R"):
2392			dx = w - f.cMargin - f.GetStringWidth(txtStr)
2393		case strings.Contains(alignStr, "C"):
2394			dx = (w - f.GetStringWidth(txtStr)) / 2
2395		default:
2396			dx = f.cMargin
2397		}
2398
2399		// Vertical alignment
2400		switch {
2401		case strings.Contains(alignStr, "T"):
2402			dy = (f.fontSize - h) / 2.0
2403		case strings.Contains(alignStr, "B"):
2404			dy = (h - f.fontSize) / 2.0
2405		case strings.Contains(alignStr, "A"):
2406			var descent float64
2407			d := f.currentFont.Desc
2408			if d.Descent == 0 {
2409				// not defined (standard font?), use average of 19%
2410				descent = -0.19 * f.fontSize
2411			} else {
2412				descent = float64(d.Descent) * f.fontSize / float64(d.Ascent-d.Descent)
2413			}
2414			dy = (h-f.fontSize)/2.0 - descent
2415		default:
2416			dy = 0
2417		}
2418		if f.colorFlag {
2419			s.printf("q %s ", f.color.text.str)
2420		}
2421		//If multibyte, Tw has no effect - do word spacing using an adjustment before each space
2422		if (f.ws != 0 || alignStr == "J") && f.isCurrentUTF8 { // && f.ws != 0
2423			if f.isRTL {
2424				txtStr = reverseText(txtStr)
2425			}
2426			wmax := int(math.Ceil((w - 2*f.cMargin) * 1000 / f.fontSize))
2427			for _, uni := range []rune(txtStr) {
2428				f.currentFont.usedRunes[int(uni)] = int(uni)
2429			}
2430			space := f.escape(utf8toutf16(" ", false))
2431			strSize := f.GetStringSymbolWidth(txtStr)
2432			s.printf("BT 0 Tw %.2f %.2f Td [", (f.x+dx)*k, (f.h-(f.y+.5*h+.3*f.fontSize))*k)
2433			t := strings.Split(txtStr, " ")
2434			shift := float64((wmax - strSize)) / float64(len(t)-1)
2435			numt := len(t)
2436			for i := 0; i < numt; i++ {
2437				tx := t[i]
2438				tx = "(" + f.escape(utf8toutf16(tx, false)) + ")"
2439				s.printf("%s ", tx)
2440				if (i + 1) < numt {
2441					s.printf("%.3f(%s) ", -shift, space)
2442				}
2443			}
2444			s.printf("] TJ ET")
2445		} else {
2446			var txt2 string
2447			if f.isCurrentUTF8 {
2448				if f.isRTL {
2449					txtStr = reverseText(txtStr)
2450				}
2451				txt2 = f.escape(utf8toutf16(txtStr, false))
2452				for _, uni := range []rune(txtStr) {
2453					f.currentFont.usedRunes[int(uni)] = int(uni)
2454				}
2455			} else {
2456
2457				txt2 = strings.Replace(txtStr, "\\", "\\\\", -1)
2458				txt2 = strings.Replace(txt2, "(", "\\(", -1)
2459				txt2 = strings.Replace(txt2, ")", "\\)", -1)
2460			}
2461			bt := (f.x + dx) * k
2462			td := (f.h - (f.y + dy + .5*h + .3*f.fontSize)) * k
2463			s.printf("BT %.2f %.2f Td (%s)Tj ET", bt, td, txt2)
2464			//BT %.2F %.2F Td (%s) Tj ET',(f.x+dx)*k,(f.h-(f.y+.5*h+.3*f.FontSize))*k,txt2);
2465		}
2466
2467		if f.underline {
2468			s.printf(" %s", f.dounderline(f.x+dx, f.y+dy+.5*h+.3*f.fontSize, txtStr))
2469		}
2470		if f.strikeout {
2471			s.printf(" %s", f.dostrikeout(f.x+dx, f.y+dy+.5*h+.3*f.fontSize, txtStr))
2472		}
2473		if f.colorFlag {
2474			s.printf(" Q")
2475		}
2476		if link > 0 || len(linkStr) > 0 {
2477			f.newLink(f.x+dx, f.y+dy+.5*h-.5*f.fontSize, f.GetStringWidth(txtStr), f.fontSize, link, linkStr)
2478		}
2479	}
2480	str := s.String()
2481	if len(str) > 0 {
2482		f.out(str)
2483	}
2484	f.lasth = h
2485	if ln > 0 {
2486		// Go to next line
2487		f.y += h
2488		if ln == 1 {
2489			f.x = f.lMargin
2490		}
2491	} else {
2492		f.x += w
2493	}
2494	return
2495}
2496
2497// Revert string to use in RTL languages
2498func reverseText(text string) string {
2499	oldText := []rune(text)
2500	newText := make([]rune, len(oldText))
2501	length := len(oldText) - 1
2502	for i, r := range oldText {
2503		newText[length-i] = r
2504	}
2505	return string(newText)
2506}
2507
2508// Cell is a simpler version of CellFormat with no fill, border, links or
2509// special alignment. The Cell_strikeout() example demonstrates this method.
2510func (f *Fpdf) Cell(w, h float64, txtStr string) {
2511	f.CellFormat(w, h, txtStr, "", 0, "L", false, 0, "")
2512}
2513
2514// Cellf is a simpler printf-style version of CellFormat with no fill, border,
2515// links or special alignment. See documentation for the fmt package for
2516// details on fmtStr and args.
2517func (f *Fpdf) Cellf(w, h float64, fmtStr string, args ...interface{}) {
2518	f.CellFormat(w, h, sprintf(fmtStr, args...), "", 0, "L", false, 0, "")
2519}
2520
2521// SplitLines splits text into several lines using the current font. Each line
2522// has its length limited to a maximum width given by w. This function can be
2523// used to determine the total height of wrapped text for vertical placement
2524// purposes.
2525//
2526// This method is useful for codepage-based fonts only. For UTF-8 encoded text,
2527// use SplitText().
2528//
2529// You can use MultiCell if you want to print a text on several lines in a
2530// simple way.
2531func (f *Fpdf) SplitLines(txt []byte, w float64) [][]byte {
2532	// Function contributed by Bruno Michel
2533	lines := [][]byte{}
2534	cw := f.currentFont.Cw
2535	wmax := int(math.Ceil((w - 2*f.cMargin) * 1000 / f.fontSize))
2536	s := bytes.Replace(txt, []byte("\r"), []byte{}, -1)
2537	nb := len(s)
2538	for nb > 0 && s[nb-1] == '\n' {
2539		nb--
2540	}
2541	s = s[0:nb]
2542	sep := -1
2543	i := 0
2544	j := 0
2545	l := 0
2546	for i < nb {
2547		c := s[i]
2548		l += cw[c]
2549		if c == ' ' || c == '\t' || c == '\n' {
2550			sep = i
2551		}
2552		if c == '\n' || l > wmax {
2553			if sep == -1 {
2554				if i == j {
2555					i++
2556				}
2557				sep = i
2558			} else {
2559				i = sep + 1
2560			}
2561			lines = append(lines, s[j:sep])
2562			sep = -1
2563			j = i
2564			l = 0
2565		} else {
2566			i++
2567		}
2568	}
2569	if i != j {
2570		lines = append(lines, s[j:i])
2571	}
2572	return lines
2573}
2574
2575// MultiCell supports printing text with line breaks. They can be automatic (as
2576// soon as the text reaches the right border of the cell) or explicit (via the
2577// \n character). As many cells as necessary are output, one below the other.
2578//
2579// Text can be aligned, centered or justified. The cell block can be framed and
2580// the background painted. See CellFormat() for more details.
2581//
2582// The current position after calling MultiCell() is the beginning of the next
2583// line, equivalent to calling CellFormat with ln equal to 1.
2584//
2585// w is the width of the cells. A value of zero indicates cells that reach to
2586// the right margin.
2587//
2588// h indicates the line height of each cell in the unit of measure specified in New().
2589//
2590// Note: this method has a known bug that treats UTF-8 fonts differently than
2591// non-UTF-8 fonts. With UTF-8 fonts, all trailing newlines in txtStr are
2592// removed. With a non-UTF-8 font, if txtStr has one or more trailing newlines,
2593// only the last is removed. In the next major module version, the UTF-8 logic
2594// will be changed to match the non-UTF-8 logic. To prepare for that change,
2595// applications that use UTF-8 fonts and depend on having all trailing newlines
2596// removed should call strings.TrimRight(txtStr, "\r\n") before calling this
2597// method.
2598func (f *Fpdf) MultiCell(w, h float64, txtStr, borderStr, alignStr string, fill bool) {
2599	if f.err != nil {
2600		return
2601	}
2602	// dbg("MultiCell")
2603	if alignStr == "" {
2604		alignStr = "J"
2605	}
2606	cw := f.currentFont.Cw
2607	if w == 0 {
2608		w = f.w - f.rMargin - f.x
2609	}
2610	wmax := int(math.Ceil((w - 2*f.cMargin) * 1000 / f.fontSize))
2611	s := strings.Replace(txtStr, "\r", "", -1)
2612	srune := []rune(s)
2613
2614	// remove extra line breaks
2615	var nb int
2616	if f.isCurrentUTF8 {
2617		nb = len(srune)
2618		for nb > 0 && srune[nb-1] == '\n' {
2619			nb--
2620		}
2621		srune = srune[0:nb]
2622	} else {
2623		nb = len(s)
2624		bytes2 := []byte(s)
2625
2626		// for nb > 0 && bytes2[nb-1] == '\n' {
2627
2628		// Prior to August 2019, if s ended with a newline, this code stripped it.
2629		// After that date, to be compatible with the UTF-8 code above, *all*
2630		// trailing newlines were removed. Because this regression caused at least
2631		// one application to break (see issue #333), the original behavior has been
2632		// reinstated with a caveat included in the documentation.
2633		if nb > 0 && bytes2[nb-1] == '\n' {
2634			nb--
2635		}
2636		s = s[0:nb]
2637	}
2638	// dbg("[%s]\n", s)
2639	var b, b2 string
2640	b = "0"
2641	if len(borderStr) > 0 {
2642		if borderStr == "1" {
2643			borderStr = "LTRB"
2644			b = "LRT"
2645			b2 = "LR"
2646		} else {
2647			b2 = ""
2648			if strings.Contains(borderStr, "L") {
2649				b2 += "L"
2650			}
2651			if strings.Contains(borderStr, "R") {
2652				b2 += "R"
2653			}
2654			if strings.Contains(borderStr, "T") {
2655				b = b2 + "T"
2656			} else {
2657				b = b2
2658			}
2659		}
2660	}
2661	sep := -1
2662	i := 0
2663	j := 0
2664	l := 0
2665	ls := 0
2666	ns := 0
2667	nl := 1
2668	for i < nb {
2669		// Get next character
2670		var c rune
2671		if f.isCurrentUTF8 {
2672			c = srune[i]
2673		} else {
2674			c = rune(s[i])
2675		}
2676		if c == '\n' {
2677			// Explicit line break
2678			if f.ws > 0 {
2679				f.ws = 0
2680				f.out("0 Tw")
2681			}
2682
2683			if f.isCurrentUTF8 {
2684				newAlignStr := alignStr
2685				if newAlignStr == "J" {
2686					if f.isRTL {
2687						newAlignStr = "R"
2688					} else {
2689						newAlignStr = "L"
2690					}
2691				}
2692				f.CellFormat(w, h, string(srune[j:i]), b, 2, newAlignStr, fill, 0, "")
2693			} else {
2694				f.CellFormat(w, h, s[j:i], b, 2, alignStr, fill, 0, "")
2695			}
2696			i++
2697			sep = -1
2698			j = i
2699			l = 0
2700			ns = 0
2701			nl++
2702			if len(borderStr) > 0 && nl == 2 {
2703				b = b2
2704			}
2705			continue
2706		}
2707		if c == ' ' || isChinese(c) {
2708			sep = i
2709			ls = l
2710			ns++
2711		}
2712		if int(c) >= len(cw) {
2713			f.err = fmt.Errorf("character outside the supported range: %s", string(c))
2714			return
2715		}
2716		if cw[int(c)] == 0 { //Marker width 0 used for missing symbols
2717			l += f.currentFont.Desc.MissingWidth
2718		} else if cw[int(c)] != 65535 { //Marker width 65535 used for zero width symbols
2719			l += cw[int(c)]
2720		}
2721		if l > wmax {
2722			// Automatic line break
2723			if sep == -1 {
2724				if i == j {
2725					i++
2726				}
2727				if f.ws > 0 {
2728					f.ws = 0
2729					f.out("0 Tw")
2730				}
2731				if f.isCurrentUTF8 {
2732					f.CellFormat(w, h, string(srune[j:i]), b, 2, alignStr, fill, 0, "")
2733				} else {
2734					f.CellFormat(w, h, s[j:i], b, 2, alignStr, fill, 0, "")
2735				}
2736			} else {
2737				if alignStr == "J" {
2738					if ns > 1 {
2739						f.ws = float64((wmax-ls)/1000) * f.fontSize / float64(ns-1)
2740					} else {
2741						f.ws = 0
2742					}
2743					f.outf("%.3f Tw", f.ws*f.k)
2744				}
2745				if f.isCurrentUTF8 {
2746					f.CellFormat(w, h, string(srune[j:sep]), b, 2, alignStr, fill, 0, "")
2747				} else {
2748					f.CellFormat(w, h, s[j:sep], b, 2, alignStr, fill, 0, "")
2749				}
2750				i = sep + 1
2751			}
2752			sep = -1
2753			j = i
2754			l = 0
2755			ns = 0
2756			nl++
2757			if len(borderStr) > 0 && nl == 2 {
2758				b = b2
2759			}
2760		} else {
2761			i++
2762		}
2763	}
2764	// Last chunk
2765	if f.ws > 0 {
2766		f.ws = 0
2767		f.out("0 Tw")
2768	}
2769	if len(borderStr) > 0 && strings.Contains(borderStr, "B") {
2770		b += "B"
2771	}
2772	if f.isCurrentUTF8 {
2773		if alignStr == "J" {
2774			if f.isRTL {
2775				alignStr = "R"
2776			} else {
2777				alignStr = ""
2778			}
2779		}
2780		f.CellFormat(w, h, string(srune[j:i]), b, 2, alignStr, fill, 0, "")
2781	} else {
2782		f.CellFormat(w, h, s[j:i], b, 2, alignStr, fill, 0, "")
2783	}
2784	f.x = f.lMargin
2785}
2786
2787// write outputs text in flowing mode
2788func (f *Fpdf) write(h float64, txtStr string, link int, linkStr string) {
2789	// dbg("Write")
2790	cw := f.currentFont.Cw
2791	w := f.w - f.rMargin - f.x
2792	wmax := (w - 2*f.cMargin) * 1000 / f.fontSize
2793	s := strings.Replace(txtStr, "\r", "", -1)
2794	var nb int
2795	if f.isCurrentUTF8 {
2796		nb = len([]rune(s))
2797		if nb == 1 && s == " " {
2798			f.x += f.GetStringWidth(s)
2799			return
2800		}
2801	} else {
2802		nb = len(s)
2803	}
2804	sep := -1
2805	i := 0
2806	j := 0
2807	l := 0.0
2808	nl := 1
2809	for i < nb {
2810		// Get next character
2811		var c rune
2812		if f.isCurrentUTF8 {
2813			c = []rune(s)[i]
2814		} else {
2815			c = rune(byte(s[i]))
2816		}
2817		if c == '\n' {
2818			// Explicit line break
2819			if f.isCurrentUTF8 {
2820				f.CellFormat(w, h, string([]rune(s)[j:i]), "", 2, "", false, link, linkStr)
2821			} else {
2822				f.CellFormat(w, h, s[j:i], "", 2, "", false, link, linkStr)
2823			}
2824			i++
2825			sep = -1
2826			j = i
2827			l = 0.0
2828			if nl == 1 {
2829				f.x = f.lMargin
2830				w = f.w - f.rMargin - f.x
2831				wmax = (w - 2*f.cMargin) * 1000 / f.fontSize
2832			}
2833			nl++
2834			continue
2835		}
2836		if c == ' ' {
2837			sep = i
2838		}
2839		l += float64(cw[int(c)])
2840		if l > wmax {
2841			// Automatic line break
2842			if sep == -1 {
2843				if f.x > f.lMargin {
2844					// Move to next line
2845					f.x = f.lMargin
2846					f.y += h
2847					w = f.w - f.rMargin - f.x
2848					wmax = (w - 2*f.cMargin) * 1000 / f.fontSize
2849					i++
2850					nl++
2851					continue
2852				}
2853				if i == j {
2854					i++
2855				}
2856				if f.isCurrentUTF8 {
2857					f.CellFormat(w, h, string([]rune(s)[j:i]), "", 2, "", false, link, linkStr)
2858				} else {
2859					f.CellFormat(w, h, s[j:i], "", 2, "", false, link, linkStr)
2860				}
2861			} else {
2862				if f.isCurrentUTF8 {
2863					f.CellFormat(w, h, string([]rune(s)[j:sep]), "", 2, "", false, link, linkStr)
2864				} else {
2865					f.CellFormat(w, h, s[j:sep], "", 2, "", false, link, linkStr)
2866				}
2867				i = sep + 1
2868			}
2869			sep = -1
2870			j = i
2871			l = 0.0
2872			if nl == 1 {
2873				f.x = f.lMargin
2874				w = f.w - f.rMargin - f.x
2875				wmax = (w - 2*f.cMargin) * 1000 / f.fontSize
2876			}
2877			nl++
2878		} else {
2879			i++
2880		}
2881	}
2882	// Last chunk
2883	if i != j {
2884		if f.isCurrentUTF8 {
2885			f.CellFormat(l/1000*f.fontSize, h, string([]rune(s)[j:]), "", 0, "", false, link, linkStr)
2886		} else {
2887			f.CellFormat(l/1000*f.fontSize, h, s[j:], "", 0, "", false, link, linkStr)
2888		}
2889	}
2890}
2891
2892// Write prints text from the current position. When the right margin is
2893// reached (or the \n character is met) a line break occurs and text continues
2894// from the left margin. Upon method exit, the current position is left just at
2895// the end of the text.
2896//
2897// It is possible to put a link on the text.
2898//
2899// h indicates the line height in the unit of measure specified in New().
2900func (f *Fpdf) Write(h float64, txtStr string) {
2901	f.write(h, txtStr, 0, "")
2902}
2903
2904// Writef is like Write but uses printf-style formatting. See the documentation
2905// for package fmt for more details on fmtStr and args.
2906func (f *Fpdf) Writef(h float64, fmtStr string, args ...interface{}) {
2907	f.write(h, sprintf(fmtStr, args...), 0, "")
2908}
2909
2910// WriteLinkString writes text that when clicked launches an external URL. See
2911// Write() for argument details.
2912func (f *Fpdf) WriteLinkString(h float64, displayStr, targetStr string) {
2913	f.write(h, displayStr, 0, targetStr)
2914}
2915
2916// WriteLinkID writes text that when clicked jumps to another location in the
2917// PDF. linkID is an identifier returned by AddLink(). See Write() for argument
2918// details.
2919func (f *Fpdf) WriteLinkID(h float64, displayStr string, linkID int) {
2920	f.write(h, displayStr, linkID, "")
2921}
2922
2923// WriteAligned is an implementation of Write that makes it possible to align
2924// text.
2925//
2926// width indicates the width of the box the text will be drawn in. This is in
2927// the unit of measure specified in New(). If it is set to 0, the bounding box
2928//of the page will be taken (pageWidth - leftMargin - rightMargin).
2929//
2930// lineHeight indicates the line height in the unit of measure specified in
2931// New().
2932//
2933// alignStr sees to horizontal alignment of the given textStr. The options are
2934// "L", "C" and "R" (Left, Center, Right). The default is "L".
2935func (f *Fpdf) WriteAligned(width, lineHeight float64, textStr, alignStr string) {
2936	lMargin, _, rMargin, _ := f.GetMargins()
2937
2938	pageWidth, _ := f.GetPageSize()
2939	if width == 0 {
2940		width = pageWidth - (lMargin + rMargin)
2941	}
2942
2943	var lines []string
2944
2945	if f.isCurrentUTF8 {
2946		lines = f.SplitText(textStr, width)
2947	} else {
2948		for _, line := range f.SplitLines([]byte(textStr), width) {
2949			lines = append(lines, string(line))
2950		}
2951	}
2952
2953	for _, lineBt := range lines {
2954		lineStr := string(lineBt)
2955		lineWidth := f.GetStringWidth(lineStr)
2956
2957		switch alignStr {
2958		case "C":
2959			f.SetLeftMargin(lMargin + ((width - lineWidth) / 2))
2960			f.Write(lineHeight, lineStr)
2961			f.SetLeftMargin(lMargin)
2962		case "R":
2963			f.SetLeftMargin(lMargin + (width - lineWidth) - 2.01*f.cMargin)
2964			f.Write(lineHeight, lineStr)
2965			f.SetLeftMargin(lMargin)
2966		default:
2967			f.SetRightMargin(pageWidth - lMargin - width)
2968			f.Write(lineHeight, lineStr)
2969			f.SetRightMargin(rMargin)
2970		}
2971	}
2972}
2973
2974// Ln performs a line break. The current abscissa goes back to the left margin
2975// and the ordinate increases by the amount passed in parameter. A negative
2976// value of h indicates the height of the last printed cell.
2977//
2978// This method is demonstrated in the example for MultiCell.
2979func (f *Fpdf) Ln(h float64) {
2980	f.x = f.lMargin
2981	if h < 0 {
2982		f.y += f.lasth
2983	} else {
2984		f.y += h
2985	}
2986}
2987
2988// ImageTypeFromMime returns the image type used in various image-related
2989// functions (for example, Image()) that is associated with the specified MIME
2990// type. For example, "jpg" is returned if mimeStr is "image/jpeg". An error is
2991// set if the specified MIME type is not supported.
2992func (f *Fpdf) ImageTypeFromMime(mimeStr string) (tp string) {
2993	switch mimeStr {
2994	case "image/png":
2995		tp = "png"
2996	case "image/jpg":
2997		tp = "jpg"
2998	case "image/jpeg":
2999		tp = "jpg"
3000	case "image/gif":
3001		tp = "gif"
3002	default:
3003		f.SetErrorf("unsupported image type: %s", mimeStr)
3004	}
3005	return
3006}
3007
3008func (f *Fpdf) imageOut(info *ImageInfoType, x, y, w, h float64, allowNegativeX, flow bool, link int, linkStr string) {
3009	// Automatic width and height calculation if needed
3010	if w == 0 && h == 0 {
3011		// Put image at 96 dpi
3012		w = -96
3013		h = -96
3014	}
3015	if w == -1 {
3016		// Set image width to whatever value for dpi we read
3017		// from the image or that was set manually
3018		w = -info.dpi
3019	}
3020	if h == -1 {
3021		// Set image height to whatever value for dpi we read
3022		// from the image or that was set manually
3023		h = -info.dpi
3024	}
3025	if w < 0 {
3026		w = -info.w * 72.0 / w / f.k
3027	}
3028	if h < 0 {
3029		h = -info.h * 72.0 / h / f.k
3030	}
3031	if w == 0 {
3032		w = h * info.w / info.h
3033	}
3034	if h == 0 {
3035		h = w * info.h / info.w
3036	}
3037	// Flowing mode
3038	if flow {
3039		if f.y+h > f.pageBreakTrigger && !f.inHeader && !f.inFooter && f.acceptPageBreak() {
3040			// Automatic page break
3041			x2 := f.x
3042			f.AddPageFormat(f.curOrientation, f.curPageSize)
3043			if f.err != nil {
3044				return
3045			}
3046			f.x = x2
3047		}
3048		y = f.y
3049		f.y += h
3050	}
3051	if !allowNegativeX {
3052		if x < 0 {
3053			x = f.x
3054		}
3055	}
3056	// dbg("h %.2f", h)
3057	// q 85.04 0 0 NaN 28.35 NaN cm /I2 Do Q
3058	f.outf("q %.5f 0 0 %.5f %.5f %.5f cm /I%s Do Q", w*f.k, h*f.k, x*f.k, (f.h-(y+h))*f.k, info.i)
3059	if link > 0 || len(linkStr) > 0 {
3060		f.newLink(x, y, w, h, link, linkStr)
3061	}
3062}
3063
3064// Image puts a JPEG, PNG or GIF image in the current page.
3065//
3066// Deprecated in favor of ImageOptions -- see that function for
3067// details on the behavior of arguments
3068func (f *Fpdf) Image(imageNameStr string, x, y, w, h float64, flow bool, tp string, link int, linkStr string) {
3069	options := ImageOptions{
3070		ReadDpi:   false,
3071		ImageType: tp,
3072	}
3073	f.ImageOptions(imageNameStr, x, y, w, h, flow, options, link, linkStr)
3074}
3075
3076// ImageOptions puts a JPEG, PNG or GIF image in the current page. The size it
3077// will take on the page can be specified in different ways. If both w and h
3078// are 0, the image is rendered at 96 dpi. If either w or h is zero, it will be
3079// calculated from the other dimension so that the aspect ratio is maintained.
3080// If w and/or h are -1, the dpi for that dimension will be read from the
3081// ImageInfoType object. PNG files can contain dpi information, and if present,
3082// this information will be populated in the ImageInfoType object and used in
3083// Width, Height, and Extent calculations. Otherwise, the SetDpi function can
3084// be used to change the dpi from the default of 72.
3085//
3086// If w and h are any other negative value, their absolute values
3087// indicate their dpi extents.
3088//
3089// Supported JPEG formats are 24 bit, 32 bit and gray scale. Supported PNG
3090// formats are 24 bit, indexed color, and 8 bit indexed gray scale. If a GIF
3091// image is animated, only the first frame is rendered. Transparency is
3092// supported. It is possible to put a link on the image.
3093//
3094// imageNameStr may be the name of an image as registered with a call to either
3095// RegisterImageReader() or RegisterImage(). In the first case, the image is
3096// loaded using an io.Reader. This is generally useful when the image is
3097// obtained from some other means than as a disk-based file. In the second
3098// case, the image is loaded as a file. Alternatively, imageNameStr may
3099// directly specify a sufficiently qualified filename.
3100//
3101// However the image is loaded, if it is used more than once only one copy is
3102// embedded in the file.
3103//
3104// If x is negative, the current abscissa is used.
3105//
3106// If flow is true, the current y value is advanced after placing the image and
3107// a page break may be made if necessary.
3108//
3109// If link refers to an internal page anchor (that is, it is non-zero; see
3110// AddLink()), the image will be a clickable internal link. Otherwise, if
3111// linkStr specifies a URL, the image will be a clickable external link.
3112func (f *Fpdf) ImageOptions(imageNameStr string, x, y, w, h float64, flow bool, options ImageOptions, link int, linkStr string) {
3113	if f.err != nil {
3114		return
3115	}
3116	info := f.RegisterImageOptions(imageNameStr, options)
3117	if f.err != nil {
3118		return
3119	}
3120	f.imageOut(info, x, y, w, h, options.AllowNegativePosition, flow, link, linkStr)
3121	return
3122}
3123
3124// RegisterImageReader registers an image, reading it from Reader r, adding it
3125// to the PDF file but not adding it to the page.
3126//
3127// This function is now deprecated in favor of RegisterImageOptionsReader
3128func (f *Fpdf) RegisterImageReader(imgName, tp string, r io.Reader) (info *ImageInfoType) {
3129	options := ImageOptions{
3130		ReadDpi:   false,
3131		ImageType: tp,
3132	}
3133	return f.RegisterImageOptionsReader(imgName, options, r)
3134}
3135
3136// ImageOptions provides a place to hang any options we want to use while
3137// parsing an image.
3138//
3139// ImageType's possible values are (case insensitive):
3140// "JPG", "JPEG", "PNG" and "GIF". If empty, the type is inferred from
3141// the file extension.
3142//
3143// ReadDpi defines whether to attempt to automatically read the image
3144// dpi information from the image file. Normally, this should be set
3145// to true (understanding that not all images will have this info
3146// available). However, for backwards compatibility with previous
3147// versions of the API, it defaults to false.
3148//
3149// AllowNegativePosition can be set to true in order to prevent the default
3150// coercion of negative x values to the current x position.
3151type ImageOptions struct {
3152	ImageType             string
3153	ReadDpi               bool
3154	AllowNegativePosition bool
3155}
3156
3157// RegisterImageOptionsReader registers an image, reading it from Reader r, adding it
3158// to the PDF file but not adding it to the page. Use Image() with the same
3159// name to add the image to the page. Note that tp should be specified in this
3160// case.
3161//
3162// See Image() for restrictions on the image and the options parameters.
3163func (f *Fpdf) RegisterImageOptionsReader(imgName string, options ImageOptions, r io.Reader) (info *ImageInfoType) {
3164	// Thanks, Ivan Daniluk, for generalizing this code to use the Reader interface.
3165	if f.err != nil {
3166		return
3167	}
3168	info, ok := f.images[imgName]
3169	if ok {
3170		return
3171	}
3172
3173	// First use of this image, get info
3174	if options.ImageType == "" {
3175		f.err = fmt.Errorf("image type should be specified if reading from custom reader")
3176		return
3177	}
3178	options.ImageType = strings.ToLower(options.ImageType)
3179	if options.ImageType == "jpeg" {
3180		options.ImageType = "jpg"
3181	}
3182	switch options.ImageType {
3183	case "jpg":
3184		info = f.parsejpg(r)
3185	case "png":
3186		info = f.parsepng(r, options.ReadDpi)
3187	case "gif":
3188		info = f.parsegif(r)
3189	default:
3190		f.err = fmt.Errorf("unsupported image type: %s", options.ImageType)
3191	}
3192	if f.err != nil {
3193		return
3194	}
3195
3196	if info.i, f.err = generateImageID(info); f.err != nil {
3197		return
3198	}
3199	f.images[imgName] = info
3200
3201	return
3202}
3203
3204// RegisterImage registers an image, adding it to the PDF file but not adding
3205// it to the page. Use Image() with the same filename to add the image to the
3206// page. Note that Image() calls this function, so this function is only
3207// necessary if you need information about the image before placing it.
3208//
3209// This function is now deprecated in favor of RegisterImageOptions.
3210// See Image() for restrictions on the image and the "tp" parameters.
3211func (f *Fpdf) RegisterImage(fileStr, tp string) (info *ImageInfoType) {
3212	options := ImageOptions{
3213		ReadDpi:   false,
3214		ImageType: tp,
3215	}
3216	return f.RegisterImageOptions(fileStr, options)
3217}
3218
3219// RegisterImageOptions registers an image, adding it to the PDF file but not
3220// adding it to the page. Use Image() with the same filename to add the image
3221// to the page. Note that Image() calls this function, so this function is only
3222// necessary if you need information about the image before placing it. See
3223// Image() for restrictions on the image and the "tp" parameters.
3224func (f *Fpdf) RegisterImageOptions(fileStr string, options ImageOptions) (info *ImageInfoType) {
3225	info, ok := f.images[fileStr]
3226	if ok {
3227		return
3228	}
3229
3230	file, err := os.Open(fileStr)
3231	if err != nil {
3232		f.err = err
3233		return
3234	}
3235	defer file.Close()
3236
3237	// First use of this image, get info
3238	if options.ImageType == "" {
3239		pos := strings.LastIndex(fileStr, ".")
3240		if pos < 0 {
3241			f.err = fmt.Errorf("image file has no extension and no type was specified: %s", fileStr)
3242			return
3243		}
3244		options.ImageType = fileStr[pos+1:]
3245	}
3246
3247	return f.RegisterImageOptionsReader(fileStr, options, file)
3248}
3249
3250// GetImageInfo returns information about the registered image specified by
3251// imageStr. If the image has not been registered, nil is returned. The
3252// internal error is not modified by this method.
3253func (f *Fpdf) GetImageInfo(imageStr string) (info *ImageInfoType) {
3254	return f.images[imageStr]
3255}
3256
3257// ImportObjects imports objects from gofpdi into current document
3258func (f *Fpdf) ImportObjects(objs map[string][]byte) {
3259	for k, v := range objs {
3260		f.importedObjs[k] = v
3261	}
3262}
3263
3264// ImportObjPos imports object hash positions from gofpdi
3265func (f *Fpdf) ImportObjPos(objPos map[string]map[int]string) {
3266	for k, v := range objPos {
3267		f.importedObjPos[k] = v
3268	}
3269}
3270
3271// putImportedTemplates writes the imported template objects to the PDF
3272func (f *Fpdf) putImportedTemplates() {
3273	nOffset := f.n + 1
3274
3275	// keep track of list of sha1 hashes (to be replaced with integers)
3276	objsIDHash := make([]string, len(f.importedObjs))
3277
3278	// actual object data with new id
3279	objsIDData := make([][]byte, len(f.importedObjs))
3280
3281	// Populate hash slice and data slice
3282	i := 0
3283	for k, v := range f.importedObjs {
3284		objsIDHash[i] = k
3285		objsIDData[i] = v
3286
3287		i++
3288	}
3289
3290	// Populate a lookup table to get an object id from a hash
3291	hashToObjID := make(map[string]int, len(f.importedObjs))
3292	for i = 0; i < len(objsIDHash); i++ {
3293		hashToObjID[objsIDHash[i]] = i + nOffset
3294	}
3295
3296	// Now, replace hashes inside data with %040d object id
3297	for i = 0; i < len(objsIDData); i++ {
3298		// get hash
3299		hash := objsIDHash[i]
3300
3301		for pos, h := range f.importedObjPos[hash] {
3302			// Convert object id into a 40 character string padded with spaces
3303			objIDPadded := fmt.Sprintf("%40s", fmt.Sprintf("%d", hashToObjID[h]))
3304
3305			// Convert objIDPadded into []byte
3306			objIDBytes := []byte(objIDPadded)
3307
3308			// Replace sha1 hash with object id padded
3309			for j := pos; j < pos+40; j++ {
3310				objsIDData[i][j] = objIDBytes[j-pos]
3311			}
3312		}
3313
3314		// Save objsIDHash so that procset dictionary has the correct object ids
3315		f.importedTplIDs[hash] = i + nOffset
3316	}
3317
3318	// Now, put objects
3319	for i = 0; i < len(objsIDData); i++ {
3320		f.newobj()
3321		f.out(string(objsIDData[i]))
3322	}
3323}
3324
3325// UseImportedTemplate uses imported template from gofpdi. It draws imported
3326// PDF page onto page.
3327func (f *Fpdf) UseImportedTemplate(tplName string, scaleX float64, scaleY float64, tX float64, tY float64) {
3328	f.outf("q 0 J 1 w 0 j 0 G 0 g q %.4F 0 0 %.4F %.4F %.4F cm %s Do Q Q\n", scaleX*f.k, scaleY*f.k, tX*f.k, (tY+f.h)*f.k, tplName)
3329}
3330
3331// ImportTemplates imports gofpdi template names into importedTplObjs for
3332// inclusion in the procset dictionary
3333func (f *Fpdf) ImportTemplates(tpls map[string]string) {
3334	for tplName, tplID := range tpls {
3335		f.importedTplObjs[tplName] = tplID
3336	}
3337}
3338
3339// GetConversionRatio returns the conversion ratio based on the unit given when
3340// creating the PDF.
3341func (f *Fpdf) GetConversionRatio() float64 {
3342	return f.k
3343}
3344
3345// GetXY returns the abscissa and ordinate of the current position.
3346//
3347// Note: the value returned for the abscissa will be affected by the current
3348// cell margin. To account for this, you may need to either add the value
3349// returned by GetCellMargin() to it or call SetCellMargin(0) to remove the
3350// cell margin.
3351func (f *Fpdf) GetXY() (float64, float64) {
3352	return f.x, f.y
3353}
3354
3355// GetX returns the abscissa of the current position.
3356//
3357// Note: the value returned will be affected by the current cell margin. To
3358// account for this, you may need to either add the value returned by
3359// GetCellMargin() to it or call SetCellMargin(0) to remove the cell margin.
3360func (f *Fpdf) GetX() float64 {
3361	return f.x
3362}
3363
3364// SetX defines the abscissa of the current position. If the passed value is
3365// negative, it is relative to the right of the page.
3366func (f *Fpdf) SetX(x float64) {
3367	if x >= 0 {
3368		f.x = x
3369	} else {
3370		f.x = f.w + x
3371	}
3372}
3373
3374// GetY returns the ordinate of the current position.
3375func (f *Fpdf) GetY() float64 {
3376	return f.y
3377}
3378
3379// SetY moves the current abscissa back to the left margin and sets the
3380// ordinate. If the passed value is negative, it is relative to the bottom of
3381// the page.
3382func (f *Fpdf) SetY(y float64) {
3383	// dbg("SetY x %.2f, lMargin %.2f", f.x, f.lMargin)
3384	f.x = f.lMargin
3385	if y >= 0 {
3386		f.y = y
3387	} else {
3388		f.y = f.h + y
3389	}
3390}
3391
3392// SetHomeXY is a convenience method that sets the current position to the left
3393// and top margins.
3394func (f *Fpdf) SetHomeXY() {
3395	f.SetY(f.tMargin)
3396	f.SetX(f.lMargin)
3397}
3398
3399// SetXY defines the abscissa and ordinate of the current position. If the
3400// passed values are negative, they are relative respectively to the right and
3401// bottom of the page.
3402func (f *Fpdf) SetXY(x, y float64) {
3403	f.SetY(y)
3404	f.SetX(x)
3405}
3406
3407// SetProtection applies certain constraints on the finished PDF document.
3408//
3409// actionFlag is a bitflag that controls various document operations.
3410// CnProtectPrint allows the document to be printed. CnProtectModify allows a
3411// document to be modified by a PDF editor. CnProtectCopy allows text and
3412// images to be copied into the system clipboard. CnProtectAnnotForms allows
3413// annotations and forms to be added by a PDF editor. These values can be
3414// combined by or-ing them together, for example,
3415// CnProtectCopy|CnProtectModify. This flag is advisory; not all PDF readers
3416// implement the constraints that this argument attempts to control.
3417//
3418// userPassStr specifies the password that will need to be provided to view the
3419// contents of the PDF. The permissions specified by actionFlag will apply.
3420//
3421// ownerPassStr specifies the password that will need to be provided to gain
3422// full access to the document regardless of the actionFlag value. An empty
3423// string for this argument will be replaced with a random value, effectively
3424// prohibiting full access to the document.
3425func (f *Fpdf) SetProtection(actionFlag byte, userPassStr, ownerPassStr string) {
3426	if f.err != nil {
3427		return
3428	}
3429	f.protect.setProtection(actionFlag, userPassStr, ownerPassStr)
3430}
3431
3432// OutputAndClose sends the PDF document to the writer specified by w. This
3433// method will close both f and w, even if an error is detected and no document
3434// is produced.
3435func (f *Fpdf) OutputAndClose(w io.WriteCloser) error {
3436	f.Output(w)
3437	w.Close()
3438	return f.err
3439}
3440
3441// OutputFileAndClose creates or truncates the file specified by fileStr and
3442// writes the PDF document to it. This method will close f and the newly
3443// written file, even if an error is detected and no document is produced.
3444//
3445// Most examples demonstrate the use of this method.
3446func (f *Fpdf) OutputFileAndClose(fileStr string) error {
3447	if f.err == nil {
3448		pdfFile, err := os.Create(fileStr)
3449		if err == nil {
3450			f.Output(pdfFile)
3451			pdfFile.Close()
3452		} else {
3453			f.err = err
3454		}
3455	}
3456	return f.err
3457}
3458
3459// Output sends the PDF document to the writer specified by w. No output will
3460// take place if an error has occurred in the document generation process. w
3461// remains open after this function returns. After returning, f is in a closed
3462// state and its methods should not be called.
3463func (f *Fpdf) Output(w io.Writer) error {
3464	if f.err != nil {
3465		return f.err
3466	}
3467	// dbg("Output")
3468	if f.state < 3 {
3469		f.Close()
3470	}
3471	_, err := f.buffer.WriteTo(w)
3472	if err != nil {
3473		f.err = err
3474	}
3475	return f.err
3476}
3477
3478func (f *Fpdf) getpagesizestr(sizeStr string) (size SizeType) {
3479	if f.err != nil {
3480		return
3481	}
3482	sizeStr = strings.ToLower(sizeStr)
3483	// dbg("Size [%s]", sizeStr)
3484	var ok bool
3485	size, ok = f.stdPageSizes[sizeStr]
3486	if ok {
3487		// dbg("found %s", sizeStr)
3488		size.Wd /= f.k
3489		size.Ht /= f.k
3490
3491	} else {
3492		f.err = fmt.Errorf("unknown page size %s", sizeStr)
3493	}
3494	return
3495}
3496
3497// GetPageSizeStr returns the SizeType for the given sizeStr (that is A4, A3, etc..)
3498func (f *Fpdf) GetPageSizeStr(sizeStr string) (size SizeType) {
3499	return f.getpagesizestr(sizeStr)
3500}
3501
3502func (f *Fpdf) _getpagesize(size SizeType) SizeType {
3503	if size.Wd > size.Ht {
3504		size.Wd, size.Ht = size.Ht, size.Wd
3505	}
3506	return size
3507}
3508
3509func (f *Fpdf) beginpage(orientationStr string, size SizeType) {
3510	if f.err != nil {
3511		return
3512	}
3513	f.page++
3514	// add the default page boxes, if any exist, to the page
3515	f.pageBoxes[f.page] = make(map[string]PageBox)
3516	for box, pb := range f.defPageBoxes {
3517		f.pageBoxes[f.page][box] = pb
3518	}
3519	f.pages = append(f.pages, bytes.NewBufferString(""))
3520	f.pageLinks = append(f.pageLinks, make([]linkType, 0, 0))
3521	f.pageAttachments = append(f.pageAttachments, []annotationAttach{})
3522	f.state = 2
3523	f.x = f.lMargin
3524	f.y = f.tMargin
3525	f.fontFamily = ""
3526	// Check page size and orientation
3527	if orientationStr == "" {
3528		orientationStr = f.defOrientation
3529	} else {
3530		orientationStr = strings.ToUpper(orientationStr[0:1])
3531	}
3532	if orientationStr != f.curOrientation || size.Wd != f.curPageSize.Wd || size.Ht != f.curPageSize.Ht {
3533		// New size or orientation
3534		if orientationStr == "P" {
3535			f.w = size.Wd
3536			f.h = size.Ht
3537		} else {
3538			f.w = size.Ht
3539			f.h = size.Wd
3540		}
3541		f.wPt = f.w * f.k
3542		f.hPt = f.h * f.k
3543		f.pageBreakTrigger = f.h - f.bMargin
3544		f.curOrientation = orientationStr
3545		f.curPageSize = size
3546	}
3547	if orientationStr != f.defOrientation || size.Wd != f.defPageSize.Wd || size.Ht != f.defPageSize.Ht {
3548		f.pageSizes[f.page] = SizeType{f.wPt, f.hPt}
3549	}
3550	return
3551}
3552
3553func (f *Fpdf) endpage() {
3554	f.EndLayer()
3555	f.state = 1
3556}
3557
3558// Load a font definition file from the given Reader
3559func (f *Fpdf) loadfont(r io.Reader) (def fontDefType) {
3560	if f.err != nil {
3561		return
3562	}
3563	// dbg("Loading font [%s]", fontStr)
3564	var buf bytes.Buffer
3565	_, err := buf.ReadFrom(r)
3566	if err != nil {
3567		f.err = err
3568		return
3569	}
3570	err = json.Unmarshal(buf.Bytes(), &def)
3571	if err != nil {
3572		f.err = err
3573		return
3574	}
3575
3576	if def.i, err = generateFontID(def); err != nil {
3577		f.err = err
3578	}
3579	// dump(def)
3580	return
3581}
3582
3583// Escape special characters in strings
3584func (f *Fpdf) escape(s string) string {
3585	s = strings.Replace(s, "\\", "\\\\", -1)
3586	s = strings.Replace(s, "(", "\\(", -1)
3587	s = strings.Replace(s, ")", "\\)", -1)
3588	s = strings.Replace(s, "\r", "\\r", -1)
3589	return s
3590}
3591
3592// textstring formats a text string
3593func (f *Fpdf) textstring(s string) string {
3594	if f.protect.encrypted {
3595		b := []byte(s)
3596		f.protect.rc4(uint32(f.n), &b)
3597		s = string(b)
3598	}
3599	return "(" + f.escape(s) + ")"
3600}
3601
3602func blankCount(str string) (count int) {
3603	l := len(str)
3604	for j := 0; j < l; j++ {
3605		if byte(' ') == str[j] {
3606			count++
3607		}
3608	}
3609	return
3610}
3611
3612// SetUnderlineThickness accepts a multiplier for adjusting the text underline
3613// thickness, defaulting to 1. See SetUnderlineThickness example.
3614func (f *Fpdf) SetUnderlineThickness(thickness float64) {
3615	f.userUnderlineThickness = thickness
3616}
3617
3618// Underline text
3619func (f *Fpdf) dounderline(x, y float64, txt string) string {
3620	up := float64(f.currentFont.Up)
3621	ut := float64(f.currentFont.Ut) * f.userUnderlineThickness
3622	w := f.GetStringWidth(txt) + f.ws*float64(blankCount(txt))
3623	return sprintf("%.2f %.2f %.2f %.2f re f", x*f.k,
3624		(f.h-(y-up/1000*f.fontSize))*f.k, w*f.k, -ut/1000*f.fontSizePt)
3625}
3626
3627func (f *Fpdf) dostrikeout(x, y float64, txt string) string {
3628	up := float64(f.currentFont.Up)
3629	ut := float64(f.currentFont.Ut)
3630	w := f.GetStringWidth(txt) + f.ws*float64(blankCount(txt))
3631	return sprintf("%.2f %.2f %.2f %.2f re f", x*f.k,
3632		(f.h-(y+4*up/1000*f.fontSize))*f.k, w*f.k, -ut/1000*f.fontSizePt)
3633}
3634
3635func bufEqual(buf []byte, str string) bool {
3636	return string(buf[0:len(str)]) == str
3637}
3638
3639func be16(buf []byte) int {
3640	return 256*int(buf[0]) + int(buf[1])
3641}
3642
3643func (f *Fpdf) newImageInfo() *ImageInfoType {
3644	// default dpi to 72 unless told otherwise
3645	return &ImageInfoType{scale: f.k, dpi: 72}
3646}
3647
3648// parsejpg extracts info from io.Reader with JPEG data
3649// Thank you, Bruno Michel, for providing this code.
3650func (f *Fpdf) parsejpg(r io.Reader) (info *ImageInfoType) {
3651	info = f.newImageInfo()
3652	var (
3653		data bytes.Buffer
3654		err  error
3655	)
3656	_, err = data.ReadFrom(r)
3657	if err != nil {
3658		f.err = err
3659		return
3660	}
3661	info.data = data.Bytes()
3662
3663	config, err := jpeg.DecodeConfig(bytes.NewReader(info.data))
3664	if err != nil {
3665		f.err = err
3666		return
3667	}
3668	info.w = float64(config.Width)
3669	info.h = float64(config.Height)
3670	info.f = "DCTDecode"
3671	info.bpc = 8
3672	switch config.ColorModel {
3673	case color.GrayModel:
3674		info.cs = "DeviceGray"
3675	case color.YCbCrModel:
3676		info.cs = "DeviceRGB"
3677	case color.CMYKModel:
3678		info.cs = "DeviceCMYK"
3679	default:
3680		f.err = fmt.Errorf("image JPEG buffer has unsupported color space (%v)", config.ColorModel)
3681		return
3682	}
3683	return
3684}
3685
3686// parsepng extracts info from a PNG data
3687func (f *Fpdf) parsepng(r io.Reader, readdpi bool) (info *ImageInfoType) {
3688	buf, err := bufferFromReader(r)
3689	if err != nil {
3690		f.err = err
3691		return
3692	}
3693	return f.parsepngstream(buf, readdpi)
3694}
3695
3696func (f *Fpdf) readBeInt32(r io.Reader) (val int32) {
3697	err := binary.Read(r, binary.BigEndian, &val)
3698	if err != nil && err != io.EOF {
3699		f.err = err
3700	}
3701	return
3702}
3703
3704func (f *Fpdf) readByte(r io.Reader) (val byte) {
3705	err := binary.Read(r, binary.BigEndian, &val)
3706	if err != nil {
3707		f.err = err
3708	}
3709	return
3710}
3711
3712// parsegif extracts info from a GIF data (via PNG conversion)
3713func (f *Fpdf) parsegif(r io.Reader) (info *ImageInfoType) {
3714	data, err := bufferFromReader(r)
3715	if err != nil {
3716		f.err = err
3717		return
3718	}
3719	var img image.Image
3720	img, err = gif.Decode(data)
3721	if err != nil {
3722		f.err = err
3723		return
3724	}
3725	pngBuf := new(bytes.Buffer)
3726	err = png.Encode(pngBuf, img)
3727	if err != nil {
3728		f.err = err
3729		return
3730	}
3731	return f.parsepngstream(pngBuf, false)
3732}
3733
3734// newobj begins a new object
3735func (f *Fpdf) newobj() {
3736	// dbg("newobj")
3737	f.n++
3738	for j := len(f.offsets); j <= f.n; j++ {
3739		f.offsets = append(f.offsets, 0)
3740	}
3741	f.offsets[f.n] = f.buffer.Len()
3742	f.outf("%d 0 obj", f.n)
3743}
3744
3745func (f *Fpdf) putstream(b []byte) {
3746	// dbg("putstream")
3747	if f.protect.encrypted {
3748		f.protect.rc4(uint32(f.n), &b)
3749	}
3750	f.out("stream")
3751	f.out(string(b))
3752	f.out("endstream")
3753}
3754
3755// out; Add a line to the document
3756func (f *Fpdf) out(s string) {
3757	if f.state == 2 {
3758		f.pages[f.page].WriteString(s)
3759		f.pages[f.page].WriteString("\n")
3760	} else {
3761		f.buffer.WriteString(s)
3762		f.buffer.WriteString("\n")
3763	}
3764}
3765
3766// outbuf adds a buffered line to the document
3767func (f *Fpdf) outbuf(r io.Reader) {
3768	if f.state == 2 {
3769		f.pages[f.page].ReadFrom(r)
3770		f.pages[f.page].WriteString("\n")
3771	} else {
3772		f.buffer.ReadFrom(r)
3773		f.buffer.WriteString("\n")
3774	}
3775}
3776
3777// RawWriteStr writes a string directly to the PDF generation buffer. This is a
3778// low-level function that is not required for normal PDF construction. An
3779// understanding of the PDF specification is needed to use this method
3780// correctly.
3781func (f *Fpdf) RawWriteStr(str string) {
3782	f.out(str)
3783}
3784
3785// RawWriteBuf writes the contents of the specified buffer directly to the PDF
3786// generation buffer. This is a low-level function that is not required for
3787// normal PDF construction. An understanding of the PDF specification is needed
3788// to use this method correctly.
3789func (f *Fpdf) RawWriteBuf(r io.Reader) {
3790	f.outbuf(r)
3791}
3792
3793// outf adds a formatted line to the document
3794func (f *Fpdf) outf(fmtStr string, args ...interface{}) {
3795	f.out(sprintf(fmtStr, args...))
3796}
3797
3798// SetDefaultCatalogSort sets the default value of the catalog sort flag that
3799// will be used when initializing a new Fpdf instance. See SetCatalogSort() for
3800// more details.
3801func SetDefaultCatalogSort(flag bool) {
3802	gl.catalogSort = flag
3803}
3804
3805// SetCatalogSort sets a flag that will be used, if true, to consistently order
3806// the document's internal resource catalogs. This method is typically only
3807// used for test purposes to facilitate PDF comparison.
3808func (f *Fpdf) SetCatalogSort(flag bool) {
3809	f.catalogSort = flag
3810}
3811
3812// SetDefaultCreationDate sets the default value of the document creation date
3813// that will be used when initializing a new Fpdf instance. See
3814// SetCreationDate() for more details.
3815func SetDefaultCreationDate(tm time.Time) {
3816	gl.creationDate = tm
3817}
3818
3819// SetDefaultModificationDate sets the default value of the document modification date
3820// that will be used when initializing a new Fpdf instance. See
3821// SetCreationDate() for more details.
3822func SetDefaultModificationDate(tm time.Time) {
3823	gl.modDate = tm
3824}
3825
3826// SetCreationDate fixes the document's internal CreationDate value. By
3827// default, the time when the document is generated is used for this value.
3828// This method is typically only used for testing purposes to facilitate PDF
3829// comparison. Specify a zero-value time to revert to the default behavior.
3830func (f *Fpdf) SetCreationDate(tm time.Time) {
3831	f.creationDate = tm
3832}
3833
3834// SetModificationDate fixes the document's internal ModDate value.
3835// See `SetCreationDate` for more details.
3836func (f *Fpdf) SetModificationDate(tm time.Time) {
3837	f.modDate = tm
3838}
3839
3840// SetJavascript adds Adobe JavaScript to the document.
3841func (f *Fpdf) SetJavascript(script string) {
3842	f.javascript = &script
3843}
3844
3845// RegisterAlias adds an (alias, replacement) pair to the document so we can
3846// replace all occurrences of that alias after writing but before the document
3847// is closed. Functions ExampleFpdf_RegisterAlias() and
3848// ExampleFpdf_RegisterAlias_utf8() in fpdf_test.go demonstrate this method.
3849func (f *Fpdf) RegisterAlias(alias, replacement string) {
3850	// Note: map[string]string assignments embed literal escape ("\00") sequences
3851	// into utf16 key and value strings. Consequently, subsequent search/replace
3852	// operations will fail unexpectedly if utf8toutf16() conversions take place
3853	// here. Instead, conversions are deferred until the actual search/replace
3854	// operation takes place when the PDF output is generated.
3855	f.aliasMap[alias] = replacement
3856}
3857
3858func (f *Fpdf) replaceAliases() {
3859	for mode := 0; mode < 2; mode++ {
3860		for alias, replacement := range f.aliasMap {
3861			if mode == 1 {
3862				alias = utf8toutf16(alias, false)
3863				replacement = utf8toutf16(replacement, false)
3864			}
3865			for n := 1; n <= f.page; n++ {
3866				s := f.pages[n].String()
3867				if strings.Contains(s, alias) {
3868					s = strings.Replace(s, alias, replacement, -1)
3869					f.pages[n].Truncate(0)
3870					f.pages[n].WriteString(s)
3871				}
3872			}
3873		}
3874	}
3875}
3876
3877func (f *Fpdf) putpages() {
3878	var wPt, hPt float64
3879	var pageSize SizeType
3880	var ok bool
3881	nb := f.page
3882	if len(f.aliasNbPagesStr) > 0 {
3883		// Replace number of pages
3884		f.RegisterAlias(f.aliasNbPagesStr, sprintf("%d", nb))
3885	}
3886	f.replaceAliases()
3887	if f.defOrientation == "P" {
3888		wPt = f.defPageSize.Wd * f.k
3889		hPt = f.defPageSize.Ht * f.k
3890	} else {
3891		wPt = f.defPageSize.Ht * f.k
3892		hPt = f.defPageSize.Wd * f.k
3893	}
3894	pagesObjectNumbers := make([]int, nb+1) // 1-based
3895	for n := 1; n <= nb; n++ {
3896		// Page
3897		f.newobj()
3898		pagesObjectNumbers[n] = f.n // save for /Kids
3899		f.out("<</Type /Page")
3900		f.out("/Parent 1 0 R")
3901		pageSize, ok = f.pageSizes[n]
3902		if ok {
3903			f.outf("/MediaBox [0 0 %.2f %.2f]", pageSize.Wd, pageSize.Ht)
3904		}
3905		for t, pb := range f.pageBoxes[n] {
3906			f.outf("/%s [%.2f %.2f %.2f %.2f]", t, pb.X, pb.Y, pb.Wd, pb.Ht)
3907		}
3908		f.out("/Resources 2 0 R")
3909		// Links
3910		if len(f.pageLinks[n])+len(f.pageAttachments[n]) > 0 {
3911			var annots fmtBuffer
3912			annots.printf("/Annots [")
3913			for _, pl := range f.pageLinks[n] {
3914				annots.printf("<</Type /Annot /Subtype /Link /Rect [%.2f %.2f %.2f %.2f] /Border [0 0 0] ",
3915					pl.x, pl.y, pl.x+pl.wd, pl.y-pl.ht)
3916				if pl.link == 0 {
3917					annots.printf("/A <</S /URI /URI %s>>>>", f.textstring(pl.linkStr))
3918				} else {
3919					l := f.links[pl.link]
3920					var sz SizeType
3921					var h float64
3922					sz, ok = f.pageSizes[l.page]
3923					if ok {
3924						h = sz.Ht
3925					} else {
3926						h = hPt
3927					}
3928					// dbg("h [%.2f], l.y [%.2f] f.k [%.2f]\n", h, l.y, f.k)
3929					annots.printf("/Dest [%d 0 R /XYZ 0 %.2f null]>>", 1+2*l.page, h-l.y*f.k)
3930				}
3931			}
3932			f.putAttachmentAnnotationLinks(&annots, n)
3933			annots.printf("]")
3934			f.out(annots.String())
3935		}
3936		if f.pdfVersion > "1.3" {
3937			f.out("/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>")
3938		}
3939		f.outf("/Contents %d 0 R>>", f.n+1)
3940		f.out("endobj")
3941		// Page content
3942		f.newobj()
3943		if f.compress {
3944			data := sliceCompress(f.pages[n].Bytes())
3945			f.outf("<</Filter /FlateDecode /Length %d>>", len(data))
3946			f.putstream(data)
3947		} else {
3948			f.outf("<</Length %d>>", f.pages[n].Len())
3949			f.putstream(f.pages[n].Bytes())
3950		}
3951		f.out("endobj")
3952	}
3953	// Pages root
3954	f.offsets[1] = f.buffer.Len()
3955	f.out("1 0 obj")
3956	f.out("<</Type /Pages")
3957	var kids fmtBuffer
3958	kids.printf("/Kids [")
3959	for i := 1; i <= nb; i++ {
3960		kids.printf("%d 0 R ", pagesObjectNumbers[i])
3961	}
3962	kids.printf("]")
3963	f.out(kids.String())
3964	f.outf("/Count %d", nb)
3965	f.outf("/MediaBox [0 0 %.2f %.2f]", wPt, hPt)
3966	f.out(">>")
3967	f.out("endobj")
3968}
3969
3970func (f *Fpdf) putfonts() {
3971	if f.err != nil {
3972		return
3973	}
3974	nf := f.n
3975	for _, diff := range f.diffs {
3976		// Encodings
3977		f.newobj()
3978		f.outf("<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [%s]>>", diff)
3979		f.out("endobj")
3980	}
3981	{
3982		var fileList []string
3983		var info fontFileType
3984		var file string
3985		for file = range f.fontFiles {
3986			fileList = append(fileList, file)
3987		}
3988		if f.catalogSort {
3989			sort.SliceStable(fileList, func(i, j int) bool { return fileList[i] < fileList[j] })
3990		}
3991		for _, file = range fileList {
3992			info = f.fontFiles[file]
3993			if info.fontType != "UTF8" {
3994				f.newobj()
3995				info.n = f.n
3996				f.fontFiles[file] = info
3997
3998				var font []byte
3999
4000				if info.embedded {
4001					font = info.content
4002				} else {
4003					var err error
4004					font, err = f.loadFontFile(file)
4005					if err != nil {
4006						f.err = err
4007						return
4008					}
4009				}
4010				compressed := file[len(file)-2:] == ".z"
4011				if !compressed && info.length2 > 0 {
4012					buf := font[6:info.length1]
4013					buf = append(buf, font[6+info.length1+6:info.length2]...)
4014					font = buf
4015				}
4016				f.outf("<</Length %d", len(font))
4017				if compressed {
4018					f.out("/Filter /FlateDecode")
4019				}
4020				f.outf("/Length1 %d", info.length1)
4021				if info.length2 > 0 {
4022					f.outf("/Length2 %d /Length3 0", info.length2)
4023				}
4024				f.out(">>")
4025				f.putstream(font)
4026				f.out("endobj")
4027			}
4028		}
4029	}
4030	{
4031		var keyList []string
4032		var font fontDefType
4033		var key string
4034		for key = range f.fonts {
4035			keyList = append(keyList, key)
4036		}
4037		if f.catalogSort {
4038			sort.SliceStable(keyList, func(i, j int) bool { return keyList[i] < keyList[j] })
4039		}
4040		for _, key = range keyList {
4041			font = f.fonts[key]
4042			// Font objects
4043			font.N = f.n + 1
4044			f.fonts[key] = font
4045			tp := font.Tp
4046			name := font.Name
4047			switch tp {
4048			case "Core":
4049				// Core font
4050				f.newobj()
4051				f.out("<</Type /Font")
4052				f.outf("/BaseFont /%s", name)
4053				f.out("/Subtype /Type1")
4054				if name != "Symbol" && name != "ZapfDingbats" {
4055					f.out("/Encoding /WinAnsiEncoding")
4056				}
4057				f.out(">>")
4058				f.out("endobj")
4059			case "Type1":
4060				fallthrough
4061			case "TrueType":
4062				// Additional Type1 or TrueType/OpenType font
4063				f.newobj()
4064				f.out("<</Type /Font")
4065				f.outf("/BaseFont /%s", name)
4066				f.outf("/Subtype /%s", tp)
4067				f.out("/FirstChar 32 /LastChar 255")
4068				f.outf("/Widths %d 0 R", f.n+1)
4069				f.outf("/FontDescriptor %d 0 R", f.n+2)
4070				if font.DiffN > 0 {
4071					f.outf("/Encoding %d 0 R", nf+font.DiffN)
4072				} else {
4073					f.out("/Encoding /WinAnsiEncoding")
4074				}
4075				f.out(">>")
4076				f.out("endobj")
4077				// Widths
4078				f.newobj()
4079				var s fmtBuffer
4080				s.WriteString("[")
4081				for j := 32; j < 256; j++ {
4082					s.printf("%d ", font.Cw[j])
4083				}
4084				s.WriteString("]")
4085				f.out(s.String())
4086				f.out("endobj")
4087				// Descriptor
4088				f.newobj()
4089				s.Truncate(0)
4090				s.printf("<</Type /FontDescriptor /FontName /%s ", name)
4091				s.printf("/Ascent %d ", font.Desc.Ascent)
4092				s.printf("/Descent %d ", font.Desc.Descent)
4093				s.printf("/CapHeight %d ", font.Desc.CapHeight)
4094				s.printf("/Flags %d ", font.Desc.Flags)
4095				s.printf("/FontBBox [%d %d %d %d] ", font.Desc.FontBBox.Xmin, font.Desc.FontBBox.Ymin,
4096					font.Desc.FontBBox.Xmax, font.Desc.FontBBox.Ymax)
4097				s.printf("/ItalicAngle %d ", font.Desc.ItalicAngle)
4098				s.printf("/StemV %d ", font.Desc.StemV)
4099				s.printf("/MissingWidth %d ", font.Desc.MissingWidth)
4100				var suffix string
4101				if tp != "Type1" {
4102					suffix = "2"
4103				}
4104				s.printf("/FontFile%s %d 0 R>>", suffix, f.fontFiles[font.File].n)
4105				f.out(s.String())
4106				f.out("endobj")
4107			case "UTF8":
4108				fontName := "utf8" + font.Name
4109				usedRunes := font.usedRunes
4110				delete(usedRunes, 0)
4111				utf8FontStream := font.utf8File.GenerateCutFont(usedRunes)
4112				utf8FontSize := len(utf8FontStream)
4113				compressedFontStream := sliceCompress(utf8FontStream)
4114				CodeSignDictionary := font.utf8File.CodeSymbolDictionary
4115				delete(CodeSignDictionary, 0)
4116
4117				f.newobj()
4118				f.out(fmt.Sprintf("<</Type /Font\n/Subtype /Type0\n/BaseFont /%s\n/Encoding /Identity-H\n/DescendantFonts [%d 0 R]\n/ToUnicode %d 0 R>>\n"+"endobj", fontName, f.n+1, f.n+2))
4119
4120				f.newobj()
4121				f.out("<</Type /Font\n/Subtype /CIDFontType2\n/BaseFont /" + fontName + "\n" +
4122					"/CIDSystemInfo " + strconv.Itoa(f.n+2) + " 0 R\n/FontDescriptor " + strconv.Itoa(f.n+3) + " 0 R")
4123				if font.Desc.MissingWidth != 0 {
4124					f.out("/DW " + strconv.Itoa(font.Desc.MissingWidth) + "")
4125				}
4126				f.generateCIDFontMap(&font, font.utf8File.LastRune)
4127				f.out("/CIDToGIDMap " + strconv.Itoa(f.n+4) + " 0 R>>")
4128				f.out("endobj")
4129
4130				f.newobj()
4131				f.out("<</Length " + strconv.Itoa(len(toUnicode)) + ">>")
4132				f.putstream([]byte(toUnicode))
4133				f.out("endobj")
4134
4135				// CIDInfo
4136				f.newobj()
4137				f.out("<</Registry (Adobe)\n/Ordering (UCS)\n/Supplement 0>>")
4138				f.out("endobj")
4139
4140				// Font descriptor
4141				f.newobj()
4142				var s fmtBuffer
4143				s.printf("<</Type /FontDescriptor /FontName /%s\n /Ascent %d", fontName, font.Desc.Ascent)
4144				s.printf(" /Descent %d", font.Desc.Descent)
4145				s.printf(" /CapHeight %d", font.Desc.CapHeight)
4146				v := font.Desc.Flags
4147				v = v | 4
4148				v = v &^ 32
4149				s.printf(" /Flags %d", v)
4150				s.printf("/FontBBox [%d %d %d %d] ", font.Desc.FontBBox.Xmin, font.Desc.FontBBox.Ymin,
4151					font.Desc.FontBBox.Xmax, font.Desc.FontBBox.Ymax)
4152				s.printf(" /ItalicAngle %d", font.Desc.ItalicAngle)
4153				s.printf(" /StemV %d", font.Desc.StemV)
4154				s.printf(" /MissingWidth %d", font.Desc.MissingWidth)
4155				s.printf("/FontFile2 %d 0 R", f.n+2)
4156				s.printf(">>")
4157				f.out(s.String())
4158				f.out("endobj")
4159
4160				// Embed CIDToGIDMap
4161				cidToGidMap := make([]byte, 256*256*2)
4162
4163				for cc, glyph := range CodeSignDictionary {
4164					cidToGidMap[cc*2] = byte(glyph >> 8)
4165					cidToGidMap[cc*2+1] = byte(glyph & 0xFF)
4166				}
4167
4168				cidToGidMap = sliceCompress(cidToGidMap)
4169				f.newobj()
4170				f.out("<</Length " + strconv.Itoa(len(cidToGidMap)) + "/Filter /FlateDecode>>")
4171				f.putstream(cidToGidMap)
4172				f.out("endobj")
4173
4174				//Font file
4175				f.newobj()
4176				f.out("<</Length " + strconv.Itoa(len(compressedFontStream)))
4177				f.out("/Filter /FlateDecode")
4178				f.out("/Length1 " + strconv.Itoa(utf8FontSize))
4179				f.out(">>")
4180				f.putstream(compressedFontStream)
4181				f.out("endobj")
4182			default:
4183				f.err = fmt.Errorf("unsupported font type: %s", tp)
4184				return
4185			}
4186		}
4187	}
4188	return
4189}
4190
4191func (f *Fpdf) generateCIDFontMap(font *fontDefType, LastRune int) {
4192	rangeID := 0
4193	cidArray := make(map[int]*untypedKeyMap)
4194	cidArrayKeys := make([]int, 0)
4195	prevCid := -2
4196	prevWidth := -1
4197	interval := false
4198	startCid := 1
4199	cwLen := LastRune + 1
4200
4201	// for each character
4202	for cid := startCid; cid < cwLen; cid++ {
4203		if font.Cw[cid] == 0x00 {
4204			continue
4205		}
4206		width := font.Cw[cid]
4207		if width == 65535 {
4208			width = 0
4209		}
4210		if numb, OK := font.usedRunes[cid]; cid > 255 && (!OK || numb == 0) {
4211			continue
4212		}
4213
4214		if cid == prevCid+1 {
4215			if width == prevWidth {
4216
4217				if width == cidArray[rangeID].get(0) {
4218					cidArray[rangeID].put(nil, width)
4219				} else {
4220					cidArray[rangeID].pop()
4221					rangeID = prevCid
4222					r := untypedKeyMap{
4223						valueSet: make([]int, 0),
4224						keySet:   make([]interface{}, 0),
4225					}
4226					cidArray[rangeID] = &r
4227					cidArrayKeys = append(cidArrayKeys, rangeID)
4228					cidArray[rangeID].put(nil, prevWidth)
4229					cidArray[rangeID].put(nil, width)
4230				}
4231				interval = true
4232				cidArray[rangeID].put("interval", 1)
4233			} else {
4234				if interval {
4235					// new range
4236					rangeID = cid
4237					r := untypedKeyMap{
4238						valueSet: make([]int, 0),
4239						keySet:   make([]interface{}, 0),
4240					}
4241					cidArray[rangeID] = &r
4242					cidArrayKeys = append(cidArrayKeys, rangeID)
4243					cidArray[rangeID].put(nil, width)
4244				} else {
4245					cidArray[rangeID].put(nil, width)
4246				}
4247				interval = false
4248			}
4249		} else {
4250			rangeID = cid
4251			r := untypedKeyMap{
4252				valueSet: make([]int, 0),
4253				keySet:   make([]interface{}, 0),
4254			}
4255			cidArray[rangeID] = &r
4256			cidArrayKeys = append(cidArrayKeys, rangeID)
4257			cidArray[rangeID].put(nil, width)
4258			interval = false
4259		}
4260		prevCid = cid
4261		prevWidth = width
4262
4263	}
4264	previousKey := -1
4265	nextKey := -1
4266	isInterval := false
4267	for g := 0; g < len(cidArrayKeys); {
4268		key := cidArrayKeys[g]
4269		ws := *cidArray[key]
4270		cws := len(ws.keySet)
4271		if (key == nextKey) && (!isInterval) && (ws.getIndex("interval") < 0 || cws < 4) {
4272			if cidArray[key].getIndex("interval") >= 0 {
4273				cidArray[key].delete("interval")
4274			}
4275			cidArray[previousKey] = arrayMerge(cidArray[previousKey], cidArray[key])
4276			cidArrayKeys = remove(cidArrayKeys, key)
4277		} else {
4278			g++
4279			previousKey = key
4280		}
4281		nextKey = key + cws
4282		// ui := ws.getIndex("interval")
4283		// ui = ui + 1
4284		if ws.getIndex("interval") >= 0 {
4285			if cws > 3 {
4286				isInterval = true
4287			} else {
4288				isInterval = false
4289			}
4290			cidArray[key].delete("interval")
4291			nextKey--
4292		} else {
4293			isInterval = false
4294		}
4295	}
4296	var w fmtBuffer
4297	for _, k := range cidArrayKeys {
4298		ws := cidArray[k]
4299		if len(arrayCountValues(ws.valueSet)) == 1 {
4300			w.printf(" %d %d %d", k, k+len(ws.valueSet)-1, ws.get(0))
4301		} else {
4302			w.printf(" %d [ %s ]\n", k, implode(" ", ws.valueSet))
4303		}
4304	}
4305	f.out("/W [" + w.String() + " ]")
4306}
4307
4308func implode(sep string, arr []int) string {
4309	var s fmtBuffer
4310	for i := 0; i < len(arr)-1; i++ {
4311		s.printf("%v", arr[i])
4312		s.printf(sep)
4313	}
4314	if len(arr) > 0 {
4315		s.printf("%v", arr[len(arr)-1])
4316	}
4317	return s.String()
4318}
4319
4320// arrayCountValues counts the occurrences of each item in the $mp array.
4321func arrayCountValues(mp []int) map[int]int {
4322	answer := make(map[int]int)
4323	for _, v := range mp {
4324		answer[v] = answer[v] + 1
4325	}
4326	return answer
4327}
4328
4329func (f *Fpdf) loadFontFile(name string) ([]byte, error) {
4330	if f.fontLoader != nil {
4331		reader, err := f.fontLoader.Open(name)
4332		if err == nil {
4333			data, err := ioutil.ReadAll(reader)
4334			if closer, ok := reader.(io.Closer); ok {
4335				closer.Close()
4336			}
4337			return data, err
4338		}
4339	}
4340	return ioutil.ReadFile(path.Join(f.fontpath, name))
4341}
4342
4343func (f *Fpdf) putimages() {
4344	var keyList []string
4345	var key string
4346	for key = range f.images {
4347		keyList = append(keyList, key)
4348	}
4349
4350	// Sort the keyList []string by the corresponding image's width.
4351	if f.catalogSort {
4352		sort.SliceStable(keyList, func(i, j int) bool { return f.images[keyList[i]].w < f.images[keyList[j]].w })
4353	}
4354
4355	// Maintain a list of inserted image SHA-1 hashes, with their
4356	// corresponding object ID number.
4357	insertedImages := map[string]int{}
4358
4359	for _, key = range keyList {
4360		image := f.images[key]
4361
4362		// Check if this image has already been inserted using it's SHA-1 hash.
4363		insertedImageObjN, isFound := insertedImages[image.i]
4364
4365		// If found, skip inserting the image as a new object, and
4366		// use the object ID from the insertedImages map.
4367		// If not, insert the image into the PDF and store the object ID.
4368		if isFound {
4369			image.n = insertedImageObjN
4370		} else {
4371			f.putimage(image)
4372			insertedImages[image.i] = image.n
4373		}
4374	}
4375}
4376
4377func (f *Fpdf) putimage(info *ImageInfoType) {
4378	f.newobj()
4379	info.n = f.n
4380	f.out("<</Type /XObject")
4381	f.out("/Subtype /Image")
4382	f.outf("/Width %d", int(info.w))
4383	f.outf("/Height %d", int(info.h))
4384	if info.cs == "Indexed" {
4385		f.outf("/ColorSpace [/Indexed /DeviceRGB %d %d 0 R]", len(info.pal)/3-1, f.n+1)
4386	} else {
4387		f.outf("/ColorSpace /%s", info.cs)
4388		if info.cs == "DeviceCMYK" {
4389			f.out("/Decode [1 0 1 0 1 0 1 0]")
4390		}
4391	}
4392	f.outf("/BitsPerComponent %d", info.bpc)
4393	if len(info.f) > 0 {
4394		f.outf("/Filter /%s", info.f)
4395	}
4396	if len(info.dp) > 0 {
4397		f.outf("/DecodeParms <<%s>>", info.dp)
4398	}
4399	if len(info.trns) > 0 {
4400		var trns fmtBuffer
4401		for _, v := range info.trns {
4402			trns.printf("%d %d ", v, v)
4403		}
4404		f.outf("/Mask [%s]", trns.String())
4405	}
4406	if info.smask != nil {
4407		f.outf("/SMask %d 0 R", f.n+1)
4408	}
4409	f.outf("/Length %d>>", len(info.data))
4410	f.putstream(info.data)
4411	f.out("endobj")
4412	// 	Soft mask
4413	if len(info.smask) > 0 {
4414		smask := &ImageInfoType{
4415			w:     info.w,
4416			h:     info.h,
4417			cs:    "DeviceGray",
4418			bpc:   8,
4419			f:     info.f,
4420			dp:    sprintf("/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns %d", int(info.w)),
4421			data:  info.smask,
4422			scale: f.k,
4423		}
4424		f.putimage(smask)
4425	}
4426	// 	Palette
4427	if info.cs == "Indexed" {
4428		f.newobj()
4429		if f.compress {
4430			pal := sliceCompress(info.pal)
4431			f.outf("<</Filter /FlateDecode /Length %d>>", len(pal))
4432			f.putstream(pal)
4433		} else {
4434			f.outf("<</Length %d>>", len(info.pal))
4435			f.putstream(info.pal)
4436		}
4437		f.out("endobj")
4438	}
4439}
4440
4441func (f *Fpdf) putxobjectdict() {
4442	{
4443		var image *ImageInfoType
4444		var key string
4445		var keyList []string
4446		for key = range f.images {
4447			keyList = append(keyList, key)
4448		}
4449		if f.catalogSort {
4450			sort.SliceStable(keyList, func(i, j int) bool { return f.images[keyList[i]].i < f.images[keyList[j]].i })
4451		}
4452		for _, key = range keyList {
4453			image = f.images[key]
4454			f.outf("/I%s %d 0 R", image.i, image.n)
4455		}
4456	}
4457	{
4458		var keyList []string
4459		var key string
4460		var tpl Template
4461		keyList = templateKeyList(f.templates, f.catalogSort)
4462		for _, key = range keyList {
4463			tpl = f.templates[key]
4464			// for _, tpl := range f.templates {
4465			id := tpl.ID()
4466			if objID, ok := f.templateObjects[id]; ok {
4467				f.outf("/TPL%s %d 0 R", id, objID)
4468			}
4469		}
4470	}
4471	{
4472		for tplName, objID := range f.importedTplObjs {
4473			// here replace obj id hash with n
4474			f.outf("%s %d 0 R", tplName, f.importedTplIDs[objID])
4475		}
4476	}
4477}
4478
4479func (f *Fpdf) putresourcedict() {
4480	f.out("/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]")
4481	f.out("/Font <<")
4482	{
4483		var keyList []string
4484		var font fontDefType
4485		var key string
4486		for key = range f.fonts {
4487			keyList = append(keyList, key)
4488		}
4489		if f.catalogSort {
4490			sort.SliceStable(keyList, func(i, j int) bool { return f.fonts[keyList[i]].i < f.fonts[keyList[j]].i })
4491		}
4492		for _, key = range keyList {
4493			font = f.fonts[key]
4494			f.outf("/F%s %d 0 R", font.i, font.N)
4495		}
4496	}
4497	f.out(">>")
4498	f.out("/XObject <<")
4499	f.putxobjectdict()
4500	f.out(">>")
4501	count := len(f.blendList)
4502	if count > 1 {
4503		f.out("/ExtGState <<")
4504		for j := 1; j < count; j++ {
4505			f.outf("/GS%d %d 0 R", j, f.blendList[j].objNum)
4506		}
4507		f.out(">>")
4508	}
4509	count = len(f.gradientList)
4510	if count > 1 {
4511		f.out("/Shading <<")
4512		for j := 1; j < count; j++ {
4513			f.outf("/Sh%d %d 0 R", j, f.gradientList[j].objNum)
4514		}
4515		f.out(">>")
4516	}
4517	// Layers
4518	f.layerPutResourceDict()
4519	f.spotColorPutResourceDict()
4520}
4521
4522func (f *Fpdf) putBlendModes() {
4523	count := len(f.blendList)
4524	for j := 1; j < count; j++ {
4525		bl := f.blendList[j]
4526		f.newobj()
4527		f.blendList[j].objNum = f.n
4528		f.outf("<</Type /ExtGState /ca %s /CA %s /BM /%s>>",
4529			bl.fillStr, bl.strokeStr, bl.modeStr)
4530		f.out("endobj")
4531	}
4532}
4533
4534func (f *Fpdf) putGradients() {
4535	count := len(f.gradientList)
4536	for j := 1; j < count; j++ {
4537		var f1 int
4538		gr := f.gradientList[j]
4539		if gr.tp == 2 || gr.tp == 3 {
4540			f.newobj()
4541			f.outf("<</FunctionType 2 /Domain [0.0 1.0] /C0 [%s] /C1 [%s] /N 1>>", gr.clr1Str, gr.clr2Str)
4542			f.out("endobj")
4543			f1 = f.n
4544		}
4545		f.newobj()
4546		f.outf("<</ShadingType %d /ColorSpace /DeviceRGB", gr.tp)
4547		if gr.tp == 2 {
4548			f.outf("/Coords [%.5f %.5f %.5f %.5f] /Function %d 0 R /Extend [true true]>>",
4549				gr.x1, gr.y1, gr.x2, gr.y2, f1)
4550		} else if gr.tp == 3 {
4551			f.outf("/Coords [%.5f %.5f 0 %.5f %.5f %.5f] /Function %d 0 R /Extend [true true]>>",
4552				gr.x1, gr.y1, gr.x2, gr.y2, gr.r, f1)
4553		}
4554		f.out("endobj")
4555		f.gradientList[j].objNum = f.n
4556	}
4557}
4558
4559func (f *Fpdf) putjavascript() {
4560	if f.javascript == nil {
4561		return
4562	}
4563
4564	f.newobj()
4565	f.nJs = f.n
4566	f.out("<<")
4567	f.outf("/Names [(EmbeddedJS) %d 0 R]", f.n+1)
4568	f.out(">>")
4569	f.out("endobj")
4570	f.newobj()
4571	f.out("<<")
4572	f.out("/S /JavaScript")
4573	f.outf("/JS %s", f.textstring(*f.javascript))
4574	f.out(">>")
4575	f.out("endobj")
4576}
4577
4578func (f *Fpdf) putresources() {
4579	if f.err != nil {
4580		return
4581	}
4582	f.layerPutLayers()
4583	f.putBlendModes()
4584	f.putGradients()
4585	f.putSpotColors()
4586	f.putfonts()
4587	if f.err != nil {
4588		return
4589	}
4590	f.putimages()
4591	f.putTemplates()
4592	f.putImportedTemplates() // gofpdi
4593	// 	Resource dictionary
4594	f.offsets[2] = f.buffer.Len()
4595	f.out("2 0 obj")
4596	f.out("<<")
4597	f.putresourcedict()
4598	f.out(">>")
4599	f.out("endobj")
4600	f.putjavascript()
4601	if f.protect.encrypted {
4602		f.newobj()
4603		f.protect.objNum = f.n
4604		f.out("<<")
4605		f.out("/Filter /Standard")
4606		f.out("/V 1")
4607		f.out("/R 2")
4608		f.outf("/O (%s)", f.escape(string(f.protect.oValue)))
4609		f.outf("/U (%s)", f.escape(string(f.protect.uValue)))
4610		f.outf("/P %d", f.protect.pValue)
4611		f.out(">>")
4612		f.out("endobj")
4613	}
4614	return
4615}
4616
4617// returns Now() if tm is zero
4618func timeOrNow(tm time.Time) time.Time {
4619	if tm.IsZero() {
4620		return time.Now()
4621	}
4622	return tm
4623}
4624
4625func (f *Fpdf) putinfo() {
4626	if len(f.producer) > 0 {
4627		f.outf("/Producer %s", f.textstring(f.producer))
4628	}
4629	if len(f.title) > 0 {
4630		f.outf("/Title %s", f.textstring(f.title))
4631	}
4632	if len(f.subject) > 0 {
4633		f.outf("/Subject %s", f.textstring(f.subject))
4634	}
4635	if len(f.author) > 0 {
4636		f.outf("/Author %s", f.textstring(f.author))
4637	}
4638	if len(f.keywords) > 0 {
4639		f.outf("/Keywords %s", f.textstring(f.keywords))
4640	}
4641	if len(f.creator) > 0 {
4642		f.outf("/Creator %s", f.textstring(f.creator))
4643	}
4644	creation := timeOrNow(f.creationDate)
4645	f.outf("/CreationDate %s", f.textstring("D:"+creation.Format("20060102150405")))
4646	mod := timeOrNow(f.modDate)
4647	f.outf("/ModDate %s", f.textstring("D:"+mod.Format("20060102150405")))
4648}
4649
4650func (f *Fpdf) putcatalog() {
4651	f.out("/Type /Catalog")
4652	f.out("/Pages 1 0 R")
4653	switch f.zoomMode {
4654	case "fullpage":
4655		f.out("/OpenAction [3 0 R /Fit]")
4656	case "fullwidth":
4657		f.out("/OpenAction [3 0 R /FitH null]")
4658	case "real":
4659		f.out("/OpenAction [3 0 R /XYZ null null 1]")
4660	}
4661	// } 	else if !is_string($this->zoomMode))
4662	// 		$this->out('/OpenAction [3 0 R /XYZ null null '.sprintf('%.2f',$this->zoomMode/100).']');
4663	switch f.layoutMode {
4664	case "single", "SinglePage":
4665		f.out("/PageLayout /SinglePage")
4666	case "continuous", "OneColumn":
4667		f.out("/PageLayout /OneColumn")
4668	case "two", "TwoColumnLeft":
4669		f.out("/PageLayout /TwoColumnLeft")
4670	case "TwoColumnRight":
4671		f.out("/PageLayout /TwoColumnRight")
4672	case "TwoPageLeft", "TwoPageRight":
4673		if f.pdfVersion < "1.5" {
4674			f.pdfVersion = "1.5"
4675		}
4676		f.out("/PageLayout /" + f.layoutMode)
4677	}
4678	// Bookmarks
4679	if len(f.outlines) > 0 {
4680		f.outf("/Outlines %d 0 R", f.outlineRoot)
4681		f.out("/PageMode /UseOutlines")
4682	}
4683	// Layers
4684	f.layerPutCatalog()
4685	// Name dictionary :
4686	//	-> Javascript
4687	//	-> Embedded files
4688	f.out("/Names <<")
4689	// JavaScript
4690	if f.javascript != nil {
4691		f.outf("/JavaScript %d 0 R", f.nJs)
4692	}
4693	// Embedded files
4694	f.outf("/EmbeddedFiles %s", f.getEmbeddedFiles())
4695	f.out(">>")
4696}
4697
4698func (f *Fpdf) putheader() {
4699	if len(f.blendMap) > 0 && f.pdfVersion < "1.4" {
4700		f.pdfVersion = "1.4"
4701	}
4702	f.outf("%%PDF-%s", f.pdfVersion)
4703}
4704
4705func (f *Fpdf) puttrailer() {
4706	f.outf("/Size %d", f.n+1)
4707	f.outf("/Root %d 0 R", f.n)
4708	f.outf("/Info %d 0 R", f.n-1)
4709	if f.protect.encrypted {
4710		f.outf("/Encrypt %d 0 R", f.protect.objNum)
4711		f.out("/ID [()()]")
4712	}
4713}
4714
4715func (f *Fpdf) putxmp() {
4716	if len(f.xmp) == 0 {
4717		return
4718	}
4719	f.newobj()
4720	f.outf("<< /Type /Metadata /Subtype /XML /Length %d >>", len(f.xmp))
4721	f.putstream(f.xmp)
4722	f.out("endobj")
4723}
4724
4725func (f *Fpdf) putbookmarks() {
4726	nb := len(f.outlines)
4727	if nb > 0 {
4728		lru := make(map[int]int)
4729		level := 0
4730		for i, o := range f.outlines {
4731			if o.level > 0 {
4732				parent := lru[o.level-1]
4733				f.outlines[i].parent = parent
4734				f.outlines[parent].last = i
4735				if o.level > level {
4736					f.outlines[parent].first = i
4737				}
4738			} else {
4739				f.outlines[i].parent = nb
4740			}
4741			if o.level <= level && i > 0 {
4742				prev := lru[o.level]
4743				f.outlines[prev].next = i
4744				f.outlines[i].prev = prev
4745			}
4746			lru[o.level] = i
4747			level = o.level
4748		}
4749		n := f.n + 1
4750		for _, o := range f.outlines {
4751			f.newobj()
4752			f.outf("<</Title %s", f.textstring(o.text))
4753			f.outf("/Parent %d 0 R", n+o.parent)
4754			if o.prev != -1 {
4755				f.outf("/Prev %d 0 R", n+o.prev)
4756			}
4757			if o.next != -1 {
4758				f.outf("/Next %d 0 R", n+o.next)
4759			}
4760			if o.first != -1 {
4761				f.outf("/First %d 0 R", n+o.first)
4762			}
4763			if o.last != -1 {
4764				f.outf("/Last %d 0 R", n+o.last)
4765			}
4766			f.outf("/Dest [%d 0 R /XYZ 0 %.2f null]", 1+2*o.p, (f.h-o.y)*f.k)
4767			f.out("/Count 0>>")
4768			f.out("endobj")
4769		}
4770		f.newobj()
4771		f.outlineRoot = f.n
4772		f.outf("<</Type /Outlines /First %d 0 R", n)
4773		f.outf("/Last %d 0 R>>", n+lru[0])
4774		f.out("endobj")
4775	}
4776}
4777
4778func (f *Fpdf) enddoc() {
4779	if f.err != nil {
4780		return
4781	}
4782	f.layerEndDoc()
4783	f.putheader()
4784	// Embedded files
4785	f.putAttachments()
4786	f.putAnnotationsAttachments()
4787	f.putpages()
4788	f.putresources()
4789	if f.err != nil {
4790		return
4791	}
4792	// Bookmarks
4793	f.putbookmarks()
4794	// Metadata
4795	f.putxmp()
4796	// 	Info
4797	f.newobj()
4798	f.out("<<")
4799	f.putinfo()
4800	f.out(">>")
4801	f.out("endobj")
4802	// 	Catalog
4803	f.newobj()
4804	f.out("<<")
4805	f.putcatalog()
4806	f.out(">>")
4807	f.out("endobj")
4808	// Cross-ref
4809	o := f.buffer.Len()
4810	f.out("xref")
4811	f.outf("0 %d", f.n+1)
4812	f.out("0000000000 65535 f ")
4813	for j := 1; j <= f.n; j++ {
4814		f.outf("%010d 00000 n ", f.offsets[j])
4815	}
4816	// Trailer
4817	f.out("trailer")
4818	f.out("<<")
4819	f.puttrailer()
4820	f.out(">>")
4821	f.out("startxref")
4822	f.outf("%d", o)
4823	f.out("%%EOF")
4824	f.state = 3
4825	return
4826}
4827
4828// Path Drawing
4829
4830// MoveTo moves the stylus to (x, y) without drawing the path from the
4831// previous point. Paths must start with a MoveTo to set the original
4832// stylus location or the result is undefined.
4833//
4834// Create a "path" by moving a virtual stylus around the page (with
4835// MoveTo, LineTo, CurveTo, CurveBezierCubicTo, ArcTo & ClosePath)
4836// then draw it or  fill it in (with DrawPath). The main advantage of
4837// using the path drawing routines rather than multiple Fpdf.Line is
4838// that PDF creates nice line joins at the angles, rather than just
4839// overlaying the lines.
4840func (f *Fpdf) MoveTo(x, y float64) {
4841	f.point(x, y)
4842	f.x, f.y = x, y
4843}
4844
4845// LineTo creates a line from the current stylus location to (x, y), which
4846// becomes the new stylus location. Note that this only creates the line in
4847// the path; it does not actually draw the line on the page.
4848//
4849// The MoveTo() example demonstrates this method.
4850func (f *Fpdf) LineTo(x, y float64) {
4851	f.outf("%.2f %.2f l", x*f.k, (f.h-y)*f.k)
4852	f.x, f.y = x, y
4853}
4854
4855// CurveTo creates a single-segment quadratic Bézier curve. The curve starts at
4856// the current stylus location and ends at the point (x, y). The control point
4857// (cx, cy) specifies the curvature. At the start point, the curve is tangent
4858// to the straight line between the current stylus location and the control
4859// point. At the end point, the curve is tangent to the straight line between
4860// the end point and the control point.
4861//
4862// The MoveTo() example demonstrates this method.
4863func (f *Fpdf) CurveTo(cx, cy, x, y float64) {
4864	f.outf("%.5f %.5f %.5f %.5f v", cx*f.k, (f.h-cy)*f.k, x*f.k, (f.h-y)*f.k)
4865	f.x, f.y = x, y
4866}
4867
4868// CurveBezierCubicTo creates a single-segment cubic Bézier curve. The curve
4869// starts at the current stylus location and ends at the point (x, y). The
4870// control points (cx0, cy0) and (cx1, cy1) specify the curvature. At the
4871// current stylus, the curve is tangent to the straight line between the
4872// current stylus location and the control point (cx0, cy0). At the end point,
4873// the curve is tangent to the straight line between the end point and the
4874// control point (cx1, cy1).
4875//
4876// The MoveTo() example demonstrates this method.
4877func (f *Fpdf) CurveBezierCubicTo(cx0, cy0, cx1, cy1, x, y float64) {
4878	f.curve(cx0, cy0, cx1, cy1, x, y)
4879	f.x, f.y = x, y
4880}
4881
4882// ClosePath creates a line from the current location to the last MoveTo point
4883// (if not the same) and mark the path as closed so the first and last lines
4884// join nicely.
4885//
4886// The MoveTo() example demonstrates this method.
4887func (f *Fpdf) ClosePath() {
4888	f.outf("h")
4889}
4890
4891// DrawPath actually draws the path on the page.
4892//
4893// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
4894// outlined and filled. An empty string will be replaced with "D".
4895// Path-painting operators as defined in the PDF specification are also
4896// allowed: "S" (Stroke the path), "s" (Close and stroke the path),
4897// "f" (fill the path, using the nonzero winding number), "f*"
4898// (Fill the path, using the even-odd rule), "B" (Fill and then stroke
4899// the path, using the nonzero winding number rule), "B*" (Fill and
4900// then stroke the path, using the even-odd rule), "b" (Close, fill,
4901// and then stroke the path, using the nonzero winding number rule) and
4902// "b*" (Close, fill, and then stroke the path, using the even-odd
4903// rule).
4904// Drawing uses the current draw color, line width, and cap style
4905// centered on the
4906// path. Filling uses the current fill color.
4907//
4908// The MoveTo() example demonstrates this method.
4909func (f *Fpdf) DrawPath(styleStr string) {
4910	f.outf(fillDrawOp(styleStr))
4911}
4912
4913// ArcTo draws an elliptical arc centered at point (x, y). rx and ry specify its
4914// horizontal and vertical radii. If the start of the arc is not at
4915// the current position, a connecting line will be drawn.
4916//
4917// degRotate specifies the angle that the arc will be rotated. degStart and
4918// degEnd specify the starting and ending angle of the arc. All angles are
4919// specified in degrees and measured counter-clockwise from the 3 o'clock
4920// position.
4921//
4922// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
4923// outlined and filled. An empty string will be replaced with "D". Drawing uses
4924// the current draw color, line width, and cap style centered on the arc's
4925// path. Filling uses the current fill color.
4926//
4927// The MoveTo() example demonstrates this method.
4928func (f *Fpdf) ArcTo(x, y, rx, ry, degRotate, degStart, degEnd float64) {
4929	f.arc(x, y, rx, ry, degRotate, degStart, degEnd, "", true)
4930}
4931
4932func (f *Fpdf) arc(x, y, rx, ry, degRotate, degStart, degEnd float64,
4933	styleStr string, path bool) {
4934	x *= f.k
4935	y = (f.h - y) * f.k
4936	rx *= f.k
4937	ry *= f.k
4938	segments := int(degEnd-degStart) / 60
4939	if segments < 2 {
4940		segments = 2
4941	}
4942	angleStart := degStart * math.Pi / 180
4943	angleEnd := degEnd * math.Pi / 180
4944	angleTotal := angleEnd - angleStart
4945	dt := angleTotal / float64(segments)
4946	dtm := dt / 3
4947	if degRotate != 0 {
4948		a := -degRotate * math.Pi / 180
4949		f.outf("q %.5f %.5f %.5f %.5f %.5f %.5f cm",
4950			math.Cos(a), -1*math.Sin(a),
4951			math.Sin(a), math.Cos(a), x, y)
4952		x = 0
4953		y = 0
4954	}
4955	t := angleStart
4956	a0 := x + rx*math.Cos(t)
4957	b0 := y + ry*math.Sin(t)
4958	c0 := -rx * math.Sin(t)
4959	d0 := ry * math.Cos(t)
4960	sx := a0 / f.k // start point of arc
4961	sy := f.h - (b0 / f.k)
4962	if path {
4963		if f.x != sx || f.y != sy {
4964			// Draw connecting line to start point
4965			f.LineTo(sx, sy)
4966		}
4967	} else {
4968		f.point(sx, sy)
4969	}
4970	for j := 1; j <= segments; j++ {
4971		// Draw this bit of the total curve
4972		t = (float64(j) * dt) + angleStart
4973		a1 := x + rx*math.Cos(t)
4974		b1 := y + ry*math.Sin(t)
4975		c1 := -rx * math.Sin(t)
4976		d1 := ry * math.Cos(t)
4977		f.curve((a0+(c0*dtm))/f.k,
4978			f.h-((b0+(d0*dtm))/f.k),
4979			(a1-(c1*dtm))/f.k,
4980			f.h-((b1-(d1*dtm))/f.k),
4981			a1/f.k,
4982			f.h-(b1/f.k))
4983		a0 = a1
4984		b0 = b1
4985		c0 = c1
4986		d0 = d1
4987		if path {
4988			f.x = a1 / f.k
4989			f.y = f.h - (b1 / f.k)
4990		}
4991	}
4992	if !path {
4993		f.out(fillDrawOp(styleStr))
4994	}
4995	if degRotate != 0 {
4996		f.out("Q")
4997	}
4998}
4999