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