annotate vendor/plugins/rfpdf/lib/tcpdf.rb @ 1452:d6b9fd02bb89 feature_36_js_refactoring

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