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