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