annotate .svn/pristine/09/09319bb04693b301a469674c2043f8c089fbdcbb.svn-base @ 1296:038ba2d95de8 redmine-2.2

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