fpdf 1.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/bookmark.rb +99 -0
- data/lib/chinese.rb +445 -0
- data/lib/fpdf.rb +1536 -0
- data/lib/fpdf_eps.rb +139 -0
- data/lib/makefont.rb +1787 -0
- metadata +50 -0
data/lib/fpdf.rb
ADDED
@@ -0,0 +1,1536 @@
|
|
1
|
+
# Ruby FPDF 1.53d
|
2
|
+
# FPDF 1.53 by Olivier Plathey ported to Ruby by Brian Ollenberger
|
3
|
+
# Copyright 2005 Brian Ollenberger
|
4
|
+
# Please retain this entire copyright notice. If you distribute any
|
5
|
+
# modifications, place an additional comment here that clearly indicates
|
6
|
+
# that it was modified. You may (but are not send any useful modifications that you make
|
7
|
+
# back to me at http://zeropluszero.com/software/fpdf/
|
8
|
+
|
9
|
+
# Bug fixes, examples, external fonts, JPEG support, and upgrade to version
|
10
|
+
# 1.53 contributed by Kim Shrier.
|
11
|
+
#
|
12
|
+
# Bookmark support contributed by Sylvain Lafleur.
|
13
|
+
#
|
14
|
+
# EPS support contributed by Thiago Jackiw, ported from the PHP version by Valentin Schmidt.
|
15
|
+
#
|
16
|
+
# Many other bug reports and fixes contributed by many other people.
|
17
|
+
|
18
|
+
require 'date'
|
19
|
+
require 'zlib'
|
20
|
+
|
21
|
+
class FPDF
|
22
|
+
FPDF_VERSION = '1.53d'
|
23
|
+
|
24
|
+
Charwidths = {
|
25
|
+
'courier'=>[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],
|
26
|
+
|
27
|
+
'courierB'=>[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],
|
28
|
+
|
29
|
+
'courierI'=>[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],
|
30
|
+
|
31
|
+
'courierBI'=>[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],
|
32
|
+
|
33
|
+
'helvetica'=>[278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 350, 556, 350, 222, 556, 333, 1000, 556, 556, 333, 1000, 667, 333, 1000, 350, 611, 350, 350, 222, 222, 333, 333, 350, 556, 1000, 333, 1000, 500, 333, 944, 350, 500, 667, 278, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 556, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611, 667, 667, 667, 667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278, 556, 556, 556, 556, 556, 556, 556, 584, 611, 556, 556, 556, 556, 500, 556, 500],
|
34
|
+
|
35
|
+
'helveticaB'=>[278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 350, 556, 350, 278, 556, 500, 1000, 556, 556, 333, 1000, 667, 333, 1000, 350, 611, 350, 350, 278, 278, 500, 500, 350, 556, 1000, 333, 1000, 556, 333, 944, 350, 500, 667, 278, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 611, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278, 611, 611, 611, 611, 611, 611, 611, 584, 611, 611, 611, 611, 611, 556, 611, 556],
|
36
|
+
|
37
|
+
'helveticaI'=>[278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 350, 556, 350, 222, 556, 333, 1000, 556, 556, 333, 1000, 667, 333, 1000, 350, 611, 350, 350, 222, 222, 333, 333, 350, 556, 1000, 333, 1000, 500, 333, 944, 350, 500, 667, 278, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 556, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611, 667, 667, 667, 667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278, 556, 556, 556, 556, 556, 556, 556, 584, 611, 556, 556, 556, 556, 500, 556, 500],
|
38
|
+
|
39
|
+
'helveticaBI'=>[278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 350, 556, 350, 278, 556, 500, 1000, 556, 556, 333, 1000, 667, 333, 1000, 350, 611, 350, 350, 278, 278, 500, 500, 350, 556, 1000, 333, 1000, 556, 333, 944, 350, 500, 667, 278, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 611, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278, 611, 611, 611, 611, 611, 611, 611, 584, 611, 611, 611, 611, 611, 556, 611, 556],
|
40
|
+
|
41
|
+
'times'=>[250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 408, 500, 500, 833, 778, 180, 333, 333, 500, 564, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 564, 564, 564, 444, 921, 722, 667, 667, 722, 611, 556, 722, 722, 333, 389, 722, 611, 889, 722, 722, 556, 722, 667, 556, 611, 722, 722, 944, 722, 722, 611, 333, 278, 333, 469, 500, 333, 444, 500, 444, 500, 444, 333, 500, 500, 278, 278, 500, 278, 778, 500, 500, 500, 500, 333, 389, 278, 500, 500, 722, 500, 500, 444, 480, 200, 480, 541, 350, 500, 350, 333, 500, 444, 1000, 500, 500, 333, 1000, 556, 333, 889, 350, 611, 350, 350, 333, 333, 444, 444, 350, 500, 1000, 333, 980, 389, 333, 722, 350, 444, 722, 250, 333, 500, 500, 500, 500, 200, 500, 333, 760, 276, 500, 564, 333, 760, 333, 400, 564, 300, 300, 333, 500, 453, 250, 333, 300, 310, 500, 750, 750, 750, 444, 722, 722, 722, 722, 722, 722, 889, 667, 611, 611, 611, 611, 333, 333, 333, 333, 722, 722, 722, 722, 722, 722, 722, 564, 722, 722, 722, 722, 722, 722, 556, 500, 444, 444, 444, 444, 444, 444, 667, 444, 444, 444, 444, 444, 278, 278, 278, 278, 500, 500, 500, 500, 500, 500, 500, 564, 500, 500, 500, 500, 500, 500, 500, 500],
|
42
|
+
|
43
|
+
'timesB'=>[250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 555, 500, 500, 1000, 833, 278, 333, 333, 500, 570, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 570, 570, 570, 500, 930, 722, 667, 722, 722, 667, 611, 778, 778, 389, 500, 778, 667, 944, 722, 778, 611, 778, 722, 556, 667, 722, 722, 1000, 722, 722, 667, 333, 278, 333, 581, 500, 333, 500, 556, 444, 556, 444, 333, 500, 556, 278, 333, 556, 278, 833, 556, 500, 556, 556, 444, 389, 333, 556, 500, 722, 500, 500, 444, 394, 220, 394, 520, 350, 500, 350, 333, 500, 500, 1000, 500, 500, 333, 1000, 556, 333, 1000, 350, 667, 350, 350, 333, 333, 500, 500, 350, 500, 1000, 333, 1000, 389, 333, 722, 350, 444, 722, 250, 333, 500, 500, 500, 500, 220, 500, 333, 747, 300, 500, 570, 333, 747, 333, 400, 570, 300, 300, 333, 556, 540, 250, 333, 300, 330, 500, 750, 750, 750, 500, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 389, 389, 389, 389, 722, 722, 778, 778, 778, 778, 778, 570, 778, 722, 722, 722, 722, 722, 611, 556, 500, 500, 500, 500, 500, 500, 722, 444, 444, 444, 444, 444, 278, 278, 278, 278, 500, 556, 500, 500, 500, 500, 500, 570, 500, 556, 556, 556, 556, 500, 556, 500],
|
44
|
+
|
45
|
+
'timesI'=>[250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 420, 500, 500, 833, 778, 214, 333, 333, 500, 675, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 675, 675, 675, 500, 920, 611, 611, 667, 722, 611, 611, 722, 722, 333, 444, 667, 556, 833, 667, 722, 611, 722, 611, 500, 556, 722, 611, 833, 611, 556, 556, 389, 278, 389, 422, 500, 333, 500, 500, 444, 500, 444, 278, 500, 500, 278, 278, 444, 278, 722, 500, 500, 500, 500, 389, 389, 278, 500, 444, 667, 444, 444, 389, 400, 275, 400, 541, 350, 500, 350, 333, 500, 556, 889, 500, 500, 333, 1000, 500, 333, 944, 350, 556, 350, 350, 333, 333, 556, 556, 350, 500, 889, 333, 980, 389, 333, 667, 350, 389, 556, 250, 389, 500, 500, 500, 500, 275, 500, 333, 760, 276, 500, 675, 333, 760, 333, 400, 675, 300, 300, 333, 500, 523, 250, 333, 300, 310, 500, 750, 750, 750, 500, 611, 611, 611, 611, 611, 611, 889, 667, 611, 611, 611, 611, 333, 333, 333, 333, 722, 667, 722, 722, 722, 722, 722, 675, 722, 722, 722, 722, 722, 556, 611, 500, 500, 500, 500, 500, 500, 500, 667, 444, 444, 444, 444, 444, 278, 278, 278, 278, 500, 500, 500, 500, 500, 500, 500, 675, 500, 500, 500, 500, 500, 444, 500, 444],
|
46
|
+
|
47
|
+
'timesBI'=>[250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 389, 555, 500, 500, 833, 778, 278, 333, 333, 500, 570, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 570, 570, 570, 500, 832, 667, 667, 667, 722, 667, 667, 722, 778, 389, 500, 667, 611, 889, 722, 722, 611, 722, 667, 556, 611, 722, 667, 889, 667, 611, 611, 333, 278, 333, 570, 500, 333, 500, 500, 444, 500, 444, 333, 500, 556, 278, 278, 500, 278, 778, 556, 500, 500, 500, 389, 389, 278, 556, 444, 667, 500, 444, 389, 348, 220, 348, 570, 350, 500, 350, 333, 500, 500, 1000, 500, 500, 333, 1000, 556, 333, 944, 350, 611, 350, 350, 333, 333, 500, 500, 350, 500, 1000, 333, 1000, 389, 333, 722, 350, 389, 611, 250, 389, 500, 500, 500, 500, 220, 500, 333, 747, 266, 500, 606, 333, 747, 333, 400, 570, 300, 300, 333, 576, 500, 250, 333, 300, 300, 500, 750, 750, 750, 500, 667, 667, 667, 667, 667, 667, 944, 667, 667, 667, 667, 667, 389, 389, 389, 389, 722, 722, 722, 722, 722, 722, 722, 570, 722, 722, 722, 722, 722, 611, 611, 500, 500, 500, 500, 500, 500, 500, 722, 444, 444, 444, 444, 444, 278, 278, 278, 278, 500, 556, 500, 500, 500, 500, 500, 570, 500, 556, 556, 556, 556, 444, 500, 444],
|
48
|
+
|
49
|
+
'symbol'=>[250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 713, 500, 549, 833, 778, 439, 333, 333, 500, 549, 250, 549, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 549, 549, 549, 444, 549, 722, 667, 722, 612, 611, 763, 603, 722, 333, 631, 722, 686, 889, 722, 722, 768, 741, 556, 592, 611, 690, 439, 768, 645, 795, 611, 333, 863, 333, 658, 500, 500, 631, 549, 549, 494, 439, 521, 411, 603, 329, 603, 549, 549, 576, 521, 549, 549, 521, 549, 603, 439, 576, 713, 686, 493, 686, 494, 480, 200, 480, 549, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 750, 620, 247, 549, 167, 713, 500, 753, 753, 753, 753, 1042, 987, 603, 987, 603, 400, 549, 411, 549, 549, 713, 494, 460, 549, 549, 549, 549, 1000, 603, 1000, 658, 823, 686, 795, 987, 768, 768, 823, 768, 768, 713, 713, 713, 713, 713, 713, 713, 768, 713, 790, 790, 890, 823, 549, 250, 713, 603, 603, 1042, 987, 603, 987, 603, 494, 329, 790, 790, 786, 713, 384, 384, 384, 384, 384, 384, 494, 494, 494, 494, 0, 329, 274, 686, 686, 686, 384, 384, 384, 384, 384, 384, 494, 494, 494, 0],
|
50
|
+
|
51
|
+
'zapfdingbats'=>[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 278, 974, 961, 974, 980, 719, 789, 790, 791, 690, 960, 939, 549, 855, 911, 933, 911, 945, 974, 755, 846, 762, 761, 571, 677, 763, 760, 759, 754, 494, 552, 537, 577, 692, 786, 788, 788, 790, 793, 794, 816, 823, 789, 841, 823, 833, 816, 831, 923, 744, 723, 749, 790, 792, 695, 776, 768, 792, 759, 707, 708, 682, 701, 826, 815, 789, 789, 707, 687, 696, 689, 786, 787, 713, 791, 785, 791, 873, 761, 762, 762, 759, 759, 892, 892, 788, 784, 438, 138, 277, 415, 392, 392, 668, 668, 0, 390, 390, 317, 317, 276, 276, 509, 509, 410, 410, 234, 234, 334, 334, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 732, 544, 544, 910, 667, 760, 760, 776, 595, 694, 626, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 894, 838, 1016, 458, 748, 924, 748, 918, 927, 928, 928, 834, 873, 828, 924, 924, 917, 930, 931, 463, 883, 836, 836, 867, 867, 696, 696, 874, 0, 874, 760, 946, 771, 865, 771, 888, 967, 888, 831, 873, 927, 970, 918, 0]
|
52
|
+
}
|
53
|
+
|
54
|
+
def initialize(orientation='P', unit='mm', format='A4')
|
55
|
+
# Initialization of properties
|
56
|
+
@page=0
|
57
|
+
@n=2
|
58
|
+
@buffer=''
|
59
|
+
@pages=[]
|
60
|
+
@OrientationChanges=[]
|
61
|
+
@state=0
|
62
|
+
@fonts={}
|
63
|
+
@FontFiles={}
|
64
|
+
@diffs=[]
|
65
|
+
@images={}
|
66
|
+
@links=[]
|
67
|
+
@PageLinks={}
|
68
|
+
@InFooter=false
|
69
|
+
@FontFamily=''
|
70
|
+
@FontStyle=''
|
71
|
+
@FontSizePt=12
|
72
|
+
@underline= false
|
73
|
+
@DrawColor='0 G'
|
74
|
+
@FillColor='0 g'
|
75
|
+
@TextColor='0 g'
|
76
|
+
@ColorFlag=false
|
77
|
+
@ws=0
|
78
|
+
@offsets=[]
|
79
|
+
|
80
|
+
# Standard fonts
|
81
|
+
@CoreFonts={}
|
82
|
+
@CoreFonts['courier']='Courier'
|
83
|
+
@CoreFonts['courierB']='Courier-Bold'
|
84
|
+
@CoreFonts['courierI']='Courier-Oblique'
|
85
|
+
@CoreFonts['courierBI']='Courier-BoldOblique'
|
86
|
+
@CoreFonts['helvetica']='Helvetica'
|
87
|
+
@CoreFonts['helveticaB']='Helvetica-Bold'
|
88
|
+
@CoreFonts['helveticaI']='Helvetica-Oblique'
|
89
|
+
@CoreFonts['helveticaBI']='Helvetica-BoldOblique'
|
90
|
+
@CoreFonts['times']='Times-Roman'
|
91
|
+
@CoreFonts['timesB']='Times-Bold'
|
92
|
+
@CoreFonts['timesI']='Times-Italic'
|
93
|
+
@CoreFonts['timesBI']='Times-BoldItalic'
|
94
|
+
@CoreFonts['symbol']='Symbol'
|
95
|
+
@CoreFonts['zapfdingbats']='ZapfDingbats'
|
96
|
+
|
97
|
+
# Scale factor
|
98
|
+
if unit=='pt'
|
99
|
+
@k=1
|
100
|
+
elsif unit=='mm'
|
101
|
+
@k=72/25.4
|
102
|
+
elsif unit=='cm'
|
103
|
+
@k=72/2.54;
|
104
|
+
elsif unit=='in'
|
105
|
+
@k=72
|
106
|
+
else
|
107
|
+
raise 'Incorrect unit: '+unit
|
108
|
+
end
|
109
|
+
|
110
|
+
# Page format
|
111
|
+
if format.is_a? String
|
112
|
+
format.downcase!
|
113
|
+
if format=='a3'
|
114
|
+
format=[841.89,1190.55]
|
115
|
+
elsif format=='a4'
|
116
|
+
format=[595.28,841.89]
|
117
|
+
elsif format=='a5'
|
118
|
+
format=[420.94,595.28]
|
119
|
+
elsif format=='letter'
|
120
|
+
format=[612,792]
|
121
|
+
elsif format=='legal'
|
122
|
+
format=[612,1008]
|
123
|
+
else
|
124
|
+
raise 'Unknown page format: '+format
|
125
|
+
end
|
126
|
+
@fwPt,@fhPt=format
|
127
|
+
else
|
128
|
+
@fwPt=format[0]*@k
|
129
|
+
@fhPt=format[1]*@k
|
130
|
+
end
|
131
|
+
@fw=@fwPt/@k;
|
132
|
+
@fh=@fhPt/@k;
|
133
|
+
|
134
|
+
# Page orientation
|
135
|
+
orientation.downcase!
|
136
|
+
if orientation=='p' or orientation=='portrait'
|
137
|
+
@DefOrientation='P'
|
138
|
+
@wPt=@fwPt
|
139
|
+
@hPt=@fhPt
|
140
|
+
elsif orientation=='l' or orientation=='landscape'
|
141
|
+
@DefOrientation='L'
|
142
|
+
@wPt=@fhPt
|
143
|
+
@hPt=@fwPt
|
144
|
+
else
|
145
|
+
raise 'Incorrect orientation: '+orientation
|
146
|
+
end
|
147
|
+
@CurOrientation=@DefOrientation
|
148
|
+
@w=@wPt/@k
|
149
|
+
@h=@hPt/@k
|
150
|
+
|
151
|
+
# Page margins (1 cm)
|
152
|
+
margin=28.35/@k
|
153
|
+
SetMargins(margin,margin)
|
154
|
+
# Interior cell margin (1 mm)
|
155
|
+
@cMargin=margin/10
|
156
|
+
# Line width (0.2 mm)
|
157
|
+
@LineWidth=0.567/@k
|
158
|
+
# Automatic page break
|
159
|
+
SetAutoPageBreak(true,2*margin)
|
160
|
+
# Full width display mode
|
161
|
+
SetDisplayMode('fullwidth')
|
162
|
+
# Enable compression
|
163
|
+
SetCompression(true)
|
164
|
+
# Set default PDF version number
|
165
|
+
@PDFVersion='1.3'
|
166
|
+
end
|
167
|
+
|
168
|
+
def SetMargins(left, top, right=-1)
|
169
|
+
# Set left, top and right margins
|
170
|
+
@lMargin=left
|
171
|
+
@tMargin=top
|
172
|
+
right=left if right==-1
|
173
|
+
@rMargin=right
|
174
|
+
end
|
175
|
+
|
176
|
+
def SetLeftMargin(margin)
|
177
|
+
# Set left margin
|
178
|
+
@lMargin=margin
|
179
|
+
@x=margin if @page>0 and @x<margin
|
180
|
+
end
|
181
|
+
|
182
|
+
def SetTopMargin(margin)
|
183
|
+
# Set top margin
|
184
|
+
@tMargin=margin
|
185
|
+
end
|
186
|
+
|
187
|
+
def SetRightMargin(margin)
|
188
|
+
#Set right margin
|
189
|
+
@rMargin=margin
|
190
|
+
end
|
191
|
+
|
192
|
+
def SetAutoPageBreak(auto, margin=0)
|
193
|
+
# Set auto page break mode and triggering margin
|
194
|
+
@AutoPageBreak=auto
|
195
|
+
@bMargin=margin
|
196
|
+
@PageBreakTrigger=@h-margin
|
197
|
+
end
|
198
|
+
|
199
|
+
def SetDisplayMode(zoom, layout='continuous')
|
200
|
+
# Set display mode in viewer
|
201
|
+
if zoom=='fullpage' or zoom=='fullwidth' or zoom=='real' or
|
202
|
+
zoom=='default' or not zoom.kind_of? String
|
203
|
+
|
204
|
+
@ZoomMode=zoom;
|
205
|
+
elsif zoom=='zoom'
|
206
|
+
@ZoomMode=layout
|
207
|
+
else
|
208
|
+
raise 'Incorrect zoom display mode: '+zoom
|
209
|
+
end
|
210
|
+
if layout=='single' or layout=='continuous' or layout=='two' or
|
211
|
+
layout=='default'
|
212
|
+
|
213
|
+
@LayoutMode=layout
|
214
|
+
elsif zoom!='zoom'
|
215
|
+
raise 'Incorrect layout display mode: '+layout
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def SetCompression(compress)
|
220
|
+
# Set page compression
|
221
|
+
@compress = compress
|
222
|
+
end
|
223
|
+
|
224
|
+
def SetTitle(title)
|
225
|
+
# Title of document
|
226
|
+
@title=title
|
227
|
+
end
|
228
|
+
|
229
|
+
def SetSubject(subject)
|
230
|
+
# Subject of document
|
231
|
+
@subject=subject
|
232
|
+
end
|
233
|
+
|
234
|
+
def SetAuthor(author)
|
235
|
+
# Author of document
|
236
|
+
@author=author
|
237
|
+
end
|
238
|
+
|
239
|
+
def SetKeywords(keywords)
|
240
|
+
# Keywords of document
|
241
|
+
@keywords=keywords
|
242
|
+
end
|
243
|
+
|
244
|
+
def SetCreator(creator)
|
245
|
+
# Creator of document
|
246
|
+
@creator=creator
|
247
|
+
end
|
248
|
+
|
249
|
+
def AliasNbPages(aliasnb='{nb}')
|
250
|
+
# Define an alias for total number of pages
|
251
|
+
@AliasNbPages=aliasnb
|
252
|
+
end
|
253
|
+
|
254
|
+
def Error(msg)
|
255
|
+
raise 'FPDF error: '+msg
|
256
|
+
end
|
257
|
+
|
258
|
+
def Open
|
259
|
+
# Begin document
|
260
|
+
@state=1
|
261
|
+
end
|
262
|
+
|
263
|
+
def Close
|
264
|
+
# Terminate document
|
265
|
+
return if @state==3
|
266
|
+
self.AddPage if @page==0
|
267
|
+
# Page footer
|
268
|
+
@InFooter=true
|
269
|
+
self.Footer
|
270
|
+
@InFooter=false
|
271
|
+
# Close page
|
272
|
+
endpage
|
273
|
+
# Close document
|
274
|
+
enddoc
|
275
|
+
end
|
276
|
+
|
277
|
+
def AddPage(orientation='')
|
278
|
+
# Start a new page
|
279
|
+
self.Open if @state==0
|
280
|
+
family=@FontFamily
|
281
|
+
style=@FontStyle+(@underline ? 'U' : '')
|
282
|
+
size=@FontSizePt
|
283
|
+
lw=@LineWidth
|
284
|
+
dc=@DrawColor
|
285
|
+
fc=@FillColor
|
286
|
+
tc=@TextColor
|
287
|
+
cf=@ColorFlag
|
288
|
+
if @page>0
|
289
|
+
# Page footer
|
290
|
+
@InFooter=true
|
291
|
+
self.Footer
|
292
|
+
@InFooter=false
|
293
|
+
# Close page
|
294
|
+
endpage
|
295
|
+
end
|
296
|
+
# Start new page
|
297
|
+
beginpage(orientation)
|
298
|
+
# Set line cap style to square
|
299
|
+
out('2 J')
|
300
|
+
# Set line width
|
301
|
+
@LineWidth=lw
|
302
|
+
out(sprintf('%.2f w',lw*@k))
|
303
|
+
# Set font
|
304
|
+
SetFont(family,style,size) if family
|
305
|
+
# Set colors
|
306
|
+
@DrawColor=dc
|
307
|
+
out(dc) if dc!='0 G'
|
308
|
+
@FillColor=fc
|
309
|
+
out(fc) if fc!='0 g'
|
310
|
+
@TextColor=tc
|
311
|
+
@ColorFlag=cf
|
312
|
+
# Page header
|
313
|
+
self.Header
|
314
|
+
# Restore line width
|
315
|
+
if @LineWidth!=lw
|
316
|
+
@LineWidth=lw
|
317
|
+
out(sprintf('%.2f w',lw*@k))
|
318
|
+
end
|
319
|
+
# Restore font
|
320
|
+
self.SetFont(family,style,size) if family
|
321
|
+
# Restore colors
|
322
|
+
if @DrawColor!=dc
|
323
|
+
@DrawColor=dc
|
324
|
+
out(dc)
|
325
|
+
end
|
326
|
+
if @FillColor!=fc
|
327
|
+
@FillColor=fc
|
328
|
+
out(fc)
|
329
|
+
end
|
330
|
+
@TextColor=tc
|
331
|
+
@ColorFlag=cf
|
332
|
+
end
|
333
|
+
|
334
|
+
def Header
|
335
|
+
# To be implemented in your inherited class
|
336
|
+
end
|
337
|
+
|
338
|
+
def Footer
|
339
|
+
# To be implemented in your inherited class
|
340
|
+
end
|
341
|
+
|
342
|
+
def PageNo
|
343
|
+
# Get current page number
|
344
|
+
@page
|
345
|
+
end
|
346
|
+
|
347
|
+
def SetDrawColor(r,g=-1,b=-1)
|
348
|
+
# Set color for all stroking operations
|
349
|
+
if (r==0 and g==0 and b==0) or g==-1
|
350
|
+
@DrawColor=sprintf('%.3f G',r/255.0)
|
351
|
+
else
|
352
|
+
@DrawColor=sprintf('%.3f %.3f %.3f RG',r/255.0,g/255.0,b/255.0)
|
353
|
+
end
|
354
|
+
out(@DrawColor) if(@page>0)
|
355
|
+
end
|
356
|
+
|
357
|
+
def SetFillColor(r,g=-1,b=-1)
|
358
|
+
# Set color for all filling operations
|
359
|
+
if (r==0 and g==0 and b==0) or g==-1
|
360
|
+
@FillColor=sprintf('%.3f g',r/255.0)
|
361
|
+
else
|
362
|
+
@FillColor=sprintf('%.3f %.3f %.3f rg',r/255.0,g/255.0,b/255.0)
|
363
|
+
end
|
364
|
+
@ColorFlag=(@FillColor!=@TextColor)
|
365
|
+
out(@FillColor) if(@page>0)
|
366
|
+
end
|
367
|
+
|
368
|
+
def SetTextColor(r,g=-1,b=-1)
|
369
|
+
# Set color for text
|
370
|
+
if (r==0 and g==0 and b==0) or g==-1
|
371
|
+
@TextColor=sprintf('%.3f g',r/255.0)
|
372
|
+
else
|
373
|
+
@TextColor=sprintf('%.3f %.3f %.3f rg',r/255.0,g/255.0,b/255.0)
|
374
|
+
end
|
375
|
+
@ColorFlag=(@FillColor!=@TextColor)
|
376
|
+
end
|
377
|
+
|
378
|
+
def GetStringWidth(s)
|
379
|
+
# Get width of a string in the current font
|
380
|
+
cw=@CurrentFont['cw']
|
381
|
+
w=0
|
382
|
+
s.each_byte do |c|
|
383
|
+
w=w+cw[c]
|
384
|
+
end
|
385
|
+
w*@FontSize/1000.0
|
386
|
+
end
|
387
|
+
|
388
|
+
def SetLineWidth(width)
|
389
|
+
# Set line width
|
390
|
+
@LineWidth=width
|
391
|
+
out(sprintf('%.2f w',width*@k)) if @page>0
|
392
|
+
end
|
393
|
+
|
394
|
+
def Line(x1, y1, x2, y2)
|
395
|
+
# Draw a line
|
396
|
+
out(sprintf('%.2f %.2f m %.2f %.2f l S',
|
397
|
+
x1*@k,(@h-y1)*@k,x2*@k,(@h-y2)*@k))
|
398
|
+
end
|
399
|
+
|
400
|
+
def Rect(x, y, w, h, style='')
|
401
|
+
# Draw a rectangle
|
402
|
+
if style=='F'
|
403
|
+
op='f'
|
404
|
+
elsif style=='FD' or style=='DF'
|
405
|
+
op='B'
|
406
|
+
else
|
407
|
+
op='S'
|
408
|
+
end
|
409
|
+
out(sprintf('%.2f %.2f %.2f %.2f re %s', x*@k,(@h-y)*@k,w*@k,-h*@k,op))
|
410
|
+
end
|
411
|
+
|
412
|
+
def AddFont(family, style='', file='')
|
413
|
+
# Add a TrueType or Type1 font
|
414
|
+
family = family.downcase
|
415
|
+
family = 'helvetica' if family == 'arial'
|
416
|
+
|
417
|
+
style = style.upcase
|
418
|
+
style = 'BI' if style == 'IB'
|
419
|
+
|
420
|
+
fontkey = family + style
|
421
|
+
|
422
|
+
if @fonts.has_key?(fontkey)
|
423
|
+
self.Error("Font already added: #{family} #{style}")
|
424
|
+
end
|
425
|
+
|
426
|
+
file = family.gsub(' ', '') + style.downcase + '.rb' if file == ''
|
427
|
+
|
428
|
+
if self.class.const_defined? 'FPDF_FONTPATH'
|
429
|
+
if FPDF_FONTPATH[-1,1] == '/'
|
430
|
+
file = FPDF_FONTPATH + file
|
431
|
+
else
|
432
|
+
file = FPDF_FONTPATH + '/' + file
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
# Changed from "require file" to fix bug reported by Hans Allis.
|
437
|
+
load file
|
438
|
+
|
439
|
+
if FontDef.desc.nil?
|
440
|
+
self.Error("Could not include font definition file #{file}")
|
441
|
+
end
|
442
|
+
|
443
|
+
i = @fonts.length + 1
|
444
|
+
|
445
|
+
@fonts[fontkey] = {'i' => i,
|
446
|
+
'type' => FontDef.type,
|
447
|
+
'name' => FontDef.name,
|
448
|
+
'desc' => FontDef.desc,
|
449
|
+
'up' => FontDef.up,
|
450
|
+
'ut' => FontDef.ut,
|
451
|
+
'cw' => FontDef.cw,
|
452
|
+
'enc' => FontDef.enc,
|
453
|
+
'file' => FontDef.file
|
454
|
+
}
|
455
|
+
|
456
|
+
if FontDef.diff
|
457
|
+
# Search existing encodings
|
458
|
+
idx = @diffs.index(FontDef.diff)
|
459
|
+
|
460
|
+
unless idx
|
461
|
+
@diffs.push(FontDef.diff)
|
462
|
+
@fonts[fontkey]['diff'] = @diffs.length
|
463
|
+
else
|
464
|
+
@fonts[fontkey]['diff'] = idx + 1
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
if FontDef.file
|
469
|
+
if FontDef.type == 'TrueType'
|
470
|
+
@FontFiles[FontDef.file] = {'length1' => FontDef.originalsize}
|
471
|
+
else
|
472
|
+
@FontFiles[FontDef.file] = {'length1' => FontDef.size1, 'length2' => FontDef.size2}
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
return self
|
477
|
+
end
|
478
|
+
|
479
|
+
def SetFont(family, style='', size=0)
|
480
|
+
# Select a font; size given in points
|
481
|
+
family.downcase!
|
482
|
+
family=@FontFamily if family==''
|
483
|
+
if family=='arial'
|
484
|
+
family='helvetica'
|
485
|
+
elsif family=='symbol' or family=='zapfdingbats'
|
486
|
+
style=''
|
487
|
+
end
|
488
|
+
style.upcase!
|
489
|
+
unless style.index('U').nil?
|
490
|
+
@underline=true
|
491
|
+
style.gsub!('U','')
|
492
|
+
else
|
493
|
+
@underline=false;
|
494
|
+
end
|
495
|
+
style='BI' if style=='IB'
|
496
|
+
size=@FontSizePt if size==0
|
497
|
+
# Test if font is already selected
|
498
|
+
return if @FontFamily==family and
|
499
|
+
@FontStyle==style and @FontSizePt==size
|
500
|
+
# Test if used for the first time
|
501
|
+
fontkey=family+style
|
502
|
+
unless @fonts.has_key?(fontkey)
|
503
|
+
if @CoreFonts.has_key?(fontkey)
|
504
|
+
unless Charwidths.has_key?(fontkey)
|
505
|
+
raise 'Font unavailable'
|
506
|
+
end
|
507
|
+
@fonts[fontkey]={
|
508
|
+
'i'=>@fonts.size,
|
509
|
+
'type'=>'core',
|
510
|
+
'name'=>@CoreFonts[fontkey],
|
511
|
+
'up'=>-100,
|
512
|
+
'ut'=>50,
|
513
|
+
'cw'=>Charwidths[fontkey]}
|
514
|
+
else
|
515
|
+
raise 'Font unavailable'
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
#Select it
|
520
|
+
@FontFamily=family
|
521
|
+
@FontStyle=style;
|
522
|
+
@FontSizePt=size
|
523
|
+
@FontSize=size/@k;
|
524
|
+
@CurrentFont=@fonts[fontkey]
|
525
|
+
if @page>0
|
526
|
+
out(sprintf('BT /F%d %.2f Tf ET', @CurrentFont['i'], @FontSizePt))
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
def SetFontSize(size)
|
531
|
+
# Set font size in points
|
532
|
+
return if @FontSizePt==size
|
533
|
+
@FontSizePt=size
|
534
|
+
@FontSize=size/@k
|
535
|
+
if @page>0
|
536
|
+
out(sprintf('BT /F%d %.2f Tf ET',@CurrentFont['i'],@FontSizePt))
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
def AddLink
|
541
|
+
# Create a new internal link
|
542
|
+
@links.push([0, 0])
|
543
|
+
@links.size
|
544
|
+
end
|
545
|
+
|
546
|
+
def SetLink(link, y=0, page=-1)
|
547
|
+
# Set destination of internal link
|
548
|
+
y=@y if y==-1
|
549
|
+
page=@page if page==-1
|
550
|
+
@links[link]=[page, y]
|
551
|
+
end
|
552
|
+
|
553
|
+
def Link(x, y, w, h, link)
|
554
|
+
# Put a link on the page
|
555
|
+
@PageLinks[@page]=Array.new unless @PageLinks.has_key?(@page)
|
556
|
+
@PageLinks[@page].push([x*@k,@hPt-y*@k,w*@k,h*@k,link])
|
557
|
+
end
|
558
|
+
|
559
|
+
def Text(x, y, txt)
|
560
|
+
# Output a string
|
561
|
+
txt.gsub!(')', '\\)')
|
562
|
+
txt.gsub!('(', '\\(')
|
563
|
+
txt.gsub!('\\', '\\\\')
|
564
|
+
s=sprintf('BT %.2f %.2f Td (%s) Tj ET',x*@k,(@h-y)*@k,txt);
|
565
|
+
s=s+' '+dounderline(x,y,txt) if @underline and txt!=''
|
566
|
+
s='q '+@TextColor+' '+s+' Q' if @ColorFlag
|
567
|
+
out(s)
|
568
|
+
end
|
569
|
+
|
570
|
+
def AcceptPageBreak
|
571
|
+
# Accept automatic page break or not
|
572
|
+
@AutoPageBreak
|
573
|
+
end
|
574
|
+
|
575
|
+
def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
|
576
|
+
# Output a cell
|
577
|
+
if @y+h>@PageBreakTrigger and !@InFooter and self.AcceptPageBreak
|
578
|
+
# Automatic page break
|
579
|
+
x=@x
|
580
|
+
ws=@ws
|
581
|
+
if ws>0
|
582
|
+
@ws=0
|
583
|
+
out('0 Tw')
|
584
|
+
end
|
585
|
+
self.AddPage(@CurOrientation)
|
586
|
+
@x=x
|
587
|
+
if ws>0
|
588
|
+
@ws=ws
|
589
|
+
out(sprintf('%.3f Tw',ws*@k))
|
590
|
+
end
|
591
|
+
end
|
592
|
+
w=@w-@rMargin-@x if w==0
|
593
|
+
s=''
|
594
|
+
if fill==1 or border==1
|
595
|
+
if fill==1
|
596
|
+
op=(border==1) ? 'B' : 'f'
|
597
|
+
else
|
598
|
+
op='S'
|
599
|
+
end
|
600
|
+
s=sprintf('%.2f %.2f %.2f %.2f re %s ',@x*@k,(@h-@y)*@k,w*@k,-h*@k,op)
|
601
|
+
end
|
602
|
+
if border.is_a? String
|
603
|
+
x=@x
|
604
|
+
y=@y
|
605
|
+
unless border.index('L').nil?
|
606
|
+
s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
|
607
|
+
x*@k,(@h-y)*@k,x*@k,(@h-(y+h))*@k)
|
608
|
+
end
|
609
|
+
unless border.index('T').nil?
|
610
|
+
s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
|
611
|
+
x*@k,(@h-y)*@k,(x+w)*@k,(@h-y)*@k)
|
612
|
+
end
|
613
|
+
unless border.index('R').nil?
|
614
|
+
s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
|
615
|
+
(x+w)*@k,(@h-y)*@k,(x+w)*@k,(@h-(y+h))*@k)
|
616
|
+
end
|
617
|
+
unless border.index('B').nil?
|
618
|
+
s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
|
619
|
+
x*@k,(@h-(y+h))*@k,(x+w)*@k,(@h-(y+h))*@k)
|
620
|
+
end
|
621
|
+
end
|
622
|
+
if txt!=''
|
623
|
+
if align=='R'
|
624
|
+
dx=w-@cMargin-self.GetStringWidth(txt)
|
625
|
+
elsif align=='C'
|
626
|
+
dx=(w-self.GetStringWidth(txt))/2
|
627
|
+
else
|
628
|
+
dx=@cMargin
|
629
|
+
end
|
630
|
+
txt = txt.gsub(')', '\\)')
|
631
|
+
txt.gsub!('(', '\\(')
|
632
|
+
txt.gsub!('\\', '\\\\')
|
633
|
+
if @ColorFlag
|
634
|
+
s=s+'q '+@TextColor+' '
|
635
|
+
end
|
636
|
+
s=s+sprintf('BT %.2f %.2f Td (%s) Tj ET',
|
637
|
+
(@x+dx)*@k,(@h-(@y+0.5*h+0.3*@FontSize))*@k,txt)
|
638
|
+
s=s+' '+dounderline(@x+dx,@y+0.5*h+0.3*@FontSize,txt) if @underline
|
639
|
+
s=s+' Q' if @ColorFlag
|
640
|
+
if link and link != ''
|
641
|
+
Link(@x+dx,@y+0.5*h-0.5*@FontSize,GetStringWidth(txt),@FontSize,link)
|
642
|
+
end
|
643
|
+
end
|
644
|
+
out(s) if s
|
645
|
+
@lasth=h
|
646
|
+
if ln>0
|
647
|
+
# Go to next line
|
648
|
+
@y=@y+h
|
649
|
+
@x=@lMargin if ln==1
|
650
|
+
else
|
651
|
+
@x=@x+w
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
def MultiCell(w,h,txt,border=0,align='J',fill=0)
|
656
|
+
# Output text with automatic or explicit line breaks
|
657
|
+
cw=@CurrentFont['cw']
|
658
|
+
w=@w-@rMargin-@x if w==0
|
659
|
+
wmax=(w-2*@cMargin)*1000/@FontSize
|
660
|
+
s=txt.gsub('\r','')
|
661
|
+
nb=s.length
|
662
|
+
nb=nb-1 if nb>0 and s[nb-1].chr=='\n'
|
663
|
+
b=0
|
664
|
+
if border!=0
|
665
|
+
if border==1
|
666
|
+
border='LTRB'
|
667
|
+
b='LRT'
|
668
|
+
b2='LR'
|
669
|
+
else
|
670
|
+
b2=''
|
671
|
+
b2='L' unless border.index('L').nil?
|
672
|
+
b2=b2+'R' unless border.index('R').nil?
|
673
|
+
b=(not border.index('T').nil?) ? (b2+'T') : b2
|
674
|
+
end
|
675
|
+
end
|
676
|
+
sep=-1
|
677
|
+
i=0
|
678
|
+
j=0
|
679
|
+
l=0
|
680
|
+
ns=0
|
681
|
+
nl=1
|
682
|
+
while i<nb
|
683
|
+
# Get next character
|
684
|
+
c=s[i].chr
|
685
|
+
if c=="\n"
|
686
|
+
# Explicit line break
|
687
|
+
if @ws>0
|
688
|
+
@ws=0
|
689
|
+
out('0 Tw')
|
690
|
+
end
|
691
|
+
|
692
|
+
# Changed from s[j..i] to fix bug reported by Hans Allis.
|
693
|
+
self.Cell(w,h,s[j..i-1],b,2,align,fill)
|
694
|
+
|
695
|
+
i=i+1
|
696
|
+
sep=-1
|
697
|
+
j=i
|
698
|
+
l=0
|
699
|
+
ns=0
|
700
|
+
nl=nl+1
|
701
|
+
b=b2 if border and nl==2
|
702
|
+
else
|
703
|
+
if c==' '
|
704
|
+
sep=i
|
705
|
+
ls=l
|
706
|
+
ns=ns+1
|
707
|
+
end
|
708
|
+
l=l+cw[c[0]]
|
709
|
+
if l>wmax
|
710
|
+
# Automatic line break
|
711
|
+
if sep==-1
|
712
|
+
i=i+1 if i==j
|
713
|
+
if @ws>0
|
714
|
+
@ws=0
|
715
|
+
out('0 Tw')
|
716
|
+
end
|
717
|
+
self.Cell(w,h,s[j..i],b,2,align,fill)
|
718
|
+
else
|
719
|
+
if align=='J'
|
720
|
+
@ws=(ns>1) ? (wmax-ls)/1000.0*@FontSize/(ns-1) : 0
|
721
|
+
out(sprintf('%.3f Tw',@ws*@k))
|
722
|
+
end
|
723
|
+
self.Cell(w,h,s[j..sep],b,2,align,fill)
|
724
|
+
i=sep+1
|
725
|
+
end
|
726
|
+
sep=-1
|
727
|
+
j=i
|
728
|
+
l=0
|
729
|
+
ns=0
|
730
|
+
nl=nl+1
|
731
|
+
b=b2 if border and nl==2
|
732
|
+
else
|
733
|
+
i=i+1
|
734
|
+
end
|
735
|
+
end
|
736
|
+
end
|
737
|
+
|
738
|
+
# Last chunk
|
739
|
+
if @ws>0
|
740
|
+
@ws=0
|
741
|
+
out('0 Tw')
|
742
|
+
end
|
743
|
+
b=b+'B' if border!=0 and not border.index('B').nil?
|
744
|
+
self.Cell(w,h,s[j..i],b,2,align,fill)
|
745
|
+
@x=@lMargin
|
746
|
+
end
|
747
|
+
|
748
|
+
def Write(h,txt,link='')
|
749
|
+
# Output text in flowing mode
|
750
|
+
cw=@CurrentFont['cw']
|
751
|
+
w=@w-@rMargin-@x
|
752
|
+
wmax=(w-2*@cMargin)*1000/@FontSize
|
753
|
+
s=txt.gsub("\r",'')
|
754
|
+
nb=s.length
|
755
|
+
sep=-1
|
756
|
+
i=0
|
757
|
+
j=0
|
758
|
+
l=0
|
759
|
+
nl=1
|
760
|
+
while i<nb
|
761
|
+
# Get next character
|
762
|
+
c=s[i]
|
763
|
+
if c=="\n"[0]
|
764
|
+
# Explicit line break
|
765
|
+
self.Cell(w,h,s[j,i-j],0,2,'',0,link)
|
766
|
+
i=i+1
|
767
|
+
sep=-1
|
768
|
+
j=i
|
769
|
+
l=0
|
770
|
+
if nl==1
|
771
|
+
@x=@lMargin
|
772
|
+
w=@w-@rMargin-@x
|
773
|
+
wmax=(w-2*@cMargin)*1000/@FontSize
|
774
|
+
end
|
775
|
+
nl=nl+1
|
776
|
+
next
|
777
|
+
end
|
778
|
+
if c==' '[0]
|
779
|
+
sep=i
|
780
|
+
ls=l
|
781
|
+
end
|
782
|
+
l=l+cw[c];
|
783
|
+
if l>wmax
|
784
|
+
# Automatic line break
|
785
|
+
if sep==-1
|
786
|
+
if @x>@lMargin
|
787
|
+
# Move to next line
|
788
|
+
@x=@lMargin
|
789
|
+
@y=@y+h
|
790
|
+
w=@w-@rMargin-@x
|
791
|
+
wmax=(w-2*@cMargin)*1000/@FontSize
|
792
|
+
i=i+1
|
793
|
+
nl=nl+1
|
794
|
+
next
|
795
|
+
end
|
796
|
+
i=i+1 if i==j
|
797
|
+
self.Cell(w,h,s[j,i-j],0,2,'',0,link)
|
798
|
+
else
|
799
|
+
self.Cell(w,h,s[j,sep-j],0,2,'',0,link)
|
800
|
+
i=sep+1
|
801
|
+
end
|
802
|
+
sep=-1
|
803
|
+
j=i
|
804
|
+
l=0
|
805
|
+
if nl==1
|
806
|
+
@x=@lMargin
|
807
|
+
w=@w-@rMargin-@x
|
808
|
+
wmax=(w-2*@cMargin)*1000/@FontSize
|
809
|
+
end
|
810
|
+
nl=nl+1
|
811
|
+
else
|
812
|
+
i=i+1
|
813
|
+
end
|
814
|
+
end
|
815
|
+
# Last chunk
|
816
|
+
self.Cell(l/1000.0*@FontSize,h,s[j,i],0,0,'',0,link) if i!=j
|
817
|
+
end
|
818
|
+
|
819
|
+
def Image(file,x,y,w,h=0,type='',link='')
|
820
|
+
# Put an image on the page
|
821
|
+
unless @images.has_key?(file)
|
822
|
+
# First use of image, get info
|
823
|
+
if type==''
|
824
|
+
pos=file.rindex('.')
|
825
|
+
if pos.nil?
|
826
|
+
self.Error('Image file has no extension and no type was '+
|
827
|
+
'specified: '+file)
|
828
|
+
end
|
829
|
+
type=file[pos+1..-1]
|
830
|
+
end
|
831
|
+
type.downcase!
|
832
|
+
if type=='jpg' or type=='jpeg'
|
833
|
+
info=parsejpg(file)
|
834
|
+
elsif type=='png'
|
835
|
+
info=parsepng(file)
|
836
|
+
else
|
837
|
+
self.Error('Unsupported image file type: '+type)
|
838
|
+
end
|
839
|
+
info['i']=@images.length+1
|
840
|
+
@images[file]=info
|
841
|
+
else
|
842
|
+
info=@images[file]
|
843
|
+
end
|
844
|
+
# Automatic width or height calculation
|
845
|
+
w=h*info['w']/info['h'] if w==0
|
846
|
+
h=w*info['h']/info['w'] if h==0
|
847
|
+
out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q',
|
848
|
+
w*@k,h*@k,x*@k,(@h-(y+h))*@k,info['i']))
|
849
|
+
Link(x,y,w,h,link) if link and link != ''
|
850
|
+
end
|
851
|
+
|
852
|
+
def Ln(h='')
|
853
|
+
# Line feed; default value is last cell height
|
854
|
+
@x=@lMargin
|
855
|
+
if h.kind_of?(String)
|
856
|
+
@y=@y+@lasth
|
857
|
+
else
|
858
|
+
@y=@y+h
|
859
|
+
end
|
860
|
+
end
|
861
|
+
|
862
|
+
def GetX
|
863
|
+
# Get x position
|
864
|
+
@x
|
865
|
+
end
|
866
|
+
|
867
|
+
def SetX(x)
|
868
|
+
# Set x position
|
869
|
+
if x>=0
|
870
|
+
@x=x
|
871
|
+
else
|
872
|
+
@x=@w+x
|
873
|
+
end
|
874
|
+
end
|
875
|
+
|
876
|
+
def GetY
|
877
|
+
# Get y position
|
878
|
+
@y
|
879
|
+
end
|
880
|
+
|
881
|
+
def SetY(y)
|
882
|
+
# Set y position and reset x
|
883
|
+
@x=@lMargin
|
884
|
+
if y>=0
|
885
|
+
@y=y
|
886
|
+
else
|
887
|
+
@y=@h+y
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
891
|
+
def SetXY(x,y)
|
892
|
+
# Set x and y positions
|
893
|
+
SetY(y)
|
894
|
+
SetX(x)
|
895
|
+
end
|
896
|
+
|
897
|
+
def Output(file=nil)
|
898
|
+
# Output PDF to file or return as a string
|
899
|
+
|
900
|
+
# Finish document if necessary
|
901
|
+
self.Close if(@state<3)
|
902
|
+
|
903
|
+
if file.nil?
|
904
|
+
# Return as a string
|
905
|
+
return @buffer
|
906
|
+
else
|
907
|
+
# Save file locally
|
908
|
+
open(file,'wb') do |f|
|
909
|
+
f.write(@buffer)
|
910
|
+
end
|
911
|
+
end
|
912
|
+
end
|
913
|
+
|
914
|
+
private
|
915
|
+
|
916
|
+
def putpages
|
917
|
+
nb=@page
|
918
|
+
unless @AliasNbPages.nil? or @AliasNbPages==''
|
919
|
+
# Replace number of pages
|
920
|
+
1.upto(nb) do |n|
|
921
|
+
@pages[n].gsub!(@AliasNbPages,nb.to_s)
|
922
|
+
end
|
923
|
+
end
|
924
|
+
if @DefOrientation=='P'
|
925
|
+
wPt=@fwPt
|
926
|
+
hPt=@fhPt
|
927
|
+
else
|
928
|
+
wPt=@fhPt
|
929
|
+
hPt=@fwPt
|
930
|
+
end
|
931
|
+
filter=(@compress) ? '/Filter /FlateDecode ' : ''
|
932
|
+
1.upto(nb) do |n|
|
933
|
+
# Page
|
934
|
+
newobj
|
935
|
+
out('<</Type /Page')
|
936
|
+
out('/Parent 1 0 R')
|
937
|
+
unless @OrientationChanges[n].nil?
|
938
|
+
out(sprintf('/MediaBox [0 0 %.2f %.2f]',hPt,wPt))
|
939
|
+
end
|
940
|
+
out('/Resources 2 0 R')
|
941
|
+
if @PageLinks[n]
|
942
|
+
# Links
|
943
|
+
annots='/Annots ['
|
944
|
+
@PageLinks[n].each do |pl|
|
945
|
+
rect=sprintf('%.2f %.2f %.2f %.2f',
|
946
|
+
pl[0],pl[1],pl[0]+pl[2],pl[1]-pl[3])
|
947
|
+
annots=annots+'<</Type /Annot /Subtype /Link /Rect ['+rect+
|
948
|
+
'] /Border [0 0 0] '
|
949
|
+
if pl[4].kind_of?(String)
|
950
|
+
annots=annots+'/A <</S /URI /URI '+textstring(pl[4])+
|
951
|
+
'>>>>'
|
952
|
+
else
|
953
|
+
l=@links[pl[4]]
|
954
|
+
h=@OrientationChanges[l[0]].nil? ? hPt : wPt
|
955
|
+
annots=annots+sprintf(
|
956
|
+
'/Dest [%d 0 R /XYZ 0 %.2f null]>>',
|
957
|
+
1+2*l[0],h-l[1]*@k)
|
958
|
+
end
|
959
|
+
end
|
960
|
+
out(annots+']')
|
961
|
+
end
|
962
|
+
out('/Contents '+(@n+1).to_s+' 0 R>>')
|
963
|
+
out('endobj')
|
964
|
+
# Page content
|
965
|
+
p=(@compress) ? Zlib::Deflate.deflate(@pages[n]) : @pages[n]
|
966
|
+
newobj
|
967
|
+
out('<<'+filter+'/Length '+p.length.to_s+'>>')
|
968
|
+
putstream(p)
|
969
|
+
out('endobj')
|
970
|
+
end
|
971
|
+
# Pages root
|
972
|
+
@offsets[1]=@buffer.length
|
973
|
+
out('1 0 obj')
|
974
|
+
out('<</Type /Pages')
|
975
|
+
kids='/Kids ['
|
976
|
+
nb.times do |i|
|
977
|
+
kids=kids+(3+2*i).to_s+' 0 R '
|
978
|
+
end
|
979
|
+
out(kids+']')
|
980
|
+
out('/Count '+nb.to_s)
|
981
|
+
out(sprintf('/MediaBox [0 0 %.2f %.2f]',wPt,hPt))
|
982
|
+
out('>>')
|
983
|
+
out('endobj')
|
984
|
+
end
|
985
|
+
|
986
|
+
def putfonts
|
987
|
+
nf=@n
|
988
|
+
@diffs.each do |diff|
|
989
|
+
# Encodings
|
990
|
+
newobj
|
991
|
+
out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences '+
|
992
|
+
'['+diff+']>>')
|
993
|
+
out('endobj')
|
994
|
+
end
|
995
|
+
|
996
|
+
@FontFiles.each do |file, info|
|
997
|
+
# Font file embedding
|
998
|
+
newobj
|
999
|
+
@FontFiles[file]['n'] = @n
|
1000
|
+
|
1001
|
+
if self.class.const_defined? 'FPDF_FONTPATH' then
|
1002
|
+
if FPDF_FONTPATH[-1,1] == '/' then
|
1003
|
+
file = FPDF_FONTPATH + file
|
1004
|
+
else
|
1005
|
+
file = FPDF_FONTPATH + '/' + file
|
1006
|
+
end
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
size = File.size(file)
|
1010
|
+
unless File.exists?(file)
|
1011
|
+
Error('Font file not found')
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
out('<</Length ' + size.to_s)
|
1015
|
+
|
1016
|
+
if file[-2, 2] == '.z' then
|
1017
|
+
out('/Filter /FlateDecode')
|
1018
|
+
end
|
1019
|
+
out('/Length1 ' + info['length1'])
|
1020
|
+
out('/Length2 ' + info['length2'] + ' /Length3 0') if info['length2']
|
1021
|
+
out('>>')
|
1022
|
+
open(file, 'rb') do |f|
|
1023
|
+
putstream(f.read())
|
1024
|
+
end
|
1025
|
+
out('endobj')
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
file = 0
|
1029
|
+
@fonts.each do |k, font|
|
1030
|
+
# Font objects
|
1031
|
+
@fonts[k]['n']=@n+1
|
1032
|
+
type=font['type']
|
1033
|
+
name=font['name']
|
1034
|
+
if type=='core'
|
1035
|
+
# Standard font
|
1036
|
+
newobj
|
1037
|
+
out('<</Type /Font')
|
1038
|
+
out('/BaseFont /'+name)
|
1039
|
+
out('/Subtype /Type1')
|
1040
|
+
if name!='Symbol' and name!='ZapfDingbats'
|
1041
|
+
out('/Encoding /WinAnsiEncoding')
|
1042
|
+
end
|
1043
|
+
out('>>')
|
1044
|
+
out('endobj')
|
1045
|
+
elsif type=='Type1' or type=='TrueType'
|
1046
|
+
# Additional Type1 or TrueType font
|
1047
|
+
newobj
|
1048
|
+
out('<</Type /Font')
|
1049
|
+
out('/BaseFont /'+name)
|
1050
|
+
out('/Subtype /'+type)
|
1051
|
+
out('/FirstChar 32 /LastChar 255')
|
1052
|
+
out('/Widths '+(@n+1).to_s+' 0 R')
|
1053
|
+
out('/FontDescriptor '+(@n+2).to_s+' 0 R')
|
1054
|
+
if font['enc'] and font['enc'] != ''
|
1055
|
+
unless font['diff'].nil?
|
1056
|
+
out('/Encoding '+(nf+font['diff']).to_s+' 0 R')
|
1057
|
+
else
|
1058
|
+
out('/Encoding /WinAnsiEncoding')
|
1059
|
+
end
|
1060
|
+
end
|
1061
|
+
out('>>')
|
1062
|
+
out('endobj')
|
1063
|
+
# Widths
|
1064
|
+
newobj
|
1065
|
+
cw=font['cw']
|
1066
|
+
s='['
|
1067
|
+
32.upto(255) do |i|
|
1068
|
+
s << cw[i].to_s+' '
|
1069
|
+
end
|
1070
|
+
out(s+']')
|
1071
|
+
out('endobj')
|
1072
|
+
# Descriptor
|
1073
|
+
newobj
|
1074
|
+
s='<</Type /FontDescriptor /FontName /'+name
|
1075
|
+
font['desc'].each do |k, v|
|
1076
|
+
s << ' /'+k+' '+v
|
1077
|
+
end
|
1078
|
+
file=font['file']
|
1079
|
+
if file
|
1080
|
+
s << ' /FontFile'+(type=='Type1' ? '' : '2')+' '+
|
1081
|
+
@FontFiles[file]['n'].to_s+' 0 R'
|
1082
|
+
end
|
1083
|
+
out(s+'>>')
|
1084
|
+
out('endobj')
|
1085
|
+
else
|
1086
|
+
# Allow for additional types
|
1087
|
+
mtd='put'+type.downcase
|
1088
|
+
unless self.respond_to?(mtd)
|
1089
|
+
self.Error('Unsupported font type: '+type)
|
1090
|
+
end
|
1091
|
+
self.send(mtd, font)
|
1092
|
+
end
|
1093
|
+
end
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
def putimages
|
1097
|
+
filter=(@compress) ? '/Filter /FlateDecode ' : ''
|
1098
|
+
@images.each do |file, info|
|
1099
|
+
newobj
|
1100
|
+
@images[file]['n']=@n
|
1101
|
+
out('<</Type /XObject')
|
1102
|
+
out('/Subtype /Image')
|
1103
|
+
out('/Width '+info['w'].to_s)
|
1104
|
+
out('/Height '+info['h'].to_s)
|
1105
|
+
if info['cs']=='Indexed'
|
1106
|
+
out("/ColorSpace [/Indexed /DeviceRGB #{info['pal'].length/3-1} #{(@n+1)} 0 R]")
|
1107
|
+
else
|
1108
|
+
out('/ColorSpace /'+info['cs'])
|
1109
|
+
if info['cs']=='DeviceCMYK'
|
1110
|
+
out('/Decode [1 0 1 0 1 0 1 0]')
|
1111
|
+
end
|
1112
|
+
end
|
1113
|
+
out('/BitsPerComponent '+info['bpc'].to_s)
|
1114
|
+
out('/Filter /'+info['f']) if info['f']
|
1115
|
+
unless info['parms'].nil?
|
1116
|
+
out(info['parms'])
|
1117
|
+
end
|
1118
|
+
if info['trns'] and info['trns'].kind_of?(Array)
|
1119
|
+
trns=''
|
1120
|
+
info['trns'].length.times do |i|
|
1121
|
+
trns=trns+info['trns'][i].to_s+' '+info['trns'][i].to_s+' '
|
1122
|
+
end
|
1123
|
+
out('/Mask ['+trns+']')
|
1124
|
+
end
|
1125
|
+
out('/Length '+info['data'].length.to_s+'>>')
|
1126
|
+
putstream(info['data'])
|
1127
|
+
@images[file]['data']=nil
|
1128
|
+
out('endobj')
|
1129
|
+
# Palette
|
1130
|
+
if info['cs']=='Indexed'
|
1131
|
+
newobj
|
1132
|
+
pal=(@compress) ? Zlib::Deflate.deflate(info['pal']) : info['pal']
|
1133
|
+
out('<<'+filter+'/Length '+pal.length.to_s+'>>')
|
1134
|
+
putstream(pal)
|
1135
|
+
out('endobj')
|
1136
|
+
end
|
1137
|
+
end
|
1138
|
+
end
|
1139
|
+
|
1140
|
+
def putxobjectdict
|
1141
|
+
@images.each_value do |image|
|
1142
|
+
out('/I'+image['i'].to_s+' '+image['n'].to_s+' 0 R')
|
1143
|
+
end
|
1144
|
+
end
|
1145
|
+
|
1146
|
+
def putresourcedict
|
1147
|
+
out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]')
|
1148
|
+
out('/Font <<')
|
1149
|
+
@fonts.each_value do |font|
|
1150
|
+
out('/F'+font['i'].to_s+' '+font['n'].to_s+' 0 R')
|
1151
|
+
end
|
1152
|
+
out('>>')
|
1153
|
+
out('/XObject <<')
|
1154
|
+
putxobjectdict
|
1155
|
+
out('>>')
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
def putresources
|
1159
|
+
putfonts
|
1160
|
+
putimages
|
1161
|
+
# Resource dictionary
|
1162
|
+
@offsets[2]=@buffer.length
|
1163
|
+
out('2 0 obj')
|
1164
|
+
out('<<')
|
1165
|
+
putresourcedict
|
1166
|
+
out('>>')
|
1167
|
+
out('endobj')
|
1168
|
+
end
|
1169
|
+
|
1170
|
+
def putinfo
|
1171
|
+
out('/Producer '+textstring('Ruby FPDF '+FPDF_VERSION));
|
1172
|
+
unless @title.nil?
|
1173
|
+
out('/Title '+textstring(@title))
|
1174
|
+
end
|
1175
|
+
unless @subject.nil?
|
1176
|
+
out('/Subject '+textstring(@subject))
|
1177
|
+
end
|
1178
|
+
unless @author.nil?
|
1179
|
+
out('/Author '+textstring(@author))
|
1180
|
+
end
|
1181
|
+
unless @keywords.nil?
|
1182
|
+
out('/Keywords '+textstring(@keywords))
|
1183
|
+
end
|
1184
|
+
unless @creator.nil?
|
1185
|
+
out('/Creator '+textstring(@creator))
|
1186
|
+
end
|
1187
|
+
out('/CreationDate '+textstring('D: '+DateTime.now.to_s))
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
def putcatalog
|
1191
|
+
out('/Type /Catalog')
|
1192
|
+
out('/Pages 1 0 R')
|
1193
|
+
if @ZoomMode=='fullpage'
|
1194
|
+
out('/OpenAction [3 0 R /Fit]')
|
1195
|
+
elsif @ZoomMode=='fullwidth'
|
1196
|
+
out('/OpenAction [3 0 R /FitH null]')
|
1197
|
+
elsif @ZoomMode=='real'
|
1198
|
+
out('/OpenAction [3 0 R /XYZ null null 1]')
|
1199
|
+
elsif not @ZoomMode.kind_of?(String)
|
1200
|
+
out('/OpenAction [3 0 R /XYZ null null '+(@ZoomMode/100)+']')
|
1201
|
+
end
|
1202
|
+
|
1203
|
+
if @LayoutMode=='single'
|
1204
|
+
out('/PageLayout /SinglePage')
|
1205
|
+
elsif @LayoutMode=='continuous'
|
1206
|
+
out('/PageLayout /OneColumn')
|
1207
|
+
elsif @LayoutMode=='two'
|
1208
|
+
out('/PageLayout /TwoColumnLeft')
|
1209
|
+
end
|
1210
|
+
end
|
1211
|
+
|
1212
|
+
def putheader
|
1213
|
+
out('%PDF-'+@PDFVersion)
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
def puttrailer
|
1217
|
+
out('/Size '+(@n+1).to_s)
|
1218
|
+
out('/Root '+@n.to_s+' 0 R')
|
1219
|
+
out('/Info '+(@n-1).to_s+' 0 R')
|
1220
|
+
end
|
1221
|
+
|
1222
|
+
def enddoc
|
1223
|
+
putheader
|
1224
|
+
putpages
|
1225
|
+
putresources
|
1226
|
+
# Info
|
1227
|
+
newobj
|
1228
|
+
out('<<')
|
1229
|
+
putinfo
|
1230
|
+
out('>>')
|
1231
|
+
out('endobj')
|
1232
|
+
# Catalog
|
1233
|
+
newobj
|
1234
|
+
out('<<')
|
1235
|
+
putcatalog
|
1236
|
+
out('>>')
|
1237
|
+
out('endobj')
|
1238
|
+
# Cross-ref
|
1239
|
+
o=@buffer.length
|
1240
|
+
out('xref')
|
1241
|
+
out('0 '+(@n+1).to_s)
|
1242
|
+
out('0000000000 65535 f ')
|
1243
|
+
1.upto(@n) do |i|
|
1244
|
+
out(sprintf('%010d 00000 n ',@offsets[i]))
|
1245
|
+
end
|
1246
|
+
# Trailer
|
1247
|
+
out('trailer')
|
1248
|
+
out('<<')
|
1249
|
+
puttrailer
|
1250
|
+
out('>>')
|
1251
|
+
out('startxref')
|
1252
|
+
out(o)
|
1253
|
+
out('%%EOF')
|
1254
|
+
state=3
|
1255
|
+
end
|
1256
|
+
|
1257
|
+
def beginpage(orientation)
|
1258
|
+
@page=@page+1
|
1259
|
+
@pages[@page]=''
|
1260
|
+
@state=2
|
1261
|
+
@x=@lMargin
|
1262
|
+
@y=@tMargin
|
1263
|
+
@lasth=0
|
1264
|
+
@FontFamily=''
|
1265
|
+
# Page orientation
|
1266
|
+
if orientation==''
|
1267
|
+
orientation=@DefOrientation
|
1268
|
+
else
|
1269
|
+
orientation=orientation[0].chr.upcase
|
1270
|
+
if orientation!=@DefOrientation
|
1271
|
+
@OrientationChanges[@page]=true
|
1272
|
+
end
|
1273
|
+
end
|
1274
|
+
if orientation!=@CurOrientation
|
1275
|
+
# Change orientation
|
1276
|
+
if orientation=='P'
|
1277
|
+
@wPt=@fwPt
|
1278
|
+
@hPt=@fhPt
|
1279
|
+
@w=@fw
|
1280
|
+
@h=@fh
|
1281
|
+
else
|
1282
|
+
@wPt=@fhPt
|
1283
|
+
@hPt=@fwPt
|
1284
|
+
@w=@fh
|
1285
|
+
@h=@fw
|
1286
|
+
end
|
1287
|
+
@PageBreakTrigger=@h-@bMargin
|
1288
|
+
@CurOrientation=orientation
|
1289
|
+
end
|
1290
|
+
end
|
1291
|
+
|
1292
|
+
def endpage
|
1293
|
+
# End of page contents
|
1294
|
+
@state=1
|
1295
|
+
end
|
1296
|
+
|
1297
|
+
def newobj
|
1298
|
+
# Begin a new object
|
1299
|
+
@n=@n+1
|
1300
|
+
@offsets[@n]=@buffer.length
|
1301
|
+
out(@n.to_s+' 0 obj')
|
1302
|
+
end
|
1303
|
+
|
1304
|
+
def dounderline(x,y,txt)
|
1305
|
+
# Underline text
|
1306
|
+
up=@CurrentFont['up']
|
1307
|
+
ut=@CurrentFont['ut']
|
1308
|
+
w=GetStringWidth(txt)+@ws*txt.count(' ')
|
1309
|
+
sprintf('%.2f %.2f %.2f %.2f re f',
|
1310
|
+
x*@k,(@h-(y-up/1000.0*@FontSize))*@k,w*@k,-ut/1000.0*@FontSizePt)
|
1311
|
+
end
|
1312
|
+
|
1313
|
+
def parsejpg(file)
|
1314
|
+
# Extract info from a JPEG file
|
1315
|
+
a=extractjpginfo(file)
|
1316
|
+
raise "Missing or incorrect JPEG file: #{file}" if a.nil?
|
1317
|
+
|
1318
|
+
if a['channels'].nil? || a['channels']==3 then
|
1319
|
+
colspace='DeviceRGB'
|
1320
|
+
elsif a['channels']==4 then
|
1321
|
+
colspace='DeviceCMYK'
|
1322
|
+
else
|
1323
|
+
colspace='DeviceGray'
|
1324
|
+
end
|
1325
|
+
bpc= a['bits'] ? a['bits'].to_i : 8
|
1326
|
+
|
1327
|
+
# Read whole file
|
1328
|
+
data = nil
|
1329
|
+
open(file, 'rb') do |f|
|
1330
|
+
data = f.read
|
1331
|
+
end
|
1332
|
+
return {'w'=>a['width'],'h'=>a['height'],'cs'=>colspace,'bpc'=>bpc,'f'=>'DCTDecode','data'=>data}
|
1333
|
+
end
|
1334
|
+
|
1335
|
+
def parsepng(file)
|
1336
|
+
# Extract info from a PNG file
|
1337
|
+
f=open(file,'rb')
|
1338
|
+
# Check signature
|
1339
|
+
unless f.read(8)==137.chr+'PNG'+13.chr+10.chr+26.chr+10.chr
|
1340
|
+
self.Error('Not a PNG file: '+file)
|
1341
|
+
end
|
1342
|
+
# Read header chunk
|
1343
|
+
f.read(4)
|
1344
|
+
if f.read(4)!='IHDR'
|
1345
|
+
self.Error('Incorrect PNG file: '+file)
|
1346
|
+
end
|
1347
|
+
w=freadint(f)
|
1348
|
+
h=freadint(f)
|
1349
|
+
bpc=f.read(1)[0]
|
1350
|
+
if bpc>8
|
1351
|
+
self.Error('16-bit depth not supported: '+file)
|
1352
|
+
end
|
1353
|
+
ct=f.read(1)[0]
|
1354
|
+
if ct==0
|
1355
|
+
colspace='DeviceGray'
|
1356
|
+
elsif ct==2
|
1357
|
+
colspace='DeviceRGB'
|
1358
|
+
elsif ct==3
|
1359
|
+
colspace='Indexed'
|
1360
|
+
else
|
1361
|
+
self.Error('Alpha channel not supported: '+file)
|
1362
|
+
end
|
1363
|
+
if f.read(1)[0]!=0
|
1364
|
+
self.Error('Unknown compression method: '+file)
|
1365
|
+
end
|
1366
|
+
if f.read(1)[0]!=0
|
1367
|
+
self.Error('Unknown filter method: '+file)
|
1368
|
+
end
|
1369
|
+
if f.read(1)[0]!=0
|
1370
|
+
self.Error('Interlacing not supported: '+file)
|
1371
|
+
end
|
1372
|
+
f.read(4)
|
1373
|
+
parms='/DecodeParms <</Predictor 15 /Colors '+(ct==2 ? '3' : '1')+
|
1374
|
+
' /BitsPerComponent '+bpc.to_s+' /Columns '+w.to_s+'>>'
|
1375
|
+
# Scan chunks looking for palette, transparency and image data
|
1376
|
+
pal=''
|
1377
|
+
trns=''
|
1378
|
+
data=''
|
1379
|
+
begin
|
1380
|
+
n=freadint(f)
|
1381
|
+
type=f.read(4)
|
1382
|
+
if type=='PLTE'
|
1383
|
+
# Read palette
|
1384
|
+
pal=f.read(n)
|
1385
|
+
f.read(4)
|
1386
|
+
elsif type=='tRNS'
|
1387
|
+
# Read transparency info
|
1388
|
+
t=f.read(n)
|
1389
|
+
if ct==0
|
1390
|
+
trns=[t[1]]
|
1391
|
+
elsif ct==2
|
1392
|
+
trns=[t[1],t[3],t[5]]
|
1393
|
+
else
|
1394
|
+
pos=t.index(0)
|
1395
|
+
trns=[pos] unless pos.nil?
|
1396
|
+
end
|
1397
|
+
f.read(4)
|
1398
|
+
elsif type=='IDAT'
|
1399
|
+
# Read image data block
|
1400
|
+
data << f.read(n)
|
1401
|
+
f.read(4)
|
1402
|
+
elsif type=='IEND'
|
1403
|
+
break
|
1404
|
+
else
|
1405
|
+
f.read(n+4)
|
1406
|
+
end
|
1407
|
+
end while n
|
1408
|
+
if colspace=='Indexed' and pal==''
|
1409
|
+
self.Error('Missing palette in '+file)
|
1410
|
+
end
|
1411
|
+
f.close
|
1412
|
+
{'w'=>w,'h'=>h,'cs'=>colspace,'bpc'=>bpc,'f'=>'FlateDecode',
|
1413
|
+
'parms'=>parms,'pal'=>pal,'trns'=>trns,'data'=>data}
|
1414
|
+
end
|
1415
|
+
|
1416
|
+
def freadint(f)
|
1417
|
+
# Read a 4-byte integer from file
|
1418
|
+
a = f.read(4).unpack('N')
|
1419
|
+
return a[0]
|
1420
|
+
end
|
1421
|
+
|
1422
|
+
def freadshort(f)
|
1423
|
+
a = f.read(2).unpack('n')
|
1424
|
+
return a[0]
|
1425
|
+
end
|
1426
|
+
|
1427
|
+
def freadbyte(f)
|
1428
|
+
a = f.read(1).unpack('C')
|
1429
|
+
return a[0]
|
1430
|
+
end
|
1431
|
+
|
1432
|
+
def textstring(s)
|
1433
|
+
# Format a text string
|
1434
|
+
'('+escape(s)+')'
|
1435
|
+
end
|
1436
|
+
|
1437
|
+
def escape(s)
|
1438
|
+
# Add \ before \, ( and )
|
1439
|
+
s.gsub('\\','\\\\').gsub('(','\\(').gsub(')','\\)')
|
1440
|
+
end
|
1441
|
+
|
1442
|
+
def putstream(s)
|
1443
|
+
out('stream')
|
1444
|
+
out(s)
|
1445
|
+
out('endstream')
|
1446
|
+
end
|
1447
|
+
|
1448
|
+
def out(s)
|
1449
|
+
# Add a line to the document
|
1450
|
+
if @state==2
|
1451
|
+
@pages[@page]=@pages[@page]+s+"\n"
|
1452
|
+
else
|
1453
|
+
@buffer=@buffer+s.to_s+"\n"
|
1454
|
+
end
|
1455
|
+
end
|
1456
|
+
|
1457
|
+
# jpeg marker codes
|
1458
|
+
|
1459
|
+
M_SOF0 = 0xc0
|
1460
|
+
M_SOF1 = 0xc1
|
1461
|
+
M_SOF2 = 0xc2
|
1462
|
+
M_SOF3 = 0xc3
|
1463
|
+
|
1464
|
+
M_SOF5 = 0xc5
|
1465
|
+
M_SOF6 = 0xc6
|
1466
|
+
M_SOF7 = 0xc7
|
1467
|
+
|
1468
|
+
M_SOF9 = 0xc9
|
1469
|
+
M_SOF10 = 0xca
|
1470
|
+
M_SOF11 = 0xcb
|
1471
|
+
|
1472
|
+
M_SOF13 = 0xcd
|
1473
|
+
M_SOF14 = 0xce
|
1474
|
+
M_SOF15 = 0xcf
|
1475
|
+
|
1476
|
+
M_SOI = 0xd8
|
1477
|
+
M_EOI = 0xd9
|
1478
|
+
M_SOS = 0xda
|
1479
|
+
|
1480
|
+
def extractjpginfo(file)
|
1481
|
+
result = nil
|
1482
|
+
|
1483
|
+
open(file, "rb") do |f|
|
1484
|
+
marker = jpegnextmarker(f)
|
1485
|
+
|
1486
|
+
if marker != M_SOI
|
1487
|
+
return nil
|
1488
|
+
end
|
1489
|
+
|
1490
|
+
while true
|
1491
|
+
marker = jpegnextmarker(f)
|
1492
|
+
|
1493
|
+
case marker
|
1494
|
+
when M_SOF0, M_SOF1, M_SOF2, M_SOF3,
|
1495
|
+
M_SOF5, M_SOF6, M_SOF7, M_SOF9,
|
1496
|
+
M_SOF10, M_SOF11, M_SOF13, M_SOF14,
|
1497
|
+
M_SOF15 then
|
1498
|
+
|
1499
|
+
length = freadshort(f)
|
1500
|
+
|
1501
|
+
if result.nil?
|
1502
|
+
result = {}
|
1503
|
+
|
1504
|
+
result['bits'] = freadbyte(f)
|
1505
|
+
result['height'] = freadshort(f)
|
1506
|
+
result['width'] = freadshort(f)
|
1507
|
+
result['channels'] = freadbyte(f)
|
1508
|
+
|
1509
|
+
f.seek(length - 8, IO::SEEK_CUR)
|
1510
|
+
else
|
1511
|
+
f.seek(length - 2, IO::SEEK_CUR)
|
1512
|
+
end
|
1513
|
+
when M_SOS, M_EOI then
|
1514
|
+
return result
|
1515
|
+
else
|
1516
|
+
length = freadshort(f)
|
1517
|
+
f.seek(length - 2, IO::SEEK_CUR)
|
1518
|
+
end
|
1519
|
+
end
|
1520
|
+
end
|
1521
|
+
end
|
1522
|
+
|
1523
|
+
def jpegnextmarker(f)
|
1524
|
+
while true
|
1525
|
+
# look for 0xff
|
1526
|
+
while (c = freadbyte(f)) != 0xff
|
1527
|
+
end
|
1528
|
+
|
1529
|
+
c = freadbyte(f)
|
1530
|
+
|
1531
|
+
if c != 0
|
1532
|
+
return c
|
1533
|
+
end
|
1534
|
+
end
|
1535
|
+
end
|
1536
|
+
end
|