annotate .svn/pristine/56/56813b39ed887e573bb8518a2b58c7a03d6e9767.svn-base @ 1519:afce8026aaeb redmine-2.4-integration

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