annotate vendor/plugins/rfpdf/lib/tcpdf.rb @ 1478:5ca1f4a47171 bibplugin_db_migrations

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