annotate .svn/pristine/52/526a207c3d794968f2a7c5b89f01aadb8abb89d2.svn-base @ 929:5f33065ddc4b redmine-1.3

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