##// END OF EJS Templates
PDF: fix syntax errors at vendor/plugins/rfpdf/lib/tcpdf.rb on Ruby 1.9 (#61)....
Toshi MARUYAMA -
r5147:dbe71b4f3bae
parent child
Show More
@@ -1,3826 +1,3826
1 1 #============================================================+
2 2 # File name : tcpdf.rb
3 3 # Begin : 2002-08-03
4 4 # Last Update : 2007-03-20
5 5 # Author : Nicola Asuni
6 6 # Version : 1.53.0.TC031
7 7 # License : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
8 8 #
9 9 # Description : This is a Ruby class for generating PDF files
10 10 # on-the-fly without requiring external
11 11 # extensions.
12 12 #
13 13 # IMPORTANT:
14 14 # This class is an extension and improvement of the Public Domain
15 15 # FPDF class by Olivier Plathey (http://www.fpdf.org).
16 16 #
17 17 # Main changes by Nicola Asuni:
18 18 # Ruby porting;
19 19 # UTF-8 Unicode support;
20 20 # code refactoring;
21 21 # source code clean up;
22 22 # code style and formatting;
23 23 # source code documentation using phpDocumentor (www.phpdoc.org);
24 24 # All ISO page formats were included;
25 25 # image scale factor;
26 26 # includes methods to parse and printsome XHTML code, supporting the following elements: h1, h2, h3, h4, h5, h6, b, u, i, a, img, p, br, strong, em, font, blockquote, li, ul, ol, hr, td, th, tr, table, sup, sub, small;
27 27 # includes a method to print various barcode formats using an improved version of "Generic Barcode Render Class" by Karim Mribti (http://www.mribti.com/barcode/) (require GD library: http://www.boutell.com/gd/);
28 28 # defines standard Header() and Footer() methods.
29 29 #
30 30 # Ported to Ruby by Ed Moss 2007-08-06
31 31 #
32 32 #============================================================+
33 33
34 34 #
35 35 # TCPDF Class.
36 36 # @package com.tecnick.tcpdf
37 37 #
38 38
39 39 @@version = "1.53.0.TC031"
40 40 @@fpdf_charwidths = {}
41 41
42 42 PDF_PRODUCER = 'TCPDF via RFPDF 1.53.0.TC031 (http://tcpdf.sourceforge.net)'
43 43
44 44 module TCPDFFontDescriptor
45 45 @@descriptors = { 'freesans' => {} }
46 46 @@font_name = 'freesans'
47 47
48 48 def self.font(font_name)
49 49 @@descriptors[font_name.gsub(".rb", "")]
50 50 end
51 51
52 52 def self.define(font_name = 'freesans')
53 53 @@descriptors[font_name] ||= {}
54 54 yield @@descriptors[font_name]
55 55 end
56 56 end
57 57
58 58 # This is a Ruby class for generating PDF files on-the-fly without requiring external extensions.<br>
59 59 # This class is an extension and improvement of the FPDF class by Olivier Plathey (http://www.fpdf.org).<br>
60 60 # This version contains some changes: [porting to Ruby, support for UTF-8 Unicode, code style and formatting, php documentation (www.phpdoc.org), ISO page formats, minor improvements, image scale factor]<br>
61 61 # TCPDF project (http://tcpdf.sourceforge.net) is based on the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org).<br>
62 62 # To add your own TTF fonts please read /fonts/README.TXT
63 63 # @name TCPDF
64 64 # @package com.tecnick.tcpdf
65 65 # @@version 1.53.0.TC031
66 66 # @author Nicola Asuni
67 67 # @link http://tcpdf.sourceforge.net
68 68 # @license http://www.gnu.org/copyleft/lesser.html LGPL
69 69 #
70 70 class TCPDF
71 71 include RFPDF
72 72 include Core::RFPDF
73 73 include RFPDF::Math
74 74
75 75 cattr_accessor :k_cell_height_ratio
76 76 @@k_cell_height_ratio = 1.25
77 77
78 78 cattr_accessor :k_blank_image
79 79 @@k_blank_image = ""
80 80
81 81 cattr_accessor :k_small_ratio
82 82 @@k_small_ratio = 2/3.0
83 83
84 84 cattr_accessor :k_path_cache
85 85 @@k_path_cache = File.join(RAILS_ROOT, 'tmp')
86 86
87 87 cattr_accessor :k_path_url_cache
88 88 @@k_path_url_cache = File.join(RAILS_ROOT, 'tmp')
89 89
90 90 cattr_accessor :decoder
91 91
92 92 attr_accessor :barcode
93 93
94 94 attr_accessor :buffer
95 95
96 96 attr_accessor :diffs
97 97
98 98 attr_accessor :color_flag
99 99
100 100 attr_accessor :default_table_columns
101 101
102 102 attr_accessor :default_font
103 103
104 104 attr_accessor :draw_color
105 105
106 106 attr_accessor :encoding
107 107
108 108 attr_accessor :fill_color
109 109
110 110 attr_accessor :fonts
111 111
112 112 attr_accessor :font_family
113 113
114 114 attr_accessor :font_files
115 115
116 116 cattr_accessor :font_path
117 117
118 118 attr_accessor :font_style
119 119
120 120 attr_accessor :font_size_pt
121 121
122 122 attr_accessor :header_width
123 123
124 124 attr_accessor :header_logo
125 125
126 126 attr_accessor :header_logo_width
127 127
128 128 attr_accessor :header_title
129 129
130 130 attr_accessor :header_string
131 131
132 132 attr_accessor :images
133 133
134 134 attr_accessor :img_scale
135 135
136 136 attr_accessor :in_footer
137 137
138 138 attr_accessor :is_unicode
139 139
140 140 attr_accessor :lasth
141 141
142 142 attr_accessor :links
143 143
144 144 attr_accessor :listordered
145 145
146 146 attr_accessor :listcount
147 147
148 148 attr_accessor :lispacer
149 149
150 150 attr_accessor :n
151 151
152 152 attr_accessor :offsets
153 153
154 154 attr_accessor :orientation_changes
155 155
156 156 attr_accessor :page
157 157
158 158 attr_accessor :page_links
159 159
160 160 attr_accessor :pages
161 161
162 162 attr_accessor :pdf_version
163 163
164 164 attr_accessor :prevfill_color
165 165
166 166 attr_accessor :prevtext_color
167 167
168 168 attr_accessor :print_header
169 169
170 170 attr_accessor :print_footer
171 171
172 172 attr_accessor :state
173 173
174 174 attr_accessor :tableborder
175 175
176 176 attr_accessor :tdbegin
177 177
178 178 attr_accessor :tdwidth
179 179
180 180 attr_accessor :tdheight
181 181
182 182 attr_accessor :tdalign
183 183
184 184 attr_accessor :tdfill
185 185
186 186 attr_accessor :tempfontsize
187 187
188 188 attr_accessor :text_color
189 189
190 190 attr_accessor :underline
191 191
192 192 attr_accessor :ws
193 193
194 194 #
195 195 # This is the class constructor.
196 196 # It allows to set up the page format, the orientation and
197 197 # the measure unit used in all the methods (except for the font sizes).
198 198 # @since 1.0
199 199 # @param string :orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul>
200 200 # @param string :unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.
201 201 # @param mixed :format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>
202 202 # @param boolean :unicode TRUE means that the input text is unicode (default = true)
203 203 # @param String :encoding charset encoding; default is UTF-8
204 204 #
205 205 def initialize(orientation = 'P', unit = 'mm', format = 'A4', unicode = true, encoding = "UTF-8")
206 206
207 207 # Set internal character encoding to ASCII#
208 208 #FIXME 2007-05-25 (EJM) Level=0 -
209 209 # if (respond_to?("mb_internal_encoding") and mb_internal_encoding())
210 210 # @internal_encoding = mb_internal_encoding();
211 211 # mb_internal_encoding("ASCII");
212 212 # }
213 213
214 214 #Some checks
215 215 dochecks();
216 216
217 217 begin
218 218 @@decoder = HTMLEntities.new
219 219 rescue
220 220 @@decoder = nil
221 221 end
222 222
223 223 #Initialization of properties
224 224 @barcode ||= false
225 225 @buffer ||= ''
226 226 @diffs ||= []
227 227 @color_flag ||= false
228 228 @default_table_columns ||= 4
229 229 @default_font ||= "FreeSans" if unicode
230 230 @default_font ||= "Helvetica"
231 231 @draw_color ||= '0 G'
232 232 @encoding ||= "UTF-8"
233 233 @fill_color ||= '0 g'
234 234 @fonts ||= {}
235 235 @font_family ||= ''
236 236 @font_files ||= {}
237 237 @font_style ||= ''
238 238 @font_size ||= 12
239 239 @font_size_pt ||= 12
240 240 @header_width ||= 0
241 241 @header_logo ||= ""
242 242 @header_logo_width ||= 30
243 243 @header_title ||= ""
244 244 @header_string ||= ""
245 245 @images ||= {}
246 246 @img_scale ||= 1
247 247 @in_footer ||= false
248 248 @is_unicode = unicode
249 249 @lasth ||= 0
250 250 @links ||= []
251 251 @listordered ||= false
252 252 @listcount ||= 0
253 253 @lispacer ||= ""
254 254 @n ||= 2
255 255 @offsets ||= []
256 256 @orientation_changes ||= []
257 257 @page ||= 0
258 258 @page_links ||= {}
259 259 @pages ||= []
260 260 @pdf_version ||= "1.3"
261 261 @prevfill_color ||= [255,255,255]
262 262 @prevtext_color ||= [0,0,0]
263 263 @print_header ||= false
264 264 @print_footer ||= false
265 265 @state ||= 0
266 266 @tableborder ||= 0
267 267 @tdbegin ||= false
268 268 @tdwidth ||= 0
269 269 @tdheight ||= 0
270 270 @tdalign ||= "L"
271 271 @tdfill ||= 0
272 272 @tempfontsize ||= 10
273 273 @text_color ||= '0 g'
274 274 @underline ||= false
275 275 @ws ||= 0
276 276
277 277 #Standard Unicode fonts
278 278 @core_fonts = {
279 279 'courier'=>'Courier',
280 280 'courierB'=>'Courier-Bold',
281 281 'courierI'=>'Courier-Oblique',
282 282 'courierBI'=>'Courier-BoldOblique',
283 283 'helvetica'=>'Helvetica',
284 284 'helveticaB'=>'Helvetica-Bold',
285 285 'helveticaI'=>'Helvetica-Oblique',
286 286 'helveticaBI'=>'Helvetica-BoldOblique',
287 287 'times'=>'Times-Roman',
288 288 'timesB'=>'Times-Bold',
289 289 'timesI'=>'Times-Italic',
290 290 'timesBI'=>'Times-BoldItalic',
291 291 'symbol'=>'Symbol',
292 292 'zapfdingbats'=>'ZapfDingbats'}
293 293
294 294 #Scale factor
295 295 case unit.downcase
296 when 'pt': @k=1
297 when 'mm': @k=72/25.4
298 when 'cm': @k=72/2.54
299 when 'in': @k=72
296 when 'pt' ; @k=1
297 when 'mm' ; @k=72/25.4
298 when 'cm' ; @k=72/2.54
299 when 'in' ; @k=72
300 300 else Error("Incorrect unit: #{unit}")
301 301 end
302 302
303 303 #Page format
304 304 if format.is_a?(String)
305 305 # Page formats (45 standard ISO paper formats and 4 american common formats).
306 306 # Paper cordinates are calculated in this way: (inches# 72) where (1 inch = 2.54 cm)
307 307 case (format.upcase)
308 when '4A0': format = [4767.87,6740.79]
309 when '2A0': format = [3370.39,4767.87]
310 when 'A0': format = [2383.94,3370.39]
311 when 'A1': format = [1683.78,2383.94]
312 when 'A2': format = [1190.55,1683.78]
313 when 'A3': format = [841.89,1190.55]
314 when 'A4': format = [595.28,841.89] #: default
315 when 'A5': format = [419.53,595.28]
316 when 'A6': format = [297.64,419.53]
317 when 'A7': format = [209.76,297.64]
318 when 'A8': format = [147.40,209.76]
319 when 'A9': format = [104.88,147.40]
320 when 'A10': format = [73.70,104.88]
321 when 'B0': format = [2834.65,4008.19]
322 when 'B1': format = [2004.09,2834.65]
323 when 'B2': format = [1417.32,2004.09]
324 when 'B3': format = [1000.63,1417.32]
325 when 'B4': format = [708.66,1000.63]
326 when 'B5': format = [498.90,708.66]
327 when 'B6': format = [354.33,498.90]
328 when 'B7': format = [249.45,354.33]
329 when 'B8': format = [175.75,249.45]
330 when 'B9': format = [124.72,175.75]
331 when 'B10': format = [87.87,124.72]
332 when 'C0': format = [2599.37,3676.54]
333 when 'C1': format = [1836.85,2599.37]
334 when 'C2': format = [1298.27,1836.85]
335 when 'C3': format = [918.43,1298.27]
336 when 'C4': format = [649.13,918.43]
337 when 'C5': format = [459.21,649.13]
338 when 'C6': format = [323.15,459.21]
339 when 'C7': format = [229.61,323.15]
340 when 'C8': format = [161.57,229.61]
341 when 'C9': format = [113.39,161.57]
342 when 'C10': format = [79.37,113.39]
343 when 'RA0': format = [2437.80,3458.27]
344 when 'RA1': format = [1729.13,2437.80]
345 when 'RA2': format = [1218.90,1729.13]
346 when 'RA3': format = [864.57,1218.90]
347 when 'RA4': format = [609.45,864.57]
348 when 'SRA0': format = [2551.18,3628.35]
349 when 'SRA1': format = [1814.17,2551.18]
350 when 'SRA2': format = [1275.59,1814.17]
351 when 'SRA3': format = [907.09,1275.59]
352 when 'SRA4': format = [637.80,907.09]
353 when 'LETTER': format = [612.00,792.00]
354 when 'LEGAL': format = [612.00,1008.00]
355 when 'EXECUTIVE': format = [521.86,756.00]
356 when 'FOLIO': format = [612.00,936.00]
308 when '4A0' ; format = [4767.87,6740.79]
309 when '2A0' ; format = [3370.39,4767.87]
310 when 'A0' ; format = [2383.94,3370.39]
311 when 'A1' ; format = [1683.78,2383.94]
312 when 'A2' ; format = [1190.55,1683.78]
313 when 'A3' ; format = [841.89,1190.55]
314 when 'A4' ; format = [595.28,841.89] # ; default
315 when 'A5' ; format = [419.53,595.28]
316 when 'A6' ; format = [297.64,419.53]
317 when 'A7' ; format = [209.76,297.64]
318 when 'A8' ; format = [147.40,209.76]
319 when 'A9' ; format = [104.88,147.40]
320 when 'A10' ; format = [73.70,104.88]
321 when 'B0' ; format = [2834.65,4008.19]
322 when 'B1' ; format = [2004.09,2834.65]
323 when 'B2' ; format = [1417.32,2004.09]
324 when 'B3' ; format = [1000.63,1417.32]
325 when 'B4' ; format = [708.66,1000.63]
326 when 'B5' ; format = [498.90,708.66]
327 when 'B6' ; format = [354.33,498.90]
328 when 'B7' ; format = [249.45,354.33]
329 when 'B8' ; format = [175.75,249.45]
330 when 'B9' ; format = [124.72,175.75]
331 when 'B10' ; format = [87.87,124.72]
332 when 'C0' ; format = [2599.37,3676.54]
333 when 'C1' ; format = [1836.85,2599.37]
334 when 'C2' ; format = [1298.27,1836.85]
335 when 'C3' ; format = [918.43,1298.27]
336 when 'C4' ; format = [649.13,918.43]
337 when 'C5' ; format = [459.21,649.13]
338 when 'C6' ; format = [323.15,459.21]
339 when 'C7' ; format = [229.61,323.15]
340 when 'C8' ; format = [161.57,229.61]
341 when 'C9' ; format = [113.39,161.57]
342 when 'C10' ; format = [79.37,113.39]
343 when 'RA0' ; format = [2437.80,3458.27]
344 when 'RA1' ; format = [1729.13,2437.80]
345 when 'RA2' ; format = [1218.90,1729.13]
346 when 'RA3' ; format = [864.57,1218.90]
347 when 'RA4' ; format = [609.45,864.57]
348 when 'SRA0' ; format = [2551.18,3628.35]
349 when 'SRA1' ; format = [1814.17,2551.18]
350 when 'SRA2' ; format = [1275.59,1814.17]
351 when 'SRA3' ; format = [907.09,1275.59]
352 when 'SRA4' ; format = [637.80,907.09]
353 when 'LETTER' ; format = [612.00,792.00]
354 when 'LEGAL' ; format = [612.00,1008.00]
355 when 'EXECUTIVE' ; format = [521.86,756.00]
356 when 'FOLIO' ; format = [612.00,936.00]
357 357 #else then Error("Unknown page format: #{format}"
358 358 end
359 359 @fw_pt = format[0]
360 360 @fh_pt = format[1]
361 361 else
362 362 @fw_pt = format[0]*@k
363 363 @fh_pt = format[1]*@k
364 364 end
365 365
366 366 @fw = @fw_pt/@k
367 367 @fh = @fh_pt/@k
368 368
369 369 #Page orientation
370 370 orientation = orientation.downcase
371 371 if orientation == 'p' or orientation == 'portrait'
372 372 @def_orientation = 'P'
373 373 @w_pt = @fw_pt
374 374 @h_pt = @fh_pt
375 375 elsif orientation == 'l' or orientation == 'landscape'
376 376 @def_orientation = 'L'
377 377 @w_pt = @fh_pt
378 378 @h_pt = @fw_pt
379 379 else
380 380 Error("Incorrect orientation: #{orientation}")
381 381 end
382 382
383 383 @cur_orientation = @def_orientation
384 384 @w = @w_pt/@k
385 385 @h = @h_pt/@k
386 386 #Page margins (1 cm)
387 387 margin = 28.35/@k
388 388 SetMargins(margin, margin)
389 389 #Interior cell margin (1 mm)
390 390 @c_margin = margin / 10
391 391 #Line width (0.2 mm)
392 392 @line_width = 0.567 / @k
393 393 #Automatic page break
394 394 SetAutoPageBreak(true, 2 * margin)
395 395 #Full width display mode
396 396 SetDisplayMode('fullwidth')
397 397 #Compression
398 398 SetCompression(true)
399 399 #Set default PDF version number
400 400 @pdf_version = "1.3"
401 401
402 402 @encoding = encoding
403 403 @b = 0
404 404 @i = 0
405 405 @u = 0
406 406 @href = ''
407 407 @fontlist = ["arial", "times", "courier", "helvetica", "symbol"]
408 408 @issetfont = false
409 409 @issetcolor = false
410 410
411 411 SetFillColor(200, 200, 200, true)
412 412 SetTextColor(0, 0, 0, true)
413 413 end
414 414
415 415 #
416 416 # Set the image scale.
417 417 # @param float :scale image scale.
418 418 # @author Nicola Asuni
419 419 # @since 1.5.2
420 420 #
421 421 def SetImageScale(scale)
422 422 @img_scale = scale;
423 423 end
424 424 alias_method :set_image_scale, :SetImageScale
425 425
426 426 #
427 427 # Returns the image scale.
428 428 # @return float image scale.
429 429 # @author Nicola Asuni
430 430 # @since 1.5.2
431 431 #
432 432 def GetImageScale()
433 433 return @img_scale;
434 434 end
435 435 alias_method :get_image_scale, :GetImageScale
436 436
437 437 #
438 438 # Returns the page width in units.
439 439 # @return int page width.
440 440 # @author Nicola Asuni
441 441 # @since 1.5.2
442 442 #
443 443 def GetPageWidth()
444 444 return @w;
445 445 end
446 446 alias_method :get_page_width, :GetPageWidth
447 447
448 448 #
449 449 # Returns the page height in units.
450 450 # @return int page height.
451 451 # @author Nicola Asuni
452 452 # @since 1.5.2
453 453 #
454 454 def GetPageHeight()
455 455 return @h;
456 456 end
457 457 alias_method :get_page_height, :GetPageHeight
458 458
459 459 #
460 460 # Returns the page break margin.
461 461 # @return int page break margin.
462 462 # @author Nicola Asuni
463 463 # @since 1.5.2
464 464 #
465 465 def GetBreakMargin()
466 466 return @b_margin;
467 467 end
468 468 alias_method :get_break_margin, :GetBreakMargin
469 469
470 470 #
471 471 # Returns the scale factor (number of points in user unit).
472 472 # @return int scale factor.
473 473 # @author Nicola Asuni
474 474 # @since 1.5.2
475 475 #
476 476 def GetScaleFactor()
477 477 return @k;
478 478 end
479 479 alias_method :get_scale_factor, :GetScaleFactor
480 480
481 481 #
482 482 # Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them.
483 483 # @param float :left Left margin.
484 484 # @param float :top Top margin.
485 485 # @param float :right Right margin. Default value is the left one.
486 486 # @since 1.0
487 487 # @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
488 488 #
489 489 def SetMargins(left, top, right=-1)
490 490 #Set left, top and right margins
491 491 @l_margin = left
492 492 @t_margin = top
493 493 if (right == -1)
494 494 right = left
495 495 end
496 496 @r_margin = right
497 497 end
498 498 alias_method :set_margins, :SetMargins
499 499
500 500 #
501 501 # Defines the left margin. The method can be called before creating the first page. If the current abscissa gets out of page, it is brought back to the margin.
502 502 # @param float :margin The margin.
503 503 # @since 1.4
504 504 # @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
505 505 #
506 506 def SetLeftMargin(margin)
507 507 #Set left margin
508 508 @l_margin = margin
509 509 if ((@page>0) and (@x < margin))
510 510 @x = margin
511 511 end
512 512 end
513 513 alias_method :set_left_margin, :SetLeftMargin
514 514
515 515 #
516 516 # Defines the top margin. The method can be called before creating the first page.
517 517 # @param float :margin The margin.
518 518 # @since 1.5
519 519 # @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
520 520 #
521 521 def SetTopMargin(margin)
522 522 #Set top margin
523 523 @t_margin = margin
524 524 end
525 525 alias_method :set_top_margin, :SetTopMargin
526 526
527 527 #
528 528 # Defines the right margin. The method can be called before creating the first page.
529 529 # @param float :margin The margin.
530 530 # @since 1.5
531 531 # @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
532 532 #
533 533 def SetRightMargin(margin)
534 534 #Set right margin
535 535 @r_margin = margin
536 536 end
537 537 alias_method :set_right_margin, :SetRightMargin
538 538
539 539 #
540 540 # Enables or disables the automatic page breaking mode. When enabling, the second parameter is the distance from the bottom of the page that defines the triggering limit. By default, the mode is on and the margin is 2 cm.
541 541 # @param boolean :auto Boolean indicating if mode should be on or off.
542 542 # @param float :margin Distance from the bottom of the page.
543 543 # @since 1.0
544 544 # @see Cell(), MultiCell(), AcceptPageBreak()
545 545 #
546 546 def SetAutoPageBreak(auto, margin=0)
547 547 #Set auto page break mode and triggering margin
548 548 @auto_page_break = auto
549 549 @b_margin = margin
550 550 @page_break_trigger = @h - margin
551 551 end
552 552 alias_method :set_auto_page_break, :SetAutoPageBreak
553 553
554 554 #
555 555 # Defines the way the document is to be displayed by the viewer. The zoom level can be set: pages can be displayed entirely on screen, occupy the full width of the window, use real size, be scaled by a specific zooming factor or use viewer default (configured in the Preferences menu of Acrobat). The page layout can be specified too: single at once, continuous display, two columns or viewer default. By default, documents use the full width mode with continuous display.
556 556 # @param mixed :zoom The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. <ul><li>fullpage: displays the entire page on screen </li><li>fullwidth: uses maximum width of window</li><li>real: uses real size (equivalent to 100% zoom)</li><li>default: uses viewer default mode</li></ul>
557 557 # @param string :layout The page layout. Possible values are:<ul><li>single: displays one page at once</li><li>continuous: displays pages continuously (default)</li><li>two: displays two pages on two columns</li><li>default: uses viewer default mode</li></ul>
558 558 # @since 1.2
559 559 #
560 560 def SetDisplayMode(zoom, layout = 'continuous')
561 561 #Set display mode in viewer
562 562 if (zoom == 'fullpage' or zoom == 'fullwidth' or zoom == 'real' or zoom == 'default' or !zoom.is_a?(String))
563 563 @zoom_mode = zoom
564 564 else
565 565 Error("Incorrect zoom display mode: #{zoom}")
566 566 end
567 567 if (layout == 'single' or layout == 'continuous' or layout == 'two' or layout == 'default')
568 568 @layout_mode = layout
569 569 else
570 570 Error("Incorrect layout display mode: #{layout}")
571 571 end
572 572 end
573 573 alias_method :set_display_mode, :SetDisplayMode
574 574
575 575 #
576 576 # Activates or deactivates page compression. When activated, the internal representation of each page is compressed, which leads to a compression ratio of about 2 for the resulting document. Compression is on by default.
577 577 # Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
578 578 # @param boolean :compress Boolean indicating if compression must be enabled.
579 579 # @since 1.4
580 580 #
581 581 def SetCompression(compress)
582 582 #Set page compression
583 583 if (respond_to?('gzcompress'))
584 584 @compress = compress
585 585 else
586 586 @compress = false
587 587 end
588 588 end
589 589 alias_method :set_compression, :SetCompression
590 590
591 591 #
592 592 # Defines the title of the document.
593 593 # @param string :title The title.
594 594 # @since 1.2
595 595 # @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
596 596 #
597 597 def SetTitle(title)
598 598 #Title of document
599 599 @title = title
600 600 end
601 601 alias_method :set_title, :SetTitle
602 602
603 603 #
604 604 # Defines the subject of the document.
605 605 # @param string :subject The subject.
606 606 # @since 1.2
607 607 # @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
608 608 #
609 609 def SetSubject(subject)
610 610 #Subject of document
611 611 @subject = subject
612 612 end
613 613 alias_method :set_subject, :SetSubject
614 614
615 615 #
616 616 # Defines the author of the document.
617 617 # @param string :author The name of the author.
618 618 # @since 1.2
619 619 # @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
620 620 #
621 621 def SetAuthor(author)
622 622 #Author of document
623 623 @author = author
624 624 end
625 625 alias_method :set_author, :SetAuthor
626 626
627 627 #
628 628 # Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
629 629 # @param string :keywords The list of keywords.
630 630 # @since 1.2
631 631 # @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
632 632 #
633 633 def SetKeywords(keywords)
634 634 #Keywords of document
635 635 @keywords = keywords
636 636 end
637 637 alias_method :set_keywords, :SetKeywords
638 638
639 639 #
640 640 # Defines the creator of the document. This is typically the name of the application that generates the PDF.
641 641 # @param string :creator The name of the creator.
642 642 # @since 1.2
643 643 # @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
644 644 #
645 645 def SetCreator(creator)
646 646 #Creator of document
647 647 @creator = creator
648 648 end
649 649 alias_method :set_creator, :SetCreator
650 650
651 651 #
652 652 # Defines an alias for the total number of pages. It will be substituted as the document is closed.<br />
653 653 # <b>Example:</b><br />
654 654 # <pre>
655 655 # class PDF extends TCPDF {
656 656 # def Footer()
657 657 # #Go to 1.5 cm from bottom
658 658 # SetY(-15);
659 659 # #Select Arial italic 8
660 660 # SetFont('Arial','I',8);
661 661 # #Print current and total page numbers
662 662 # Cell(0,10,'Page '.PageNo().'/{nb}',0,0,'C');
663 663 # end
664 664 # }
665 665 # :pdf=new PDF();
666 666 # :pdf->alias_nb_pages();
667 667 # </pre>
668 668 # @param string :alias The alias. Default valuenb}.
669 669 # @since 1.4
670 670 # @see PageNo(), Footer()
671 671 #
672 672 def alias_nb_pages(alias_nb ='{nb}')
673 673 #Define an alias for total number of pages
674 674 @alias_nb_pages = escapetext(alias_nb)
675 675 end
676 676
677 677 #
678 678 # This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. An inherited class may override it to customize the error handling but should always halt the script, or the resulting document would probably be invalid.
679 679 # 2004-06-11 :: Nicola Asuni : changed bold tag with strong
680 680 # @param string :msg The error message
681 681 # @since 1.0
682 682 #
683 683 def Error(msg)
684 684 #Fatal error
685 685 raise ("TCPDF error: #{msg}")
686 686 end
687 687 alias_method :error, :Error
688 688
689 689 #
690 690 # This method begins the generation of the PDF document. It is not necessary to call it explicitly because AddPage() does it automatically.
691 691 # Note: no page is created by this method
692 692 # @since 1.0
693 693 # @see AddPage(), Close()
694 694 #
695 695 def Open()
696 696 #Begin document
697 697 @state = 1
698 698 end
699 699 # alias_method :open, :Open
700 700
701 701 #
702 702 # Terminates the PDF document. It is not necessary to call this method explicitly because Output() does it automatically. If the document contains no page, AddPage() is called to prevent from getting an invalid document.
703 703 # @since 1.0
704 704 # @see Open(), Output()
705 705 #
706 706 def Close()
707 707 #Terminate document
708 708 if (@state==3)
709 709 return;
710 710 end
711 711 if (@page==0)
712 712 AddPage();
713 713 end
714 714 #Page footer
715 715 @in_footer=true;
716 716 Footer();
717 717 @in_footer=false;
718 718 #Close page
719 719 endpage();
720 720 #Close document
721 721 enddoc();
722 722 end
723 723 # alias_method :close, :Close
724 724
725 725 #
726 726 # Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer. Then the page is added, the current position set to the top-left corner according to the left and top margins, and Header() is called to display the header.
727 727 # The font which was set before calling is automatically restored. There is no need to call SetFont() again if you want to continue with the same font. The same is true for colors and line width.
728 728 # The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
729 729 # @param string :orientation Page orientation. Possible values are (case insensitive):<ul><li>P or Portrait</li><li>L or Landscape</li></ul> The default value is the one passed to the constructor.
730 730 # @since 1.0
731 731 # @see TCPDF(), Header(), Footer(), SetMargins()
732 732 #
733 733 def AddPage(orientation='')
734 734 #Start a new page
735 735 if (@state==0)
736 736 Open();
737 737 end
738 738 family=@font_family;
739 739 style=@font_style + (@underline ? 'U' : '');
740 740 size=@font_size_pt;
741 741 lw=@line_width;
742 742 dc=@draw_color;
743 743 fc=@fill_color;
744 744 tc=@text_color;
745 745 cf=@color_flag;
746 746 if (@page>0)
747 747 #Page footer
748 748 @in_footer=true;
749 749 Footer();
750 750 @in_footer=false;
751 751 #Close page
752 752 endpage();
753 753 end
754 754 #Start new page
755 755 beginpage(orientation);
756 756 #Set line cap style to square
757 757 out('2 J');
758 758 #Set line width
759 759 @line_width = lw;
760 760 out(sprintf('%.2f w', lw*@k));
761 761 #Set font
762 762 if (family)
763 763 SetFont(family, style, size);
764 764 end
765 765 #Set colors
766 766 @draw_color = dc;
767 767 if (dc!='0 G')
768 768 out(dc);
769 769 end
770 770 @fill_color = fc;
771 771 if (fc!='0 g')
772 772 out(fc);
773 773 end
774 774 @text_color = tc;
775 775 @color_flag = cf;
776 776 #Page header
777 777 Header();
778 778 #Restore line width
779 779 if (@line_width != lw)
780 780 @line_width = lw;
781 781 out(sprintf('%.2f w', lw*@k));
782 782 end
783 783 #Restore font
784 784 if (family)
785 785 SetFont(family, style, size);
786 786 end
787 787 #Restore colors
788 788 if (@draw_color != dc)
789 789 @draw_color = dc;
790 790 out(dc);
791 791 end
792 792 if (@fill_color != fc)
793 793 @fill_color = fc;
794 794 out(fc);
795 795 end
796 796 @text_color = tc;
797 797 @color_flag = cf;
798 798 end
799 799 alias_method :add_page, :AddPage
800 800
801 801 #
802 802 # Rotate object.
803 803 # @param float :angle angle in degrees for counter-clockwise rotation
804 804 # @param int :x abscissa of the rotation center. Default is current x position
805 805 # @param int :y ordinate of the rotation center. Default is current y position
806 806 #
807 807 def Rotate(angle, x="", y="")
808 808
809 809 if (x == '')
810 810 x = @x;
811 811 end
812 812
813 813 if (y == '')
814 814 y = @y;
815 815 end
816 816
817 817 if (@rtl)
818 818 x = @w - x;
819 819 angle = -@angle;
820 820 end
821 821
822 822 y = (@h - y) * @k;
823 823 x *= @k;
824 824
825 825 # calculate elements of transformation matrix
826 826 tm = []
827 827 tm[0] = ::Math::cos(deg2rad(angle));
828 828 tm[1] = ::Math::sin(deg2rad(angle));
829 829 tm[2] = -tm[1];
830 830 tm[3] = tm[0];
831 831 tm[4] = x + tm[1] * y - tm[0] * x;
832 832 tm[5] = y - tm[0] * y - tm[1] * x;
833 833
834 834 # generate the transformation matrix
835 835 Transform(tm);
836 836 end
837 837 alias_method :rotate, :Rotate
838 838
839 839 #
840 840 # Starts a 2D tranformation saving current graphic state.
841 841 # This function must be called before scaling, mirroring, translation, rotation and skewing.
842 842 # Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
843 843 #
844 844 def StartTransform
845 845 out('q');
846 846 end
847 847 alias_method :start_transform, :StartTransform
848 848
849 849 #
850 850 # Stops a 2D tranformation restoring previous graphic state.
851 851 # This function must be called after scaling, mirroring, translation, rotation and skewing.
852 852 # Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
853 853 #
854 854 def StopTransform
855 855 out('Q');
856 856 end
857 857 alias_method :stop_transform, :StopTransform
858 858
859 859 #
860 860 # Apply graphic transformations.
861 861 # @since 2.1.000 (2008-01-07)
862 862 # @see StartTransform(), StopTransform()
863 863 #
864 864 def Transform(tm)
865 865 x = out(sprintf('%.3f %.3f %.3f %.3f %.3f %.3f cm', tm[0], tm[1], tm[2], tm[3], tm[4], tm[5]));
866 866 end
867 867 alias_method :transform, :Transform
868 868
869 869 #
870 870 # Set header data.
871 871 # @param string :ln header image logo
872 872 # @param string :lw header image logo width in mm
873 873 # @param string :ht string to print as title on document header
874 874 # @param string :hs string to print on document header
875 875 #
876 876 def SetHeaderData(ln="", lw=0, ht="", hs="")
877 877 @header_logo = ln || ""
878 878 @header_logo_width = lw || 0
879 879 @header_title = ht || ""
880 880 @header_string = hs || ""
881 881 end
882 882 alias_method :set_header_data, :SetHeaderData
883 883
884 884 #
885 885 # Set header margin.
886 886 # (minimum distance between header and top page margin)
887 887 # @param int :hm distance in millimeters
888 888 #
889 889 def SetHeaderMargin(hm=10)
890 890 @header_margin = hm;
891 891 end
892 892 alias_method :set_header_margin, :SetHeaderMargin
893 893
894 894 #
895 895 # Set footer margin.
896 896 # (minimum distance between footer and bottom page margin)
897 897 # @param int :fm distance in millimeters
898 898 #
899 899 def SetFooterMargin(fm=10)
900 900 @footer_margin = fm;
901 901 end
902 902 alias_method :set_footer_margin, :SetFooterMargin
903 903
904 904 #
905 905 # Set a flag to print page header.
906 906 # @param boolean :val set to true to print the page header (default), false otherwise.
907 907 #
908 908 def SetPrintHeader(val=true)
909 909 @print_header = val;
910 910 end
911 911 alias_method :set_print_header, :SetPrintHeader
912 912
913 913 #
914 914 # Set a flag to print page footer.
915 915 # @param boolean :value set to true to print the page footer (default), false otherwise.
916 916 #
917 917 def SetPrintFooter(val=true)
918 918 @print_footer = val;
919 919 end
920 920 alias_method :set_print_footer, :SetPrintFooter
921 921
922 922 #
923 923 # This method is used to render the page header.
924 924 # It is automatically called by AddPage() and could be overwritten in your own inherited class.
925 925 #
926 926 def Header()
927 927 if (@print_header)
928 928 if (@original_l_margin.nil?)
929 929 @original_l_margin = @l_margin;
930 930 end
931 931 if (@original_r_margin.nil?)
932 932 @original_r_margin = @r_margin;
933 933 end
934 934
935 935 #set current position
936 936 SetXY(@original_l_margin, @header_margin);
937 937
938 938 if ((@header_logo) and (@header_logo != @@k_blank_image))
939 939 Image(@header_logo, @original_l_margin, @header_margin, @header_logo_width);
940 940 else
941 941 @img_rb_y = GetY();
942 942 end
943 943
944 944 cell_height = ((@@k_cell_height_ratio * @header_font[2]) / @k).round(2)
945 945
946 946 header_x = @original_l_margin + (@header_logo_width * 1.05); #set left margin for text data cell
947 947
948 948 # header title
949 949 SetFont(@header_font[0], 'B', @header_font[2] + 1);
950 950 SetX(header_x);
951 951 Cell(@header_width, cell_height, @header_title, 0, 1, 'L');
952 952
953 953 # header string
954 954 SetFont(@header_font[0], @header_font[1], @header_font[2]);
955 955 SetX(header_x);
956 956 MultiCell(@header_width, cell_height, @header_string, 0, 'L', 0);
957 957
958 958 # print an ending header line
959 959 if (@header_width)
960 960 #set style for cell border
961 961 SetLineWidth(0.3);
962 962 SetDrawColor(0, 0, 0);
963 963 SetY(1 + (@img_rb_y > GetY() ? @img_rb_y : GetY()));
964 964 SetX(@original_l_margin);
965 965 Cell(0, 0, '', 'T', 0, 'C');
966 966 end
967 967
968 968 #restore position
969 969 SetXY(@original_l_margin, @t_margin);
970 970 end
971 971 end
972 972 alias_method :header, :Header
973 973
974 974 #
975 975 # This method is used to render the page footer.
976 976 # It is automatically called by AddPage() and could be overwritten in your own inherited class.
977 977 #
978 978 def Footer()
979 979 if (@print_footer)
980 980
981 981 if (@original_l_margin.nil?)
982 982 @original_l_margin = @l_margin;
983 983 end
984 984 if (@original_r_margin.nil?)
985 985 @original_r_margin = @r_margin;
986 986 end
987 987
988 988 #set font
989 989 SetFont(@footer_font[0], @footer_font[1] , @footer_font[2]);
990 990 #set style for cell border
991 991 line_width = 0.3;
992 992 SetLineWidth(line_width);
993 993 SetDrawColor(0, 0, 0);
994 994
995 995 footer_height = ((@@k_cell_height_ratio * @footer_font[2]) / @k).round; #footer height, was , 2)
996 996 #get footer y position
997 997 footer_y = @h - @footer_margin - footer_height;
998 998 #set current position
999 999 SetXY(@original_l_margin, footer_y);
1000 1000
1001 1001 #print document barcode
1002 1002 if (@barcode)
1003 1003 Ln();
1004 1004 barcode_width = ((@w - @original_l_margin - @original_r_margin)).round; #max width
1005 1005 writeBarcode(@original_l_margin, footer_y + line_width, barcode_width, footer_height - line_width, "C128B", false, false, 2, @barcode);
1006 1006 end
1007 1007
1008 1008 SetXY(@original_l_margin, footer_y);
1009 1009
1010 1010 #Print page number
1011 1011 Cell(0, footer_height, @l['w_page'] + " " + PageNo().to_s + ' / {nb}', 'T', 0, 'R');
1012 1012 end
1013 1013 end
1014 1014 alias_method :footer, :Footer
1015 1015
1016 1016 #
1017 1017 # Returns the current page number.
1018 1018 # @return int page number
1019 1019 # @since 1.0
1020 1020 # @see alias_nb_pages()
1021 1021 #
1022 1022 def PageNo()
1023 1023 #Get current page number
1024 1024 return @page;
1025 1025 end
1026 1026 alias_method :page_no, :PageNo
1027 1027
1028 1028 #
1029 1029 # Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
1030 1030 # @param int :r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255
1031 1031 # @param int :g Green component (between 0 and 255)
1032 1032 # @param int :b Blue component (between 0 and 255)
1033 1033 # @since 1.3
1034 1034 # @see SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
1035 1035 #
1036 1036 def SetDrawColor(r, g=-1, b=-1)
1037 1037 #Set color for all stroking operations
1038 1038 if ((r==0 and g==0 and b==0) or g==-1)
1039 1039 @draw_color=sprintf('%.3f G', r/255.0);
1040 1040 else
1041 1041 @draw_color=sprintf('%.3f %.3f %.3f RG', r/255.0, g/255.0, b/255.0);
1042 1042 end
1043 1043 if (@page>0)
1044 1044 out(@draw_color);
1045 1045 end
1046 1046 end
1047 1047 alias_method :set_draw_color, :SetDrawColor
1048 1048
1049 1049 #
1050 1050 # Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
1051 1051 # @param int :r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255
1052 1052 # @param int :g Green component (between 0 and 255)
1053 1053 # @param int :b Blue component (between 0 and 255)
1054 1054 # @param boolean :storeprev if true stores the RGB array on :prevfill_color variable.
1055 1055 # @since 1.3
1056 1056 # @see SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
1057 1057 #
1058 1058 def SetFillColor(r, g=-1, b=-1, storeprev=false)
1059 1059 #Set color for all filling operations
1060 1060 if ((r==0 and g==0 and b==0) or g==-1)
1061 1061 @fill_color=sprintf('%.3f g', r/255.0);
1062 1062 else
1063 1063 @fill_color=sprintf('%.3f %.3f %.3f rg', r/255.0, g/255.0, b/255.0);
1064 1064 end
1065 1065 @color_flag=(@fill_color!=@text_color);
1066 1066 if (@page>0)
1067 1067 out(@fill_color);
1068 1068 end
1069 1069 if (storeprev)
1070 1070 # store color as previous value
1071 1071 @prevfill_color = [r, g, b]
1072 1072 end
1073 1073 end
1074 1074 alias_method :set_fill_color, :SetFillColor
1075 1075
1076 1076 # This hasn't been ported from tcpdf, it's a variation on SetTextColor for setting cmyk colors
1077 1077 def SetCmykFillColor(c, m, y, k, storeprev=false)
1078 1078 #Set color for all filling operations
1079 1079 @fill_color=sprintf('%.3f %.3f %.3f %.3f k', c, m, y, k);
1080 1080 @color_flag=(@fill_color!=@text_color);
1081 1081 if (storeprev)
1082 1082 # store color as previous value
1083 1083 @prevtext_color = [c, m, y, k]
1084 1084 end
1085 1085 if (@page>0)
1086 1086 out(@fill_color);
1087 1087 end
1088 1088 end
1089 1089 alias_method :set_cmyk_fill_color, :SetCmykFillColor
1090 1090
1091 1091 #
1092 1092 # Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
1093 1093 # @param int :r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255
1094 1094 # @param int :g Green component (between 0 and 255)
1095 1095 # @param int :b Blue component (between 0 and 255)
1096 1096 # @param boolean :storeprev if true stores the RGB array on :prevtext_color variable.
1097 1097 # @since 1.3
1098 1098 # @see SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
1099 1099 #
1100 1100 def SetTextColor(r, g=-1, b=-1, storeprev=false)
1101 1101 #Set color for text
1102 1102 if ((r==0 and :g==0 and :b==0) or :g==-1)
1103 1103 @text_color=sprintf('%.3f g', r/255.0);
1104 1104 else
1105 1105 @text_color=sprintf('%.3f %.3f %.3f rg', r/255.0, g/255.0, b/255.0);
1106 1106 end
1107 1107 @color_flag=(@fill_color!=@text_color);
1108 1108 if (storeprev)
1109 1109 # store color as previous value
1110 1110 @prevtext_color = [r, g, b]
1111 1111 end
1112 1112 end
1113 1113 alias_method :set_text_color, :SetTextColor
1114 1114
1115 1115 # This hasn't been ported from tcpdf, it's a variation on SetTextColor for setting cmyk colors
1116 1116 def SetCmykTextColor(c, m, y, k, storeprev=false)
1117 1117 #Set color for text
1118 1118 @text_color=sprintf('%.3f %.3f %.3f %.3f k', c, m, y, k);
1119 1119 @color_flag=(@fill_color!=@text_color);
1120 1120 if (storeprev)
1121 1121 # store color as previous value
1122 1122 @prevtext_color = [c, m, y, k]
1123 1123 end
1124 1124 end
1125 1125 alias_method :set_cmyk_text_color, :SetCmykTextColor
1126 1126
1127 1127 #
1128 1128 # Returns the length of a string in user unit. A font must be selected.<br>
1129 1129 # Support UTF-8 Unicode [Nicola Asuni, 2005-01-02]
1130 1130 # @param string :s The string whose length is to be computed
1131 1131 # @return int
1132 1132 # @since 1.2
1133 1133 #
1134 1134 def GetStringWidth(s)
1135 1135 #Get width of a string in the current font
1136 1136 s = s.to_s;
1137 1137 cw = @current_font['cw']
1138 1138 w = 0;
1139 1139 if (@is_unicode)
1140 1140 unicode = UTF8StringToArray(s);
1141 1141 unicode.each do |char|
1142 1142 if (!cw[char].nil?)
1143 1143 w += cw[char];
1144 1144 # This should not happen. UTF8StringToArray should guarentee the array is ascii values.
1145 1145 # elsif (c!cw[char[0]].nil?)
1146 1146 # w += cw[char[0]];
1147 1147 # elsif (!cw[char.chr].nil?)
1148 1148 # w += cw[char.chr];
1149 1149 elsif (!@current_font['desc']['MissingWidth'].nil?)
1150 1150 w += @current_font['desc']['MissingWidth']; # set default size
1151 1151 else
1152 1152 w += 500;
1153 1153 end
1154 1154 end
1155 1155 else
1156 1156 s.each_byte do |c|
1157 1157 if cw[c]
1158 1158 w += cw[c];
1159 1159 elsif cw[?c]
1160 1160 w += cw[?c]
1161 1161 end
1162 1162 end
1163 1163 end
1164 1164 return (w * @font_size / 1000.0);
1165 1165 end
1166 1166 alias_method :get_string_width, :GetStringWidth
1167 1167
1168 1168 #
1169 1169 # Defines the line width. By default, the value equals 0.2 mm. The method can be called before the first page is created and the value is retained from page to page.
1170 1170 # @param float :width The width.
1171 1171 # @since 1.0
1172 1172 # @see Line(), Rect(), Cell(), MultiCell()
1173 1173 #
1174 1174 def SetLineWidth(width)
1175 1175 #Set line width
1176 1176 @line_width = width;
1177 1177 if (@page>0)
1178 1178 out(sprintf('%.2f w', width*@k));
1179 1179 end
1180 1180 end
1181 1181 alias_method :set_line_width, :SetLineWidth
1182 1182
1183 1183 #
1184 1184 # Draws a line between two points.
1185 1185 # @param float :x1 Abscissa of first point
1186 1186 # @param float :y1 Ordinate of first point
1187 1187 # @param float :x2 Abscissa of second point
1188 1188 # @param float :y2 Ordinate of second point
1189 1189 # @since 1.0
1190 1190 # @see SetLineWidth(), SetDrawColor()
1191 1191 #
1192 1192 def Line(x1, y1, x2, y2)
1193 1193 #Draw a line
1194 1194 out(sprintf('%.2f %.2f m %.2f %.2f l S', x1 * @k, (@h - y1) * @k, x2 * @k, (@h - y2) * @k));
1195 1195 end
1196 1196 alias_method :line, :Line
1197 1197
1198 1198 def Circle(mid_x, mid_y, radius, style='')
1199 1199 mid_y = (@h-mid_y)*@k
1200 1200 out(sprintf("q\n")) # postscript content in pdf
1201 1201 # init line type etc. with /GSD gs G g (grey) RG rg (RGB) w=line witdh etc.
1202 1202 out(sprintf("1 j\n")) # line join
1203 1203 # translate ("move") circle to mid_y, mid_y
1204 1204 out(sprintf("1 0 0 1 %f %f cm", mid_x, mid_y))
1205 1205 kappa = 0.5522847498307933984022516322796
1206 1206 # Quadrant 1
1207 1207 x_s = 0.0 # 12 o'clock
1208 1208 y_s = 0.0 + radius
1209 1209 x_e = 0.0 + radius # 3 o'clock
1210 1210 y_e = 0.0
1211 1211 out(sprintf("%f %f m\n", x_s, y_s)) # move to 12 o'clock
1212 1212 # cubic bezier control point 1, start height and kappa * radius to the right
1213 1213 bx_e1 = x_s + (radius * kappa)
1214 1214 by_e1 = y_s
1215 1215 # cubic bezier control point 2, end and kappa * radius above
1216 1216 bx_e2 = x_e
1217 1217 by_e2 = y_e + (radius * kappa)
1218 1218 # 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
1219 1219 out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e))
1220 1220 # Quadrant 2
1221 1221 x_s = x_e
1222 1222 y_s = y_e # 3 o'clock
1223 1223 x_e = 0.0
1224 1224 y_e = 0.0 - radius # 6 o'clock
1225 1225 bx_e1 = x_s # cubic bezier point 1
1226 1226 by_e1 = y_s - (radius * kappa)
1227 1227 bx_e2 = x_e + (radius * kappa) # cubic bezier point 2
1228 1228 by_e2 = y_e
1229 1229 out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e))
1230 1230 # Quadrant 3
1231 1231 x_s = x_e
1232 1232 y_s = y_e # 6 o'clock
1233 1233 x_e = 0.0 - radius
1234 1234 y_e = 0.0 # 9 o'clock
1235 1235 bx_e1 = x_s - (radius * kappa) # cubic bezier point 1
1236 1236 by_e1 = y_s
1237 1237 bx_e2 = x_e # cubic bezier point 2
1238 1238 by_e2 = y_e - (radius * kappa)
1239 1239 out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e))
1240 1240 # Quadrant 4
1241 1241 x_s = x_e
1242 1242 y_s = y_e # 9 o'clock
1243 1243 x_e = 0.0
1244 1244 y_e = 0.0 + radius # 12 o'clock
1245 1245 bx_e1 = x_s # cubic bezier point 1
1246 1246 by_e1 = y_s + (radius * kappa)
1247 1247 bx_e2 = x_e - (radius * kappa) # cubic bezier point 2
1248 1248 by_e2 = y_e
1249 1249 out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e))
1250 1250 if style=='F'
1251 1251 op='f'
1252 1252 elsif style=='FD' or style=='DF'
1253 1253 op='b'
1254 1254 else
1255 1255 op='s'
1256 1256 end
1257 1257 out(sprintf("#{op}\n")) # stroke circle, do not fill and close path
1258 1258 # for filling etc. b, b*, f, f*
1259 1259 out(sprintf("Q\n")) # finish postscript in PDF
1260 1260 end
1261 1261 alias_method :circle, :Circle
1262 1262
1263 1263 #
1264 1264 # Outputs a rectangle. It can be drawn (border only), filled (with no border) or both.
1265 1265 # @param float :x Abscissa of upper-left corner
1266 1266 # @param float :y Ordinate of upper-left corner
1267 1267 # @param float :w Width
1268 1268 # @param float :h Height
1269 1269 # @param string :style Style of rendering. Possible values are:<ul><li>D or empty string: draw (default)</li><li>F: fill</li><li>DF or FD: draw and fill</li></ul>
1270 1270 # @since 1.0
1271 1271 # @see SetLineWidth(), SetDrawColor(), SetFillColor()
1272 1272 #
1273 1273 def Rect(x, y, w, h, style='')
1274 1274 #Draw a rectangle
1275 1275 if (style=='F')
1276 1276 op='f';
1277 1277 elsif (style=='FD' or style=='DF')
1278 1278 op='B';
1279 1279 else
1280 1280 op='S';
1281 1281 end
1282 1282 out(sprintf('%.2f %.2f %.2f %.2f re %s', x * @k, (@h - y) * @k, w * @k, -h * @k, op));
1283 1283 end
1284 1284 alias_method :rect, :Rect
1285 1285
1286 1286 #
1287 1287 # Imports a TrueType or Type1 font and makes it available. It is necessary to generate a font definition file first with the makefont.rb utility. The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by FPDF_FONTPATH if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated.
1288 1288 # Support UTF-8 Unicode [Nicola Asuni, 2005-01-02].
1289 1289 # <b>Example</b>:<br />
1290 1290 # <pre>
1291 1291 # :pdf->AddFont('Comic','I');
1292 1292 # # is equivalent to:
1293 1293 # :pdf->AddFont('Comic','I','comici.rb');
1294 1294 # </pre>
1295 1295 # @param string :family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
1296 1296 # @param string :style Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul>
1297 1297 # @param string :file The font definition file. By default, the name is built from the family and style, in lower case with no space.
1298 1298 # @since 1.5
1299 1299 # @see SetFont()
1300 1300 #
1301 1301 def AddFont(family, style='', file='')
1302 1302 if (family.empty?)
1303 1303 return;
1304 1304 end
1305 1305
1306 1306 #Add a TrueType or Type1 font
1307 1307 family = family.downcase
1308 1308 if ((!@is_unicode) and (family == 'arial'))
1309 1309 family = 'helvetica';
1310 1310 end
1311 1311
1312 1312 style=style.upcase
1313 1313 style=style.gsub('U','');
1314 1314 if (style == 'IB')
1315 1315 style = 'BI';
1316 1316 end
1317 1317
1318 1318 fontkey = family + style;
1319 1319 # check if the font has been already added
1320 1320 if !@fonts[fontkey].nil?
1321 1321 return;
1322 1322 end
1323 1323
1324 1324 if (file=='')
1325 1325 file = family.gsub(' ', '') + style.downcase + '.rb';
1326 1326 end
1327 1327 font_file_name = getfontpath(file)
1328 1328 if (font_file_name.nil?)
1329 1329 # try to load the basic file without styles
1330 1330 file = family.gsub(' ', '') + '.rb';
1331 1331 font_file_name = getfontpath(file)
1332 1332 end
1333 1333 if font_file_name.nil?
1334 1334 Error("Could not find font #{file}.")
1335 1335 end
1336 1336 require(getfontpath(file))
1337 1337 font_desc = TCPDFFontDescriptor.font(file)
1338 1338
1339 1339 if (font_desc[:name].nil? and @@fpdf_charwidths.nil?)
1340 1340 Error('Could not include font definition file');
1341 1341 end
1342 1342
1343 1343 i = @fonts.length+1;
1344 1344 if (@is_unicode)
1345 1345 @fonts[fontkey] = {'i' => i, 'type' => font_desc[:type], 'name' => font_desc[:name], 'desc' => font_desc[:desc], 'up' => font_desc[:up], 'ut' => font_desc[:ut], 'cw' => font_desc[:cw], 'enc' => font_desc[:enc], 'file' => font_desc[:file], 'ctg' => font_desc[:ctg], 'cMap' => font_desc[:cMap], 'registry' => font_desc[:registry]}
1346 1346 @@fpdf_charwidths[fontkey] = font_desc[:cw];
1347 1347 else
1348 1348 @fonts[fontkey]={'i' => i, 'type'=>'core', 'name'=>@core_fonts[fontkey], 'up'=>-100, 'ut'=>50, 'cw' => font_desc[:cw]}
1349 1349 @@fpdf_charwidths[fontkey] = font_desc[:cw];
1350 1350 end
1351 1351
1352 1352 if (!font_desc[:diff].nil? and (!font_desc[:diff].empty?))
1353 1353 #Search existing encodings
1354 1354 d=0;
1355 1355 nb=@diffs.length;
1356 1356 1.upto(nb) do |i|
1357 1357 if (@diffs[i]== font_desc[:diff])
1358 1358 d = i;
1359 1359 break;
1360 1360 end
1361 1361 end
1362 1362 if (d==0)
1363 1363 d = nb+1;
1364 1364 @diffs[d] = font_desc[:diff];
1365 1365 end
1366 1366 @fonts[fontkey]['diff'] = d;
1367 1367 end
1368 1368 if (font_desc[:file] and font_desc[:file].length > 0)
1369 1369 if (font_desc[:type] == "TrueType") or (font_desc[:type] == "TrueTypeUnicode")
1370 1370 @font_files[font_desc[:file]] = {'length1' => font_desc[:originalsize]}
1371 1371 else
1372 1372 @font_files[font_desc[:file]] = {'length1' => font_desc[:size1], 'length2' => font_desc[:size2]}
1373 1373 end
1374 1374 end
1375 1375 end
1376 1376 alias_method :add_font, :AddFont
1377 1377
1378 1378 #
1379 1379 # Sets the font used to print character strings. It is mandatory to call this method at least once before printing text or the resulting document would not be valid.
1380 1380 # The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
1381 1381 # The method can be called before the first page is created and the font is retained from page to page.
1382 1382 # If you just wish to change the current font size, it is simpler to call SetFontSize().
1383 1383 # Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:<ul><li>They are in the current directory (the one where the running script lies)</li><li>They are in one of the directories defined by the include_path parameter</li><li>They are in the directory defined by the FPDF_FONTPATH constant</li></ul><br />
1384 1384 # Example for the last case (note the trailing slash):<br />
1385 1385 # <pre>
1386 1386 # define('FPDF_FONTPATH','/home/www/font/');
1387 1387 # require('tcpdf.rb');
1388 1388 #
1389 1389 # #Times regular 12
1390 1390 # :pdf->SetFont('Times');
1391 1391 # #Arial bold 14
1392 1392 # :pdf->SetFont('Arial','B',14);
1393 1393 # #Removes bold
1394 1394 # :pdf->SetFont('');
1395 1395 # #Times bold, italic and underlined 14
1396 1396 # :pdf->SetFont('Times','BIU');
1397 1397 # </pre><br />
1398 1398 # If the file corresponding to the requested font is not found, the error "Could not include font metric file" is generated.
1399 1399 # @param string :family Family font. It can be either a name defined by AddFont() or one of the standard families (case insensitive):<ul><li>Courier (fixed-width)</li><li>Helvetica or Arial (synonymous; sans serif)</li><li>Times (serif)</li><li>Symbol (symbolic)</li><li>ZapfDingbats (symbolic)</li></ul>It is also possible to pass an empty string. In that case, the current family is retained.
1400 1400 # @param string :style Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li></ul>or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats
1401 1401 # @param float :size Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12
1402 1402 # @since 1.0
1403 1403 # @see AddFont(), SetFontSize(), Cell(), MultiCell(), Write()
1404 1404 #
1405 1405 def SetFont(family, style='', size=0)
1406 1406 # save previous values
1407 1407 @prevfont_family = @font_family;
1408 1408 @prevfont_style = @font_style;
1409 1409
1410 1410 family=family.downcase;
1411 1411 if (family=='')
1412 1412 family=@font_family;
1413 1413 end
1414 1414 if ((!@is_unicode) and (family == 'arial'))
1415 1415 family = 'helvetica';
1416 1416 elsif ((family=="symbol") or (family=="zapfdingbats"))
1417 1417 style='';
1418 1418 end
1419 1419
1420 1420 style=style.upcase;
1421 1421
1422 1422 if (style.include?('U'))
1423 1423 @underline=true;
1424 1424 style= style.gsub('U','');
1425 1425 else
1426 1426 @underline=false;
1427 1427 end
1428 1428 if (style=='IB')
1429 1429 style='BI';
1430 1430 end
1431 1431 if (size==0)
1432 1432 size=@font_size_pt;
1433 1433 end
1434 1434
1435 1435 # try to add font (if not already added)
1436 1436 if (@is_unicode)
1437 1437 AddFont(family, style);
1438 1438 end
1439 1439
1440 1440 #Test if font is already selected
1441 1441 if ((@font_family == family) and (@font_style == style) and (@font_size_pt == size))
1442 1442 return;
1443 1443 end
1444 1444
1445 1445 fontkey = family + style;
1446 1446 style = '' if (@fonts[fontkey].nil? and !@fonts[family].nil?)
1447 1447
1448 1448 #Test if used for the first time
1449 1449 if (@fonts[fontkey].nil?)
1450 1450 #Check if one of the standard fonts
1451 1451 if (!@core_fonts[fontkey].nil?)
1452 1452 if @@fpdf_charwidths[fontkey].nil?
1453 1453 #Load metric file
1454 1454 file = family;
1455 1455 if ((family!='symbol') and (family!='zapfdingbats'))
1456 1456 file += style.downcase;
1457 1457 end
1458 1458 if (getfontpath(file + '.rb').nil?)
1459 1459 # try to load the basic file without styles
1460 1460 file = family;
1461 1461 fontkey = family;
1462 1462 end
1463 1463 require(getfontpath(file + '.rb'));
1464 1464 font_desc = TCPDFFontDescriptor.font(file)
1465 1465 if ((@is_unicode and ctg.nil?) or ((!@is_unicode) and (@@fpdf_charwidths[fontkey].nil?)) )
1466 1466 Error("Could not include font metric file [" + fontkey + "]: " + getfontpath(file + ".rb"));
1467 1467 end
1468 1468 end
1469 1469 i = @fonts.length + 1;
1470 1470
1471 1471 if (@is_unicode)
1472 1472 @fonts[fontkey] = {'i' => i, 'type' => font_desc[:type], 'name' => font_desc[:name], 'desc' => font_desc[:desc], 'up' => font_desc[:up], 'ut' => font_desc[:ut], 'cw' => font_desc[:cw], 'enc' => font_desc[:enc], 'file' => font_desc[:file], 'ctg' => font_desc[:ctg]}
1473 1473 @@fpdf_charwidths[fontkey] = font_desc[:cw];
1474 1474 else
1475 1475 @fonts[fontkey] = {'i' => i, 'type'=>'core', 'name'=>@core_fonts[fontkey], 'up'=>-100, 'ut'=>50, 'cw' => font_desc[:cw]}
1476 1476 @@fpdf_charwidths[fontkey] = font_desc[:cw];
1477 1477 end
1478 1478 else
1479 1479 Error('Undefined font: ' + family + ' ' + style);
1480 1480 end
1481 1481 end
1482 1482 #Select it
1483 1483 @font_family = family;
1484 1484 @font_style = style;
1485 1485 @font_size_pt = size;
1486 1486 @font_size = size / @k;
1487 1487 @current_font = @fonts[fontkey]; # was & may need deep copy?
1488 1488 if (@page>0)
1489 1489 out(sprintf('BT /F%d %.2f Tf ET', @current_font['i'], @font_size_pt));
1490 1490 end
1491 1491 end
1492 1492 alias_method :set_font, :SetFont
1493 1493
1494 1494 #
1495 1495 # Defines the size of the current font.
1496 1496 # @param float :size The size (in points)
1497 1497 # @since 1.0
1498 1498 # @see SetFont()
1499 1499 #
1500 1500 def SetFontSize(size)
1501 1501 #Set font size in points
1502 1502 if (@font_size_pt== size)
1503 1503 return;
1504 1504 end
1505 1505 @font_size_pt = size;
1506 1506 @font_size = size.to_f / @k;
1507 1507 if (@page > 0)
1508 1508 out(sprintf('BT /F%d %.2f Tf ET', @current_font['i'], @font_size_pt));
1509 1509 end
1510 1510 end
1511 1511 alias_method :set_font_size, :SetFontSize
1512 1512
1513 1513 #
1514 1514 # Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.<br />
1515 1515 # The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
1516 1516 # @since 1.5
1517 1517 # @see Cell(), Write(), Image(), Link(), SetLink()
1518 1518 #
1519 1519 def AddLink()
1520 1520 #Create a new internal link
1521 1521 n=@links.length+1;
1522 1522 @links[n]=[0,0];
1523 1523 return n;
1524 1524 end
1525 1525 alias_method :add_link, :AddLink
1526 1526
1527 1527 #
1528 1528 # Defines the page and position a link points to
1529 1529 # @param int :link The link identifier returned by AddLink()
1530 1530 # @param float :y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
1531 1531 # @param int :page Number of target page; -1 indicates the current page. This is the default value
1532 1532 # @since 1.5
1533 1533 # @see AddLink()
1534 1534 #
1535 1535 def SetLink(link, y=0, page=-1)
1536 1536 #Set destination of internal link
1537 1537 if (y==-1)
1538 1538 y=@y;
1539 1539 end
1540 1540 if (page==-1)
1541 1541 page=@page;
1542 1542 end
1543 1543 @links[link] = [page, y]
1544 1544 end
1545 1545 alias_method :set_link, :SetLink
1546 1546
1547 1547 #
1548 1548 # Puts a link on a rectangular area of the page. Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image.
1549 1549 # @param float :x Abscissa of the upper-left corner of the rectangle
1550 1550 # @param float :y Ordinate of the upper-left corner of the rectangle
1551 1551 # @param float :w Width of the rectangle
1552 1552 # @param float :h Height of the rectangle
1553 1553 # @param mixed :link URL or identifier returned by AddLink()
1554 1554 # @since 1.5
1555 1555 # @see AddLink(), Cell(), Write(), Image()
1556 1556 #
1557 1557 def Link(x, y, w, h, link)
1558 1558 #Put a link on the page
1559 1559 @page_links ||= Array.new
1560 1560 @page_links[@page] ||= Array.new
1561 1561 @page_links[@page].push([x * @k, @h_pt - y * @k, w * @k, h*@k, link]);
1562 1562 end
1563 1563 alias_method :link, :Link
1564 1564
1565 1565 #
1566 1566 # Prints a character string. The origin is on the left of the first charcter, on the baseline. This method allows to place a string precisely on the page, but it is usually easier to use Cell(), MultiCell() or Write() which are the standard methods to print text.
1567 1567 # @param float :x Abscissa of the origin
1568 1568 # @param float :y Ordinate of the origin
1569 1569 # @param string :txt String to print
1570 1570 # @since 1.0
1571 1571 # @see SetFont(), SetTextColor(), Cell(), MultiCell(), Write()
1572 1572 #
1573 1573 def Text(x, y, txt)
1574 1574 #Output a string
1575 1575 s=sprintf('BT %.2f %.2f Td (%s) Tj ET', x * @k, (@h-y) * @k, escapetext(txt));
1576 1576 if (@underline and (txt!=''))
1577 1577 s += ' ' + dounderline(x, y, txt);
1578 1578 end
1579 1579 if (@color_flag)
1580 1580 s='q ' + @text_color + ' ' + s + ' Q';
1581 1581 end
1582 1582 out(s);
1583 1583 end
1584 1584 alias_method :text, :Text
1585 1585
1586 1586 #
1587 1587 # Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
1588 1588 # This method is called automatically and should not be called directly by the application.<br />
1589 1589 # <b>Example:</b><br />
1590 1590 # The method is overriden in an inherited class in order to obtain a 3 column layout:<br />
1591 1591 # <pre>
1592 1592 # class PDF extends TCPDF {
1593 1593 # var :col=0;
1594 1594 #
1595 1595 # def SetCol(col)
1596 1596 # #Move position to a column
1597 1597 # @col = col;
1598 1598 # :x=10+:col*65;
1599 1599 # SetLeftMargin(x);
1600 1600 # SetX(x);
1601 1601 # end
1602 1602 #
1603 1603 # def AcceptPageBreak()
1604 1604 # if (@col<2)
1605 1605 # #Go to next column
1606 1606 # SetCol(@col+1);
1607 1607 # SetY(10);
1608 1608 # return false;
1609 1609 # end
1610 1610 # else
1611 1611 # #Go back to first column and issue page break
1612 1612 # SetCol(0);
1613 1613 # return true;
1614 1614 # end
1615 1615 # end
1616 1616 # }
1617 1617 #
1618 1618 # :pdf=new PDF();
1619 1619 # :pdf->Open();
1620 1620 # :pdf->AddPage();
1621 1621 # :pdf->SetFont('Arial','',12);
1622 1622 # for(i=1;:i<=300;:i++)
1623 1623 # :pdf->Cell(0,5,"Line :i",0,1);
1624 1624 # }
1625 1625 # :pdf->Output();
1626 1626 # </pre>
1627 1627 # @return boolean
1628 1628 # @since 1.4
1629 1629 # @see SetAutoPageBreak()
1630 1630 #
1631 1631 def AcceptPageBreak()
1632 1632 #Accept automatic page break or not
1633 1633 return @auto_page_break;
1634 1634 end
1635 1635 alias_method :accept_page_break, :AcceptPageBreak
1636 1636
1637 1637 def BreakThePage?(h)
1638 1638 if ((@y + h) > @page_break_trigger and !@in_footer and AcceptPageBreak())
1639 1639 true
1640 1640 else
1641 1641 false
1642 1642 end
1643 1643 end
1644 1644 alias_method :break_the_page?, :BreakThePage?
1645 1645 #
1646 1646 # Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
1647 1647 # If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
1648 1648 # @param float :w Cell width. If 0, the cell extends up to the right margin.
1649 1649 # @param float :h Cell height. Default value: 0.
1650 1650 # @param string :txt String to print. Default value: empty string.
1651 1651 # @param mixed :border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
1652 1652 # @param int :ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>
1653 1653 # Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
1654 1654 # @param string :align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li></ul>
1655 1655 # @param int :fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
1656 1656 # @param mixed :link URL or identifier returned by AddLink().
1657 1657 # @since 1.0
1658 1658 # @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
1659 1659 #
1660 1660 def Cell(w, h=0, txt='', border=0, ln=0, align='', fill=0, link=nil)
1661 1661 #Output a cell
1662 1662 k=@k;
1663 1663 if ((@y + h) > @page_break_trigger and !@in_footer and AcceptPageBreak())
1664 1664 #Automatic page break
1665 1665 x = @x;
1666 1666 ws = @ws;
1667 1667 if (ws > 0)
1668 1668 @ws = 0;
1669 1669 out('0 Tw');
1670 1670 end
1671 1671 AddPage(@cur_orientation);
1672 1672 @x = x;
1673 1673 if (ws > 0)
1674 1674 @ws = ws;
1675 1675 out(sprintf('%.3f Tw', ws * k));
1676 1676 end
1677 1677 end
1678 1678 if (w == 0)
1679 1679 w = @w - @r_margin - @x;
1680 1680 end
1681 1681 s = '';
1682 1682 if ((fill.to_i == 1) or (border.to_i == 1))
1683 1683 if (fill.to_i == 1)
1684 1684 op = (border.to_i == 1) ? 'B' : 'f';
1685 1685 else
1686 1686 op = 'S';
1687 1687 end
1688 1688 s = sprintf('%.2f %.2f %.2f %.2f re %s ', @x * k, (@h - @y) * k, w * k, -h * k, op);
1689 1689 end
1690 1690 if (border.is_a?(String))
1691 1691 x=@x;
1692 1692 y=@y;
1693 1693 if (border.include?('L'))
1694 1694 s<<sprintf('%.2f %.2f m %.2f %.2f l S ', x*k,(@h-y)*k, x*k,(@h-(y+h))*k);
1695 1695 end
1696 1696 if (border.include?('T'))
1697 1697 s<<sprintf('%.2f %.2f m %.2f %.2f l S ', x*k,(@h-y)*k,(x+w)*k,(@h-y)*k);
1698 1698 end
1699 1699 if (border.include?('R'))
1700 1700 s<<sprintf('%.2f %.2f m %.2f %.2f l S ',(x+w)*k,(@h-y)*k,(x+w)*k,(@h-(y+h))*k);
1701 1701 end
1702 1702 if (border.include?('B'))
1703 1703 s<<sprintf('%.2f %.2f m %.2f %.2f l S ', x*k,(@h-(y+h))*k,(x+w)*k,(@h-(y+h))*k);
1704 1704 end
1705 1705 end
1706 1706 if (txt != '')
1707 1707 width = GetStringWidth(txt);
1708 1708 if (align == 'R' || align == 'right')
1709 1709 dx = w - @c_margin - width;
1710 1710 elsif (align=='C' || align == 'center')
1711 1711 dx = (w - width)/2;
1712 1712 else
1713 1713 dx = @c_margin;
1714 1714 end
1715 1715 if (@color_flag)
1716 1716 s << 'q ' + @text_color + ' ';
1717 1717 end
1718 1718 txt2 = escapetext(txt);
1719 1719 s<<sprintf('BT %.2f %.2f Td (%s) Tj ET', (@x + dx) * k, (@h - (@y + 0.5 * h + 0.3 * @font_size)) * k, txt2);
1720 1720 if (@underline)
1721 1721 s<<' ' + dounderline(@x + dx, @y + 0.5 * h + 0.3 * @font_size, txt);
1722 1722 end
1723 1723 if (@color_flag)
1724 1724 s<<' Q';
1725 1725 end
1726 1726 if link && !link.empty?
1727 1727 Link(@x + dx, @y + 0.5 * h - 0.5 * @font_size, width, @font_size, link);
1728 1728 end
1729 1729 end
1730 1730 if (s)
1731 1731 out(s);
1732 1732 end
1733 1733 @lasth = h;
1734 1734 if (ln.to_i>0)
1735 1735 # Go to next line
1736 1736 @y += h;
1737 1737 if (ln == 1)
1738 1738 @x = @l_margin;
1739 1739 end
1740 1740 else
1741 1741 @x += w;
1742 1742 end
1743 1743 end
1744 1744 alias_method :cell, :Cell
1745 1745
1746 1746 #
1747 1747 # This method allows printing text with line breaks. They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.<br />
1748 1748 # Text can be aligned, centered or justified. The cell block can be framed and the background painted.
1749 1749 # @param float :w Width of cells. If 0, they extend up to the right margin of the page.
1750 1750 # @param float :h Height of cells.
1751 1751 # @param string :txt String to print
1752 1752 # @param mixed :border Indicates if borders must be drawn around the cell block. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
1753 1753 # @param string :align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value)</li></ul>
1754 1754 # @param int :fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
1755 1755 # @param int :ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul>
1756 1756 # @since 1.3
1757 1757 # @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
1758 1758 #
1759 1759 def MultiCell(w, h, txt, border=0, align='J', fill=0, ln=1)
1760 1760
1761 1761 # save current position
1762 1762 prevx = @x;
1763 1763 prevy = @y;
1764 1764
1765 1765 #Output text with automatic or explicit line breaks
1766 1766
1767 1767 if (w == 0)
1768 1768 w = @w - @r_margin - @x;
1769 1769 end
1770 1770
1771 1771 wmax = (w - 2 * @c_margin);
1772 1772
1773 1773 s = txt.gsub("\r", ''); # remove carriage returns
1774 1774 nb = s.length;
1775 1775
1776 1776 b=0;
1777 1777 if (border)
1778 1778 if (border==1)
1779 1779 border='LTRB';
1780 1780 b='LRT';
1781 1781 b2='LR';
1782 1782 elsif border.is_a?(String)
1783 1783 b2='';
1784 1784 if (border.include?('L'))
1785 1785 b2<<'L';
1786 1786 end
1787 1787 if (border.include?('R'))
1788 1788 b2<<'R';
1789 1789 end
1790 1790 b=(border.include?('T')) ? b2 + 'T' : b2;
1791 1791 end
1792 1792 end
1793 1793 sep=-1;
1794 1794 to_index=0;
1795 1795 from_j=0;
1796 1796 l=0;
1797 1797 ns=0;
1798 1798 nl=1;
1799 1799
1800 1800 while to_index < nb
1801 1801 #Get next character
1802 1802 c = s[to_index];
1803 1803 if c == "\n"[0]
1804 1804 #Explicit line break
1805 1805 if @ws > 0
1806 1806 @ws = 0
1807 1807 out('0 Tw')
1808 1808 end
1809 1809 #Ed Moss - change begin
1810 1810 end_i = to_index == 0 ? 0 : to_index - 1
1811 1811 # Changed from s[from_j..to_index] to fix bug reported by Hans Allis.
1812 1812 from_j = to_index == 0 ? 1 : from_j
1813 1813 Cell(w, h, s[from_j..end_i], b, 2, align, fill)
1814 1814 #change end
1815 1815 to_index += 1
1816 1816 sep=-1
1817 1817 from_j=to_index
1818 1818 l=0
1819 1819 ns=0
1820 1820 nl += 1
1821 1821 b = b2 if border and nl==2
1822 1822 next
1823 1823 end
1824 1824 if (c == " "[0])
1825 1825 sep = to_index;
1826 1826 ls = l;
1827 1827 ns += 1;
1828 1828 end
1829 1829
1830 1830 l = GetStringWidth(s[from_j, to_index-from_j]);
1831 1831
1832 1832 if (l > wmax)
1833 1833 #Automatic line break
1834 1834 if (sep == -1)
1835 1835 if (to_index == from_j)
1836 1836 to_index += 1;
1837 1837 end
1838 1838 if (@ws > 0)
1839 1839 @ws = 0;
1840 1840 out('0 Tw');
1841 1841 end
1842 1842 Cell(w, h, s[from_j..to_index-1], b, 2, align, fill) # my FPDF version
1843 1843 else
1844 1844 if (align=='J' || align=='justify' || align=='justified')
1845 1845 @ws = (ns>1) ? (wmax-ls)/(ns-1) : 0;
1846 1846 out(sprintf('%.3f Tw', @ws * @k));
1847 1847 end
1848 1848 Cell(w, h, s[from_j..sep], b, 2, align, fill);
1849 1849 to_index = sep + 1;
1850 1850 end
1851 1851 sep=-1;
1852 1852 from_j = to_index;
1853 1853 l=0;
1854 1854 ns=0;
1855 1855 nl += 1;
1856 1856 if (border and (nl==2))
1857 1857 b = b2;
1858 1858 end
1859 1859 else
1860 1860 to_index += 1;
1861 1861 end
1862 1862 end
1863 1863 #Last chunk
1864 1864 if (@ws>0)
1865 1865 @ws=0;
1866 1866 out('0 Tw');
1867 1867 end
1868 1868 if (border.is_a?(String) and border.include?('B'))
1869 1869 b<<'B';
1870 1870 end
1871 1871 Cell(w, h, s[from_j, to_index-from_j], b, 2, align, fill);
1872 1872
1873 1873 # move cursor to specified position
1874 1874 # since 2007-03-03
1875 1875 if (ln == 1)
1876 1876 # go to the beginning of the next line
1877 1877 @x = @l_margin;
1878 1878 elsif (ln == 0)
1879 1879 # go to the top-right of the cell
1880 1880 @y = prevy;
1881 1881 @x = prevx + w;
1882 1882 elsif (ln == 2)
1883 1883 # go to the bottom-left of the cell
1884 1884 @x = prevx;
1885 1885 end
1886 1886 end
1887 1887 alias_method :multi_cell, :MultiCell
1888 1888
1889 1889 #
1890 1890 # This method prints text from the current position. When the right margin is reached (or the \n character is met) a line break occurs and text continues from the left margin. Upon method exit, the current position is left just at the end of the text. It is possible to put a link on the text.<br />
1891 1891 # <b>Example:</b><br />
1892 1892 # <pre>
1893 1893 # #Begin with regular font
1894 1894 # :pdf->SetFont('Arial','',14);
1895 1895 # :pdf->Write(5,'Visit ');
1896 1896 # #Then put a blue underlined link
1897 1897 # :pdf->SetTextColor(0,0,255);
1898 1898 # :pdf->SetFont('','U');
1899 1899 # :pdf->Write(5,'www.tecnick.com','http://www.tecnick.com');
1900 1900 # </pre>
1901 1901 # @param float :h Line height
1902 1902 # @param string :txt String to print
1903 1903 # @param mixed :link URL or identifier returned by AddLink()
1904 1904 # @param int :fill Indicates if the background must be painted (1) or transparent (0). Default value: 0.
1905 1905 # @since 1.5
1906 1906 # @see SetFont(), SetTextColor(), AddLink(), MultiCell(), SetAutoPageBreak()
1907 1907 #
1908 1908 def Write(h, txt, link=nil, fill=0)
1909 1909
1910 1910 #Output text in flowing mode
1911 1911 w = @w - @r_margin - @x;
1912 1912 wmax = (w - 2 * @c_margin);
1913 1913
1914 1914 s = txt.gsub("\r", '');
1915 1915 nb = s.length;
1916 1916
1917 1917 # handle single space character
1918 1918 if ((nb==1) and (s == " "))
1919 1919 @x += GetStringWidth(s);
1920 1920 return;
1921 1921 end
1922 1922
1923 1923 sep=-1;
1924 1924 i=0;
1925 1925 j=0;
1926 1926 l=0;
1927 1927 nl=1;
1928 1928 while(i<nb)
1929 1929 #Get next character
1930 1930 c = s[i];
1931 1931 if (c == "\n"[0])
1932 1932 #Explicit line break
1933 1933 Cell(w, h, s[j,i-j], 0, 2, '', fill, link);
1934 1934 i += 1;
1935 1935 sep = -1;
1936 1936 j = i;
1937 1937 l = 0;
1938 1938 if (nl == 1)
1939 1939 @x = @l_margin;
1940 1940 w = @w - @r_margin - @x;
1941 1941 wmax = (w - 2 * @c_margin);
1942 1942 end
1943 1943 nl += 1;
1944 1944 next
1945 1945 end
1946 1946 if (c == " "[0])
1947 1947 sep= i;
1948 1948 end
1949 1949 l = GetStringWidth(s[j, i-j]);
1950 1950 if (l > wmax)
1951 1951 #Automatic line break (word wrapping)
1952 1952 if (sep == -1)
1953 1953 if (@x > @l_margin)
1954 1954 #Move to next line
1955 1955 @x = @l_margin;
1956 1956 @y += h;
1957 1957 w=@w - @r_margin - @x;
1958 1958 wmax=(w - 2 * @c_margin);
1959 1959 i += 1
1960 1960 nl += 1
1961 1961 next
1962 1962 end
1963 1963 if (i == j)
1964 1964 i += 1
1965 1965 end
1966 1966 Cell(w, h, s[j, (i-1)], 0, 2, '', fill, link);
1967 1967 else
1968 1968 Cell(w, h, s[j, (sep-j)], 0, 2, '', fill, link);
1969 1969 i = sep+1;
1970 1970 end
1971 1971 sep = -1;
1972 1972 j = i;
1973 1973 l = 0;
1974 1974 if (nl==1)
1975 1975 @x = @l_margin;
1976 1976 w = @w - @r_margin - @x;
1977 1977 wmax = (w - 2 * @c_margin);
1978 1978 end
1979 1979 nl += 1;
1980 1980 else
1981 1981 i += 1;
1982 1982 end
1983 1983 end
1984 1984 #Last chunk
1985 1985 if (i != j)
1986 1986 Cell(GetStringWidth(s[j..i]), h, s[j..i], 0, 0, '', fill, link);
1987 1987 end
1988 1988 end
1989 1989 alias_method :write, :Write
1990 1990
1991 1991 #
1992 1992 # Puts an image in the page. The upper-left corner must be given. The dimensions can be specified in different ways:<ul><li>explicit width and height (expressed in user unit)</li><li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li><li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
1993 1993 # Supported formats are JPEG and PNG.
1994 1994 # For JPEG, all flavors are allowed:<ul><li>gray scales</li><li>true colors (24 bits)</li><li>CMYK (32 bits)</li></ul>
1995 1995 # For PNG, are allowed:<ul><li>gray scales on at most 8 bits (256 levels)</li><li>indexed colors</li><li>true colors (24 bits)</li></ul>
1996 1996 # but are not supported:<ul><li>Interlacing</li><li>Alpha channel</li></ul>
1997 1997 # If a transparent color is defined, it will be taken into account (but will be only interpreted by Acrobat 4 and above).<br />
1998 1998 # The format can be specified explicitly or inferred from the file extension.<br />
1999 1999 # It is possible to put a link on the image.<br />
2000 2000 # Remark: if an image is used several times, only one copy will be embedded in the file.<br />
2001 2001 # @param string :file Name of the file containing the image.
2002 2002 # @param float :x Abscissa of the upper-left corner.
2003 2003 # @param float :y Ordinate of the upper-left corner.
2004 2004 # @param float :w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
2005 2005 # @param float :h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
2006 2006 # @param string :type Image format. Possible values are (case insensitive): JPG, JPEG, PNG. If not specified, the type is inferred from the file extension.
2007 2007 # @param mixed :link URL or identifier returned by AddLink().
2008 2008 # @since 1.1
2009 2009 # @see AddLink()
2010 2010 #
2011 2011 def Image(file, x, y, w=0, h=0, type='', link=nil)
2012 2012 #Put an image on the page
2013 2013 if (@images[file].nil?)
2014 2014 #First use of image, get info
2015 2015 if (type == '')
2016 2016 pos = file.rindex('.');
2017 2017 if (pos == 0)
2018 2018 Error('Image file has no extension and no type was specified: ' + file);
2019 2019 end
2020 2020 type = file[pos+1..-1];
2021 2021 end
2022 2022 type.downcase!
2023 2023 if (type == 'jpg' or type == 'jpeg')
2024 2024 info=parsejpg(file);
2025 2025 elsif (type == 'png')
2026 2026 info=parsepng(file);
2027 2027 else
2028 2028 #Allow for additional formats
2029 2029 mtd='parse' + type;
2030 2030 if (!self.respond_to?(mtd))
2031 2031 Error('Unsupported image type: ' + type);
2032 2032 end
2033 2033 info=send(mtd, file);
2034 2034 end
2035 2035 info['i']=@images.length+1;
2036 2036 @images[file] = info;
2037 2037 else
2038 2038 info=@images[file];
2039 2039 end
2040 2040 #Automatic width and height calculation if needed
2041 2041 if ((w == 0) and (h == 0))
2042 2042 #Put image at 72 dpi
2043 2043 # 2004-06-14 :: Nicola Asuni, scale factor where added
2044 2044 w = info['w'] / (@img_scale * @k);
2045 2045 h = info['h'] / (@img_scale * @k);
2046 2046 end
2047 2047 if (w == 0)
2048 2048 w = h * info['w'] / info['h'];
2049 2049 end
2050 2050 if (h == 0)
2051 2051 h = w * info['h'] / info['w'];
2052 2052 end
2053 2053 out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q', w*@k, h*@k, x*@k, (@h-(y+h))*@k, info['i']));
2054 2054 if (link)
2055 2055 Link(x, y, w, h, link);
2056 2056 end
2057 2057
2058 2058 #2002-07-31 - Nicola Asuni
2059 2059 # set right-bottom corner coordinates
2060 2060 @img_rb_x = x + w;
2061 2061 @img_rb_y = y + h;
2062 2062 end
2063 2063 alias_method :image, :Image
2064 2064
2065 2065 #
2066 2066 # Performs a line break. The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
2067 2067 # @param float :h The height of the break. By default, the value equals the height of the last printed cell.
2068 2068 # @since 1.0
2069 2069 # @see Cell()
2070 2070 #
2071 2071 def Ln(h='')
2072 2072 #Line feed; default value is last cell height
2073 2073 @x=@l_margin;
2074 2074 if (h.is_a?(String))
2075 2075 @y += @lasth;
2076 2076 else
2077 2077 @y += h;
2078 2078 end
2079 2079 end
2080 2080 alias_method :ln, :Ln
2081 2081
2082 2082 #
2083 2083 # Returns the abscissa of the current position.
2084 2084 # @return float
2085 2085 # @since 1.2
2086 2086 # @see SetX(), GetY(), SetY()
2087 2087 #
2088 2088 def GetX()
2089 2089 #Get x position
2090 2090 return @x;
2091 2091 end
2092 2092 alias_method :get_x, :GetX
2093 2093
2094 2094 #
2095 2095 # Defines the abscissa of the current position. If the passed value is negative, it is relative to the right of the page.
2096 2096 # @param float :x The value of the abscissa.
2097 2097 # @since 1.2
2098 2098 # @see GetX(), GetY(), SetY(), SetXY()
2099 2099 #
2100 2100 def SetX(x)
2101 2101 #Set x position
2102 2102 if (x>=0)
2103 2103 @x = x;
2104 2104 else
2105 2105 @x=@w+x;
2106 2106 end
2107 2107 end
2108 2108 alias_method :set_x, :SetX
2109 2109
2110 2110 #
2111 2111 # Returns the ordinate of the current position.
2112 2112 # @return float
2113 2113 # @since 1.0
2114 2114 # @see SetY(), GetX(), SetX()
2115 2115 #
2116 2116 def GetY()
2117 2117 #Get y position
2118 2118 return @y;
2119 2119 end
2120 2120 alias_method :get_y, :GetY
2121 2121
2122 2122 #
2123 2123 # Moves the current abscissa back to the left margin and sets the ordinate. If the passed value is negative, it is relative to the bottom of the page.
2124 2124 # @param float :y The value of the ordinate.
2125 2125 # @since 1.0
2126 2126 # @see GetX(), GetY(), SetY(), SetXY()
2127 2127 #
2128 2128 def SetY(y)
2129 2129 #Set y position and reset x
2130 2130 @x=@l_margin;
2131 2131 if (y>=0)
2132 2132 @y = y;
2133 2133 else
2134 2134 @y=@h+y;
2135 2135 end
2136 2136 end
2137 2137 alias_method :set_y, :SetY
2138 2138
2139 2139 #
2140 2140 # Defines the abscissa and ordinate of the current position. If the passed values are negative, they are relative respectively to the right and bottom of the page.
2141 2141 # @param float :x The value of the abscissa
2142 2142 # @param float :y The value of the ordinate
2143 2143 # @since 1.2
2144 2144 # @see SetX(), SetY()
2145 2145 #
2146 2146 def SetXY(x, y)
2147 2147 #Set x and y positions
2148 2148 SetY(y);
2149 2149 SetX(x);
2150 2150 end
2151 2151 alias_method :set_xy, :SetXY
2152 2152
2153 2153 #
2154 2154 # Send the document to a given destination: string, local file or browser. In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
2155 2155 # The method first calls Close() if necessary to terminate the document.
2156 2156 # @param string :name The name of the file. If not given, the document will be sent to the browser (destination I) with the name doc.pdf.
2157 2157 # @param string :dest Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser. The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local file with the name given by name.</li><li>S: return the document as a string. name is ignored.</li></ul>If the parameter is not specified but a name is given, destination is F. If no parameter is specified at all, destination is I.<br />
2158 2158 # @since 1.0
2159 2159 # @see Close()
2160 2160 #
2161 2161 def Output(name='', dest='')
2162 2162 #Output PDF to some destination
2163 2163 #Finish document if necessary
2164 2164 if (@state < 3)
2165 2165 Close();
2166 2166 end
2167 2167 #Normalize parameters
2168 2168 # Boolean no longer supported
2169 2169 # if (dest.is_a?(Boolean))
2170 2170 # dest = dest ? 'D' : 'F';
2171 2171 # end
2172 2172 dest = dest.upcase
2173 2173 if (dest=='')
2174 2174 if (name=='')
2175 2175 name='doc.pdf';
2176 2176 dest='I';
2177 2177 else
2178 2178 dest='F';
2179 2179 end
2180 2180 end
2181 2181 case (dest)
2182 2182 when 'I'
2183 2183 # This is PHP specific code
2184 2184 ##Send to standard output
2185 2185 # if (ob_get_contents())
2186 2186 # Error('Some data has already been output, can\'t send PDF file');
2187 2187 # end
2188 2188 # if (php_sapi_name()!='cli')
2189 2189 # #We send to a browser
2190 2190 # header('Content-Type: application/pdf');
2191 2191 # if (headers_sent())
2192 2192 # Error('Some data has already been output to browser, can\'t send PDF file');
2193 2193 # end
2194 2194 # header('Content-Length: ' + @buffer.length);
2195 2195 # header('Content-disposition: inline; filename="' + name + '"');
2196 2196 # end
2197 2197 return @buffer;
2198 2198
2199 2199 when 'D'
2200 2200 # PHP specific
2201 2201 #Download file
2202 2202 # if (ob_get_contents())
2203 2203 # Error('Some data has already been output, can\'t send PDF file');
2204 2204 # end
2205 2205 # if (!_SERVER['HTTP_USER_AGENT'].nil? && SERVER['HTTP_USER_AGENT'].include?('MSIE'))
2206 2206 # header('Content-Type: application/force-download');
2207 2207 # else
2208 2208 # header('Content-Type: application/octet-stream');
2209 2209 # end
2210 2210 # if (headers_sent())
2211 2211 # Error('Some data has already been output to browser, can\'t send PDF file');
2212 2212 # end
2213 2213 # header('Content-Length: '+ @buffer.length);
2214 2214 # header('Content-disposition: attachment; filename="' + name + '"');
2215 2215 return @buffer;
2216 2216
2217 2217 when 'F'
2218 2218 open(name,'wb') do |f|
2219 2219 f.write(@buffer)
2220 2220 end
2221 2221 # PHP code
2222 2222 # #Save to local file
2223 2223 # f=open(name,'wb');
2224 2224 # if (!f)
2225 2225 # Error('Unable to create output file: ' + name);
2226 2226 # end
2227 2227 # fwrite(f,@buffer,@buffer.length);
2228 2228 # f.close
2229 2229
2230 2230 when 'S'
2231 2231 #Return as a string
2232 2232 return @buffer;
2233 2233 else
2234 2234 Error('Incorrect output destination: ' + dest);
2235 2235
2236 2236 end
2237 2237 return '';
2238 2238 end
2239 2239 alias_method :output, :Output
2240 2240
2241 2241 # Protected methods
2242 2242
2243 2243 #
2244 2244 # Check for locale-related bug
2245 2245 # @access protected
2246 2246 #
2247 2247 def dochecks()
2248 2248 #Check for locale-related bug
2249 2249 if (1.1==1)
2250 2250 Error('Don\'t alter the locale before including class file');
2251 2251 end
2252 2252 #Check for decimal separator
2253 2253 if (sprintf('%.1f',1.0)!='1.0')
2254 2254 setlocale(LC_NUMERIC,'C');
2255 2255 end
2256 2256 end
2257 2257
2258 2258 #
2259 2259 # Return fonts path
2260 2260 # @access protected
2261 2261 #
2262 2262 def getfontpath(file)
2263 2263 # Is it in the @@font_path?
2264 2264 if @@font_path
2265 2265 fpath = File.join @@font_path, file
2266 2266 if File.exists?(fpath)
2267 2267 return fpath
2268 2268 end
2269 2269 end
2270 2270 # Is it in this plugin's font folder?
2271 2271 fpath = File.join File.dirname(__FILE__), 'fonts', file
2272 2272 if File.exists?(fpath)
2273 2273 return fpath
2274 2274 end
2275 2275 # Could not find it.
2276 2276 nil
2277 2277 end
2278 2278
2279 2279 #
2280 2280 # Start document
2281 2281 # @access protected
2282 2282 #
2283 2283 def begindoc()
2284 2284 #Start document
2285 2285 @state=1;
2286 2286 out('%PDF-1.3');
2287 2287 end
2288 2288
2289 2289 #
2290 2290 # putpages
2291 2291 # @access protected
2292 2292 #
2293 2293 def putpages()
2294 2294 nb = @page;
2295 2295 if (@alias_nb_pages)
2296 2296 nbstr = UTF8ToUTF16BE(nb, false);
2297 2297 #Replace number of pages
2298 2298 1.upto(nb) do |n|
2299 2299 @pages[n].gsub!(@alias_nb_pages, nbstr)
2300 2300 end
2301 2301 end
2302 2302 if @def_orientation=='P'
2303 2303 w_pt=@fw_pt
2304 2304 h_pt=@fh_pt
2305 2305 else
2306 2306 w_pt=@fh_pt
2307 2307 h_pt=@fw_pt
2308 2308 end
2309 2309 filter=(@compress) ? '/Filter /FlateDecode ' : ''
2310 2310 1.upto(nb) do |n|
2311 2311 #Page
2312 2312 newobj
2313 2313 out('<</Type /Page')
2314 2314 out('/Parent 1 0 R')
2315 2315 unless @orientation_changes[n].nil?
2316 2316 out(sprintf('/MediaBox [0 0 %.2f %.2f]', h_pt, w_pt))
2317 2317 end
2318 2318 out('/Resources 2 0 R')
2319 2319 if @page_links[n]
2320 2320 #Links
2321 2321 annots='/Annots ['
2322 2322 @page_links[n].each do |pl|
2323 2323 rect=sprintf('%.2f %.2f %.2f %.2f', pl[0], pl[1], pl[0]+pl[2], pl[1]-pl[3]);
2324 2324 annots<<'<</Type /Annot /Subtype /Link /Rect [' + rect + '] /Border [0 0 0] ';
2325 2325 if (pl[4].is_a?(String))
2326 2326 annots<<'/A <</S /URI /URI (' + escape(pl[4]) + ')>>>>';
2327 2327 else
2328 2328 l=@links[pl[4]];
2329 2329 h=!@orientation_changes[l[0]].nil? ? w_pt : h_pt;
2330 2330 annots<<sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]>>',1+2*l[0], h-l[1]*@k);
2331 2331 end
2332 2332 end
2333 2333 out(annots + ']');
2334 2334 end
2335 2335 out('/Contents ' + (@n+1).to_s + ' 0 R>>');
2336 2336 out('endobj');
2337 2337 #Page content
2338 2338 p=(@compress) ? gzcompress(@pages[n]) : @pages[n];
2339 2339 newobj();
2340 2340 out('<<' + filter + '/Length '+ p.length.to_s + '>>');
2341 2341 putstream(p);
2342 2342 out('endobj');
2343 2343 end
2344 2344 #Pages root
2345 2345 @offsets[1]=@buffer.length;
2346 2346 out('1 0 obj');
2347 2347 out('<</Type /Pages');
2348 2348 kids='/Kids [';
2349 2349 0.upto(nb) do |i|
2350 2350 kids<<(3+2*i).to_s + ' 0 R ';
2351 2351 end
2352 2352 out(kids + ']');
2353 2353 out('/Count ' + nb.to_s);
2354 2354 out(sprintf('/MediaBox [0 0 %.2f %.2f]', w_pt, h_pt));
2355 2355 out('>>');
2356 2356 out('endobj');
2357 2357 end
2358 2358
2359 2359 #
2360 2360 # Adds fonts
2361 2361 # putfonts
2362 2362 # @access protected
2363 2363 #
2364 2364 def putfonts()
2365 2365 nf=@n;
2366 2366 @diffs.each do |diff|
2367 2367 #Encodings
2368 2368 newobj();
2369 2369 out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [' + diff + ']>>');
2370 2370 out('endobj');
2371 2371 end
2372 2372 @font_files.each do |file, info|
2373 2373 #Font file embedding
2374 2374 newobj();
2375 2375 @font_files[file]['n']=@n;
2376 2376 font='';
2377 2377 open(getfontpath(file),'rb') do |f|
2378 2378 font = f.read();
2379 2379 end
2380 2380 compressed=(file[-2,2]=='.z');
2381 2381 if (!compressed && !info['length2'].nil?)
2382 2382 header=((font[0][0])==128);
2383 2383 if (header)
2384 2384 #Strip first binary header
2385 2385 font=font[6];
2386 2386 end
2387 2387 if header && (font[info['length1']][0] == 128)
2388 2388 #Strip second binary header
2389 2389 font=font[0..info['length1']] + font[info['length1']+6];
2390 2390 end
2391 2391 end
2392 2392 out('<</Length '+ font.length.to_s);
2393 2393 if (compressed)
2394 2394 out('/Filter /FlateDecode');
2395 2395 end
2396 2396 out('/Length1 ' + info['length1'].to_s);
2397 2397 if (!info['length2'].nil?)
2398 2398 out('/Length2 ' + info['length2'].to_s + ' /Length3 0');
2399 2399 end
2400 2400 out('>>');
2401 2401 open(getfontpath(file),'rb') do |f|
2402 2402 putstream(font)
2403 2403 end
2404 2404 out('endobj');
2405 2405 end
2406 2406 @fonts.each do |k, font|
2407 2407 #Font objects
2408 2408 @fonts[k]['n']=@n+1;
2409 2409 type = font['type'];
2410 2410 name = font['name'];
2411 2411 if (type=='core')
2412 2412 #Standard font
2413 2413 newobj();
2414 2414 out('<</Type /Font');
2415 2415 out('/BaseFont /' + name);
2416 2416 out('/Subtype /Type1');
2417 2417 if (name!='Symbol' && name!='ZapfDingbats')
2418 2418 out('/Encoding /WinAnsiEncoding');
2419 2419 end
2420 2420 out('>>');
2421 2421 out('endobj');
2422 2422 elsif type == 'Type0'
2423 2423 putType0(font)
2424 2424 elsif (type=='Type1' || type=='TrueType')
2425 2425 #Additional Type1 or TrueType font
2426 2426 newobj();
2427 2427 out('<</Type /Font');
2428 2428 out('/BaseFont /' + name);
2429 2429 out('/Subtype /' + type);
2430 2430 out('/FirstChar 32 /LastChar 255');
2431 2431 out('/Widths ' + (@n+1).to_s + ' 0 R');
2432 2432 out('/FontDescriptor ' + (@n+2).to_s + ' 0 R');
2433 2433 if (font['enc'])
2434 2434 if (!font['diff'].nil?)
2435 2435 out('/Encoding ' + (nf+font['diff']).to_s + ' 0 R');
2436 2436 else
2437 2437 out('/Encoding /WinAnsiEncoding');
2438 2438 end
2439 2439 end
2440 2440 out('>>');
2441 2441 out('endobj');
2442 2442 #Widths
2443 2443 newobj();
2444 2444 cw=font['cw']; # &
2445 2445 s='[';
2446 2446 32.upto(255) do |i|
2447 2447 s << cw[i.chr] + ' ';
2448 2448 end
2449 2449 out(s + ']');
2450 2450 out('endobj');
2451 2451 #Descriptor
2452 2452 newobj();
2453 2453 s='<</Type /FontDescriptor /FontName /' + name;
2454 2454 font['desc'].each do |k, v|
2455 2455 s<<' /' + k + ' ' + v;
2456 2456 end
2457 2457 file = font['file'];
2458 2458 if (file)
2459 2459 s<<' /FontFile' + (type=='Type1' ? '' : '2') + ' ' + @font_files[file]['n'] + ' 0 R';
2460 2460 end
2461 2461 out(s + '>>');
2462 2462 out('endobj');
2463 2463 else
2464 2464 #Allow for additional types
2465 2465 mtd='put' + type.downcase;
2466 2466 if (!self.respond_to?(mtd))
2467 2467 Error('Unsupported font type: ' + type)
2468 2468 else
2469 2469 self.send(mtd,font)
2470 2470 end
2471 2471 end
2472 2472 end
2473 2473 end
2474 2474
2475 2475 def putType0(font)
2476 2476 #Type0
2477 2477 out('<</Type /Font')
2478 2478 out('/Subtype /Type0')
2479 2479 out('/BaseFont /'+font['name']+'-'+font['cMap'])
2480 2480 out('/Encoding /'+font['cMap'])
2481 2481 out('/DescendantFonts ['+(@n+1).to_s+' 0 R]')
2482 2482 out('>>')
2483 2483 out('endobj')
2484 2484 #CIDFont
2485 2485 newobj()
2486 2486 out('<</Type /Font')
2487 2487 out('/Subtype /CIDFontType0')
2488 2488 out('/BaseFont /'+font['name'])
2489 2489 out('/CIDSystemInfo <</Registry (Adobe) /Ordering ('+font['registry']['ordering']+') /Supplement '+font['registry']['supplement'].to_s+'>>')
2490 2490 out('/FontDescriptor '+(@n+1).to_s+' 0 R')
2491 2491 w='/W [1 ['
2492 2492 font['cw'].keys.sort.each {|key|
2493 2493 w+=font['cw'][key].to_s + " "
2494 2494 # ActionController::Base::logger.debug key.to_s
2495 2495 # ActionController::Base::logger.debug font['cw'][key].to_s
2496 2496 }
2497 2497 out(w+'] 231 325 500 631 [500] 326 389 500]')
2498 2498 out('>>')
2499 2499 out('endobj')
2500 2500 #Font descriptor
2501 2501 newobj()
2502 2502 out('<</Type /FontDescriptor')
2503 2503 out('/FontName /'+font['name'])
2504 2504 out('/Flags 6')
2505 2505 out('/FontBBox [0 -200 1000 900]')
2506 2506 out('/ItalicAngle 0')
2507 2507 out('/Ascent 800')
2508 2508 out('/Descent -200')
2509 2509 out('/CapHeight 800')
2510 2510 out('/StemV 60')
2511 2511 out('>>')
2512 2512 out('endobj')
2513 2513 end
2514 2514
2515 2515 #
2516 2516 # putimages
2517 2517 # @access protected
2518 2518 #
2519 2519 def putimages()
2520 2520 filter=(@compress) ? '/Filter /FlateDecode ' : '';
2521 2521 @images.each do |file, info| # was while(list(file, info)=each(@images))
2522 2522 newobj();
2523 2523 @images[file]['n']=@n;
2524 2524 out('<</Type /XObject');
2525 2525 out('/Subtype /Image');
2526 2526 out('/Width ' + info['w'].to_s);
2527 2527 out('/Height ' + info['h'].to_s);
2528 2528 if (info['cs']=='Indexed')
2529 2529 out('/ColorSpace [/Indexed /DeviceRGB ' + (info['pal'].length/3-1) + ' ' + (@n+1) + ' 0 R]');
2530 2530 else
2531 2531 out('/ColorSpace /' + info['cs']);
2532 2532 if (info['cs']=='DeviceCMYK')
2533 2533 out('/Decode [1 0 1 0 1 0 1 0]');
2534 2534 end
2535 2535 end
2536 2536 out('/BitsPerComponent ' + info['bpc'].to_s);
2537 2537 if (!info['f'].nil?)
2538 2538 out('/Filter /' + info['f']);
2539 2539 end
2540 2540 if (!info['parms'].nil?)
2541 2541 out(info['parms']);
2542 2542 end
2543 2543 if (!info['trns'].nil? and info['trns'].kind_of?(Array))
2544 2544 trns='';
2545 2545 0.upto(info['trns'].length) do |i|
2546 2546 trns << info['trns'][i] + ' ' + info['trns'][i] + ' ';
2547 2547 end
2548 2548 out('/Mask [' + trns + ']');
2549 2549 end
2550 2550 out('/Length ' + info['data'].length.to_s + '>>');
2551 2551 putstream(info['data']);
2552 2552 @images[file]['data']=nil
2553 2553 out('endobj');
2554 2554 #Palette
2555 2555 if (info['cs']=='Indexed')
2556 2556 newobj();
2557 2557 pal=(@compress) ? gzcompress(info['pal']) : :info['pal'];
2558 2558 out('<<' + filter + '/Length ' + pal.length.to_s + '>>');
2559 2559 putstream(pal);
2560 2560 out('endobj');
2561 2561 end
2562 2562 end
2563 2563 end
2564 2564
2565 2565 #
2566 2566 # putxobjectdict
2567 2567 # @access protected
2568 2568 #
2569 2569 def putxobjectdict()
2570 2570 @images.each_value do |image|
2571 2571 out('/I' + image['i'].to_s + ' ' + image['n'].to_s + ' 0 R');
2572 2572 end
2573 2573 end
2574 2574
2575 2575 #
2576 2576 # putresourcedict
2577 2577 # @access protected
2578 2578 #
2579 2579 def putresourcedict()
2580 2580 out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
2581 2581 out('/Font <<');
2582 2582 @fonts.each_value do |font|
2583 2583 out('/F' + font['i'].to_s + ' ' + font['n'].to_s + ' 0 R');
2584 2584 end
2585 2585 out('>>');
2586 2586 out('/XObject <<');
2587 2587 putxobjectdict();
2588 2588 out('>>');
2589 2589 end
2590 2590
2591 2591 #
2592 2592 # putresources
2593 2593 # @access protected
2594 2594 #
2595 2595 def putresources()
2596 2596 putfonts();
2597 2597 putimages();
2598 2598 #Resource dictionary
2599 2599 @offsets[2]=@buffer.length;
2600 2600 out('2 0 obj');
2601 2601 out('<<');
2602 2602 putresourcedict();
2603 2603 out('>>');
2604 2604 out('endobj');
2605 2605 end
2606 2606
2607 2607 #
2608 2608 # putinfo
2609 2609 # @access protected
2610 2610 #
2611 2611 def putinfo()
2612 2612 out('/Producer ' + textstring(PDF_PRODUCER));
2613 2613 if (!@title.nil?)
2614 2614 out('/Title ' + textstring(@title));
2615 2615 end
2616 2616 if (!@subject.nil?)
2617 2617 out('/Subject ' + textstring(@subject));
2618 2618 end
2619 2619 if (!@author.nil?)
2620 2620 out('/Author ' + textstring(@author));
2621 2621 end
2622 2622 if (!@keywords.nil?)
2623 2623 out('/Keywords ' + textstring(@keywords));
2624 2624 end
2625 2625 if (!@creator.nil?)
2626 2626 out('/Creator ' + textstring(@creator));
2627 2627 end
2628 2628 out('/CreationDate ' + textstring('D:' + Time.now.strftime('%Y%m%d%H%M%S')));
2629 2629 end
2630 2630
2631 2631 #
2632 2632 # putcatalog
2633 2633 # @access protected
2634 2634 #
2635 2635 def putcatalog()
2636 2636 out('/Type /Catalog');
2637 2637 out('/Pages 1 0 R');
2638 2638 if (@zoom_mode=='fullpage')
2639 2639 out('/OpenAction [3 0 R /Fit]');
2640 2640 elsif (@zoom_mode=='fullwidth')
2641 2641 out('/OpenAction [3 0 R /FitH null]');
2642 2642 elsif (@zoom_mode=='real')
2643 2643 out('/OpenAction [3 0 R /XYZ null null 1]');
2644 2644 elsif (!@zoom_mode.is_a?(String))
2645 2645 out('/OpenAction [3 0 R /XYZ null null ' + (@zoom_mode/100) + ']');
2646 2646 end
2647 2647 if (@layout_mode=='single')
2648 2648 out('/PageLayout /SinglePage');
2649 2649 elsif (@layout_mode=='continuous')
2650 2650 out('/PageLayout /OneColumn');
2651 2651 elsif (@layout_mode=='two')
2652 2652 out('/PageLayout /TwoColumnLeft');
2653 2653 end
2654 2654 end
2655 2655
2656 2656 #
2657 2657 # puttrailer
2658 2658 # @access protected
2659 2659 #
2660 2660 def puttrailer()
2661 2661 out('/Size ' + (@n+1).to_s);
2662 2662 out('/Root ' + @n.to_s + ' 0 R');
2663 2663 out('/Info ' + (@n-1).to_s + ' 0 R');
2664 2664 end
2665 2665
2666 2666 #
2667 2667 # putheader
2668 2668 # @access protected
2669 2669 #
2670 2670 def putheader()
2671 2671 out('%PDF-' + @pdf_version);
2672 2672 end
2673 2673
2674 2674 #
2675 2675 # enddoc
2676 2676 # @access protected
2677 2677 #
2678 2678 def enddoc()
2679 2679 putheader();
2680 2680 putpages();
2681 2681 putresources();
2682 2682 #Info
2683 2683 newobj();
2684 2684 out('<<');
2685 2685 putinfo();
2686 2686 out('>>');
2687 2687 out('endobj');
2688 2688 #Catalog
2689 2689 newobj();
2690 2690 out('<<');
2691 2691 putcatalog();
2692 2692 out('>>');
2693 2693 out('endobj');
2694 2694 #Cross-ref
2695 2695 o=@buffer.length;
2696 2696 out('xref');
2697 2697 out('0 ' + (@n+1).to_s);
2698 2698 out('0000000000 65535 f ');
2699 2699 1.upto(@n) do |i|
2700 2700 out(sprintf('%010d 00000 n ',@offsets[i]));
2701 2701 end
2702 2702 #Trailer
2703 2703 out('trailer');
2704 2704 out('<<');
2705 2705 puttrailer();
2706 2706 out('>>');
2707 2707 out('startxref');
2708 2708 out(o);
2709 2709 out('%%EOF');
2710 2710 @state=3;
2711 2711 end
2712 2712
2713 2713 #
2714 2714 # beginpage
2715 2715 # @access protected
2716 2716 #
2717 2717 def beginpage(orientation)
2718 2718 @page += 1;
2719 2719 @pages[@page]='';
2720 2720 @state=2;
2721 2721 @x=@l_margin;
2722 2722 @y=@t_margin;
2723 2723 @font_family='';
2724 2724 #Page orientation
2725 2725 if (orientation.empty?)
2726 2726 orientation=@def_orientation;
2727 2727 else
2728 2728 orientation.upcase!
2729 2729 if (orientation!=@def_orientation)
2730 2730 @orientation_changes[@page]=true;
2731 2731 end
2732 2732 end
2733 2733 if (orientation!=@cur_orientation)
2734 2734 #Change orientation
2735 2735 if (orientation=='P')
2736 2736 @w_pt=@fw_pt;
2737 2737 @h_pt=@fh_pt;
2738 2738 @w=@fw;
2739 2739 @h=@fh;
2740 2740 else
2741 2741 @w_pt=@fh_pt;
2742 2742 @h_pt=@fw_pt;
2743 2743 @w=@fh;
2744 2744 @h=@fw;
2745 2745 end
2746 2746 @page_break_trigger=@h-@b_margin;
2747 2747 @cur_orientation = orientation;
2748 2748 end
2749 2749 end
2750 2750
2751 2751 #
2752 2752 # End of page contents
2753 2753 # @access protected
2754 2754 #
2755 2755 def endpage()
2756 2756 @state=1;
2757 2757 end
2758 2758
2759 2759 #
2760 2760 # Begin a new object
2761 2761 # @access protected
2762 2762 #
2763 2763 def newobj()
2764 2764 @n += 1;
2765 2765 @offsets[@n]=@buffer.length;
2766 2766 out(@n.to_s + ' 0 obj');
2767 2767 end
2768 2768
2769 2769 #
2770 2770 # Underline text
2771 2771 # @access protected
2772 2772 #
2773 2773 def dounderline(x, y, txt)
2774 2774 up = @current_font['up'];
2775 2775 ut = @current_font['ut'];
2776 2776 w = GetStringWidth(txt) + @ws * txt.count(' ');
2777 2777 sprintf('%.2f %.2f %.2f %.2f re f', x * @k, (@h - (y - up / 1000.0 * @font_size)) * @k, w * @k, -ut / 1000.0 * @font_size_pt);
2778 2778 end
2779 2779
2780 2780 #
2781 2781 # Extract info from a JPEG file
2782 2782 # @access protected
2783 2783 #
2784 2784 def parsejpg(file)
2785 2785 a=getimagesize(file);
2786 2786 if (a.empty?)
2787 2787 Error('Missing or incorrect image file: ' + file);
2788 2788 end
2789 2789 if (a[2]!='JPEG')
2790 2790 Error('Not a JPEG file: ' + file);
2791 2791 end
2792 2792 if (a['channels'].nil? or a['channels']==3)
2793 2793 colspace='DeviceRGB';
2794 2794 elsif (a['channels']==4)
2795 2795 colspace='DeviceCMYK';
2796 2796 else
2797 2797 colspace='DeviceGray';
2798 2798 end
2799 2799 bpc=!a['bits'].nil? ? a['bits'] : 8;
2800 2800 #Read whole file
2801 2801 data='';
2802 2802 open(file,'rb') do |f|
2803 2803 data<<f.read();
2804 2804 end
2805 2805 return {'w' => a[0],'h' => a[1],'cs' => colspace,'bpc' => bpc,'f'=>'DCTDecode','data' => data}
2806 2806 end
2807 2807
2808 2808 #
2809 2809 # Extract info from a PNG file
2810 2810 # @access protected
2811 2811 #
2812 2812 def parsepng(file)
2813 2813 f=open(file,'rb');
2814 2814 #Check signature
2815 2815 if (f.read(8)!=137.chr + 'PNG' + 13.chr + 10.chr + 26.chr + 10.chr)
2816 2816 Error('Not a PNG file: ' + file);
2817 2817 end
2818 2818 #Read header chunk
2819 2819 f.read(4);
2820 2820 if (f.read(4)!='IHDR')
2821 2821 Error('Incorrect PNG file: ' + file);
2822 2822 end
2823 2823 w=freadint(f);
2824 2824 h=freadint(f);
2825 2825 bpc=f.read(1)[0];
2826 2826 if (bpc>8)
2827 2827 Error('16-bit depth not supported: ' + file);
2828 2828 end
2829 2829 ct=f.read(1)[0];
2830 2830 if (ct==0)
2831 2831 colspace='DeviceGray';
2832 2832 elsif (ct==2)
2833 2833 colspace='DeviceRGB';
2834 2834 elsif (ct==3)
2835 2835 colspace='Indexed';
2836 2836 else
2837 2837 Error('Alpha channel not supported: ' + file);
2838 2838 end
2839 2839 if (f.read(1)[0] != 0)
2840 2840 Error('Unknown compression method: ' + file);
2841 2841 end
2842 2842 if (f.read(1)[0]!=0)
2843 2843 Error('Unknown filter method: ' + file);
2844 2844 end
2845 2845 if (f.read(1)[0]!=0)
2846 2846 Error('Interlacing not supported: ' + file);
2847 2847 end
2848 2848 f.read(4);
2849 2849 parms='/DecodeParms <</Predictor 15 /Colors ' + (ct==2 ? 3 : 1).to_s + ' /BitsPerComponent ' + bpc.to_s + ' /Columns ' + w.to_s + '>>';
2850 2850 #Scan chunks looking for palette, transparency and image data
2851 2851 pal='';
2852 2852 trns='';
2853 2853 data='';
2854 2854 begin
2855 2855 n=freadint(f);
2856 2856 type=f.read(4);
2857 2857 if (type=='PLTE')
2858 2858 #Read palette
2859 2859 pal=f.read( n);
2860 2860 f.read(4);
2861 2861 elsif (type=='tRNS')
2862 2862 #Read transparency info
2863 2863 t=f.read( n);
2864 2864 if (ct==0)
2865 2865 trns = t[1][0]
2866 2866 elsif (ct==2)
2867 2867 trns = t[[1][0], t[3][0], t[5][0]]
2868 2868 else
2869 2869 pos=t.include?(0.chr);
2870 2870 if (pos!=false)
2871 2871 trns = [pos]
2872 2872 end
2873 2873 end
2874 2874 f.read(4);
2875 2875 elsif (type=='IDAT')
2876 2876 #Read image data block
2877 2877 data<<f.read( n);
2878 2878 f.read(4);
2879 2879 elsif (type=='IEND')
2880 2880 break;
2881 2881 else
2882 2882 f.read( n+4);
2883 2883 end
2884 2884 end while(n)
2885 2885 if (colspace=='Indexed' and pal.empty?)
2886 2886 Error('Missing palette in ' + file);
2887 2887 end
2888 2888 f.close
2889 2889 return {'w' => w, 'h' => h, 'cs' => colspace, 'bpc' => bpc, 'f'=>'FlateDecode', 'parms' => parms, 'pal' => pal, 'trns' => trns, 'data' => data}
2890 2890 end
2891 2891
2892 2892 #
2893 2893 # Read a 4-byte integer from file
2894 2894 # @access protected
2895 2895 #
2896 2896 def freadint(f)
2897 2897 # Read a 4-byte integer from file
2898 2898 a = f.read(4).unpack('N')
2899 2899 return a[0]
2900 2900 end
2901 2901
2902 2902 #
2903 2903 # Format a text string
2904 2904 # @access protected
2905 2905 #
2906 2906 def textstring(s)
2907 2907 if (@is_unicode)
2908 2908 #Convert string to UTF-16BE
2909 2909 s = UTF8ToUTF16BE(s, true);
2910 2910 end
2911 2911 return '(' + escape(s) + ')';
2912 2912 end
2913 2913
2914 2914 #
2915 2915 # Format a text string
2916 2916 # @access protected
2917 2917 #
2918 2918 def escapetext(s)
2919 2919 if (@is_unicode)
2920 2920 #Convert string to UTF-16BE
2921 2921 s = UTF8ToUTF16BE(s, false);
2922 2922 end
2923 2923 return escape(s);
2924 2924 end
2925 2925
2926 2926 #
2927 2927 # Add \ before \, ( and )
2928 2928 # @access protected
2929 2929 #
2930 2930 def escape(s)
2931 2931 # Add \ before \, ( and )
2932 2932 s.gsub('\\','\\\\\\').gsub('(','\\(').gsub(')','\\)').gsub(13.chr, '\r')
2933 2933 end
2934 2934
2935 2935 #
2936 2936 #
2937 2937 # @access protected
2938 2938 #
2939 2939 def putstream(s)
2940 2940 out('stream');
2941 2941 out(s);
2942 2942 out('endstream');
2943 2943 end
2944 2944
2945 2945 #
2946 2946 # Add a line to the document
2947 2947 # @access protected
2948 2948 #
2949 2949 def out(s)
2950 2950 if (@state==2)
2951 2951 @pages[@page] << s.to_s + "\n";
2952 2952 else
2953 2953 @buffer << s.to_s + "\n";
2954 2954 end
2955 2955 end
2956 2956
2957 2957 #
2958 2958 # Adds unicode fonts.<br>
2959 2959 # Based on PDF Reference 1.3 (section 5)
2960 2960 # @access protected
2961 2961 # @author Nicola Asuni
2962 2962 # @since 1.52.0.TC005 (2005-01-05)
2963 2963 #
2964 2964 def puttruetypeunicode(font)
2965 2965 # Type0 Font
2966 2966 # A composite font composed of other fonts, organized hierarchically
2967 2967 newobj();
2968 2968 out('<</Type /Font');
2969 2969 out('/Subtype /Type0');
2970 2970 out('/BaseFont /' + font['name'] + '');
2971 2971 out('/Encoding /Identity-H'); #The horizontal identity mapping for 2-byte CIDs; may be used with CIDFonts using any Registry, Ordering, and Supplement values.
2972 2972 out('/DescendantFonts [' + (@n + 1).to_s + ' 0 R]');
2973 2973 out('/ToUnicode ' + (@n + 2).to_s + ' 0 R');
2974 2974 out('>>');
2975 2975 out('endobj');
2976 2976
2977 2977 # CIDFontType2
2978 2978 # A CIDFont whose glyph descriptions are based on TrueType font technology
2979 2979 newobj();
2980 2980 out('<</Type /Font');
2981 2981 out('/Subtype /CIDFontType2');
2982 2982 out('/BaseFont /' + font['name'] + '');
2983 2983 out('/CIDSystemInfo ' + (@n + 2).to_s + ' 0 R');
2984 2984 out('/FontDescriptor ' + (@n + 3).to_s + ' 0 R');
2985 2985 if (!font['desc']['MissingWidth'].nil?)
2986 2986 out('/DW ' + font['desc']['MissingWidth'].to_s + ''); # The default width for glyphs in the CIDFont MissingWidth
2987 2987 end
2988 2988 w = "";
2989 2989 font['cw'].each do |cid, width|
2990 2990 w << '' + cid.to_s + ' [' + width.to_s + '] '; # define a specific width for each individual CID
2991 2991 end
2992 2992 out('/W [' + w + ']'); # A description of the widths for the glyphs in the CIDFont
2993 2993 out('/CIDToGIDMap ' + (@n + 4).to_s + ' 0 R');
2994 2994 out('>>');
2995 2995 out('endobj');
2996 2996
2997 2997 # ToUnicode
2998 2998 # is a stream object that contains the definition of the CMap
2999 2999 # (PDF Reference 1.3 chap. 5.9)
3000 3000 newobj();
3001 3001 out('<</Length 383>>');
3002 3002 out('stream');
3003 3003 out('/CIDInit /ProcSet findresource begin');
3004 3004 out('12 dict begin');
3005 3005 out('begincmap');
3006 3006 out('/CIDSystemInfo');
3007 3007 out('<</Registry (Adobe)');
3008 3008 out('/Ordering (UCS)');
3009 3009 out('/Supplement 0');
3010 3010 out('>> def');
3011 3011 out('/CMapName /Adobe-Identity-UCS def');
3012 3012 out('/CMapType 2 def');
3013 3013 out('1 begincodespacerange');
3014 3014 out('<0000> <FFFF>');
3015 3015 out('endcodespacerange');
3016 3016 out('1 beginbfrange');
3017 3017 out('<0000> <FFFF> <0000>');
3018 3018 out('endbfrange');
3019 3019 out('endcmap');
3020 3020 out('CMapName currentdict /CMap defineresource pop');
3021 3021 out('end');
3022 3022 out('end');
3023 3023 out('endstream');
3024 3024 out('endobj');
3025 3025
3026 3026 # CIDSystemInfo dictionary
3027 3027 # A dictionary containing entries that define the character collection of the CIDFont.
3028 3028 newobj();
3029 3029 out('<</Registry (Adobe)'); # A string identifying an issuer of character collections
3030 3030 out('/Ordering (UCS)'); # A string that uniquely names a character collection issued by a specific registry
3031 3031 out('/Supplement 0'); # The supplement number of the character collection.
3032 3032 out('>>');
3033 3033 out('endobj');
3034 3034
3035 3035 # Font descriptor
3036 3036 # A font descriptor describing the CIDFont default metrics other than its glyph widths
3037 3037 newobj();
3038 3038 out('<</Type /FontDescriptor');
3039 3039 out('/FontName /' + font['name']);
3040 3040 font['desc'].each do |key, value|
3041 3041 out('/' + key.to_s + ' ' + value.to_s);
3042 3042 end
3043 3043 if (font['file'])
3044 3044 # A stream containing a TrueType font program
3045 3045 out('/FontFile2 ' + @font_files[font['file']]['n'].to_s + ' 0 R');
3046 3046 end
3047 3047 out('>>');
3048 3048 out('endobj');
3049 3049
3050 3050 # Embed CIDToGIDMap
3051 3051 # A specification of the mapping from CIDs to glyph indices
3052 3052 newobj();
3053 3053 ctgfile = getfontpath(font['ctg'])
3054 3054 if (!ctgfile)
3055 3055 Error('Font file not found: ' + ctgfile);
3056 3056 end
3057 3057 size = File.size(ctgfile);
3058 3058 out('<</Length ' + size.to_s + '');
3059 3059 if (ctgfile[-2,2] == '.z') # check file extension
3060 3060 # Decompresses data encoded using the public-domain
3061 3061 # zlib/deflate compression method, reproducing the
3062 3062 # original text or binary data#
3063 3063 out('/Filter /FlateDecode');
3064 3064 end
3065 3065 out('>>');
3066 3066 open(ctgfile) do |f|
3067 3067 putstream(f.read())
3068 3068 end
3069 3069 out('endobj');
3070 3070 end
3071 3071
3072 3072 #
3073 3073 # Converts UTF-8 strings to codepoints array.<br>
3074 3074 # Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
3075 3075 # Based on: http://www.faqs.org/rfcs/rfc3629.html
3076 3076 # <pre>
3077 3077 # Char. number range | UTF-8 octet sequence
3078 3078 # (hexadecimal) | (binary)
3079 3079 # --------------------+-----------------------------------------------
3080 3080 # 0000 0000-0000 007F | 0xxxxxxx
3081 3081 # 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
3082 3082 # 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
3083 3083 # 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
3084 3084 # ---------------------------------------------------------------------
3085 3085 #
3086 3086 # ABFN notation:
3087 3087 # ---------------------------------------------------------------------
3088 3088 # UTF8-octets =#( UTF8-char )
3089 3089 # UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
3090 3090 # UTF8-1 = %x00-7F
3091 3091 # UTF8-2 = %xC2-DF UTF8-tail
3092 3092 #
3093 3093 # UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
3094 3094 # %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
3095 3095 # UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
3096 3096 # %xF4 %x80-8F 2( UTF8-tail )
3097 3097 # UTF8-tail = %x80-BF
3098 3098 # ---------------------------------------------------------------------
3099 3099 # </pre>
3100 3100 # @param string :str string to process.
3101 3101 # @return array containing codepoints (UTF-8 characters values)
3102 3102 # @access protected
3103 3103 # @author Nicola Asuni
3104 3104 # @since 1.53.0.TC005 (2005-01-05)
3105 3105 #
3106 3106 def UTF8StringToArray(str)
3107 3107 if (!@is_unicode)
3108 3108 return str; # string is not in unicode
3109 3109 end
3110 3110
3111 3111 unicode = [] # array containing unicode values
3112 3112 bytes = [] # array containing single character byte sequences
3113 3113 numbytes = 1; # number of octetc needed to represent the UTF-8 character
3114 3114
3115 3115 str = str.to_s; # force :str to be a string
3116 3116
3117 3117 str.each_byte do |char|
3118 3118 if (bytes.length == 0) # get starting octect
3119 3119 if (char <= 0x7F)
3120 3120 unicode << char # use the character "as is" because is ASCII
3121 3121 numbytes = 1
3122 3122 elsif ((char >> 0x05) == 0x06) # 2 bytes character (0x06 = 110 BIN)
3123 3123 bytes << ((char - 0xC0) << 0x06)
3124 3124 numbytes = 2
3125 3125 elsif ((char >> 0x04) == 0x0E) # 3 bytes character (0x0E = 1110 BIN)
3126 3126 bytes << ((char - 0xE0) << 0x0C)
3127 3127 numbytes = 3
3128 3128 elsif ((char >> 0x03) == 0x1E) # 4 bytes character (0x1E = 11110 BIN)
3129 3129 bytes << ((char - 0xF0) << 0x12)
3130 3130 numbytes = 4
3131 3131 else
3132 3132 # use replacement character for other invalid sequences
3133 3133 unicode << 0xFFFD
3134 3134 bytes = []
3135 3135 numbytes = 1
3136 3136 end
3137 3137 elsif ((char >> 0x06) == 0x02) # bytes 2, 3 and 4 must start with 0x02 = 10 BIN
3138 3138 bytes << (char - 0x80)
3139 3139 if (bytes.length == numbytes)
3140 3140 # compose UTF-8 bytes to a single unicode value
3141 3141 char = bytes[0]
3142 3142 1.upto(numbytes-1) do |j|
3143 3143 char += (bytes[j] << ((numbytes - j - 1) * 0x06))
3144 3144 end
3145 3145 if (((char >= 0xD800) and (char <= 0xDFFF)) or (char >= 0x10FFFF))
3146 3146 # The definition of UTF-8 prohibits encoding character numbers between
3147 3147 # U+D800 and U+DFFF, which are reserved for use with the UTF-16
3148 3148 # encoding form (as surrogate pairs) and do not directly represent
3149 3149 # characters
3150 3150 unicode << 0xFFFD; # use replacement character
3151 3151 else
3152 3152 unicode << char; # add char to array
3153 3153 end
3154 3154 # reset data for next char
3155 3155 bytes = []
3156 3156 numbytes = 1;
3157 3157 end
3158 3158 else
3159 3159 # use replacement character for other invalid sequences
3160 3160 unicode << 0xFFFD;
3161 3161 bytes = []
3162 3162 numbytes = 1;
3163 3163 end
3164 3164 end
3165 3165 return unicode;
3166 3166 end
3167 3167
3168 3168 #
3169 3169 # Converts UTF-8 strings to UTF16-BE.<br>
3170 3170 # Based on: http://www.faqs.org/rfcs/rfc2781.html
3171 3171 # <pre>
3172 3172 # Encoding UTF-16:
3173 3173 #
3174 3174 # Encoding of a single character from an ISO 10646 character value to
3175 3175 # UTF-16 proceeds as follows. Let U be the character number, no greater
3176 3176 # than 0x10FFFF.
3177 3177 #
3178 3178 # 1) If U < 0x10000, encode U as a 16-bit unsigned integer and
3179 3179 # terminate.
3180 3180 #
3181 3181 # 2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
3182 3182 # U' must be less than or equal to 0xFFFFF. That is, U' can be
3183 3183 # represented in 20 bits.
3184 3184 #
3185 3185 # 3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
3186 3186 # 0xDC00, respectively. These integers each have 10 bits free to
3187 3187 # encode the character value, for a total of 20 bits.
3188 3188 #
3189 3189 # 4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
3190 3190 # bits of W1 and the 10 low-order bits of U' to the 10 low-order
3191 3191 # bits of W2. Terminate.
3192 3192 #
3193 3193 # Graphically, steps 2 through 4 look like:
3194 3194 # U' = yyyyyyyyyyxxxxxxxxxx
3195 3195 # W1 = 110110yyyyyyyyyy
3196 3196 # W2 = 110111xxxxxxxxxx
3197 3197 # </pre>
3198 3198 # @param string :str string to process.
3199 3199 # @param boolean :setbom if true set the Byte Order Mark (BOM = 0xFEFF)
3200 3200 # @return string
3201 3201 # @access protected
3202 3202 # @author Nicola Asuni
3203 3203 # @since 1.53.0.TC005 (2005-01-05)
3204 3204 # @uses UTF8StringToArray
3205 3205 #
3206 3206 def UTF8ToUTF16BE(str, setbom=true)
3207 3207 if (!@is_unicode)
3208 3208 return str; # string is not in unicode
3209 3209 end
3210 3210 outstr = ""; # string to be returned
3211 3211 unicode = UTF8StringToArray(str); # array containing UTF-8 unicode values
3212 3212 numitems = unicode.length;
3213 3213
3214 3214 if (setbom)
3215 3215 outstr << "\xFE\xFF"; # Byte Order Mark (BOM)
3216 3216 end
3217 3217 unicode.each do |char|
3218 3218 if (char == 0xFFFD)
3219 3219 outstr << "\xFF\xFD"; # replacement character
3220 3220 elsif (char < 0x10000)
3221 3221 outstr << (char >> 0x08).chr;
3222 3222 outstr << (char & 0xFF).chr;
3223 3223 else
3224 3224 char -= 0x10000;
3225 3225 w1 = 0xD800 | (char >> 0x10);
3226 3226 w2 = 0xDC00 | (char & 0x3FF);
3227 3227 outstr << (w1 >> 0x08).chr;
3228 3228 outstr << (w1 & 0xFF).chr;
3229 3229 outstr << (w2 >> 0x08).chr;
3230 3230 outstr << (w2 & 0xFF).chr;
3231 3231 end
3232 3232 end
3233 3233 return outstr;
3234 3234 end
3235 3235
3236 3236 # ====================================================
3237 3237
3238 3238 #
3239 3239 # Set header font.
3240 3240 # @param array :font font
3241 3241 # @since 1.1
3242 3242 #
3243 3243 def SetHeaderFont(font)
3244 3244 @header_font = font;
3245 3245 end
3246 3246 alias_method :set_header_font, :SetHeaderFont
3247 3247
3248 3248 #
3249 3249 # Set footer font.
3250 3250 # @param array :font font
3251 3251 # @since 1.1
3252 3252 #
3253 3253 def SetFooterFont(font)
3254 3254 @footer_font = font;
3255 3255 end
3256 3256 alias_method :set_footer_font, :SetFooterFont
3257 3257
3258 3258 #
3259 3259 # Set language array.
3260 3260 # @param array :language
3261 3261 # @since 1.1
3262 3262 #
3263 3263 def SetLanguageArray(language)
3264 3264 @l = language;
3265 3265 end
3266 3266 alias_method :set_language_array, :SetLanguageArray
3267 3267 #
3268 3268 # Set document barcode.
3269 3269 # @param string :bc barcode
3270 3270 #
3271 3271 def SetBarcode(bc="")
3272 3272 @barcode = bc;
3273 3273 end
3274 3274
3275 3275 #
3276 3276 # Print Barcode.
3277 3277 # @param int :x x position in user units
3278 3278 # @param int :y y position in user units
3279 3279 # @param int :w width in user units
3280 3280 # @param int :h height position in user units
3281 3281 # @param string :type type of barcode (I25, C128A, C128B, C128C, C39)
3282 3282 # @param string :style barcode style
3283 3283 # @param string :font font for text
3284 3284 # @param int :xres x resolution
3285 3285 # @param string :code code to print
3286 3286 #
3287 3287 def writeBarcode(x, y, w, h, type, style, font, xres, code)
3288 3288 require(File.dirname(__FILE__) + "/barcode/barcode.rb");
3289 3289 require(File.dirname(__FILE__) + "/barcode/i25object.rb");
3290 3290 require(File.dirname(__FILE__) + "/barcode/c39object.rb");
3291 3291 require(File.dirname(__FILE__) + "/barcode/c128aobject.rb");
3292 3292 require(File.dirname(__FILE__) + "/barcode/c128bobject.rb");
3293 3293 require(File.dirname(__FILE__) + "/barcode/c128cobject.rb");
3294 3294
3295 3295 if (code.empty?)
3296 3296 return;
3297 3297 end
3298 3298
3299 3299 if (style.empty?)
3300 3300 style = BCS_ALIGN_LEFT;
3301 3301 style |= BCS_IMAGE_PNG;
3302 3302 style |= BCS_TRANSPARENT;
3303 3303 #:style |= BCS_BORDER;
3304 3304 #:style |= BCS_DRAW_TEXT;
3305 3305 #:style |= BCS_STRETCH_TEXT;
3306 3306 #:style |= BCS_REVERSE_COLOR;
3307 3307 end
3308 3308 if (font.empty?) then font = BCD_DEFAULT_FONT; end
3309 3309 if (xres.empty?) then xres = BCD_DEFAULT_XRES; end
3310 3310
3311 3311 scale_factor = 1.5 * xres * @k;
3312 3312 bc_w = (w * scale_factor).round #width in points
3313 3313 bc_h = (h * scale_factor).round #height in points
3314 3314
3315 3315 case (type.upcase)
3316 3316 when "I25"
3317 3317 obj = I25Object.new(bc_w, bc_h, style, code);
3318 3318 when "C128A"
3319 3319 obj = C128AObject.new(bc_w, bc_h, style, code);
3320 3320 when "C128B"
3321 3321 obj = C128BObject.new(bc_w, bc_h, style, code);
3322 3322 when "C128C"
3323 3323 obj = C128CObject.new(bc_w, bc_h, style, code);
3324 3324 when "C39"
3325 3325 obj = C39Object.new(bc_w, bc_h, style, code);
3326 3326 end
3327 3327
3328 3328 obj.SetFont(font);
3329 3329 obj.DrawObject(xres);
3330 3330
3331 3331 #use a temporary file....
3332 3332 tmpName = tempnam(@@k_path_cache,'img');
3333 3333 imagepng(obj.getImage(), tmpName);
3334 3334 Image(tmpName, x, y, w, h, 'png');
3335 3335 obj.DestroyObject();
3336 3336 obj = nil
3337 3337 unlink(tmpName);
3338 3338 end
3339 3339
3340 3340 #
3341 3341 # Returns the PDF data.
3342 3342 #
3343 3343 def GetPDFData()
3344 3344 if (@state < 3)
3345 3345 Close();
3346 3346 end
3347 3347 return @buffer;
3348 3348 end
3349 3349
3350 3350 # --- HTML PARSER FUNCTIONS ---
3351 3351
3352 3352 #
3353 3353 # Allows to preserve some HTML formatting.<br />
3354 3354 # Supports: h1, h2, h3, h4, h5, h6, b, u, i, a, img, p, br, strong, em, font, blockquote, li, ul, ol, hr, td, th, tr, table, sup, sub, small
3355 3355 # @param string :html text to display
3356 3356 # @param boolean :ln if true add a new line after text (default = true)
3357 3357 # @param int :fill Indicates if the background must be painted (1) or transparent (0). Default value: 0.
3358 3358 #
3359 3359 def writeHTML(html, ln=true, fill=0, h=0)
3360 3360
3361 3361 @lasth = h if h > 0
3362 3362 if (@lasth == 0)
3363 3363 #set row height
3364 3364 @lasth = @font_size * @@k_cell_height_ratio;
3365 3365 end
3366 3366
3367 3367 @href = nil
3368 3368 @style = {}
3369 3369 html.gsub!(/[\t\r\n\f]/, "")#\0\x0B
3370 3370 html.split(/(<[^>]+>)/).each do |element|
3371 3371 if "<" == element[0,1]
3372 3372 #Tag
3373 3373 if (element[1, 1] == '/')
3374 3374 closedHTMLTagHandler(element[2..-2].downcase);
3375 3375 else
3376 3376 #Extract attributes
3377 3377 # get tag name
3378 3378 tag = element.scan(/([a-zA-Z0-9]*)/).flatten.delete_if {|x| x.length == 0}
3379 3379 tag = tag[0].downcase;
3380 3380
3381 3381 # get attributes
3382 3382 attr_array = element.scan(/([^=\s]*)=["\']?([^"\']*)["\']?/)
3383 3383 attrs = {}
3384 3384 attr_array.each do |name, value|
3385 3385 attrs[name.downcase] = value;
3386 3386 end
3387 3387 openHTMLTagHandler(tag, attrs, fill);
3388 3388 end
3389 3389
3390 3390 else
3391 3391 #Text
3392 3392 if (@href)
3393 3393 addHtmlLink(@href, element, fill);
3394 3394 elsif (@tdbegin)
3395 3395 if ((element.strip.length > 0) and (element != "&nbsp;"))
3396 3396 Cell(@tdwidth, @tdheight, unhtmlentities(element.strip), @tableborder, 0, @tdalign, @tdfill);
3397 3397 elsif (element == "&nbsp;")
3398 3398 Cell(@tdwidth, @tdheight, '', @tableborder, 0, @tdalign, @tdfill);
3399 3399 end
3400 3400 elsif ((element.strip.length > 0) and (element != "&nbsp;"))
3401 3401 Write(@lasth, unhtmlentities(element), '', fill);
3402 3402 end
3403 3403 end
3404 3404 end
3405 3405
3406 3406 if (ln)
3407 3407 Ln(@lasth);
3408 3408 end
3409 3409 end
3410 3410 alias_method :write_html, :writeHTML
3411 3411
3412 3412 #
3413 3413 # Prints a cell (rectangular area) with optional borders, background color and html text string. The upper-left corner of the cell corresponds to the current position. After the call, the current position moves to the right or to the next line.<br />
3414 3414 # If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
3415 3415 # @param float :w Cell width. If 0, the cell extends up to the right margin.
3416 3416 # @param float :h Cell minimum height. The cell extends automatically if needed.
3417 3417 # @param float :x upper-left corner X coordinate
3418 3418 # @param float :y upper-left corner Y coordinate
3419 3419 # @param string :html html text to print. Default value: empty string.
3420 3420 # @param mixed :border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
3421 3421 # @param int :ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>
3422 3422 # Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
3423 3423 # @param int :fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3424 3424 # @see Cell()
3425 3425 #
3426 3426 def writeHTMLCell(w, h, x, y, html='', border=0, ln=0, fill=0)
3427 3427
3428 3428 if (@lasth == 0)
3429 3429 #set row height
3430 3430 @lasth = @font_size * @@k_cell_height_ratio;
3431 3431 end
3432 3432
3433 3433 if (x == 0)
3434 3434 x = GetX();
3435 3435 end
3436 3436 if (y == 0)
3437 3437 y = GetY();
3438 3438 end
3439 3439
3440 3440 # get current page number
3441 3441 pagenum = @page;
3442 3442
3443 3443 SetX(x);
3444 3444 SetY(y);
3445 3445
3446 3446 if (w == 0)
3447 3447 w = @fw - x - @r_margin;
3448 3448 end
3449 3449
3450 3450 # store original margin values
3451 3451 l_margin = @l_margin;
3452 3452 r_margin = @r_margin;
3453 3453
3454 3454 # set new margin values
3455 3455 SetLeftMargin(x);
3456 3456 SetRightMargin(@fw - x - w);
3457 3457
3458 3458 # calculate remaining vertical space on page
3459 3459 restspace = GetPageHeight() - GetY() - GetBreakMargin();
3460 3460
3461 3461 writeHTML(html, true, fill); # write html text
3462 3462
3463 3463 currentY = GetY();
3464 3464
3465 3465 # check if a new page has been created
3466 3466 if (@page > pagenum)
3467 3467 # design a cell around the text on first page
3468 3468 currentpage = @page;
3469 3469 @page = pagenum;
3470 3470 SetY(GetPageHeight() - restspace - GetBreakMargin());
3471 3471 h = restspace - 1;
3472 3472 Cell(w, h, "", border, ln, 'L', 0);
3473 3473 # design a cell around the text on last page
3474 3474 @page = currentpage;
3475 3475 h = currentY - @t_margin;
3476 3476 SetY(@t_margin); # put cursor at the beginning of text
3477 3477 Cell(w, h, "", border, ln, 'L', 0);
3478 3478 else
3479 3479 h = [h, (currentY - y)].max;
3480 3480 SetY(y); # put cursor at the beginning of text
3481 3481 # design a cell around the text
3482 3482 Cell(w, h, "", border, ln, 'L', 0);
3483 3483 end
3484 3484
3485 3485 # restore original margin values
3486 3486 SetLeftMargin(l_margin);
3487 3487 SetRightMargin(r_margin);
3488 3488
3489 3489 if (ln)
3490 3490 Ln(0);
3491 3491 end
3492 3492 end
3493 3493 alias_method :write_html_cell, :writeHTMLCell
3494 3494
3495 3495 #
3496 3496 # Process opening tags.
3497 3497 # @param string :tag tag name (in upcase)
3498 3498 # @param string :attr tag attribute (in upcase)
3499 3499 # @param int :fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3500 3500 # @access private
3501 3501 #
3502 3502 def openHTMLTagHandler(tag, attrs, fill=0)
3503 3503 #Opening tag
3504 3504 case (tag)
3505 3505 when 'table'
3506 3506 if attrs['border'].nil? or attrs['border'] == ''
3507 3507 @tableborder = 0;
3508 3508 else
3509 3509 @tableborder = attrs['border'];
3510 3510 end
3511 3511 when 'tr', 'td', 'th'
3512 3512 # SetStyle('b', true) if tag == 'th'
3513 3513
3514 3514 if ((!attrs['width'].nil?) and (attrs['width'] != ''))
3515 3515 @tdwidth = (attrs['width'].to_i/4);
3516 3516 else
3517 3517 @tdwidth = ((@w - @l_margin - @r_margin) / @default_table_columns);
3518 3518 end
3519 3519 if ((!attrs['height'].nil?) and (attrs['height'] != ''))
3520 3520 @tdheight=(attrs['height'].to_i / @k);
3521 3521 else
3522 3522 @tdheight = @lasth;
3523 3523 end
3524 3524 if ((!attrs['align'].nil?) and (attrs['align'] != ''))
3525 3525 case (attrs['align'])
3526 3526 when 'center'
3527 3527 @tdalign = "C";
3528 3528 when 'right'
3529 3529 @tdalign = "R";
3530 3530 when 'left'
3531 3531 @tdalign = "L";
3532 3532 end
3533 3533 end
3534 3534 if ((!attrs['bgcolor'].nil?) and (attrs['bgcolor'] != ''))
3535 3535 coul = convertColorHexToDec(attrs['bgcolor']);
3536 3536 SetFillColor(coul['R'], coul['G'], coul['B']);
3537 3537 @tdfill=1;
3538 3538 end
3539 3539 @tdbegin=true;
3540 3540
3541 3541 when 'hr'
3542 3542 Ln();
3543 3543 if ((!attrs['width'].nil?) and (attrs['width'] != ''))
3544 3544 hrWidth = attrs['width'];
3545 3545 else
3546 3546 hrWidth = @w - @l_margin - @r_margin;
3547 3547 end
3548 3548 x = GetX();
3549 3549 y = GetY();
3550 3550 SetLineWidth(0.2);
3551 3551 Line(x, y, x + hrWidth, y);
3552 3552 SetLineWidth(0.2);
3553 3553 Ln();
3554 3554
3555 3555 when 'strong'
3556 3556 SetStyle('b', true);
3557 3557
3558 3558 when 'em'
3559 3559 SetStyle('i', true);
3560 3560
3561 3561 when 'b', 'i', 'u'
3562 3562 SetStyle(tag, true);
3563 3563
3564 3564 when 'a'
3565 3565 @href = attrs['href'];
3566 3566
3567 3567 when 'img'
3568 3568 if (!attrs['src'].nil?)
3569 3569 # replace relative path with real server path
3570 3570 attrs['src'] = attrs['src'].gsub(@@k_path_url_cache, @@k_path_cache);
3571 3571 if (attrs['width'].nil?)
3572 3572 attrs['width'] = 0;
3573 3573 end
3574 3574 if (attrs['height'].nil?)
3575 3575 attrs['height'] = 0;
3576 3576 end
3577 3577
3578 3578 Image(attrs['src'], GetX(),GetY(), pixelsToMillimeters(attrs['width']), pixelsToMillimeters(attrs['height']));
3579 3579 #SetX(@img_rb_x);
3580 3580 SetY(@img_rb_y);
3581 3581
3582 3582 end
3583 3583
3584 3584 when 'ul'
3585 3585 @listordered = false;
3586 3586 @listcount = 0;
3587 3587
3588 3588 when 'ol'
3589 3589 @listordered = true;
3590 3590 @listcount = 0;
3591 3591
3592 3592 when 'li'
3593 3593 Ln();
3594 3594 if (@listordered)
3595 3595 @listcount += 1
3596 3596 @lispacer = " " + (@listcount).to_s + ". ";
3597 3597 else
3598 3598 #unordered list simbol
3599 3599 @lispacer = " - ";
3600 3600 end
3601 3601 Write(@lasth, @lispacer, '', fill);
3602 3602
3603 3603 when 'blockquote', 'br'
3604 3604 Ln();
3605 3605 if (@lispacer.length > 0)
3606 3606 @x += GetStringWidth(@lispacer);
3607 3607 end
3608 3608
3609 3609 when 'p'
3610 3610 Ln();
3611 3611 Ln();
3612 3612
3613 3613 when 'sup'
3614 3614 currentfont_size = @font_size;
3615 3615 @tempfontsize = @font_size_pt;
3616 3616 SetFontSize(@font_size_pt * @@k_small_ratio);
3617 3617 SetXY(GetX(), GetY() - ((currentfont_size - @font_size)*(@@k_small_ratio)));
3618 3618
3619 3619 when 'sub'
3620 3620 currentfont_size = @font_size;
3621 3621 @tempfontsize = @font_size_pt;
3622 3622 SetFontSize(@font_size_pt * @@k_small_ratio);
3623 3623 SetXY(GetX(), GetY() + ((currentfont_size - @font_size)*(@@k_small_ratio)));
3624 3624
3625 3625 when 'small'
3626 3626 currentfont_size = @font_size;
3627 3627 @tempfontsize = @font_size_pt;
3628 3628 SetFontSize(@font_size_pt * @@k_small_ratio);
3629 3629 SetXY(GetX(), GetY() + ((currentfont_size - @font_size)/3));
3630 3630
3631 3631 when 'font'
3632 3632 if (!attrs['color'].nil? and attrs['color']!='')
3633 3633 coul = convertColorHexToDec(attrs['color']);
3634 3634 SetTextColor(coul['R'], coul['G'], coul['B']);
3635 3635 @issetcolor=true;
3636 3636 end
3637 3637 if (!attrs['face'].nil? and @fontlist.include?(attrs['face'].downcase))
3638 3638 SetFont(attrs['face'].downcase);
3639 3639 @issetfont=true;
3640 3640 end
3641 3641 if (!attrs['size'].nil?)
3642 3642 headsize = attrs['size'].to_i;
3643 3643 else
3644 3644 headsize = 0;
3645 3645 end
3646 3646 currentfont_size = @font_size;
3647 3647 @tempfontsize = @font_size_pt;
3648 3648 SetFontSize(@font_size_pt + headsize);
3649 3649 @lasth = @font_size * @@k_cell_height_ratio;
3650 3650
3651 3651 when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
3652 3652 headsize = (4 - tag[1,1].to_f) * 2
3653 3653 @tempfontsize = @font_size_pt;
3654 3654 SetFontSize(@font_size_pt + headsize);
3655 3655 SetStyle('b', true);
3656 3656 @lasth = @font_size * @@k_cell_height_ratio;
3657 3657
3658 3658 end
3659 3659 end
3660 3660
3661 3661 #
3662 3662 # Process closing tags.
3663 3663 # @param string :tag tag name (in upcase)
3664 3664 # @access private
3665 3665 #
3666 3666 def closedHTMLTagHandler(tag)
3667 3667 #Closing tag
3668 3668 case (tag)
3669 3669 when 'td','th'
3670 3670 @tdbegin = false;
3671 3671 @tdwidth = 0;
3672 3672 @tdheight = 0;
3673 3673 @tdalign = "L";
3674 3674 @tdfill = 0;
3675 3675 SetFillColor(@prevfill_color[0], @prevfill_color[1], @prevfill_color[2]);
3676 3676
3677 3677 when 'tr'
3678 3678 Ln();
3679 3679
3680 3680 when 'table'
3681 3681 @tableborder=0;
3682 3682
3683 3683 when 'strong'
3684 3684 SetStyle('b', false);
3685 3685
3686 3686 when 'em'
3687 3687 SetStyle('i', false);
3688 3688
3689 3689 when 'b', 'i', 'u'
3690 3690 SetStyle(tag, false);
3691 3691
3692 3692 when 'a'
3693 3693 @href = nil;
3694 3694
3695 3695 when 'sup'
3696 3696 currentfont_size = @font_size;
3697 3697 SetFontSize(@tempfontsize);
3698 3698 @tempfontsize = @font_size_pt;
3699 3699 SetXY(GetX(), GetY() - ((currentfont_size - @font_size)*(@@k_small_ratio)));
3700 3700
3701 3701 when 'sub'
3702 3702 currentfont_size = @font_size;
3703 3703 SetFontSize(@tempfontsize);
3704 3704 @tempfontsize = @font_size_pt;
3705 3705 SetXY(GetX(), GetY() + ((currentfont_size - @font_size)*(@@k_small_ratio)));
3706 3706
3707 3707 when 'small'
3708 3708 currentfont_size = @font_size;
3709 3709 SetFontSize(@tempfontsize);
3710 3710 @tempfontsize = @font_size_pt;
3711 3711 SetXY(GetX(), GetY() - ((@font_size - currentfont_size)/3));
3712 3712
3713 3713 when 'font'
3714 3714 if (@issetcolor == true)
3715 3715 SetTextColor(@prevtext_color[0], @prevtext_color[1], @prevtext_color[2]);
3716 3716 end
3717 3717 if (@issetfont)
3718 3718 @font_family = @prevfont_family;
3719 3719 @font_style = @prevfont_style;
3720 3720 SetFont(@font_family);
3721 3721 @issetfont = false;
3722 3722 end
3723 3723 currentfont_size = @font_size;
3724 3724 SetFontSize(@tempfontsize);
3725 3725 @tempfontsize = @font_size_pt;
3726 3726 #@text_color = @prevtext_color;
3727 3727 @lasth = @font_size * @@k_cell_height_ratio;
3728 3728
3729 3729 when 'ul'
3730 3730 Ln();
3731 3731
3732 3732 when 'ol'
3733 3733 Ln();
3734 3734
3735 3735 when 'li'
3736 3736 @lispacer = "";
3737 3737
3738 3738 when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
3739 3739 SetFontSize(@tempfontsize);
3740 3740 @tempfontsize = @font_size_pt;
3741 3741 SetStyle('b', false);
3742 3742 Ln();
3743 3743 @lasth = @font_size * @@k_cell_height_ratio;
3744 3744
3745 3745 end
3746 3746 end
3747 3747
3748 3748 #
3749 3749 # Sets font style.
3750 3750 # @param string :tag tag name (in lowercase)
3751 3751 # @param boolean :enable
3752 3752 # @access private
3753 3753 #
3754 3754 def SetStyle(tag, enable)
3755 3755 #Modify style and select corresponding font
3756 3756 style='';
3757 3757 ['b', 'i', 'u'].each do |s|
3758 3758 style << s if tag.downcase == s and enable
3759 3759 end
3760 3760 SetFont('', style);
3761 3761 end
3762 3762
3763 3763 #
3764 3764 # Output anchor link.
3765 3765 # @param string :url link URL
3766 3766 # @param string :name link name
3767 3767 # @param int :fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3768 3768 # @access public
3769 3769 #
3770 3770 def addHtmlLink(url, name, fill=0)
3771 3771 #Put a hyperlink
3772 3772 SetTextColor(0, 0, 255);
3773 3773 SetStyle('u', true);
3774 3774 Write(@lasth, name, url, fill);
3775 3775 SetStyle('u', false);
3776 3776 SetTextColor(0);
3777 3777 end
3778 3778
3779 3779 #
3780 3780 # Returns an associative array (keys: R,G,B) from
3781 3781 # a hex html code (e.g. #3FE5AA).
3782 3782 # @param string :color hexadecimal html color [#rrggbb]
3783 3783 # @return array
3784 3784 # @access private
3785 3785 #
3786 3786 def convertColorHexToDec(color = "#000000")
3787 3787 tbl_color = {}
3788 3788 tbl_color['R'] = color[1,2].hex.to_i;
3789 3789 tbl_color['G'] = color[3,2].hex.to_i;
3790 3790 tbl_color['B'] = color[5,2].hex.to_i;
3791 3791 return tbl_color;
3792 3792 end
3793 3793
3794 3794 #
3795 3795 # Converts pixels to millimeters in 72 dpi.
3796 3796 # @param int :px pixels
3797 3797 # @return float millimeters
3798 3798 # @access private
3799 3799 #
3800 3800 def pixelsToMillimeters(px)
3801 3801 return px.to_f * 25.4 / 72;
3802 3802 end
3803 3803
3804 3804 #
3805 3805 # Reverse function for htmlentities.
3806 3806 # Convert entities in UTF-8.
3807 3807 #
3808 3808 # @param :text_to_convert Text to convert.
3809 3809 # @return string converted
3810 3810 #
3811 3811 def unhtmlentities(string)
3812 3812 if @@decoder.nil?
3813 3813 CGI.unescapeHTML(string)
3814 3814 else
3815 3815 @@decoder.decode(string)
3816 3816 end
3817 3817 end
3818 3818
3819 3819 end # END OF CLASS
3820 3820
3821 3821 #TODO 2007-05-25 (EJM) Level=0 -
3822 3822 #Handle special IE contype request
3823 3823 # if (!_SERVER['HTTP_USER_AGENT'].nil? and (_SERVER['HTTP_USER_AGENT']=='contype'))
3824 3824 # header('Content-Type: application/pdf');
3825 3825 # exit;
3826 3826 # }
General Comments 0
You need to be logged in to leave comments. Login now