annotate .svn/pristine/00/00205ce9aaa0cc98a4089319a62880b29732ab31.svn-base @ 1524:82fac3dcf466 redmine-2.5-integration

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