annotate .svn/pristine/56/56813b39ed887e573bb8518a2b58c7a03d6e9767.svn-base @ 1298:4f746d8966dd redmine_2.3_integration

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