##// END OF EJS Templates
Merged r6024 from trunk....
Toshi MARUYAMA -
r5907:44ed963a467b
parent child
Show More
@@ -1,430 +1,437
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 set_language_if_valid lang
37 set_language_if_valid lang
38 pdf_encoding = l(:general_pdf_encoding).upcase
38 pdf_encoding = l(:general_pdf_encoding).upcase
39 if RUBY_VERSION < '1.9'
39 if RUBY_VERSION < '1.9'
40 @ic = Iconv.new(pdf_encoding, 'UTF-8')
40 @ic = Iconv.new(pdf_encoding, 'UTF-8')
41 end
41 end
42 super('P', 'mm', 'A4', (pdf_encoding == 'UTF-8'), pdf_encoding)
42 super('P', 'mm', 'A4', (pdf_encoding == 'UTF-8'), pdf_encoding)
43 case pdf_encoding
43 case pdf_encoding
44 when 'UTF-8'
44 when 'UTF-8'
45 @font_for_content = 'FreeSans'
45 @font_for_content = 'FreeSans'
46 @font_for_footer = 'FreeSans'
46 @font_for_footer = 'FreeSans'
47 when 'CP949'
47 when 'CP949'
48 extend(PDF_Korean)
48 extend(PDF_Korean)
49 AddUHCFont()
49 AddUHCFont()
50 @font_for_content = 'UHC'
50 @font_for_content = 'UHC'
51 @font_for_footer = 'UHC'
51 @font_for_footer = 'UHC'
52 when 'CP932', 'SJIS', 'SHIFT_JIS'
52 when 'CP932', 'SJIS', 'SHIFT_JIS'
53 extend(PDF_Japanese)
53 extend(PDF_Japanese)
54 AddSJISFont()
54 AddSJISFont()
55 @font_for_content = 'SJIS'
55 @font_for_content = 'SJIS'
56 @font_for_footer = 'SJIS'
56 @font_for_footer = 'SJIS'
57 when 'GB18030'
57 when 'GB18030'
58 extend(PDF_Chinese)
58 extend(PDF_Chinese)
59 AddGBFont()
59 AddGBFont()
60 @font_for_content = 'GB'
60 @font_for_content = 'GB'
61 @font_for_footer = 'GB'
61 @font_for_footer = 'GB'
62 when 'BIG5'
62 when 'BIG5'
63 extend(PDF_Chinese)
63 extend(PDF_Chinese)
64 AddBig5Font()
64 AddBig5Font()
65 @font_for_content = 'Big5'
65 @font_for_content = 'Big5'
66 @font_for_footer = 'Big5'
66 @font_for_footer = 'Big5'
67 else
67 else
68 @font_for_content = 'Arial'
68 @font_for_content = 'Arial'
69 @font_for_footer = 'Helvetica'
69 @font_for_footer = 'Helvetica'
70 end
70 end
71 SetCreator(Redmine::Info.app_name)
71 SetCreator(Redmine::Info.app_name)
72 SetFont(@font_for_content)
72 SetFont(@font_for_content)
73 end
73 end
74
74
75 def SetFontStyle(style, size)
75 def SetFontStyle(style, size)
76 SetFont(@font_for_content, style, size)
76 SetFont(@font_for_content, style, size)
77 end
77 end
78
78
79 def SetTitle(txt)
79 def SetTitle(txt)
80 txt = begin
80 txt = begin
81 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
81 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
82 hextxt = "<FEFF" # FEFF is BOM
82 hextxt = "<FEFF" # FEFF is BOM
83 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
83 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
84 hextxt << ">"
84 hextxt << ">"
85 rescue
85 rescue
86 txt
86 txt
87 end || ''
87 end || ''
88 super(txt)
88 super(txt)
89 end
89 end
90
90
91 def textstring(s)
91 def textstring(s)
92 # Format a text string
92 # Format a text string
93 if s =~ /^</ # This means the string is hex-dumped.
93 if s =~ /^</ # This means the string is hex-dumped.
94 return s
94 return s
95 else
95 else
96 return '('+escape(s)+')'
96 return '('+escape(s)+')'
97 end
97 end
98 end
98 end
99
99
100 def fix_text_encoding(txt)
100 def fix_text_encoding(txt)
101 RDMPdfEncoding::rdm_pdf_iconv(@ic, txt)
101 RDMPdfEncoding::rdm_pdf_iconv(@ic, txt)
102 end
102 end
103
103
104 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='')
105 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)
106 end
106 end
107
107
108 def RDMMultiCell(w,h=0,txt='',border=0,align='',fill=0)
108 def RDMMultiCell(w,h=0,txt='',border=0,align='',fill=0)
109 MultiCell(w,h,fix_text_encoding(txt),border,align,fill)
109 MultiCell(w,h,fix_text_encoding(txt),border,align,fill)
110 end
110 end
111
111
112 def Footer
112 def Footer
113 SetFont(@font_for_footer, 'I', 8)
113 SetFont(@font_for_footer, 'I', 8)
114 SetY(-15)
114 SetY(-15)
115 SetX(15)
115 SetX(15)
116 RDMCell(0, 5, @footer_date, 0, 0, 'L')
116 RDMCell(0, 5, @footer_date, 0, 0, 'L')
117 SetY(-15)
117 SetY(-15)
118 SetX(-30)
118 SetX(-30)
119 RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
119 RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
120 end
120 end
121 end
121 end
122
122
123 # Returns a PDF string of a list of issues
123 # Returns a PDF string of a list of issues
124 def issues_to_pdf(issues, project, query)
124 def issues_to_pdf(issues, project, query)
125 pdf = ITCPDF.new(current_language)
125 pdf = ITCPDF.new(current_language)
126 title = query.new_record? ? l(:label_issue_plural) : query.name
126 title = query.new_record? ? l(:label_issue_plural) : query.name
127 title = "#{project} - #{title}" if project
127 title = "#{project} - #{title}" if project
128 pdf.SetTitle(title)
128 pdf.SetTitle(title)
129 pdf.alias_nb_pages
129 pdf.alias_nb_pages
130 pdf.footer_date = format_date(Date.today)
130 pdf.footer_date = format_date(Date.today)
131 pdf.SetAutoPageBreak(false)
131 pdf.SetAutoPageBreak(false)
132 pdf.AddPage("L")
132 pdf.AddPage("L")
133
133
134 # Landscape A4 = 210 x 297 mm
134 # Landscape A4 = 210 x 297 mm
135 page_height = 210
135 page_height = 210
136 page_width = 297
136 page_width = 297
137 right_margin = 10
137 right_margin = 10
138 bottom_margin = 20
138 bottom_margin = 20
139 col_id_width = 10
139 col_id_width = 10
140 row_height = 5
140 row_height = 5
141
141
142 # column widths
142 # column widths
143 table_width = page_width - right_margin - 10 # fixed left margin
143 table_width = page_width - right_margin - 10 # fixed left margin
144 col_width = []
144 col_width = []
145 unless query.columns.empty?
145 unless query.columns.empty?
146 col_width = query.columns.collect do |c|
146 col_width = query.columns.collect do |c|
147 (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) && ['string', 'text'].include?(c.custom_field.field_format)))? 4.0 : 1.0
147 (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) && ['string', 'text'].include?(c.custom_field.field_format)))? 4.0 : 1.0
148 end
148 end
149 ratio = (table_width - col_id_width) / col_width.inject(0) {|s,w| s += w}
149 ratio = (table_width - col_id_width) / col_width.inject(0) {|s,w| s += w}
150 col_width = col_width.collect {|w| w * ratio}
150 col_width = col_width.collect {|w| w * ratio}
151 end
151 end
152
152
153 # title
153 # title
154 pdf.SetFontStyle('B',11)
154 pdf.SetFontStyle('B',11)
155 pdf.RDMCell(190,10, title)
155 pdf.RDMCell(190,10, title)
156 pdf.Ln
156 pdf.Ln
157
157
158 # headers
158 # headers
159 pdf.SetFontStyle('B',8)
159 pdf.SetFontStyle('B',8)
160 pdf.SetFillColor(230, 230, 230)
160 pdf.SetFillColor(230, 230, 230)
161
161
162 # render it background to find the max height used
162 # render it background to find the max height used
163 base_x = pdf.GetX
163 base_x = pdf.GetX
164 base_y = pdf.GetY
164 base_y = pdf.GetY
165 max_height = issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
165 max_height = issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
166 pdf.Rect(base_x, base_y, table_width, max_height, 'FD');
166 pdf.Rect(base_x, base_y, table_width, max_height, 'FD');
167 pdf.SetXY(base_x, base_y);
167 pdf.SetXY(base_x, base_y);
168
168
169 # write the cells on page
169 # write the cells on page
170 pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1)
170 pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1)
171 issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
171 issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
172 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
172 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
173 pdf.SetY(base_y + max_height);
173 pdf.SetY(base_y + max_height);
174
174
175 # rows
175 # rows
176 pdf.SetFontStyle('',8)
176 pdf.SetFontStyle('',8)
177 pdf.SetFillColor(255, 255, 255)
177 pdf.SetFillColor(255, 255, 255)
178 previous_group = false
178 previous_group = false
179 issues.each do |issue|
179 issues.each do |issue|
180 if query.grouped? &&
180 if query.grouped? &&
181 (group = query.group_by_column.value(issue)) != previous_group
181 (group = query.group_by_column.value(issue)) != previous_group
182 pdf.SetFontStyle('B',9)
182 pdf.SetFontStyle('B',9)
183 pdf.RDMCell(277, row_height,
183 pdf.RDMCell(277, row_height,
184 (group.blank? ? 'None' : group.to_s) + " (#{query.issue_count_by_group[group]})",
184 (group.blank? ? 'None' : group.to_s) + " (#{query.issue_count_by_group[group]})",
185 1, 1, 'L')
185 1, 1, 'L')
186 pdf.SetFontStyle('',8)
186 pdf.SetFontStyle('',8)
187 previous_group = group
187 previous_group = group
188 end
188 end
189 # fetch all the row values
189 # fetch all the row values
190 col_values = query.columns.collect do |column|
190 col_values = query.columns.collect do |column|
191 s = if column.is_a?(QueryCustomFieldColumn)
191 s = if column.is_a?(QueryCustomFieldColumn)
192 cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
192 cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
193 show_value(cv)
193 show_value(cv)
194 else
194 else
195 value = issue.send(column.name)
195 value = issue.send(column.name)
196 if value.is_a?(Date)
196 if value.is_a?(Date)
197 format_date(value)
197 format_date(value)
198 elsif value.is_a?(Time)
198 elsif value.is_a?(Time)
199 format_time(value)
199 format_time(value)
200 else
200 else
201 value
201 value
202 end
202 end
203 end
203 end
204 s.to_s
204 s.to_s
205 end
205 end
206
206
207 # render it off-page to find the max height used
207 # render it off-page to find the max height used
208 base_x = pdf.GetX
208 base_x = pdf.GetX
209 base_y = pdf.GetY
209 base_y = pdf.GetY
210 pdf.SetY(2 * page_height)
210 pdf.SetY(2 * page_height)
211 max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
211 max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
212 pdf.SetXY(base_x, base_y)
212 pdf.SetXY(base_x, base_y)
213
213
214 # make new page if it doesn't fit on the current one
214 # make new page if it doesn't fit on the current one
215 space_left = page_height - base_y - bottom_margin
215 space_left = page_height - base_y - bottom_margin
216 if max_height > space_left
216 if max_height > space_left
217 pdf.AddPage("L")
217 pdf.AddPage("L")
218 base_x = pdf.GetX
218 base_x = pdf.GetX
219 base_y = pdf.GetY
219 base_y = pdf.GetY
220 end
220 end
221
221
222 # write the cells on page
222 # write the cells on page
223 pdf.RDMCell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1)
223 pdf.RDMCell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1)
224 issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
224 issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
225 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
225 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
226 pdf.SetY(base_y + max_height);
226 pdf.SetY(base_y + max_height);
227 end
227 end
228
228
229 if issues.size == Setting.issues_export_limit.to_i
229 if issues.size == Setting.issues_export_limit.to_i
230 pdf.SetFontStyle('B',10)
230 pdf.SetFontStyle('B',10)
231 pdf.RDMCell(0, row_height, '...')
231 pdf.RDMCell(0, row_height, '...')
232 end
232 end
233 pdf.Output
233 pdf.Output
234 end
234 end
235
235
236 # Renders MultiCells and returns the maximum height used
236 # Renders MultiCells and returns the maximum height used
237 def issues_to_pdf_write_cells(pdf, col_values, col_widths,
237 def issues_to_pdf_write_cells(pdf, col_values, col_widths,
238 row_height, head=false)
238 row_height, head=false)
239 base_y = pdf.GetY
239 base_y = pdf.GetY
240 max_height = row_height
240 max_height = row_height
241 col_values.each_with_index do |column, i|
241 col_values.each_with_index do |column, i|
242 col_x = pdf.GetX
242 col_x = pdf.GetX
243 if head == true
243 if head == true
244 pdf.RDMMultiCell(col_widths[i], row_height, column.caption, "T", 'L', 1)
244 pdf.RDMMultiCell(col_widths[i], row_height, column.caption, "T", 'L', 1)
245 else
245 else
246 pdf.RDMMultiCell(col_widths[i], row_height, column, "T", 'L', 1)
246 pdf.RDMMultiCell(col_widths[i], row_height, column, "T", 'L', 1)
247 end
247 end
248 max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height
248 max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height
249 pdf.SetXY(col_x + col_widths[i], base_y);
249 pdf.SetXY(col_x + col_widths[i], base_y);
250 end
250 end
251 return max_height
251 return max_height
252 end
252 end
253
253
254 # Draw lines to close the row (MultiCell border drawing in not uniform)
254 # Draw lines to close the row (MultiCell border drawing in not uniform)
255 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y,
255 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y,
256 id_width, col_widths)
256 id_width, col_widths)
257 col_x = top_x + id_width
257 col_x = top_x + id_width
258 pdf.Line(col_x, top_y, col_x, lower_y) # id right border
258 pdf.Line(col_x, top_y, col_x, lower_y) # id right border
259 col_widths.each do |width|
259 col_widths.each do |width|
260 col_x += width
260 col_x += width
261 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border
261 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border
262 end
262 end
263 pdf.Line(top_x, top_y, top_x, lower_y) # left border
263 pdf.Line(top_x, top_y, top_x, lower_y) # left border
264 pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border
264 pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border
265 end
265 end
266
266
267 # Returns a PDF string of a single issue
267 # Returns a PDF string of a single issue
268 def issue_to_pdf(issue)
268 def issue_to_pdf(issue)
269 pdf = ITCPDF.new(current_language)
269 pdf = ITCPDF.new(current_language)
270 pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
270 pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
271 pdf.alias_nb_pages
271 pdf.alias_nb_pages
272 pdf.footer_date = format_date(Date.today)
272 pdf.footer_date = format_date(Date.today)
273 pdf.AddPage
273 pdf.AddPage
274 pdf.SetFontStyle('B',11)
274 pdf.SetFontStyle('B',11)
275 pdf.RDMMultiCell(190,5,
275 pdf.RDMMultiCell(190,5,
276 "#{issue.project} - #{issue.tracker} # #{issue.id}: #{issue.subject}")
276 "#{issue.project} - #{issue.tracker} # #{issue.id}: #{issue.subject}")
277 pdf.Ln
277 pdf.Ln
278
278
279 y0 = pdf.GetY
279 y0 = pdf.GetY
280
280
281 pdf.SetFontStyle('B',9)
281 pdf.SetFontStyle('B',9)
282 pdf.RDMCell(35,5, l(:field_status) + ":","LT")
282 pdf.RDMCell(35,5, l(:field_status) + ":","LT")
283 pdf.SetFontStyle('',9)
283 pdf.SetFontStyle('',9)
284 pdf.RDMCell(60,5, issue.status.to_s,"RT")
284 pdf.RDMCell(60,5, issue.status.to_s,"RT")
285 pdf.SetFontStyle('B',9)
285 pdf.SetFontStyle('B',9)
286 pdf.RDMCell(35,5, l(:field_priority) + ":","LT")
286 pdf.RDMCell(35,5, l(:field_priority) + ":","LT")
287 pdf.SetFontStyle('',9)
287 pdf.SetFontStyle('',9)
288 pdf.RDMCell(60,5, issue.priority.to_s,"RT")
288 pdf.RDMCell(60,5, issue.priority.to_s,"RT")
289 pdf.Ln
289 pdf.Ln
290
290
291 pdf.SetFontStyle('B',9)
291 pdf.SetFontStyle('B',9)
292 pdf.RDMCell(35,5, l(:field_author) + ":","L")
292 pdf.RDMCell(35,5, l(:field_author) + ":","L")
293 pdf.SetFontStyle('',9)
293 pdf.SetFontStyle('',9)
294 pdf.RDMCell(60,5, issue.author.to_s,"R")
294 pdf.RDMCell(60,5, issue.author.to_s,"R")
295 pdf.SetFontStyle('B',9)
295 pdf.SetFontStyle('B',9)
296 pdf.RDMCell(35,5, l(:field_category) + ":","L")
296 pdf.RDMCell(35,5, l(:field_category) + ":","L")
297 pdf.SetFontStyle('',9)
297 pdf.SetFontStyle('',9)
298 pdf.RDMCell(60,5, issue.category.to_s,"R")
298 pdf.RDMCell(60,5, issue.category.to_s,"R")
299 pdf.Ln
299 pdf.Ln
300
300
301 pdf.SetFontStyle('B',9)
301 pdf.SetFontStyle('B',9)
302 pdf.RDMCell(35,5, l(:field_created_on) + ":","L")
302 pdf.RDMCell(35,5, l(:field_created_on) + ":","L")
303 pdf.SetFontStyle('',9)
303 pdf.SetFontStyle('',9)
304 pdf.RDMCell(60,5, format_date(issue.created_on),"R")
304 pdf.RDMCell(60,5, format_date(issue.created_on),"R")
305 pdf.SetFontStyle('B',9)
305 pdf.SetFontStyle('B',9)
306 pdf.RDMCell(35,5, l(:field_assigned_to) + ":","L")
306 pdf.RDMCell(35,5, l(:field_assigned_to) + ":","L")
307 pdf.SetFontStyle('',9)
307 pdf.SetFontStyle('',9)
308 pdf.RDMCell(60,5, issue.assigned_to.to_s,"R")
308 pdf.RDMCell(60,5, issue.assigned_to.to_s,"R")
309 pdf.Ln
309 pdf.Ln
310
310
311 pdf.SetFontStyle('B',9)
311 pdf.SetFontStyle('B',9)
312 pdf.RDMCell(35,5, l(:field_updated_on) + ":","LB")
312 pdf.RDMCell(35,5, l(:field_updated_on) + ":","LB")
313 pdf.SetFontStyle('',9)
313 pdf.SetFontStyle('',9)
314 pdf.RDMCell(60,5, format_date(issue.updated_on),"RB")
314 pdf.RDMCell(60,5, format_date(issue.updated_on),"RB")
315 pdf.SetFontStyle('B',9)
315 pdf.SetFontStyle('B',9)
316 pdf.RDMCell(35,5, l(:field_due_date) + ":","LB")
316 pdf.RDMCell(35,5, l(:field_due_date) + ":","LB")
317 pdf.SetFontStyle('',9)
317 pdf.SetFontStyle('',9)
318 pdf.RDMCell(60,5, format_date(issue.due_date),"RB")
318 pdf.RDMCell(60,5, format_date(issue.due_date),"RB")
319 pdf.Ln
319 pdf.Ln
320
320
321 for custom_value in issue.custom_field_values
321 for custom_value in issue.custom_field_values
322 pdf.SetFontStyle('B',9)
322 pdf.SetFontStyle('B',9)
323 pdf.RDMCell(35,5, custom_value.custom_field.name + ":","L")
323 pdf.RDMCell(35,5, custom_value.custom_field.name + ":","L")
324 pdf.SetFontStyle('',9)
324 pdf.SetFontStyle('',9)
325 pdf.RDMMultiCell(155,5, (show_value custom_value),"R")
325 pdf.RDMMultiCell(155,5, (show_value custom_value),"R")
326 end
326 end
327
327
328 pdf.SetFontStyle('B',9)
328 pdf.SetFontStyle('B',9)
329 pdf.RDMCell(35,5, l(:field_subject) + ":","LT")
329 pdf.RDMCell(35,5, l(:field_subject) + ":","LT")
330 pdf.SetFontStyle('',9)
330 pdf.SetFontStyle('',9)
331 pdf.RDMMultiCell(155,5, issue.subject,"RT")
331 pdf.RDMMultiCell(155,5, issue.subject,"RT")
332
332
333 pdf.SetFontStyle('B',9)
333 pdf.SetFontStyle('B',9)
334 pdf.RDMCell(35,5, l(:field_description) + ":","LT")
334 pdf.RDMCell(35,5, l(:field_description) + ":","LT")
335 pdf.SetFontStyle('',9)
335 pdf.SetFontStyle('',9)
336 pdf.RDMMultiCell(155,5, issue.description.to_s,"RT")
336 pdf.RDMMultiCell(155,5, issue.description.to_s,"RT")
337
337
338 pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
338 pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
339 pdf.Line(pdf.GetX, pdf.GetY, pdf.GetX + 190, pdf.GetY)
339 pdf.Line(pdf.GetX, pdf.GetY, pdf.GetX + 190, pdf.GetY)
340 pdf.Ln
340 pdf.Ln
341
341
342 if issue.changesets.any? &&
342 if issue.changesets.any? &&
343 User.current.allowed_to?(:view_changesets, issue.project)
343 User.current.allowed_to?(:view_changesets, issue.project)
344 pdf.SetFontStyle('B',9)
344 pdf.SetFontStyle('B',9)
345 pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
345 pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
346 pdf.Ln
346 pdf.Ln
347 for changeset in issue.changesets
347 for changeset in issue.changesets
348 pdf.SetFontStyle('B',8)
348 pdf.SetFontStyle('B',8)
349 pdf.RDMCell(190,5,
349 pdf.RDMCell(190,5,
350 format_time(changeset.committed_on) + " - " + changeset.author.to_s)
350 format_time(changeset.committed_on) + " - " + changeset.author.to_s)
351 pdf.Ln
351 pdf.Ln
352 unless changeset.comments.blank?
352 unless changeset.comments.blank?
353 pdf.SetFontStyle('',8)
353 pdf.SetFontStyle('',8)
354 pdf.RDMMultiCell(190,5, changeset.comments.to_s)
354 pdf.RDMMultiCell(190,5, changeset.comments.to_s)
355 end
355 end
356 pdf.Ln
356 pdf.Ln
357 end
357 end
358 end
358 end
359
359
360 pdf.SetFontStyle('B',9)
360 pdf.SetFontStyle('B',9)
361 pdf.RDMCell(190,5, l(:label_history), "B")
361 pdf.RDMCell(190,5, l(:label_history), "B")
362 pdf.Ln
362 pdf.Ln
363 for journal in issue.journals.find(
363 for journal in issue.journals.find(
364 :all, :include => [:user, :details],
364 :all, :include => [:user, :details],
365 :order => "#{Journal.table_name}.created_on ASC")
365 :order => "#{Journal.table_name}.created_on ASC")
366 pdf.SetFontStyle('B',8)
366 pdf.SetFontStyle('B',8)
367 pdf.RDMCell(190,5,
367 pdf.RDMCell(190,5,
368 format_time(journal.created_on) + " - " + journal.user.name)
368 format_time(journal.created_on) + " - " + journal.user.name)
369 pdf.Ln
369 pdf.Ln
370 pdf.SetFontStyle('I',8)
370 pdf.SetFontStyle('I',8)
371 for detail in journal.details
371 for detail in journal.details
372 pdf.RDMMultiCell(190,5, "- " + show_detail(detail, true))
372 pdf.RDMMultiCell(190,5, "- " + show_detail(detail, true))
373 end
373 end
374 if journal.notes?
374 if journal.notes?
375 pdf.Ln unless journal.details.empty?
375 pdf.Ln unless journal.details.empty?
376 pdf.SetFontStyle('',8)
376 pdf.SetFontStyle('',8)
377 pdf.RDMMultiCell(190,5, journal.notes.to_s)
377 pdf.RDMMultiCell(190,5, journal.notes.to_s)
378 end
378 end
379 pdf.Ln
379 pdf.Ln
380 end
380 end
381
381
382 if issue.attachments.any?
382 if issue.attachments.any?
383 pdf.SetFontStyle('B',9)
383 pdf.SetFontStyle('B',9)
384 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
384 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
385 pdf.Ln
385 pdf.Ln
386 for attachment in issue.attachments
386 for attachment in issue.attachments
387 pdf.SetFontStyle('',8)
387 pdf.SetFontStyle('',8)
388 pdf.RDMCell(80,5, attachment.filename)
388 pdf.RDMCell(80,5, attachment.filename)
389 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
389 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
390 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
390 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
391 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
391 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
392 pdf.Ln
392 pdf.Ln
393 end
393 end
394 end
394 end
395 pdf.Output
395 pdf.Output
396 end
396 end
397
397
398 class RDMPdfEncoding
398 class RDMPdfEncoding
399 include Redmine::I18n
399 include Redmine::I18n
400 def self.rdm_pdf_iconv(ic, txt)
400 def self.rdm_pdf_iconv(ic, txt)
401 txt ||= ''
401 txt ||= ''
402 if txt.respond_to?(:force_encoding)
402 if txt.respond_to?(:force_encoding)
403 txt.force_encoding('UTF-8')
403 txt.force_encoding('UTF-8')
404 if l(:general_pdf_encoding).upcase != 'UTF-8'
404 if l(:general_pdf_encoding).upcase != 'UTF-8'
405 txt = txt.encode(l(:general_pdf_encoding), :invalid => :replace,
405 txt = txt.encode(l(:general_pdf_encoding), :invalid => :replace,
406 :undef => :replace, :replace => '?')
406 :undef => :replace, :replace => '?')
407 else
407 else
408 txt = Redmine::CodesetUtil.replace_invalid_utf8(txt)
408 txt = Redmine::CodesetUtil.replace_invalid_utf8(txt)
409 end
409 end
410 txt.force_encoding('ASCII-8BIT')
410 txt.force_encoding('ASCII-8BIT')
411 elsif RUBY_PLATFORM == 'java'
412 begin
413 ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
414 txt = ic.iconv(txt)
415 rescue
416 txt = txt.gsub(%r{[^\r\n\t\x20-\x7e]}, '?')
417 end
411 else
418 else
412 ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
419 ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
413 txtar = ""
420 txtar = ""
414 begin
421 begin
415 txtar += ic.iconv(txt)
422 txtar += ic.iconv(txt)
416 rescue Iconv::IllegalSequence
423 rescue Iconv::IllegalSequence
417 txtar += $!.success
424 txtar += $!.success
418 txt = '?' + $!.failed[1,$!.failed.length]
425 txt = '?' + $!.failed[1,$!.failed.length]
419 retry
426 retry
420 rescue
427 rescue
421 txtar += $!.success
428 txtar += $!.success
422 end
429 end
423 txt = txtar
430 txt = txtar
424 end
431 end
425 txt
432 txt
426 end
433 end
427 end
434 end
428 end
435 end
429 end
436 end
430 end
437 end
@@ -1,113 +1,120
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../../test_helper', __FILE__)
19 require 'iconv'
19 require 'iconv'
20
20
21 class PdfTest < ActiveSupport::TestCase
21 class PdfTest < ActiveSupport::TestCase
22 include Redmine::I18n
22 include Redmine::I18n
23
23
24 def test_fix_text_encoding_nil
24 def test_fix_text_encoding_nil
25 set_language_if_valid 'ja'
25 set_language_if_valid 'ja'
26 assert_equal 'CP932', l(:general_pdf_encoding)
26 assert_equal 'CP932', l(:general_pdf_encoding)
27 if RUBY_VERSION < '1.9'
27 if RUBY_VERSION < '1.9'
28 if RUBY_PLATFORM == 'java'
28 if RUBY_PLATFORM == 'java'
29 ic = Iconv.new("SJIS", 'UTF-8')
29 ic = Iconv.new("SJIS", 'UTF-8')
30 else
30 else
31 ic = Iconv.new(l(:general_pdf_encoding), 'UTF-8')
31 ic = Iconv.new(l(:general_pdf_encoding), 'UTF-8')
32 end
32 end
33 end
33 end
34 assert_equal '', Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, nil)
34 assert_equal '', Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, nil)
35 end
35 end
36
36
37 def test_rdm_pdf_iconv_cannot_convert_ja_cp932
37 def test_rdm_pdf_iconv_cannot_convert_ja_cp932
38 set_language_if_valid 'ja'
38 set_language_if_valid 'ja'
39 assert_equal 'CP932', l(:general_pdf_encoding)
39 assert_equal 'CP932', l(:general_pdf_encoding)
40 if RUBY_VERSION < '1.9'
40 if RUBY_VERSION < '1.9'
41 if RUBY_PLATFORM == 'java'
41 if RUBY_PLATFORM == 'java'
42 ic = Iconv.new("SJIS", 'UTF-8')
42 ic = Iconv.new("SJIS", 'UTF-8')
43 else
43 else
44 ic = Iconv.new(l(:general_pdf_encoding), 'UTF-8')
44 ic = Iconv.new(l(:general_pdf_encoding), 'UTF-8')
45 end
45 end
46 end
46 end
47 utf8_txt_1 = "\xe7\x8b\x80\xe6\x85\x8b"
47 utf8_txt_1 = "\xe7\x8b\x80\xe6\x85\x8b"
48 utf8_txt_2 = "\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
48 utf8_txt_2 = "\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
49 utf8_txt_3 = "\xe7\x8b\x80\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
49 utf8_txt_3 = "\xe7\x8b\x80\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
50 if utf8_txt_1.respond_to?(:force_encoding)
50 if utf8_txt_1.respond_to?(:force_encoding)
51 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_1)
51 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_1)
52 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_2)
52 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_2)
53 txt_3 = Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_3)
53 txt_3 = Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_3)
54 assert_equal "?\x91\xd4", txt_1
54 assert_equal "?\x91\xd4", txt_1
55 assert_equal "?\x91\xd4?", txt_2
55 assert_equal "?\x91\xd4?", txt_2
56 assert_equal "??\x91\xd4?", txt_3
56 assert_equal "??\x91\xd4?", txt_3
57 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
57 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
58 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
58 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
59 assert_equal "ASCII-8BIT", txt_3.encoding.to_s
59 assert_equal "ASCII-8BIT", txt_3.encoding.to_s
60 elsif RUBY_PLATFORM == 'java'
61 assert_equal "??",
62 Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_1)
63 assert_equal "???",
64 Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_2)
65 assert_equal "????",
66 Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_3)
60 else
67 else
61 assert_equal "???\x91\xd4",
68 assert_equal "???\x91\xd4",
62 Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_1)
69 Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_1)
63 assert_equal "???\x91\xd4???",
70 assert_equal "???\x91\xd4???",
64 Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_2)
71 Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_2)
65 assert_equal "??????\x91\xd4???",
72 assert_equal "??????\x91\xd4???",
66 Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_3)
73 Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, utf8_txt_3)
67 end
74 end
68 end
75 end
69
76
70 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_en
77 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_en
71 set_language_if_valid 'en'
78 set_language_if_valid 'en'
72 assert_equal 'UTF-8', l(:general_pdf_encoding)
79 assert_equal 'UTF-8', l(:general_pdf_encoding)
73 str1 = "Texte encod\xe9 en ISO-8859-1"
80 str1 = "Texte encod\xe9 en ISO-8859-1"
74 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
81 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
75 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
82 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
76 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
83 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
77 if RUBY_VERSION < '1.9'
84 if RUBY_VERSION < '1.9'
78 ic = Iconv.new(l(:general_pdf_encoding), 'UTF-8')
85 ic = Iconv.new(l(:general_pdf_encoding), 'UTF-8')
79 end
86 end
80 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, str1)
87 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, str1)
81 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, str2)
88 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, str2)
82 if txt_1.respond_to?(:force_encoding)
89 if txt_1.respond_to?(:force_encoding)
83 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
90 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
84 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
91 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
85 end
92 end
86 assert_equal "Texte encod? en ISO-8859-1", txt_1
93 assert_equal "Texte encod? en ISO-8859-1", txt_1
87 assert_equal "?a?b?c?d?e test", txt_2
94 assert_equal "?a?b?c?d?e test", txt_2
88 end
95 end
89
96
90 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_ja
97 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_ja
91 set_language_if_valid 'ja'
98 set_language_if_valid 'ja'
92 assert_equal 'CP932', l(:general_pdf_encoding)
99 assert_equal 'CP932', l(:general_pdf_encoding)
93 str1 = "Texte encod\xe9 en ISO-8859-1"
100 str1 = "Texte encod\xe9 en ISO-8859-1"
94 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
101 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
95 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
102 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
96 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
103 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
97 if RUBY_VERSION < '1.9'
104 if RUBY_VERSION < '1.9'
98 if RUBY_PLATFORM == 'java'
105 if RUBY_PLATFORM == 'java'
99 ic = Iconv.new("SJIS", 'UTF-8')
106 ic = Iconv.new("SJIS", 'UTF-8')
100 else
107 else
101 ic = Iconv.new(l(:general_pdf_encoding), 'UTF-8')
108 ic = Iconv.new(l(:general_pdf_encoding), 'UTF-8')
102 end
109 end
103 end
110 end
104 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, str1)
111 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, str1)
105 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, str2)
112 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_pdf_iconv(ic, str2)
106 if txt_1.respond_to?(:force_encoding)
113 if txt_1.respond_to?(:force_encoding)
107 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
114 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
108 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
115 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
109 end
116 end
110 assert_equal "Texte encod? en ISO-8859-1", txt_1
117 assert_equal "Texte encod? en ISO-8859-1", txt_1
111 assert_equal "?a?b?c?d?e test", txt_2
118 assert_equal "?a?b?c?d?e test", txt_2
112 end
119 end
113 end
120 end
General Comments 0
You need to be logged in to leave comments. Login now