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