1
16
17 package gofpdf
18
19
20
21
22
23
24 import (
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
45 var gl struct {
46 catalogSort bool
47 noCompress bool
48 creationDate time.Time
49 modDate time.Time
50 }
51
52 type fmtBuffer struct {
53 bytes.Buffer
54 }
55
56 func (b *fmtBuffer) printf(fmtStr string, args ...interface{}) {
57 b.Buffer.WriteString(fmt.Sprintf(fmtStr, args...))
58 }
59
60 func 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(""))
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))
96 f.links = make([]intLinkType, 0, 8)
97 f.links = append(f.links, intLinkType{})
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
116 f.coreFonts = map[string]bool{
117 "courier": true,
118 "helvetica": true,
119 "times": true,
120 "symbol": true,
121 "zapfdingbats": true,
122 }
123
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
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
159 switch orientationStr {
160 case "p", "portrait":
161 f.defOrientation = "P"
162 f.w = f.defPageSize.Wd
163 f.h = f.defPageSize.Ht
164
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
177 margin := 28.35 / f.k
178 f.SetMargins(margin, margin, margin)
179
180 f.cMargin = margin / 10
181
182 f.lineWidth = 0.567 / f.k
183
184 f.SetAutoPageBreak(true, 2*margin)
185
186 f.SetDisplayMode("default", "default")
187 if f.err != nil {
188 return
189 }
190 f.acceptPageBreak = func() bool {
191 return f.autoPageBreak
192 }
193
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{})
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{})
203
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
215
216
217
218 func NewCustom(init *InitType) (f *Fpdf) {
219 return fpdfNew(init.OrientationStr, init.UnitStr, init.SizeStr, init.FontDirStr, init.Size)
220 }
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242 func New(orientationStr, unitStr, sizeStr, fontDirStr string) (f *Fpdf) {
243 return fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr, SizeType{0, 0})
244 }
245
246
247 func (f *Fpdf) Ok() bool {
248 return f.err == nil
249 }
250
251
252 func (f *Fpdf) Err() bool {
253 return f.err != nil
254 }
255
256
257
258
259
260 func (f *Fpdf) ClearError() {
261 f.err = nil
262 }
263
264
265
266
267
268
269
270 func (f *Fpdf) SetErrorf(fmtStr string, args ...interface{}) {
271 if f.err == nil {
272 f.err = fmt.Errorf(fmtStr, args...)
273 }
274 }
275
276
277
278 func (f *Fpdf) String() string {
279 return "Fpdf " + cnFpdfVersion
280 }
281
282
283
284 func (f *Fpdf) SetError(err error) {
285 if f.err == nil && err != nil {
286 f.err = err
287 }
288 }
289
290
291 func (f *Fpdf) Error() error {
292 return f.err
293 }
294
295
296
297
298 func (f *Fpdf) GetPageSize() (width, height float64) {
299 width = f.w
300 height = f.h
301 return
302 }
303
304
305
306
307 func (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
316
317
318 func (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
328
329
330 func (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
338
339
340 func (f *Fpdf) GetCellMargin() float64 {
341 return f.cMargin
342 }
343
344
345
346
347 func (f *Fpdf) SetCellMargin(margin float64) {
348 f.cMargin = margin
349 }
350
351
352
353
354
355 func (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
388 f.defPageBoxes[t] = pb
389 }
390
391
392
393
394 func (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
399
400 func (f *Fpdf) SetPage(pageNum int) {
401 if (pageNum > 0) && (pageNum < len(f.pages)) {
402 f.page = pageNum
403 }
404 }
405
406
407
408
409 func (f *Fpdf) PageCount() int {
410 return len(f.pages) - 1
411 }
412
413
414
415 func (f *Fpdf) SetFontLocation(fontDirStr string) {
416 f.fontpath = fontDirStr
417 }
418
419
420
421
422
423
424 func (f *Fpdf) SetFontLoader(loader FontLoader) {
425 f.fontLoader = loader
426 }
427
428
429
430
431
432 func (f *Fpdf) SetHeaderFuncMode(fnc func(), homeMode bool) {
433 f.headerFnc = fnc
434 f.headerHomeMode = homeMode
435 }
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450 func (f *Fpdf) SetHeaderFunc(fnc func()) {
451 f.headerFnc = fnc
452 }
453
454
455
456
457
458
459
460
461
462
463 func (f *Fpdf) SetFooterFunc(fnc func()) {
464 f.footerFnc = fnc
465 f.footerFncLpi = nil
466 }
467
468
469
470
471
472
473
474
475 func (f *Fpdf) SetFooterFuncLpi(fnc func(lastPage bool)) {
476 f.footerFncLpi = fnc
477 f.footerFnc = nil
478 }
479
480
481
482 func (f *Fpdf) SetTopMargin(margin float64) {
483 f.tMargin = margin
484 }
485
486
487
488 func (f *Fpdf) SetRightMargin(margin float64) {
489 f.rMargin = margin
490 }
491
492
493
494
495 func (f *Fpdf) GetAutoPageBreak() (auto bool, margin float64) {
496 auto = f.autoPageBreak
497 margin = f.bMargin
498 return
499 }
500
501
502
503
504
505 func (f *Fpdf) SetAutoPageBreak(auto bool, margin float64) {
506 f.autoPageBreak = auto
507 f.bMargin = margin
508 f.pageBreakTrigger = f.h - margin
509 }
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530 func (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
555
556
557 func SetDefaultCompression(compress bool) {
558 gl.noCompress = !compress
559 }
560
561
562
563
564
565 func (f *Fpdf) SetCompression(compress bool) {
566 f.compress = compress
567 }
568
569
570
571 func (f *Fpdf) SetProducer(producerStr string, isUTF8 bool) {
572 if isUTF8 {
573 producerStr = utf8toutf16(producerStr)
574 }
575 f.producer = producerStr
576 }
577
578
579
580 func (f *Fpdf) SetTitle(titleStr string, isUTF8 bool) {
581 if isUTF8 {
582 titleStr = utf8toutf16(titleStr)
583 }
584 f.title = titleStr
585 }
586
587
588
589 func (f *Fpdf) SetSubject(subjectStr string, isUTF8 bool) {
590 if isUTF8 {
591 subjectStr = utf8toutf16(subjectStr)
592 }
593 f.subject = subjectStr
594 }
595
596
597
598 func (f *Fpdf) SetAuthor(authorStr string, isUTF8 bool) {
599 if isUTF8 {
600 authorStr = utf8toutf16(authorStr)
601 }
602 f.author = authorStr
603 }
604
605
606
607
608 func (f *Fpdf) SetKeywords(keywordsStr string, isUTF8 bool) {
609 if isUTF8 {
610 keywordsStr = utf8toutf16(keywordsStr)
611 }
612 f.keywords = keywordsStr
613 }
614
615
616
617 func (f *Fpdf) SetCreator(creatorStr string, isUTF8 bool) {
618 if isUTF8 {
619 creatorStr = utf8toutf16(creatorStr)
620 }
621 f.creator = creatorStr
622 }
623
624
625 func (f *Fpdf) SetXmpMetadata(xmpStream []byte) {
626 f.xmp = xmpStream
627 }
628
629
630
631
632
633
634 func (f *Fpdf) AliasNbPages(aliasStr string) {
635 if aliasStr == "" {
636 aliasStr = "{nb}"
637 }
638 f.aliasNbPagesStr = aliasStr
639 }
640
641
642 func (f *Fpdf) RTL() {
643 f.isRTL = true
644 }
645
646
647 func (f *Fpdf) LTR() {
648 f.isRTL = false
649 }
650
651
652 func (f *Fpdf) open() {
653 f.state = 1
654 }
655
656
657
658
659
660 func (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
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
690 f.endpage()
691
692 f.enddoc()
693 return
694 }
695
696
697
698
699
700
701 func (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
707 }
708 return sz.Wd, sz.Ht, f.unitStr
709 }
710
711
712
713
714
715
716
717
718
719 func (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
747 if f.footerFnc != nil {
748 f.footerFnc()
749
750 } else if f.footerFncLpi != nil {
751 f.footerFncLpi(false)
752 }
753 f.inFooter = false
754
755 f.endpage()
756 }
757
758 f.beginpage(orientationStr, size)
759
760
761 f.outf("%d J", f.capStyle)
762
763 f.outf("%d j", f.joinStyle)
764
765 f.lineWidth = lw
766 f.outf("%.2f w", lw*f.k)
767
768 if len(f.dashArray) > 0 {
769 f.outputDashPattern()
770 }
771
772 if familyStr != "" {
773 f.SetFont(familyStr, style, fontsize)
774 if f.err != nil {
775 return
776 }
777 }
778
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
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
799 if f.lineWidth != lw {
800 f.lineWidth = lw
801 f.outf("%.2f w", lw*f.k)
802 }
803
804 if familyStr != "" {
805 f.SetFont(familyStr, style, fontsize)
806 if f.err != nil {
807 return
808 }
809 }
810
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
825
826
827
828
829
830
831
832
833
834
835
836
837
838 func (f *Fpdf) AddPage() {
839 if f.err != nil {
840 return
841 }
842
843 f.AddPageFormat(f.defOrientation, f.defPageSize)
844 return
845 }
846
847
848
849
850 func (f *Fpdf) PageNo() int {
851 return f.page
852 }
853
854 func 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
863 func 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
882
883
884
885 func (f *Fpdf) SetDrawColor(r, g, b int) {
886 f.setDrawColor(r, g, b)
887 }
888
889 func (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
897
898
899 func (f *Fpdf) GetDrawColor() (int, int, int) {
900 return f.color.draw.ir, f.color.draw.ig, f.color.draw.ib
901 }
902
903
904
905
906
907 func (f *Fpdf) SetFillColor(r, g, b int) {
908 f.setFillColor(r, g, b)
909 }
910
911 func (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
920
921
922 func (f *Fpdf) GetFillColor() (int, int, int) {
923 return f.color.fill.ir, f.color.fill.ig, f.color.fill.ib
924 }
925
926
927
928
929 func (f *Fpdf) SetTextColor(r, g, b int) {
930 f.setTextColor(r, g, b)
931 }
932
933 func (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
939
940
941 func (f *Fpdf) GetTextColor() (int, int, int) {
942 return f.color.text.ir, f.color.text.ig, f.color.text.ib
943 }
944
945
946
947 func (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
956
957 func (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
988
989
990 func (f *Fpdf) SetLineWidth(width float64) {
991 f.setLineWidth(width)
992 }
993
994 func (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
1002 func (f *Fpdf) GetLineWidth() float64 {
1003 return f.lineWidth
1004 }
1005
1006
1007
1008
1009
1010 func (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
1027
1028
1029 func (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
1046
1047
1048
1049
1050
1051
1052
1053 func (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
1068 func (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
1084
1085 func (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
1090 func fillDrawOp(styleStr string) (opStr string) {
1091 switch strings.ToUpper(styleStr) {
1092 case "", "D":
1093
1094 opStr = "S"
1095 case "F":
1096
1097 opStr = "f"
1098 case "F*":
1099
1100 opStr = "f*"
1101 case "FD", "DF":
1102
1103 opStr = "B"
1104 case "FD*", "DF*":
1105
1106 opStr = "B*"
1107 default:
1108 opStr = styleStr
1109 }
1110 return
1111 }
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121 func (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
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135 func (f *Fpdf) RoundedRect(x, y, w, h, r float64, corners string, stylestr string) {
1136
1137 var rTL, rTR, rBR, rBL float64
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
1154
1155
1156
1157 func (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
1163
1164
1165
1166
1167
1168 func (f *Fpdf) Circle(x, y, r float64, styleStr string) {
1169 f.Ellipse(x, y, r, r, 0, styleStr)
1170 }
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184 func (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
1189
1190
1191
1192
1193
1194
1195
1196
1197 func (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
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222 func (f *Fpdf) Beziergon(points []PointType, styleStr string) {
1223
1224
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
1244 func (f *Fpdf) point(x, y float64) {
1245 f.outf("%.2f %.2f m", x*f.k, (f.h-y)*f.k)
1246 }
1247
1248
1249 func (f *Fpdf) curve(cx0, cy0, cx1, cy1, x, y float64) {
1250
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
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268 func (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
1275
1276
1277 func (f *Fpdf) CurveCubic(x0, y0, cx0, cy0, x1, y1, cx1, cy1 float64, styleStr string) {
1278
1279
1280
1281 f.CurveBezierCubic(x0, y0, cx0, cy0, cx1, cy1, x1, y1, styleStr)
1282 }
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300 func (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
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320 func (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
1325
1326
1327 func (f *Fpdf) GetAlpha() (alpha float64, blendModeStr string) {
1328 return f.alpha, f.blendMode
1329 }
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344 func (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)
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
1377 func (f *Fpdf) gradientClipStart(x, y, w, h float64) {
1378
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
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
1384 func (f *Fpdf) gradientClipEnd() {
1385
1386 f.out("Q")
1387 }
1388
1389 func (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
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414 func (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
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438 func (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
1445
1446
1447
1448
1449
1450
1451
1452
1453 func (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
1459
1460
1461
1462
1463
1464
1465
1466 func (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
1471 func (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
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487 func (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
1492
1493
1494
1495 func (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
1502
1503
1504 func (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
1536
1537
1538
1539
1540
1541
1542
1543
1544 func (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
1571
1572
1573
1574
1575
1576
1577
1578 func (f *Fpdf) ClipCircle(x, y, r float64, outline bool) {
1579 f.ClipEllipse(x, y, r, r, outline)
1580 }
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593 func (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
1607
1608
1609
1610
1611
1612 func (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
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643 func (f *Fpdf) AddFont(familyStr, styleStr, fileStr string) {
1644 f.addFont(fontFamilyEscape(familyStr), styleStr, fileStr, false)
1645 }
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667 func (f *Fpdf) AddUTF8Font(familyStr, styleStr, fileStr string) {
1668 f.addFont(fontFamilyEscape(familyStr), styleStr, fileStr, true)
1669 }
1670
1671 func (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
1770 func 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
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793 func (f *Fpdf) AddFontFromBytes(familyStr, styleStr string, jsonFileBytes, zFileBytes []byte) {
1794 f.addFontFromBytes(fontFamilyEscape(familyStr), styleStr, jsonFileBytes, zFileBytes, nil)
1795 }
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812 func (f *Fpdf) AddUTF8FontFromBytes(familyStr, styleStr string, utf8Bytes []byte) {
1813 f.addFontFromBytes(fontFamilyEscape(familyStr), styleStr, nil, nil, utf8Bytes)
1814 }
1815
1816 func (f *Fpdf) addFontFromBytes(familyStr, styleStr string, jsonFileBytes, zFileBytes, utf8Bytes []byte) {
1817 if f.err != nil {
1818 return
1819 }
1820
1821
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
1833
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
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
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
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
1935 func 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
1945
1946
1947 func (f *Fpdf) AddFontFromReader(familyStr, styleStr string, r io.Reader) {
1948 if f.err != nil {
1949 return
1950 }
1951
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
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
1980 if len(info.File) > 0 {
1981
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
1993
1994
1995
1996
1997 func (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
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031 func (f *Fpdf) SetFont(familyStr, styleStr string, size float64) {
2032
2033
2034 if f.err != nil {
2035 return
2036 }
2037
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
2062 fontKey := familyStr + styleStr
2063 _, ok = f.fonts[fontKey]
2064 if !ok {
2065
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
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
2111 func (f *Fpdf) SetFontStyle(styleStr string) {
2112 f.SetFont(f.fontFamily, styleStr, f.fontSizePt)
2113 }
2114
2115
2116
2117 func (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
2126
2127 func (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
2136
2137
2138 func (f *Fpdf) GetFontSize() (ptSize, unitSize float64) {
2139 return f.fontSizePt, f.fontSize
2140 }
2141
2142
2143
2144
2145
2146 func (f *Fpdf) AddLink() int {
2147 f.links = append(f.links, intLinkType{})
2148 return len(f.links) - 1
2149 }
2150
2151
2152 func (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
2163 func (f *Fpdf) newLink(x, y, w, h float64, link int, linkStr string) {
2164
2165
2166
2167
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
2174
2175
2176
2177 func (f *Fpdf) Link(x, y, w, h float64, link int) {
2178 f.newLink(x, y, w, h, link, "")
2179 }
2180
2181
2182
2183
2184
2185 func (f *Fpdf) LinkString(x, y, w, h float64, linkStr string) {
2186 f.newLink(x, y, w, h, 0, linkStr)
2187 }
2188
2189
2190
2191
2192
2193
2194 func (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
2205
2206
2207
2208 func (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
2236
2237 func (f *Fpdf) SetWordSpacing(space float64) {
2238 f.out(sprintf("%.5f Tw", space*f.k))
2239 }
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252 func (f *Fpdf) SetTextRenderingMode(mode int) {
2253 if mode >= 0 && mode <= 7 {
2254 f.out(sprintf("%d Tr", mode))
2255 }
2256 }
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269 func (f *Fpdf) SetAcceptPageBreakFunc(fnc func() bool) {
2270 f.acceptPageBreak = fnc
2271 }
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312 func (f *Fpdf) CellFormat(w, h float64, txtStr, borderStr string, ln int,
2313 alignStr string, fill bool, link int, linkStr string) {
2314
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
2328 x := f.x
2329 ws := f.ws
2330
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
2355 } else {
2356 op = "f"
2357
2358 }
2359 } else {
2360
2361 op = "S"
2362 }
2363
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
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
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
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
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
2422 if (f.ws != 0 || alignStr == "J") && f.isCurrentUTF8 {
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
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
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
2498 func 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
2509
2510 func (f *Fpdf) Cell(w, h float64, txtStr string) {
2511 f.CellFormat(w, h, txtStr, "", 0, "L", false, 0, "")
2512 }
2513
2514
2515
2516
2517 func (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
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531 func (f *Fpdf) SplitLines(txt []byte, w float64) [][]byte {
2532
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
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598 func (f *Fpdf) MultiCell(w, h float64, txtStr, borderStr, alignStr string, fill bool) {
2599 if f.err != nil {
2600 return
2601 }
2602
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
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
2627
2628
2629
2630
2631
2632
2633 if nb > 0 && bytes2[nb-1] == '\n' {
2634 nb--
2635 }
2636 s = s[0:nb]
2637 }
2638
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
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
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 {
2717 l += f.currentFont.Desc.MissingWidth
2718 } else if cw[int(c)] != 65535 {
2719 l += cw[int(c)]
2720 }
2721 if l > wmax {
2722
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
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
2788 func (f *Fpdf) write(h float64, txtStr string, link int, linkStr string) {
2789
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
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
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
2842 if sep == -1 {
2843 if f.x > f.lMargin {
2844
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
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
2893
2894
2895
2896
2897
2898
2899
2900 func (f *Fpdf) Write(h float64, txtStr string) {
2901 f.write(h, txtStr, 0, "")
2902 }
2903
2904
2905
2906 func (f *Fpdf) Writef(h float64, fmtStr string, args ...interface{}) {
2907 f.write(h, sprintf(fmtStr, args...), 0, "")
2908 }
2909
2910
2911
2912 func (f *Fpdf) WriteLinkString(h float64, displayStr, targetStr string) {
2913 f.write(h, displayStr, 0, targetStr)
2914 }
2915
2916
2917
2918
2919 func (f *Fpdf) WriteLinkID(h float64, displayStr string, linkID int) {
2920 f.write(h, displayStr, linkID, "")
2921 }
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935 func (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
2975
2976
2977
2978
2979 func (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
2989
2990
2991
2992 func (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
3008 func (f *Fpdf) imageOut(info *ImageInfoType, x, y, w, h float64, allowNegativeX, flow bool, link int, linkStr string) {
3009
3010 if w == 0 && h == 0 {
3011
3012 w = -96
3013 h = -96
3014 }
3015 if w == -1 {
3016
3017
3018 w = -info.dpi
3019 }
3020 if h == -1 {
3021
3022
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
3038 if flow {
3039 if f.y+h > f.pageBreakTrigger && !f.inHeader && !f.inFooter && f.acceptPageBreak() {
3040
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
3057
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
3065
3066
3067
3068 func (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
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112 func (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
3125
3126
3127
3128 func (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
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151 type ImageOptions struct {
3152 ImageType string
3153 ReadDpi bool
3154 AllowNegativePosition bool
3155 }
3156
3157
3158
3159
3160
3161
3162
3163 func (f *Fpdf) RegisterImageOptionsReader(imgName string, options ImageOptions, r io.Reader) (info *ImageInfoType) {
3164
3165 if f.err != nil {
3166 return
3167 }
3168 info, ok := f.images[imgName]
3169 if ok {
3170 return
3171 }
3172
3173
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
3205
3206
3207
3208
3209
3210
3211 func (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
3220
3221
3222
3223
3224 func (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
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
3251
3252
3253 func (f *Fpdf) GetImageInfo(imageStr string) (info *ImageInfoType) {
3254 return f.images[imageStr]
3255 }
3256
3257
3258 func (f *Fpdf) ImportObjects(objs map[string][]byte) {
3259 for k, v := range objs {
3260 f.importedObjs[k] = v
3261 }
3262 }
3263
3264
3265 func (f *Fpdf) ImportObjPos(objPos map[string]map[int]string) {
3266 for k, v := range objPos {
3267 f.importedObjPos[k] = v
3268 }
3269 }
3270
3271
3272 func (f *Fpdf) putImportedTemplates() {
3273 nOffset := f.n + 1
3274
3275
3276 objsIDHash := make([]string, len(f.importedObjs))
3277
3278
3279 objsIDData := make([][]byte, len(f.importedObjs))
3280
3281
3282 i := 0
3283 for k, v := range f.importedObjs {
3284 objsIDHash[i] = k
3285 objsIDData[i] = v
3286
3287 i++
3288 }
3289
3290
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
3297 for i = 0; i < len(objsIDData); i++ {
3298
3299 hash := objsIDHash[i]
3300
3301 for pos, h := range f.importedObjPos[hash] {
3302
3303 objIDPadded := fmt.Sprintf("%40s", fmt.Sprintf("%d", hashToObjID[h]))
3304
3305
3306 objIDBytes := []byte(objIDPadded)
3307
3308
3309 for j := pos; j < pos+40; j++ {
3310 objsIDData[i][j] = objIDBytes[j-pos]
3311 }
3312 }
3313
3314
3315 f.importedTplIDs[hash] = i + nOffset
3316 }
3317
3318
3319 for i = 0; i < len(objsIDData); i++ {
3320 f.newobj()
3321 f.out(string(objsIDData[i]))
3322 }
3323 }
3324
3325
3326
3327 func (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
3332
3333 func (f *Fpdf) ImportTemplates(tpls map[string]string) {
3334 for tplName, tplID := range tpls {
3335 f.importedTplObjs[tplName] = tplID
3336 }
3337 }
3338
3339
3340
3341 func (f *Fpdf) GetConversionRatio() float64 {
3342 return f.k
3343 }
3344
3345
3346
3347
3348
3349
3350
3351 func (f *Fpdf) GetXY() (float64, float64) {
3352 return f.x, f.y
3353 }
3354
3355
3356
3357
3358
3359
3360 func (f *Fpdf) GetX() float64 {
3361 return f.x
3362 }
3363
3364
3365
3366 func (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
3375 func (f *Fpdf) GetY() float64 {
3376 return f.y
3377 }
3378
3379
3380
3381
3382 func (f *Fpdf) SetY(y float64) {
3383
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
3393
3394 func (f *Fpdf) SetHomeXY() {
3395 f.SetY(f.tMargin)
3396 f.SetX(f.lMargin)
3397 }
3398
3399
3400
3401
3402 func (f *Fpdf) SetXY(x, y float64) {
3403 f.SetY(y)
3404 f.SetX(x)
3405 }
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425 func (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
3433
3434
3435 func (f *Fpdf) OutputAndClose(w io.WriteCloser) error {
3436 f.Output(w)
3437 w.Close()
3438 return f.err
3439 }
3440
3441
3442
3443
3444
3445
3446 func (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
3460
3461
3462
3463 func (f *Fpdf) Output(w io.Writer) error {
3464 if f.err != nil {
3465 return f.err
3466 }
3467
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
3478 func (f *Fpdf) getpagesizestr(sizeStr string) (size SizeType) {
3479 if f.err != nil {
3480 return
3481 }
3482 sizeStr = strings.ToLower(sizeStr)
3483
3484 var ok bool
3485 size, ok = f.stdPageSizes[sizeStr]
3486 if ok {
3487
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
3498 func (f *Fpdf) GetPageSizeStr(sizeStr string) (size SizeType) {
3499 return f.getpagesizestr(sizeStr)
3500 }
3501
3502 func (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
3509 func (f *Fpdf) beginpage(orientationStr string, size SizeType) {
3510 if f.err != nil {
3511 return
3512 }
3513 f.page++
3514
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
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
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
3553 func (f *Fpdf) endpage() {
3554 f.EndLayer()
3555 f.state = 1
3556 }
3557
3558
3559 func (f *Fpdf) loadfont(r io.Reader) (def fontDefType) {
3560 if f.err != nil {
3561 return
3562 }
3563
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
3580 return
3581 }
3582
3583
3584 func (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
3593 func (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
3602 func 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
3613
3614 func (f *Fpdf) SetUnderlineThickness(thickness float64) {
3615 f.userUnderlineThickness = thickness
3616 }
3617
3618
3619 func (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
3627 func (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
3635 func bufEqual(buf []byte, str string) bool {
3636 return string(buf[0:len(str)]) == str
3637 }
3638
3639 func be16(buf []byte) int {
3640 return 256*int(buf[0]) + int(buf[1])
3641 }
3642
3643 func (f *Fpdf) newImageInfo() *ImageInfoType {
3644
3645 return &ImageInfoType{scale: f.k, dpi: 72}
3646 }
3647
3648
3649
3650 func (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
3687 func (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
3696 func (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
3704 func (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
3713 func (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
3735 func (f *Fpdf) newobj() {
3736
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
3745 func (f *Fpdf) putstream(b []byte) {
3746
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
3756 func (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
3767 func (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
3778
3779
3780
3781 func (f *Fpdf) RawWriteStr(str string) {
3782 f.out(str)
3783 }
3784
3785
3786
3787
3788
3789 func (f *Fpdf) RawWriteBuf(r io.Reader) {
3790 f.outbuf(r)
3791 }
3792
3793
3794 func (f *Fpdf) outf(fmtStr string, args ...interface{}) {
3795 f.out(sprintf(fmtStr, args...))
3796 }
3797
3798
3799
3800
3801 func SetDefaultCatalogSort(flag bool) {
3802 gl.catalogSort = flag
3803 }
3804
3805
3806
3807
3808 func (f *Fpdf) SetCatalogSort(flag bool) {
3809 f.catalogSort = flag
3810 }
3811
3812
3813
3814
3815 func SetDefaultCreationDate(tm time.Time) {
3816 gl.creationDate = tm
3817 }
3818
3819
3820
3821
3822 func SetDefaultModificationDate(tm time.Time) {
3823 gl.modDate = tm
3824 }
3825
3826
3827
3828
3829
3830 func (f *Fpdf) SetCreationDate(tm time.Time) {
3831 f.creationDate = tm
3832 }
3833
3834
3835
3836 func (f *Fpdf) SetModificationDate(tm time.Time) {
3837 f.modDate = tm
3838 }
3839
3840
3841 func (f *Fpdf) SetJavascript(script string) {
3842 f.javascript = &script
3843 }
3844
3845
3846
3847
3848
3849 func (f *Fpdf) RegisterAlias(alias, replacement string) {
3850
3851
3852
3853
3854
3855 f.aliasMap[alias] = replacement
3856 }
3857
3858 func (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
3877 func (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
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)
3895 for n := 1; n <= nb; n++ {
3896
3897 f.newobj()
3898 pagesObjectNumbers[n] = f.n
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
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
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
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
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
3970 func (f *Fpdf) putfonts() {
3971 if f.err != nil {
3972 return
3973 }
3974 nf := f.n
3975 for _, diff := range f.diffs {
3976
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
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
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
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
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
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
4136 f.newobj()
4137 f.out("<</Registry (Adobe)\n/Ordering (UCS)\n/Supplement 0>>")
4138 f.out("endobj")
4139
4140
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
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
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
4191 func (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
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
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
4283
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
4308 func 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
4321 func 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
4329 func (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
4343 func (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
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
4356
4357 insertedImages := map[string]int{}
4358
4359 for _, key = range keyList {
4360 image := f.images[key]
4361
4362
4363 insertedImageObjN, isFound := insertedImages[image.i]
4364
4365
4366
4367
4368 if isFound {
4369 image.n = insertedImageObjN
4370 } else {
4371 f.putimage(image)
4372 insertedImages[image.i] = image.n
4373 }
4374 }
4375 }
4376
4377 func (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
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
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
4441 func (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
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
4474 f.outf("%s %d 0 R", tplName, f.importedTplIDs[objID])
4475 }
4476 }
4477 }
4478
4479 func (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
4518 f.layerPutResourceDict()
4519 f.spotColorPutResourceDict()
4520 }
4521
4522 func (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
4534 func (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
4559 func (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
4578 func (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()
4593
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
4618 func timeOrNow(tm time.Time) time.Time {
4619 if tm.IsZero() {
4620 return time.Now()
4621 }
4622 return tm
4623 }
4624
4625 func (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
4650 func (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
4662
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
4679 if len(f.outlines) > 0 {
4680 f.outf("/Outlines %d 0 R", f.outlineRoot)
4681 f.out("/PageMode /UseOutlines")
4682 }
4683
4684 f.layerPutCatalog()
4685
4686
4687
4688 f.out("/Names <<")
4689
4690 if f.javascript != nil {
4691 f.outf("/JavaScript %d 0 R", f.nJs)
4692 }
4693
4694 f.outf("/EmbeddedFiles %s", f.getEmbeddedFiles())
4695 f.out(">>")
4696 }
4697
4698 func (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
4705 func (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
4715 func (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
4725 func (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
4778 func (f *Fpdf) enddoc() {
4779 if f.err != nil {
4780 return
4781 }
4782 f.layerEndDoc()
4783 f.putheader()
4784
4785 f.putAttachments()
4786 f.putAnnotationsAttachments()
4787 f.putpages()
4788 f.putresources()
4789 if f.err != nil {
4790 return
4791 }
4792
4793 f.putbookmarks()
4794
4795 f.putxmp()
4796
4797 f.newobj()
4798 f.out("<<")
4799 f.putinfo()
4800 f.out(">>")
4801 f.out("endobj")
4802
4803 f.newobj()
4804 f.out("<<")
4805 f.putcatalog()
4806 f.out(">>")
4807 f.out("endobj")
4808
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
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
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840 func (f *Fpdf) MoveTo(x, y float64) {
4841 f.point(x, y)
4842 f.x, f.y = x, y
4843 }
4844
4845
4846
4847
4848
4849
4850 func (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
4856
4857
4858
4859
4860
4861
4862
4863 func (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
4869
4870
4871
4872
4873
4874
4875
4876
4877 func (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
4883
4884
4885
4886
4887 func (f *Fpdf) ClosePath() {
4888 f.outf("h")
4889 }
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909 func (f *Fpdf) DrawPath(styleStr string) {
4910 f.outf(fillDrawOp(styleStr))
4911 }
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928 func (f *Fpdf) ArcTo(x, y, rx, ry, degRotate, degStart, degEnd float64) {
4929 f.arc(x, y, rx, ry, degRotate, degStart, degEnd, "", true)
4930 }
4931
4932 func (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
4961 sy := f.h - (b0 / f.k)
4962 if path {
4963 if f.x != sx || f.y != sy {
4964
4965 f.LineTo(sx, sy)
4966 }
4967 } else {
4968 f.point(sx, sy)
4969 }
4970 for j := 1; j <= segments; j++ {
4971
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
View as plain text