##// END OF EJS Templates
PDF: switch encoding and font whether CJK and Thainland non UTF-8 or not in TCPDF Redmine class (#8312)....
Toshi MARUYAMA -
r5598:5742f78bd1b7
parent child
Show More
@@ -1,496 +1,523
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 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 'rfpdf/fpdf'
21 require 'rfpdf/fpdf'
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
25
26 module Redmine
26 module Redmine
27 module Export
27 module Export
28 module PDF
28 module PDF
29 include ActionView::Helpers::TextHelper
29 include ActionView::Helpers::TextHelper
30 include ActionView::Helpers::NumberHelper
30 include ActionView::Helpers::NumberHelper
31
31
32 class ITCPDF < TCPDF
32 class ITCPDF < TCPDF
33 include Redmine::I18n
33 include Redmine::I18n
34 attr_accessor :footer_date
34 attr_accessor :footer_date
35
35
36 def initialize(lang)
36 def initialize(lang)
37 super()
38 if RUBY_VERSION < '1.9'
37 if RUBY_VERSION < '1.9'
39 @ic = Iconv.new(l(:general_pdf_encoding), 'UTF-8')
38 @ic = Iconv.new(l(:general_pdf_encoding), 'UTF-8')
40 end
39 end
40 pdf_encoding = l(:general_pdf_encoding).upcase
41 super('P', 'mm', 'A4', (pdf_encoding == 'UTF-8'), pdf_encoding )
41 set_language_if_valid lang
42 set_language_if_valid lang
42 @font_for_content = 'FreeSans'
43 case pdf_encoding
43 @font_for_footer = 'FreeSans'
44 when 'UTF-8'
45 @font_for_content = 'FreeSans'
46 @font_for_footer = 'FreeSans'
47 when 'CP949'
48 extend(PDF_Korean)
49 AddUHCFont()
50 @font_for_content = 'UHC'
51 @font_for_footer = 'UHC'
52 when 'CP932'
53 extend(PDF_Japanese)
54 AddSJISFont()
55 @font_for_content = 'SJIS'
56 @font_for_footer = 'SJIS'
57 when 'GB18030'
58 extend(PDF_Chinese)
59 AddGBFont()
60 @font_for_content = 'GB'
61 @font_for_footer = 'GB'
62 when 'BIG5'
63 extend(PDF_Chinese)
64 AddBig5Font()
65 @font_for_content = 'Big5'
66 @font_for_footer = 'Big5'
67 else
68 @font_for_content = 'Arial'
69 @font_for_footer = 'Helvetica'
70 end
44 SetCreator(Redmine::Info.app_name)
71 SetCreator(Redmine::Info.app_name)
45 SetFont(@font_for_content)
72 SetFont(@font_for_content)
46 end
73 end
47
74
48 def SetFontStyle(style, size)
75 def SetFontStyle(style, size)
49 SetFont(@font_for_content, style, size)
76 SetFont(@font_for_content, style, size)
50 end
77 end
51
78
52 def SetTitle(txt)
79 def SetTitle(txt)
53 txt = begin
80 txt = begin
54 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
81 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
55 hextxt = "<FEFF" # FEFF is BOM
82 hextxt = "<FEFF" # FEFF is BOM
56 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
83 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
57 hextxt << ">"
84 hextxt << ">"
58 rescue
85 rescue
59 txt
86 txt
60 end || ''
87 end || ''
61 super(txt)
88 super(txt)
62 end
89 end
63
90
64 def textstring(s)
91 def textstring(s)
65 # Format a text string
92 # Format a text string
66 if s =~ /^</ # This means the string is hex-dumped.
93 if s =~ /^</ # This means the string is hex-dumped.
67 return s
94 return s
68 else
95 else
69 return '('+escape(s)+')'
96 return '('+escape(s)+')'
70 end
97 end
71 end
98 end
72
99
73 def fix_text_encoding(txt)
100 def fix_text_encoding(txt)
74 RDMPdfEncoding::rdm_pdf_iconv(@ic, txt)
101 RDMPdfEncoding::rdm_pdf_iconv(@ic, txt)
75 end
102 end
76
103
77 def RDMCell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
104 def RDMCell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
78 Cell(w,h,fix_text_encoding(txt),border,ln,align,fill,link)
105 Cell(w,h,fix_text_encoding(txt),border,ln,align,fill,link)
79 end
106 end
80
107
81 def RDMMultiCell(w,h=0,txt='',border=0,align='',fill=0)
108 def RDMMultiCell(w,h=0,txt='',border=0,align='',fill=0)
82 MultiCell(w,h,fix_text_encoding(txt),border,align,fill)
109 MultiCell(w,h,fix_text_encoding(txt),border,align,fill)
83 end
110 end
84
111
85 def Footer
112 def Footer
86 SetFont(@font_for_footer, 'I', 8)
113 SetFont(@font_for_footer, 'I', 8)
87 SetY(-15)
114 SetY(-15)
88 SetX(15)
115 SetX(15)
89 RDMCell(0, 5, @footer_date, 0, 0, 'L')
116 RDMCell(0, 5, @footer_date, 0, 0, 'L')
90 SetY(-15)
117 SetY(-15)
91 SetX(-30)
118 SetX(-30)
92 RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
119 RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
93 end
120 end
94 end
121 end
95
122
96 class IFPDF < FPDF
123 class IFPDF < FPDF
97 include Redmine::I18n
124 include Redmine::I18n
98 attr_accessor :footer_date
125 attr_accessor :footer_date
99
126
100 def initialize(lang)
127 def initialize(lang)
101 super()
128 super()
102 if RUBY_VERSION < '1.9'
129 if RUBY_VERSION < '1.9'
103 @ic = Iconv.new(l(:general_pdf_encoding), 'UTF-8')
130 @ic = Iconv.new(l(:general_pdf_encoding), 'UTF-8')
104 end
131 end
105 set_language_if_valid lang
132 set_language_if_valid lang
106 case l(:general_pdf_encoding).upcase
133 case l(:general_pdf_encoding).upcase
107 when 'CP949'
134 when 'CP949'
108 extend(PDF_Korean)
135 extend(PDF_Korean)
109 AddUHCFont()
136 AddUHCFont()
110 @font_for_content = 'UHC'
137 @font_for_content = 'UHC'
111 @font_for_footer = 'UHC'
138 @font_for_footer = 'UHC'
112 when 'CP932'
139 when 'CP932'
113 extend(PDF_Japanese)
140 extend(PDF_Japanese)
114 AddSJISFont()
141 AddSJISFont()
115 @font_for_content = 'SJIS'
142 @font_for_content = 'SJIS'
116 @font_for_footer = 'SJIS'
143 @font_for_footer = 'SJIS'
117 when 'GB18030'
144 when 'GB18030'
118 extend(PDF_Chinese)
145 extend(PDF_Chinese)
119 AddGBFont()
146 AddGBFont()
120 @font_for_content = 'GB'
147 @font_for_content = 'GB'
121 @font_for_footer = 'GB'
148 @font_for_footer = 'GB'
122 when 'BIG5'
149 when 'BIG5'
123 extend(PDF_Chinese)
150 extend(PDF_Chinese)
124 AddBig5Font()
151 AddBig5Font()
125 @font_for_content = 'Big5'
152 @font_for_content = 'Big5'
126 @font_for_footer = 'Big5'
153 @font_for_footer = 'Big5'
127 else
154 else
128 @font_for_content = 'Arial'
155 @font_for_content = 'Arial'
129 @font_for_footer = 'Helvetica'
156 @font_for_footer = 'Helvetica'
130 end
157 end
131 SetCreator(Redmine::Info.app_name)
158 SetCreator(Redmine::Info.app_name)
132 SetFont(@font_for_content)
159 SetFont(@font_for_content)
133 end
160 end
134
161
135 def SetFontStyle(style, size)
162 def SetFontStyle(style, size)
136 SetFont(@font_for_content, style, size)
163 SetFont(@font_for_content, style, size)
137 end
164 end
138
165
139 def SetTitle(txt)
166 def SetTitle(txt)
140 txt = begin
167 txt = begin
141 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
168 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
142 hextxt = "<FEFF" # FEFF is BOM
169 hextxt = "<FEFF" # FEFF is BOM
143 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
170 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
144 hextxt << ">"
171 hextxt << ">"
145 rescue
172 rescue
146 txt
173 txt
147 end || ''
174 end || ''
148 super(txt)
175 super(txt)
149 end
176 end
150
177
151 def textstring(s)
178 def textstring(s)
152 # Format a text string
179 # Format a text string
153 if s =~ /^</ # This means the string is hex-dumped.
180 if s =~ /^</ # This means the string is hex-dumped.
154 return s
181 return s
155 else
182 else
156 return '('+escape(s)+')'
183 return '('+escape(s)+')'
157 end
184 end
158 end
185 end
159
186
160 def fix_text_encoding(txt)
187 def fix_text_encoding(txt)
161 RDMPdfEncoding::rdm_pdf_iconv(@ic, txt)
188 RDMPdfEncoding::rdm_pdf_iconv(@ic, txt)
162 end
189 end
163
190
164 def RDMCell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
191 def RDMCell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
165 Cell(w,h,fix_text_encoding(txt),border,ln,align,fill,link)
192 Cell(w,h,fix_text_encoding(txt),border,ln,align,fill,link)
166 end
193 end
167
194
168 def RDMMultiCell(w,h=0,txt='',border=0,align='',fill=0)
195 def RDMMultiCell(w,h=0,txt='',border=0,align='',fill=0)
169 MultiCell(w,h,fix_text_encoding(txt),border,align,fill)
196 MultiCell(w,h,fix_text_encoding(txt),border,align,fill)
170 end
197 end
171
198
172 def Footer
199 def Footer
173 SetFont(@font_for_footer, 'I', 8)
200 SetFont(@font_for_footer, 'I', 8)
174 SetY(-15)
201 SetY(-15)
175 SetX(15)
202 SetX(15)
176 RDMCell(0, 5, @footer_date, 0, 0, 'L')
203 RDMCell(0, 5, @footer_date, 0, 0, 'L')
177 SetY(-15)
204 SetY(-15)
178 SetX(-30)
205 SetX(-30)
179 RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
206 RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
180 end
207 end
181 alias alias_nb_pages AliasNbPages
208 alias alias_nb_pages AliasNbPages
182 end
209 end
183
210
184 # Returns a PDF string of a list of issues
211 # Returns a PDF string of a list of issues
185 def issues_to_pdf(issues, project, query)
212 def issues_to_pdf(issues, project, query)
186 if l(:general_pdf_encoding).upcase != 'UTF-8'
213 if l(:general_pdf_encoding).upcase != 'UTF-8'
187 pdf = IFPDF.new(current_language)
214 pdf = IFPDF.new(current_language)
188 else
215 else
189 pdf = ITCPDF.new(current_language)
216 pdf = ITCPDF.new(current_language)
190 end
217 end
191 title = query.new_record? ? l(:label_issue_plural) : query.name
218 title = query.new_record? ? l(:label_issue_plural) : query.name
192 title = "#{project} - #{title}" if project
219 title = "#{project} - #{title}" if project
193 pdf.SetTitle(title)
220 pdf.SetTitle(title)
194 pdf.alias_nb_pages
221 pdf.alias_nb_pages
195 pdf.footer_date = format_date(Date.today)
222 pdf.footer_date = format_date(Date.today)
196 pdf.SetAutoPageBreak(false)
223 pdf.SetAutoPageBreak(false)
197 pdf.AddPage("L")
224 pdf.AddPage("L")
198
225
199 # Landscape A4 = 210 x 297 mm
226 # Landscape A4 = 210 x 297 mm
200 page_height = 210
227 page_height = 210
201 page_width = 297
228 page_width = 297
202 right_margin = 10
229 right_margin = 10
203 bottom_margin = 20
230 bottom_margin = 20
204 col_id_width = 10
231 col_id_width = 10
205 row_height = 5
232 row_height = 5
206
233
207 # column widths
234 # column widths
208 table_width = page_width - right_margin - 10 # fixed left margin
235 table_width = page_width - right_margin - 10 # fixed left margin
209 col_width = []
236 col_width = []
210 unless query.columns.empty?
237 unless query.columns.empty?
211 col_width = query.columns.collect do |c|
238 col_width = query.columns.collect do |c|
212 (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) && ['string', 'text'].include?(c.custom_field.field_format)))? 4.0 : 1.0
239 (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) && ['string', 'text'].include?(c.custom_field.field_format)))? 4.0 : 1.0
213 end
240 end
214 ratio = (table_width - col_id_width) / col_width.inject(0) {|s,w| s += w}
241 ratio = (table_width - col_id_width) / col_width.inject(0) {|s,w| s += w}
215 col_width = col_width.collect {|w| w * ratio}
242 col_width = col_width.collect {|w| w * ratio}
216 end
243 end
217
244
218 # title
245 # title
219 pdf.SetFontStyle('B',11)
246 pdf.SetFontStyle('B',11)
220 pdf.RDMCell(190,10, title)
247 pdf.RDMCell(190,10, title)
221 pdf.Ln
248 pdf.Ln
222
249
223 # headers
250 # headers
224 pdf.SetFontStyle('B',8)
251 pdf.SetFontStyle('B',8)
225 pdf.SetFillColor(230, 230, 230)
252 pdf.SetFillColor(230, 230, 230)
226
253
227 # render it background to find the max height used
254 # render it background to find the max height used
228 base_x = pdf.GetX
255 base_x = pdf.GetX
229 base_y = pdf.GetY
256 base_y = pdf.GetY
230 max_height = issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
257 max_height = issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
231 pdf.Rect(base_x, base_y, table_width, max_height, 'FD');
258 pdf.Rect(base_x, base_y, table_width, max_height, 'FD');
232 pdf.SetXY(base_x, base_y);
259 pdf.SetXY(base_x, base_y);
233
260
234 # write the cells on page
261 # write the cells on page
235 pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1)
262 pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1)
236 issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
263 issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
237 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
264 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
238 pdf.SetY(base_y + max_height);
265 pdf.SetY(base_y + max_height);
239
266
240 # rows
267 # rows
241 pdf.SetFontStyle('',8)
268 pdf.SetFontStyle('',8)
242 pdf.SetFillColor(255, 255, 255)
269 pdf.SetFillColor(255, 255, 255)
243 previous_group = false
270 previous_group = false
244 issues.each do |issue|
271 issues.each do |issue|
245 if query.grouped? &&
272 if query.grouped? &&
246 (group = query.group_by_column.value(issue)) != previous_group
273 (group = query.group_by_column.value(issue)) != previous_group
247 pdf.SetFontStyle('B',9)
274 pdf.SetFontStyle('B',9)
248 pdf.RDMCell(277, row_height,
275 pdf.RDMCell(277, row_height,
249 (group.blank? ? 'None' : group.to_s) + " (#{query.issue_count_by_group[group]})",
276 (group.blank? ? 'None' : group.to_s) + " (#{query.issue_count_by_group[group]})",
250 1, 1, 'L')
277 1, 1, 'L')
251 pdf.SetFontStyle('',8)
278 pdf.SetFontStyle('',8)
252 previous_group = group
279 previous_group = group
253 end
280 end
254 # fetch all the row values
281 # fetch all the row values
255 col_values = query.columns.collect do |column|
282 col_values = query.columns.collect do |column|
256 s = if column.is_a?(QueryCustomFieldColumn)
283 s = if column.is_a?(QueryCustomFieldColumn)
257 cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
284 cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
258 show_value(cv)
285 show_value(cv)
259 else
286 else
260 value = issue.send(column.name)
287 value = issue.send(column.name)
261 if value.is_a?(Date)
288 if value.is_a?(Date)
262 format_date(value)
289 format_date(value)
263 elsif value.is_a?(Time)
290 elsif value.is_a?(Time)
264 format_time(value)
291 format_time(value)
265 else
292 else
266 value
293 value
267 end
294 end
268 end
295 end
269 s.to_s
296 s.to_s
270 end
297 end
271
298
272 # render it off-page to find the max height used
299 # render it off-page to find the max height used
273 base_x = pdf.GetX
300 base_x = pdf.GetX
274 base_y = pdf.GetY
301 base_y = pdf.GetY
275 pdf.SetY(2 * page_height)
302 pdf.SetY(2 * page_height)
276 max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
303 max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
277 pdf.SetXY(base_x, base_y)
304 pdf.SetXY(base_x, base_y)
278
305
279 # make new page if it doesn't fit on the current one
306 # make new page if it doesn't fit on the current one
280 space_left = page_height - base_y - bottom_margin
307 space_left = page_height - base_y - bottom_margin
281 if max_height > space_left
308 if max_height > space_left
282 pdf.AddPage("L")
309 pdf.AddPage("L")
283 base_x = pdf.GetX
310 base_x = pdf.GetX
284 base_y = pdf.GetY
311 base_y = pdf.GetY
285 end
312 end
286
313
287 # write the cells on page
314 # write the cells on page
288 pdf.RDMCell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1)
315 pdf.RDMCell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1)
289 issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
316 issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
290 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
317 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
291 pdf.SetY(base_y + max_height);
318 pdf.SetY(base_y + max_height);
292 end
319 end
293
320
294 if issues.size == Setting.issues_export_limit.to_i
321 if issues.size == Setting.issues_export_limit.to_i
295 pdf.SetFontStyle('B',10)
322 pdf.SetFontStyle('B',10)
296 pdf.RDMCell(0, row_height, '...')
323 pdf.RDMCell(0, row_height, '...')
297 end
324 end
298 pdf.Output
325 pdf.Output
299 end
326 end
300
327
301 # Renders MultiCells and returns the maximum height used
328 # Renders MultiCells and returns the maximum height used
302 def issues_to_pdf_write_cells(pdf, col_values, col_widths, row_height, head=false)
329 def issues_to_pdf_write_cells(pdf, col_values, col_widths, row_height, head=false)
303 base_y = pdf.GetY
330 base_y = pdf.GetY
304 max_height = row_height
331 max_height = row_height
305 col_values.each_with_index do |column, i|
332 col_values.each_with_index do |column, i|
306 col_x = pdf.GetX
333 col_x = pdf.GetX
307 if head == true
334 if head == true
308 pdf.RDMMultiCell(col_widths[i], row_height, column.caption, "T", 'L', 1)
335 pdf.RDMMultiCell(col_widths[i], row_height, column.caption, "T", 'L', 1)
309 else
336 else
310 pdf.RDMMultiCell(col_widths[i], row_height, column, "T", 'L', 1)
337 pdf.RDMMultiCell(col_widths[i], row_height, column, "T", 'L', 1)
311 end
338 end
312 max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height
339 max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height
313 pdf.SetXY(col_x + col_widths[i], base_y);
340 pdf.SetXY(col_x + col_widths[i], base_y);
314 end
341 end
315 return max_height
342 return max_height
316 end
343 end
317
344
318 # Draw lines to close the row (MultiCell border drawing in not uniform)
345 # Draw lines to close the row (MultiCell border drawing in not uniform)
319 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y, id_width, col_widths)
346 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y, id_width, col_widths)
320 col_x = top_x + id_width
347 col_x = top_x + id_width
321 pdf.Line(col_x, top_y, col_x, lower_y) # id right border
348 pdf.Line(col_x, top_y, col_x, lower_y) # id right border
322 col_widths.each do |width|
349 col_widths.each do |width|
323 col_x += width
350 col_x += width
324 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border
351 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border
325 end
352 end
326 pdf.Line(top_x, top_y, top_x, lower_y) # left border
353 pdf.Line(top_x, top_y, top_x, lower_y) # left border
327 pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border
354 pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border
328 end
355 end
329
356
330 # Returns a PDF string of a single issue
357 # Returns a PDF string of a single issue
331 def issue_to_pdf(issue)
358 def issue_to_pdf(issue)
332 if l(:general_pdf_encoding).upcase != 'UTF-8'
359 if l(:general_pdf_encoding).upcase != 'UTF-8'
333 pdf = IFPDF.new(current_language)
360 pdf = IFPDF.new(current_language)
334 else
361 else
335 pdf = ITCPDF.new(current_language)
362 pdf = ITCPDF.new(current_language)
336 end
363 end
337 pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
364 pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
338 pdf.alias_nb_pages
365 pdf.alias_nb_pages
339 pdf.footer_date = format_date(Date.today)
366 pdf.footer_date = format_date(Date.today)
340 pdf.AddPage
367 pdf.AddPage
341 pdf.SetFontStyle('B',11)
368 pdf.SetFontStyle('B',11)
342 pdf.RDMMultiCell(190,5, "#{issue.project} - #{issue.tracker} # #{issue.id}: #{issue.subject}")
369 pdf.RDMMultiCell(190,5, "#{issue.project} - #{issue.tracker} # #{issue.id}: #{issue.subject}")
343 pdf.Ln
370 pdf.Ln
344
371
345 y0 = pdf.GetY
372 y0 = pdf.GetY
346
373
347 pdf.SetFontStyle('B',9)
374 pdf.SetFontStyle('B',9)
348 pdf.RDMCell(35,5, l(:field_status) + ":","LT")
375 pdf.RDMCell(35,5, l(:field_status) + ":","LT")
349 pdf.SetFontStyle('',9)
376 pdf.SetFontStyle('',9)
350 pdf.RDMCell(60,5, issue.status.to_s,"RT")
377 pdf.RDMCell(60,5, issue.status.to_s,"RT")
351 pdf.SetFontStyle('B',9)
378 pdf.SetFontStyle('B',9)
352 pdf.RDMCell(35,5, l(:field_priority) + ":","LT")
379 pdf.RDMCell(35,5, l(:field_priority) + ":","LT")
353 pdf.SetFontStyle('',9)
380 pdf.SetFontStyle('',9)
354 pdf.RDMCell(60,5, issue.priority.to_s,"RT")
381 pdf.RDMCell(60,5, issue.priority.to_s,"RT")
355 pdf.Ln
382 pdf.Ln
356
383
357 pdf.SetFontStyle('B',9)
384 pdf.SetFontStyle('B',9)
358 pdf.RDMCell(35,5, l(:field_author) + ":","L")
385 pdf.RDMCell(35,5, l(:field_author) + ":","L")
359 pdf.SetFontStyle('',9)
386 pdf.SetFontStyle('',9)
360 pdf.RDMCell(60,5, issue.author.to_s,"R")
387 pdf.RDMCell(60,5, issue.author.to_s,"R")
361 pdf.SetFontStyle('B',9)
388 pdf.SetFontStyle('B',9)
362 pdf.RDMCell(35,5, l(:field_category) + ":","L")
389 pdf.RDMCell(35,5, l(:field_category) + ":","L")
363 pdf.SetFontStyle('',9)
390 pdf.SetFontStyle('',9)
364 pdf.RDMCell(60,5, issue.category.to_s,"R")
391 pdf.RDMCell(60,5, issue.category.to_s,"R")
365 pdf.Ln
392 pdf.Ln
366
393
367 pdf.SetFontStyle('B',9)
394 pdf.SetFontStyle('B',9)
368 pdf.RDMCell(35,5, l(:field_created_on) + ":","L")
395 pdf.RDMCell(35,5, l(:field_created_on) + ":","L")
369 pdf.SetFontStyle('',9)
396 pdf.SetFontStyle('',9)
370 pdf.RDMCell(60,5, format_date(issue.created_on),"R")
397 pdf.RDMCell(60,5, format_date(issue.created_on),"R")
371 pdf.SetFontStyle('B',9)
398 pdf.SetFontStyle('B',9)
372 pdf.RDMCell(35,5, l(:field_assigned_to) + ":","L")
399 pdf.RDMCell(35,5, l(:field_assigned_to) + ":","L")
373 pdf.SetFontStyle('',9)
400 pdf.SetFontStyle('',9)
374 pdf.RDMCell(60,5, issue.assigned_to.to_s,"R")
401 pdf.RDMCell(60,5, issue.assigned_to.to_s,"R")
375 pdf.Ln
402 pdf.Ln
376
403
377 pdf.SetFontStyle('B',9)
404 pdf.SetFontStyle('B',9)
378 pdf.RDMCell(35,5, l(:field_updated_on) + ":","LB")
405 pdf.RDMCell(35,5, l(:field_updated_on) + ":","LB")
379 pdf.SetFontStyle('',9)
406 pdf.SetFontStyle('',9)
380 pdf.RDMCell(60,5, format_date(issue.updated_on),"RB")
407 pdf.RDMCell(60,5, format_date(issue.updated_on),"RB")
381 pdf.SetFontStyle('B',9)
408 pdf.SetFontStyle('B',9)
382 pdf.RDMCell(35,5, l(:field_due_date) + ":","LB")
409 pdf.RDMCell(35,5, l(:field_due_date) + ":","LB")
383 pdf.SetFontStyle('',9)
410 pdf.SetFontStyle('',9)
384 pdf.RDMCell(60,5, format_date(issue.due_date),"RB")
411 pdf.RDMCell(60,5, format_date(issue.due_date),"RB")
385 pdf.Ln
412 pdf.Ln
386
413
387 for custom_value in issue.custom_field_values
414 for custom_value in issue.custom_field_values
388 pdf.SetFontStyle('B',9)
415 pdf.SetFontStyle('B',9)
389 pdf.RDMCell(35,5, custom_value.custom_field.name + ":","L")
416 pdf.RDMCell(35,5, custom_value.custom_field.name + ":","L")
390 pdf.SetFontStyle('',9)
417 pdf.SetFontStyle('',9)
391 pdf.RDMMultiCell(155,5, (show_value custom_value),"R")
418 pdf.RDMMultiCell(155,5, (show_value custom_value),"R")
392 end
419 end
393
420
394 pdf.SetFontStyle('B',9)
421 pdf.SetFontStyle('B',9)
395 pdf.RDMCell(35,5, l(:field_subject) + ":","LT")
422 pdf.RDMCell(35,5, l(:field_subject) + ":","LT")
396 pdf.SetFontStyle('',9)
423 pdf.SetFontStyle('',9)
397 pdf.RDMMultiCell(155,5, issue.subject,"RT")
424 pdf.RDMMultiCell(155,5, issue.subject,"RT")
398
425
399 pdf.SetFontStyle('B',9)
426 pdf.SetFontStyle('B',9)
400 pdf.RDMCell(35,5, l(:field_description) + ":","LT")
427 pdf.RDMCell(35,5, l(:field_description) + ":","LT")
401 pdf.SetFontStyle('',9)
428 pdf.SetFontStyle('',9)
402 pdf.RDMMultiCell(155,5, issue.description.to_s,"RT")
429 pdf.RDMMultiCell(155,5, issue.description.to_s,"RT")
403
430
404 pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
431 pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
405 pdf.Line(pdf.GetX, pdf.GetY, pdf.GetX + 190, pdf.GetY)
432 pdf.Line(pdf.GetX, pdf.GetY, pdf.GetX + 190, pdf.GetY)
406 pdf.Ln
433 pdf.Ln
407
434
408 if issue.changesets.any? &&
435 if issue.changesets.any? &&
409 User.current.allowed_to?(:view_changesets, issue.project)
436 User.current.allowed_to?(:view_changesets, issue.project)
410 pdf.SetFontStyle('B',9)
437 pdf.SetFontStyle('B',9)
411 pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
438 pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
412 pdf.Ln
439 pdf.Ln
413 for changeset in issue.changesets
440 for changeset in issue.changesets
414 pdf.SetFontStyle('B',8)
441 pdf.SetFontStyle('B',8)
415 pdf.RDMCell(190,5,
442 pdf.RDMCell(190,5,
416 format_time(changeset.committed_on) + " - " + changeset.author.to_s)
443 format_time(changeset.committed_on) + " - " + changeset.author.to_s)
417 pdf.Ln
444 pdf.Ln
418 unless changeset.comments.blank?
445 unless changeset.comments.blank?
419 pdf.SetFontStyle('',8)
446 pdf.SetFontStyle('',8)
420 pdf.RDMMultiCell(190,5, changeset.comments.to_s)
447 pdf.RDMMultiCell(190,5, changeset.comments.to_s)
421 end
448 end
422 pdf.Ln
449 pdf.Ln
423 end
450 end
424 end
451 end
425
452
426 pdf.SetFontStyle('B',9)
453 pdf.SetFontStyle('B',9)
427 pdf.RDMCell(190,5, l(:label_history), "B")
454 pdf.RDMCell(190,5, l(:label_history), "B")
428 pdf.Ln
455 pdf.Ln
429 for journal in issue.journals.find(
456 for journal in issue.journals.find(
430 :all, :include => [:user, :details],
457 :all, :include => [:user, :details],
431 :order => "#{Journal.table_name}.created_on ASC")
458 :order => "#{Journal.table_name}.created_on ASC")
432 pdf.SetFontStyle('B',8)
459 pdf.SetFontStyle('B',8)
433 pdf.RDMCell(190,5,
460 pdf.RDMCell(190,5,
434 format_time(journal.created_on) + " - " + journal.user.name)
461 format_time(journal.created_on) + " - " + journal.user.name)
435 pdf.Ln
462 pdf.Ln
436 pdf.SetFontStyle('I',8)
463 pdf.SetFontStyle('I',8)
437 for detail in journal.details
464 for detail in journal.details
438 pdf.RDMMultiCell(190,5, "- " + show_detail(detail, true))
465 pdf.RDMMultiCell(190,5, "- " + show_detail(detail, true))
439 end
466 end
440 if journal.notes?
467 if journal.notes?
441 pdf.Ln unless journal.details.empty?
468 pdf.Ln unless journal.details.empty?
442 pdf.SetFontStyle('',8)
469 pdf.SetFontStyle('',8)
443 pdf.RDMMultiCell(190,5, journal.notes.to_s)
470 pdf.RDMMultiCell(190,5, journal.notes.to_s)
444 end
471 end
445 pdf.Ln
472 pdf.Ln
446 end
473 end
447
474
448 if issue.attachments.any?
475 if issue.attachments.any?
449 pdf.SetFontStyle('B',9)
476 pdf.SetFontStyle('B',9)
450 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
477 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
451 pdf.Ln
478 pdf.Ln
452 for attachment in issue.attachments
479 for attachment in issue.attachments
453 pdf.SetFontStyle('',8)
480 pdf.SetFontStyle('',8)
454 pdf.RDMCell(80,5, attachment.filename)
481 pdf.RDMCell(80,5, attachment.filename)
455 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
482 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
456 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
483 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
457 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
484 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
458 pdf.Ln
485 pdf.Ln
459 end
486 end
460 end
487 end
461 pdf.Output
488 pdf.Output
462 end
489 end
463
490
464 class RDMPdfEncoding
491 class RDMPdfEncoding
465 include Redmine::I18n
492 include Redmine::I18n
466 def self.rdm_pdf_iconv(ic, txt)
493 def self.rdm_pdf_iconv(ic, txt)
467 txt ||= ''
494 txt ||= ''
468 if txt.respond_to?(:force_encoding)
495 if txt.respond_to?(:force_encoding)
469 txt.force_encoding('UTF-8')
496 txt.force_encoding('UTF-8')
470 if l(:general_pdf_encoding).upcase != 'UTF-8'
497 if l(:general_pdf_encoding).upcase != 'UTF-8'
471 txt = txt.encode(l(:general_pdf_encoding), :invalid => :replace,
498 txt = txt.encode(l(:general_pdf_encoding), :invalid => :replace,
472 :undef => :replace, :replace => '?')
499 :undef => :replace, :replace => '?')
473 else
500 else
474 txt = Redmine::CodesetUtil.replace_invalid_utf8(txt)
501 txt = Redmine::CodesetUtil.replace_invalid_utf8(txt)
475 end
502 end
476 txt.force_encoding('ASCII-8BIT')
503 txt.force_encoding('ASCII-8BIT')
477 else
504 else
478 ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
505 ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
479 txtar = ""
506 txtar = ""
480 begin
507 begin
481 txtar += ic.iconv(txt)
508 txtar += ic.iconv(txt)
482 rescue Iconv::IllegalSequence
509 rescue Iconv::IllegalSequence
483 txtar += $!.success
510 txtar += $!.success
484 txt = '?' + $!.failed[1,$!.failed.length]
511 txt = '?' + $!.failed[1,$!.failed.length]
485 retry
512 retry
486 rescue
513 rescue
487 txtar += $!.success
514 txtar += $!.success
488 end
515 end
489 txt = txtar
516 txt = txtar
490 end
517 end
491 txt
518 txt
492 end
519 end
493 end
520 end
494 end
521 end
495 end
522 end
496 end
523 end
General Comments 0
You need to be logged in to leave comments. Login now