annotate .svn/pristine/09/09319bb04693b301a469674c2043f8c089fbdcbb.svn-base @ 1295:622f24f53b42 redmine-2.3

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