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