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