To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / lib / plugins / rfpdf / lib / tcpdf.rb @ 1297:0a574315af3e

History | View | Annotate | Download (122 KB)

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