##// END OF EJS Templates
pdf: lib: add the method to return attachment from filename and encoding (#3261)...
Toshi MARUYAMA -
r7792:f936b7b5a4ec
parent child
Show More
@@ -1,523 +1,536
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 'fpdf/chinese'
21 require 'fpdf/chinese'
22 require 'fpdf/japanese'
22 require 'fpdf/japanese'
23 require 'fpdf/korean'
23 require 'fpdf/korean'
24
24
25 module Redmine
25 module Redmine
26 module Export
26 module Export
27 module PDF
27 module PDF
28 include ActionView::Helpers::TextHelper
28 include ActionView::Helpers::TextHelper
29 include ActionView::Helpers::NumberHelper
29 include ActionView::Helpers::NumberHelper
30 include IssuesHelper
30 include IssuesHelper
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 super('P', 'mm', 'A4', (pdf_encoding == 'UTF-8'), pdf_encoding)
39 super('P', 'mm', 'A4', (pdf_encoding == 'UTF-8'), pdf_encoding)
40 case current_language.to_s.downcase
40 case current_language.to_s.downcase
41 when 'vi'
41 when 'vi'
42 @font_for_content = 'DejaVuSans'
42 @font_for_content = 'DejaVuSans'
43 @font_for_footer = 'DejaVuSans'
43 @font_for_footer = 'DejaVuSans'
44 else
44 else
45 case pdf_encoding
45 case pdf_encoding
46 when 'UTF-8'
46 when 'UTF-8'
47 @font_for_content = 'FreeSans'
47 @font_for_content = 'FreeSans'
48 @font_for_footer = 'FreeSans'
48 @font_for_footer = 'FreeSans'
49 when 'CP949'
49 when 'CP949'
50 extend(PDF_Korean)
50 extend(PDF_Korean)
51 AddUHCFont()
51 AddUHCFont()
52 @font_for_content = 'UHC'
52 @font_for_content = 'UHC'
53 @font_for_footer = 'UHC'
53 @font_for_footer = 'UHC'
54 when 'CP932', 'SJIS', 'SHIFT_JIS'
54 when 'CP932', 'SJIS', 'SHIFT_JIS'
55 extend(PDF_Japanese)
55 extend(PDF_Japanese)
56 AddSJISFont()
56 AddSJISFont()
57 @font_for_content = 'SJIS'
57 @font_for_content = 'SJIS'
58 @font_for_footer = 'SJIS'
58 @font_for_footer = 'SJIS'
59 when 'GB18030'
59 when 'GB18030'
60 extend(PDF_Chinese)
60 extend(PDF_Chinese)
61 AddGBFont()
61 AddGBFont()
62 @font_for_content = 'GB'
62 @font_for_content = 'GB'
63 @font_for_footer = 'GB'
63 @font_for_footer = 'GB'
64 when 'BIG5'
64 when 'BIG5'
65 extend(PDF_Chinese)
65 extend(PDF_Chinese)
66 AddBig5Font()
66 AddBig5Font()
67 @font_for_content = 'Big5'
67 @font_for_content = 'Big5'
68 @font_for_footer = 'Big5'
68 @font_for_footer = 'Big5'
69 else
69 else
70 @font_for_content = 'Arial'
70 @font_for_content = 'Arial'
71 @font_for_footer = 'Helvetica'
71 @font_for_footer = 'Helvetica'
72 end
72 end
73 end
73 end
74 SetCreator(Redmine::Info.app_name)
74 SetCreator(Redmine::Info.app_name)
75 SetFont(@font_for_content)
75 SetFont(@font_for_content)
76 end
76 end
77
77
78 def SetFontStyle(style, size)
78 def SetFontStyle(style, size)
79 SetFont(@font_for_content, style, size)
79 SetFont(@font_for_content, style, size)
80 end
80 end
81
81
82 def SetTitle(txt)
82 def SetTitle(txt)
83 txt = begin
83 txt = begin
84 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
84 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
85 hextxt = "<FEFF" # FEFF is BOM
85 hextxt = "<FEFF" # FEFF is BOM
86 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
86 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
87 hextxt << ">"
87 hextxt << ">"
88 rescue
88 rescue
89 txt
89 txt
90 end || ''
90 end || ''
91 super(txt)
91 super(txt)
92 end
92 end
93
93
94 def textstring(s)
94 def textstring(s)
95 # Format a text string
95 # Format a text string
96 if s =~ /^</ # This means the string is hex-dumped.
96 if s =~ /^</ # This means the string is hex-dumped.
97 return s
97 return s
98 else
98 else
99 return '('+escape(s)+')'
99 return '('+escape(s)+')'
100 end
100 end
101 end
101 end
102
102
103 def fix_text_encoding(txt)
103 def fix_text_encoding(txt)
104 RDMPdfEncoding::rdm_from_utf8(txt, l(:general_pdf_encoding))
104 RDMPdfEncoding::rdm_from_utf8(txt, l(:general_pdf_encoding))
105 end
105 end
106
106
107 def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='')
107 def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='')
108 Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link)
108 Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link)
109 end
109 end
110
110
111 def RDMMultiCell(w, h=0, txt='', border=0, align='', fill=0, ln=1)
111 def RDMMultiCell(w, h=0, txt='', border=0, align='', fill=0, ln=1)
112 MultiCell(w, h, fix_text_encoding(txt), border, align, fill, ln)
112 MultiCell(w, h, fix_text_encoding(txt), border, align, fill, ln)
113 end
113 end
114
114
115 def RDMwriteHTMLCell(w, h, x, y, html='', border=0, ln=1, fill=0)
115 def RDMwriteHTMLCell(w, h, x, y, html='', border=0, ln=1, fill=0)
116 writeHTMLCell(w, h, x, y, fix_text_encoding(html), border, ln, fill)
116 writeHTMLCell(w, h, x, y, fix_text_encoding(html), border, ln, fill)
117 end
117 end
118
118
119 def Footer
119 def Footer
120 SetFont(@font_for_footer, 'I', 8)
120 SetFont(@font_for_footer, 'I', 8)
121 SetY(-15)
121 SetY(-15)
122 SetX(15)
122 SetX(15)
123 RDMCell(0, 5, @footer_date, 0, 0, 'L')
123 RDMCell(0, 5, @footer_date, 0, 0, 'L')
124 SetY(-15)
124 SetY(-15)
125 SetX(-30)
125 SetX(-30)
126 RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
126 RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
127 end
127 end
128 end
128 end
129
129
130 # Returns a PDF string of a list of issues
130 # Returns a PDF string of a list of issues
131 def issues_to_pdf(issues, project, query)
131 def issues_to_pdf(issues, project, query)
132 pdf = ITCPDF.new(current_language)
132 pdf = ITCPDF.new(current_language)
133 title = query.new_record? ? l(:label_issue_plural) : query.name
133 title = query.new_record? ? l(:label_issue_plural) : query.name
134 title = "#{project} - #{title}" if project
134 title = "#{project} - #{title}" if project
135 pdf.SetTitle(title)
135 pdf.SetTitle(title)
136 pdf.alias_nb_pages
136 pdf.alias_nb_pages
137 pdf.footer_date = format_date(Date.today)
137 pdf.footer_date = format_date(Date.today)
138 pdf.SetAutoPageBreak(false)
138 pdf.SetAutoPageBreak(false)
139 pdf.AddPage("L")
139 pdf.AddPage("L")
140
140
141 # Landscape A4 = 210 x 297 mm
141 # Landscape A4 = 210 x 297 mm
142 page_height = 210
142 page_height = 210
143 page_width = 297
143 page_width = 297
144 right_margin = 10
144 right_margin = 10
145 bottom_margin = 20
145 bottom_margin = 20
146 col_id_width = 10
146 col_id_width = 10
147 row_height = 5
147 row_height = 5
148
148
149 # column widths
149 # column widths
150 table_width = page_width - right_margin - 10 # fixed left margin
150 table_width = page_width - right_margin - 10 # fixed left margin
151 col_width = []
151 col_width = []
152 unless query.columns.empty?
152 unless query.columns.empty?
153 col_width = query.columns.collect do |c|
153 col_width = query.columns.collect do |c|
154 (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) &&
154 (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) &&
155 ['string', 'text'].include?(c.custom_field.field_format))) ? 4.0 : 1.0
155 ['string', 'text'].include?(c.custom_field.field_format))) ? 4.0 : 1.0
156 end
156 end
157 ratio = (table_width - col_id_width) / col_width.inject(0) {|s,w| s += w}
157 ratio = (table_width - col_id_width) / col_width.inject(0) {|s,w| s += w}
158 col_width = col_width.collect {|w| w * ratio}
158 col_width = col_width.collect {|w| w * ratio}
159 end
159 end
160
160
161 # title
161 # title
162 pdf.SetFontStyle('B',11)
162 pdf.SetFontStyle('B',11)
163 pdf.RDMCell(190,10, title)
163 pdf.RDMCell(190,10, title)
164 pdf.Ln
164 pdf.Ln
165
165
166 # headers
166 # headers
167 pdf.SetFontStyle('B',8)
167 pdf.SetFontStyle('B',8)
168 pdf.SetFillColor(230, 230, 230)
168 pdf.SetFillColor(230, 230, 230)
169
169
170 # render it background to find the max height used
170 # render it background to find the max height used
171 base_x = pdf.GetX
171 base_x = pdf.GetX
172 base_y = pdf.GetY
172 base_y = pdf.GetY
173 max_height = issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
173 max_height = issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
174 pdf.Rect(base_x, base_y, table_width, max_height, 'FD');
174 pdf.Rect(base_x, base_y, table_width, max_height, 'FD');
175 pdf.SetXY(base_x, base_y);
175 pdf.SetXY(base_x, base_y);
176
176
177 # write the cells on page
177 # write the cells on page
178 pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1)
178 pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1)
179 issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
179 issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
180 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
180 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
181 pdf.SetY(base_y + max_height);
181 pdf.SetY(base_y + max_height);
182
182
183 # rows
183 # rows
184 pdf.SetFontStyle('',8)
184 pdf.SetFontStyle('',8)
185 pdf.SetFillColor(255, 255, 255)
185 pdf.SetFillColor(255, 255, 255)
186 previous_group = false
186 previous_group = false
187 issue_list(issues) do |issue, level|
187 issue_list(issues) do |issue, level|
188 if query.grouped? &&
188 if query.grouped? &&
189 (group = query.group_by_column.value(issue)) != previous_group
189 (group = query.group_by_column.value(issue)) != previous_group
190 pdf.SetFontStyle('B',9)
190 pdf.SetFontStyle('B',9)
191 pdf.RDMCell(277, row_height,
191 pdf.RDMCell(277, row_height,
192 (group.blank? ? 'None' : group.to_s) + " (#{query.issue_count_by_group[group]})",
192 (group.blank? ? 'None' : group.to_s) + " (#{query.issue_count_by_group[group]})",
193 1, 1, 'L')
193 1, 1, 'L')
194 pdf.SetFontStyle('',8)
194 pdf.SetFontStyle('',8)
195 previous_group = group
195 previous_group = group
196 end
196 end
197 # fetch all the row values
197 # fetch all the row values
198 col_values = query.columns.collect do |column|
198 col_values = query.columns.collect do |column|
199 s = if column.is_a?(QueryCustomFieldColumn)
199 s = if column.is_a?(QueryCustomFieldColumn)
200 cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
200 cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
201 show_value(cv)
201 show_value(cv)
202 else
202 else
203 value = issue.send(column.name)
203 value = issue.send(column.name)
204 if column.name == :subject
204 if column.name == :subject
205 value = " " * level + value
205 value = " " * level + value
206 end
206 end
207 if value.is_a?(Date)
207 if value.is_a?(Date)
208 format_date(value)
208 format_date(value)
209 elsif value.is_a?(Time)
209 elsif value.is_a?(Time)
210 format_time(value)
210 format_time(value)
211 else
211 else
212 value
212 value
213 end
213 end
214 end
214 end
215 s.to_s
215 s.to_s
216 end
216 end
217
217
218 # render it off-page to find the max height used
218 # render it off-page to find the max height used
219 base_x = pdf.GetX
219 base_x = pdf.GetX
220 base_y = pdf.GetY
220 base_y = pdf.GetY
221 pdf.SetY(2 * page_height)
221 pdf.SetY(2 * page_height)
222 max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
222 max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
223 pdf.SetXY(base_x, base_y)
223 pdf.SetXY(base_x, base_y)
224
224
225 # make new page if it doesn't fit on the current one
225 # make new page if it doesn't fit on the current one
226 space_left = page_height - base_y - bottom_margin
226 space_left = page_height - base_y - bottom_margin
227 if max_height > space_left
227 if max_height > space_left
228 pdf.AddPage("L")
228 pdf.AddPage("L")
229 base_x = pdf.GetX
229 base_x = pdf.GetX
230 base_y = pdf.GetY
230 base_y = pdf.GetY
231 end
231 end
232
232
233 # write the cells on page
233 # write the cells on page
234 pdf.RDMCell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1)
234 pdf.RDMCell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1)
235 issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
235 issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
236 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
236 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
237 pdf.SetY(base_y + max_height);
237 pdf.SetY(base_y + max_height);
238 end
238 end
239
239
240 if issues.size == Setting.issues_export_limit.to_i
240 if issues.size == Setting.issues_export_limit.to_i
241 pdf.SetFontStyle('B',10)
241 pdf.SetFontStyle('B',10)
242 pdf.RDMCell(0, row_height, '...')
242 pdf.RDMCell(0, row_height, '...')
243 end
243 end
244 pdf.Output
244 pdf.Output
245 end
245 end
246
246
247 # Renders MultiCells and returns the maximum height used
247 # Renders MultiCells and returns the maximum height used
248 def issues_to_pdf_write_cells(pdf, col_values, col_widths,
248 def issues_to_pdf_write_cells(pdf, col_values, col_widths,
249 row_height, head=false)
249 row_height, head=false)
250 base_y = pdf.GetY
250 base_y = pdf.GetY
251 max_height = row_height
251 max_height = row_height
252 col_values.each_with_index do |column, i|
252 col_values.each_with_index do |column, i|
253 col_x = pdf.GetX
253 col_x = pdf.GetX
254 if head == true
254 if head == true
255 pdf.RDMMultiCell(col_widths[i], row_height, column.caption, "T", 'L', 1)
255 pdf.RDMMultiCell(col_widths[i], row_height, column.caption, "T", 'L', 1)
256 else
256 else
257 pdf.RDMMultiCell(col_widths[i], row_height, column, "T", 'L', 1)
257 pdf.RDMMultiCell(col_widths[i], row_height, column, "T", 'L', 1)
258 end
258 end
259 max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height
259 max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height
260 pdf.SetXY(col_x + col_widths[i], base_y);
260 pdf.SetXY(col_x + col_widths[i], base_y);
261 end
261 end
262 return max_height
262 return max_height
263 end
263 end
264
264
265 # Draw lines to close the row (MultiCell border drawing in not uniform)
265 # Draw lines to close the row (MultiCell border drawing in not uniform)
266 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y,
266 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y,
267 id_width, col_widths)
267 id_width, col_widths)
268 col_x = top_x + id_width
268 col_x = top_x + id_width
269 pdf.Line(col_x, top_y, col_x, lower_y) # id right border
269 pdf.Line(col_x, top_y, col_x, lower_y) # id right border
270 col_widths.each do |width|
270 col_widths.each do |width|
271 col_x += width
271 col_x += width
272 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border
272 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border
273 end
273 end
274 pdf.Line(top_x, top_y, top_x, lower_y) # left border
274 pdf.Line(top_x, top_y, top_x, lower_y) # left border
275 pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border
275 pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border
276 end
276 end
277
277
278 # Returns a PDF string of a single issue
278 # Returns a PDF string of a single issue
279 def issue_to_pdf(issue)
279 def issue_to_pdf(issue)
280 pdf = ITCPDF.new(current_language)
280 pdf = ITCPDF.new(current_language)
281 pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
281 pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
282 pdf.alias_nb_pages
282 pdf.alias_nb_pages
283 pdf.footer_date = format_date(Date.today)
283 pdf.footer_date = format_date(Date.today)
284 pdf.AddPage
284 pdf.AddPage
285 pdf.SetFontStyle('B',11)
285 pdf.SetFontStyle('B',11)
286 buf = "#{issue.project} - #{issue.tracker} # #{issue.id}"
286 buf = "#{issue.project} - #{issue.tracker} # #{issue.id}"
287 pdf.RDMMultiCell(190, 5, buf)
287 pdf.RDMMultiCell(190, 5, buf)
288 pdf.Ln
288 pdf.Ln
289 pdf.SetFontStyle('',8)
289 pdf.SetFontStyle('',8)
290 base_x = pdf.GetX
290 base_x = pdf.GetX
291 i = 1
291 i = 1
292 issue.ancestors.each do |ancestor|
292 issue.ancestors.each do |ancestor|
293 pdf.SetX(base_x + i)
293 pdf.SetX(base_x + i)
294 buf = "#{ancestor.tracker} # #{ancestor.id} (#{ancestor.status.to_s}): #{ancestor.subject}"
294 buf = "#{ancestor.tracker} # #{ancestor.id} (#{ancestor.status.to_s}): #{ancestor.subject}"
295 pdf.RDMMultiCell(190 - i, 5, buf)
295 pdf.RDMMultiCell(190 - i, 5, buf)
296 i += 1 if i < 35
296 i += 1 if i < 35
297 end
297 end
298 pdf.Ln
298 pdf.Ln
299
299
300 pdf.SetFontStyle('B',9)
300 pdf.SetFontStyle('B',9)
301 pdf.RDMCell(35,5, l(:field_status) + ":","LT")
301 pdf.RDMCell(35,5, l(:field_status) + ":","LT")
302 pdf.SetFontStyle('',9)
302 pdf.SetFontStyle('',9)
303 pdf.RDMCell(60,5, issue.status.to_s,"RT")
303 pdf.RDMCell(60,5, issue.status.to_s,"RT")
304 pdf.SetFontStyle('B',9)
304 pdf.SetFontStyle('B',9)
305 pdf.RDMCell(35,5, l(:field_priority) + ":","LT")
305 pdf.RDMCell(35,5, l(:field_priority) + ":","LT")
306 pdf.SetFontStyle('',9)
306 pdf.SetFontStyle('',9)
307 pdf.RDMCell(60,5, issue.priority.to_s,"RT")
307 pdf.RDMCell(60,5, issue.priority.to_s,"RT")
308 pdf.Ln
308 pdf.Ln
309
309
310 pdf.SetFontStyle('B',9)
310 pdf.SetFontStyle('B',9)
311 pdf.RDMCell(35,5, l(:field_author) + ":","L")
311 pdf.RDMCell(35,5, l(:field_author) + ":","L")
312 pdf.SetFontStyle('',9)
312 pdf.SetFontStyle('',9)
313 pdf.RDMCell(60,5, issue.author.to_s,"R")
313 pdf.RDMCell(60,5, issue.author.to_s,"R")
314 pdf.SetFontStyle('B',9)
314 pdf.SetFontStyle('B',9)
315 pdf.RDMCell(35,5, l(:field_category) + ":","L")
315 pdf.RDMCell(35,5, l(:field_category) + ":","L")
316 pdf.SetFontStyle('',9)
316 pdf.SetFontStyle('',9)
317 pdf.RDMCell(60,5, issue.category.to_s,"R")
317 pdf.RDMCell(60,5, issue.category.to_s,"R")
318 pdf.Ln
318 pdf.Ln
319
319
320 pdf.SetFontStyle('B',9)
320 pdf.SetFontStyle('B',9)
321 pdf.RDMCell(35,5, l(:field_created_on) + ":","L")
321 pdf.RDMCell(35,5, l(:field_created_on) + ":","L")
322 pdf.SetFontStyle('',9)
322 pdf.SetFontStyle('',9)
323 pdf.RDMCell(60,5, format_date(issue.created_on),"R")
323 pdf.RDMCell(60,5, format_date(issue.created_on),"R")
324 pdf.SetFontStyle('B',9)
324 pdf.SetFontStyle('B',9)
325 pdf.RDMCell(35,5, l(:field_assigned_to) + ":","L")
325 pdf.RDMCell(35,5, l(:field_assigned_to) + ":","L")
326 pdf.SetFontStyle('',9)
326 pdf.SetFontStyle('',9)
327 pdf.RDMCell(60,5, issue.assigned_to.to_s,"R")
327 pdf.RDMCell(60,5, issue.assigned_to.to_s,"R")
328 pdf.Ln
328 pdf.Ln
329
329
330 pdf.SetFontStyle('B',9)
330 pdf.SetFontStyle('B',9)
331 pdf.RDMCell(35,5, l(:field_updated_on) + ":","LB")
331 pdf.RDMCell(35,5, l(:field_updated_on) + ":","LB")
332 pdf.SetFontStyle('',9)
332 pdf.SetFontStyle('',9)
333 pdf.RDMCell(60,5, format_date(issue.updated_on),"RB")
333 pdf.RDMCell(60,5, format_date(issue.updated_on),"RB")
334 pdf.SetFontStyle('B',9)
334 pdf.SetFontStyle('B',9)
335 pdf.RDMCell(35,5, l(:field_due_date) + ":","LB")
335 pdf.RDMCell(35,5, l(:field_due_date) + ":","LB")
336 pdf.SetFontStyle('',9)
336 pdf.SetFontStyle('',9)
337 pdf.RDMCell(60,5, format_date(issue.due_date),"RB")
337 pdf.RDMCell(60,5, format_date(issue.due_date),"RB")
338 pdf.Ln
338 pdf.Ln
339
339
340 for custom_value in issue.custom_field_values
340 for custom_value in issue.custom_field_values
341 pdf.SetFontStyle('B',9)
341 pdf.SetFontStyle('B',9)
342 pdf.RDMCell(35,5, custom_value.custom_field.name + ":","L")
342 pdf.RDMCell(35,5, custom_value.custom_field.name + ":","L")
343 pdf.SetFontStyle('',9)
343 pdf.SetFontStyle('',9)
344 pdf.RDMMultiCell(155,5, (show_value custom_value),"R")
344 pdf.RDMMultiCell(155,5, (show_value custom_value),"R")
345 end
345 end
346
346
347 y0 = pdf.GetY
347 y0 = pdf.GetY
348
348
349 pdf.SetFontStyle('B',9)
349 pdf.SetFontStyle('B',9)
350 pdf.RDMCell(35,5, l(:field_subject) + ":","LT")
350 pdf.RDMCell(35,5, l(:field_subject) + ":","LT")
351 pdf.SetFontStyle('',9)
351 pdf.SetFontStyle('',9)
352 pdf.RDMMultiCell(155,5, issue.subject,"RT")
352 pdf.RDMMultiCell(155,5, issue.subject,"RT")
353 pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
353 pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
354
354
355 pdf.SetFontStyle('B',9)
355 pdf.SetFontStyle('B',9)
356 pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)
356 pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)
357 pdf.SetFontStyle('',9)
357 pdf.SetFontStyle('',9)
358
358
359 # Set resize image scale
359 # Set resize image scale
360 pdf.SetImageScale(1.6)
360 pdf.SetImageScale(1.6)
361 pdf.RDMwriteHTMLCell(35+155, 5, 0, 0,
361 pdf.RDMwriteHTMLCell(35+155, 5, 0, 0,
362 Redmine::WikiFormatting.to_html(
362 Redmine::WikiFormatting.to_html(
363 Setting.text_formatting, issue.description.to_s),"LRB")
363 Setting.text_formatting, issue.description.to_s),"LRB")
364
364
365 unless issue.leaf?
365 unless issue.leaf?
366 # for CJK
366 # for CJK
367 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 90 : 65 )
367 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 90 : 65 )
368
368
369 pdf.SetFontStyle('B',9)
369 pdf.SetFontStyle('B',9)
370 pdf.RDMCell(35+155,5, l(:label_subtask_plural) + ":", "LTR")
370 pdf.RDMCell(35+155,5, l(:label_subtask_plural) + ":", "LTR")
371 pdf.Ln
371 pdf.Ln
372 issue_list(issue.descendants.sort_by(&:lft)) do |child, level|
372 issue_list(issue.descendants.sort_by(&:lft)) do |child, level|
373 buf = truncate("#{child.tracker} # #{child.id}: #{child.subject}",
373 buf = truncate("#{child.tracker} # #{child.id}: #{child.subject}",
374 :length => truncate_length)
374 :length => truncate_length)
375 level = 10 if level >= 10
375 level = 10 if level >= 10
376 pdf.SetFontStyle('',8)
376 pdf.SetFontStyle('',8)
377 pdf.RDMCell(35+135,5, (level >=1 ? " " * level : "") + buf, "L")
377 pdf.RDMCell(35+135,5, (level >=1 ? " " * level : "") + buf, "L")
378 pdf.SetFontStyle('B',8)
378 pdf.SetFontStyle('B',8)
379 pdf.RDMCell(20,5, child.status.to_s, "R")
379 pdf.RDMCell(20,5, child.status.to_s, "R")
380 pdf.Ln
380 pdf.Ln
381 end
381 end
382 end
382 end
383
383
384 relations = issue.relations.select { |r| r.other_issue(issue).visible? }
384 relations = issue.relations.select { |r| r.other_issue(issue).visible? }
385 unless relations.empty?
385 unless relations.empty?
386 # for CJK
386 # for CJK
387 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 80 : 60 )
387 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 80 : 60 )
388
388
389 pdf.SetFontStyle('B',9)
389 pdf.SetFontStyle('B',9)
390 pdf.RDMCell(35+155,5, l(:label_related_issues) + ":", "LTR")
390 pdf.RDMCell(35+155,5, l(:label_related_issues) + ":", "LTR")
391 pdf.Ln
391 pdf.Ln
392 relations.each do |relation|
392 relations.each do |relation|
393 buf = ""
393 buf = ""
394 buf += "#{l(relation.label_for(issue))} "
394 buf += "#{l(relation.label_for(issue))} "
395 if relation.delay && relation.delay != 0
395 if relation.delay && relation.delay != 0
396 buf += "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)}) "
396 buf += "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)}) "
397 end
397 end
398 if Setting.cross_project_issue_relations?
398 if Setting.cross_project_issue_relations?
399 buf += "#{relation.other_issue(issue).project} - "
399 buf += "#{relation.other_issue(issue).project} - "
400 end
400 end
401 buf += "#{relation.other_issue(issue).tracker}" +
401 buf += "#{relation.other_issue(issue).tracker}" +
402 " # #{relation.other_issue(issue).id}: #{relation.other_issue(issue).subject}"
402 " # #{relation.other_issue(issue).id}: #{relation.other_issue(issue).subject}"
403 buf = truncate(buf, :length => truncate_length)
403 buf = truncate(buf, :length => truncate_length)
404 pdf.SetFontStyle('', 8)
404 pdf.SetFontStyle('', 8)
405 pdf.RDMCell(35+155-50,5, buf, "L")
405 pdf.RDMCell(35+155-50,5, buf, "L")
406 pdf.SetFontStyle('B',8)
406 pdf.SetFontStyle('B',8)
407 pdf.RDMCell(10,5, relation.other_issue(issue).status.to_s, "")
407 pdf.RDMCell(10,5, relation.other_issue(issue).status.to_s, "")
408 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).start_date), "")
408 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).start_date), "")
409 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).due_date), "R")
409 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).due_date), "R")
410 pdf.Ln
410 pdf.Ln
411 end
411 end
412 end
412 end
413 pdf.RDMCell(190,5, "", "T")
413 pdf.RDMCell(190,5, "", "T")
414 pdf.Ln
414 pdf.Ln
415
415
416 if issue.changesets.any? &&
416 if issue.changesets.any? &&
417 User.current.allowed_to?(:view_changesets, issue.project)
417 User.current.allowed_to?(:view_changesets, issue.project)
418 pdf.SetFontStyle('B',9)
418 pdf.SetFontStyle('B',9)
419 pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
419 pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
420 pdf.Ln
420 pdf.Ln
421 for changeset in issue.changesets
421 for changeset in issue.changesets
422 pdf.SetFontStyle('B',8)
422 pdf.SetFontStyle('B',8)
423 csstr = "#{l(:label_revision)} #{changeset.format_identifier} - "
423 csstr = "#{l(:label_revision)} #{changeset.format_identifier} - "
424 csstr += format_time(changeset.committed_on) + " - " + changeset.author.to_s
424 csstr += format_time(changeset.committed_on) + " - " + changeset.author.to_s
425 pdf.RDMCell(190, 5, csstr)
425 pdf.RDMCell(190, 5, csstr)
426 pdf.Ln
426 pdf.Ln
427 unless changeset.comments.blank?
427 unless changeset.comments.blank?
428 pdf.SetFontStyle('',8)
428 pdf.SetFontStyle('',8)
429 pdf.RDMwriteHTMLCell(190,5,0,0,
429 pdf.RDMwriteHTMLCell(190,5,0,0,
430 Redmine::WikiFormatting.to_html(
430 Redmine::WikiFormatting.to_html(
431 Setting.text_formatting, changeset.comments.to_s), "")
431 Setting.text_formatting, changeset.comments.to_s), "")
432 end
432 end
433 pdf.Ln
433 pdf.Ln
434 end
434 end
435 end
435 end
436
436
437 pdf.SetFontStyle('B',9)
437 pdf.SetFontStyle('B',9)
438 pdf.RDMCell(190,5, l(:label_history), "B")
438 pdf.RDMCell(190,5, l(:label_history), "B")
439 pdf.Ln
439 pdf.Ln
440 for journal in issue.journals.find(
440 for journal in issue.journals.find(
441 :all, :include => [:user, :details],
441 :all, :include => [:user, :details],
442 :order => "#{Journal.table_name}.created_on ASC")
442 :order => "#{Journal.table_name}.created_on ASC")
443 pdf.SetFontStyle('B',8)
443 pdf.SetFontStyle('B',8)
444 pdf.RDMCell(190,5,
444 pdf.RDMCell(190,5,
445 format_time(journal.created_on) + " - " + journal.user.name)
445 format_time(journal.created_on) + " - " + journal.user.name)
446 pdf.Ln
446 pdf.Ln
447 pdf.SetFontStyle('I',8)
447 pdf.SetFontStyle('I',8)
448 for detail in journal.details
448 for detail in journal.details
449 pdf.RDMMultiCell(190,5, "- " + show_detail(detail, true))
449 pdf.RDMMultiCell(190,5, "- " + show_detail(detail, true))
450 end
450 end
451 if journal.notes?
451 if journal.notes?
452 pdf.Ln unless journal.details.empty?
452 pdf.Ln unless journal.details.empty?
453 pdf.SetFontStyle('',8)
453 pdf.SetFontStyle('',8)
454 pdf.RDMwriteHTMLCell(190,5,0,0,
454 pdf.RDMwriteHTMLCell(190,5,0,0,
455 Redmine::WikiFormatting.to_html(
455 Redmine::WikiFormatting.to_html(
456 Setting.text_formatting, journal.notes.to_s), "")
456 Setting.text_formatting, journal.notes.to_s), "")
457 end
457 end
458 pdf.Ln
458 pdf.Ln
459 end
459 end
460
460
461 if issue.attachments.any?
461 if issue.attachments.any?
462 pdf.SetFontStyle('B',9)
462 pdf.SetFontStyle('B',9)
463 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
463 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
464 pdf.Ln
464 pdf.Ln
465 for attachment in issue.attachments
465 for attachment in issue.attachments
466 pdf.SetFontStyle('',8)
466 pdf.SetFontStyle('',8)
467 pdf.RDMCell(80,5, attachment.filename)
467 pdf.RDMCell(80,5, attachment.filename)
468 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
468 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
469 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
469 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
470 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
470 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
471 pdf.Ln
471 pdf.Ln
472 end
472 end
473 end
473 end
474 pdf.Output
474 pdf.Output
475 end
475 end
476
476
477 # Returns a PDF string of a single wiki page
477 # Returns a PDF string of a single wiki page
478 def wiki_to_pdf(page, project)
478 def wiki_to_pdf(page, project)
479 pdf = ITCPDF.new(current_language)
479 pdf = ITCPDF.new(current_language)
480 pdf.SetTitle("#{project} - #{page.title}")
480 pdf.SetTitle("#{project} - #{page.title}")
481 pdf.alias_nb_pages
481 pdf.alias_nb_pages
482 pdf.footer_date = format_date(Date.today)
482 pdf.footer_date = format_date(Date.today)
483 pdf.AddPage
483 pdf.AddPage
484 pdf.SetFontStyle('B',11)
484 pdf.SetFontStyle('B',11)
485 pdf.RDMMultiCell(190,5,
485 pdf.RDMMultiCell(190,5,
486 "#{project} - #{page.title} - # #{page.content.version}")
486 "#{project} - #{page.title} - # #{page.content.version}")
487 pdf.Ln
487 pdf.Ln
488 # Set resize image scale
488 # Set resize image scale
489 pdf.SetImageScale(1.6)
489 pdf.SetImageScale(1.6)
490 pdf.SetFontStyle('',9)
490 pdf.SetFontStyle('',9)
491 pdf.RDMwriteHTMLCell(190,5,0,0,
491 pdf.RDMwriteHTMLCell(190,5,0,0,
492 Redmine::WikiFormatting.to_html(
492 Redmine::WikiFormatting.to_html(
493 Setting.text_formatting, page.content.text.to_s), "TLRB")
493 Setting.text_formatting, page.content.text.to_s), "TLRB")
494 if page.attachments.any?
494 if page.attachments.any?
495 pdf.Ln
495 pdf.Ln
496 pdf.SetFontStyle('B',9)
496 pdf.SetFontStyle('B',9)
497 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
497 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
498 pdf.Ln
498 pdf.Ln
499 for attachment in page.attachments
499 for attachment in page.attachments
500 pdf.SetFontStyle('',8)
500 pdf.SetFontStyle('',8)
501 pdf.RDMCell(80,5, attachment.filename)
501 pdf.RDMCell(80,5, attachment.filename)
502 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
502 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
503 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
503 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
504 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
504 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
505 pdf.Ln
505 pdf.Ln
506 end
506 end
507 end
507 end
508 pdf.Output
508 pdf.Output
509 end
509 end
510
510
511 class RDMPdfEncoding
511 class RDMPdfEncoding
512 def self.rdm_from_utf8(txt, encoding)
512 def self.rdm_from_utf8(txt, encoding)
513 txt ||= ''
513 txt ||= ''
514 txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
514 txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
515 if txt.respond_to?(:force_encoding)
515 if txt.respond_to?(:force_encoding)
516 txt.force_encoding('ASCII-8BIT')
516 txt.force_encoding('ASCII-8BIT')
517 end
517 end
518 txt
518 txt
519 end
519 end
520
521 def self.attach(attachments, filename, encoding)
522 filename_utf8 = Redmine::CodesetUtil.to_utf8(filename, encoding)
523 atta = nil
524 if filename_utf8 =~ /^[^\/"]+\.(gif|jpg|jpe|jpeg|png)$/i
525 atta = Attachment.latest_attach(attachments, filename_utf8)
526 end
527 if atta && atta.readable? && atta.visible?
528 return atta
529 else
530 return nil
531 end
532 end
520 end
533 end
521 end
534 end
522 end
535 end
523 end
536 end
@@ -1,210 +1,236
1 ---
1 ---
2 attachments_001:
2 attachments_001:
3 created_on: 2006-07-19 21:07:27 +02:00
3 created_on: 2006-07-19 21:07:27 +02:00
4 downloads: 0
4 downloads: 0
5 content_type: text/plain
5 content_type: text/plain
6 disk_filename: 060719210727_error281.txt
6 disk_filename: 060719210727_error281.txt
7 container_id: 3
7 container_id: 3
8 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
8 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
9 id: 1
9 id: 1
10 container_type: Issue
10 container_type: Issue
11 filesize: 28
11 filesize: 28
12 filename: error281.txt
12 filename: error281.txt
13 author_id: 2
13 author_id: 2
14 attachments_002:
14 attachments_002:
15 created_on: 2007-01-27 15:08:27 +01:00
15 created_on: 2007-01-27 15:08:27 +01:00
16 downloads: 0
16 downloads: 0
17 content_type: text/plain
17 content_type: text/plain
18 disk_filename: 060719210727_document.txt
18 disk_filename: 060719210727_document.txt
19 container_id: 1
19 container_id: 1
20 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
20 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
21 id: 2
21 id: 2
22 container_type: Document
22 container_type: Document
23 filesize: 28
23 filesize: 28
24 filename: document.txt
24 filename: document.txt
25 author_id: 2
25 author_id: 2
26 attachments_003:
26 attachments_003:
27 created_on: 2006-07-19 21:07:27 +02:00
27 created_on: 2006-07-19 21:07:27 +02:00
28 downloads: 0
28 downloads: 0
29 content_type: image/gif
29 content_type: image/gif
30 disk_filename: 060719210727_logo.gif
30 disk_filename: 060719210727_logo.gif
31 container_id: 4
31 container_id: 4
32 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
32 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
33 id: 3
33 id: 3
34 container_type: WikiPage
34 container_type: WikiPage
35 filesize: 280
35 filesize: 280
36 filename: logo.gif
36 filename: logo.gif
37 description: This is a logo
37 description: This is a logo
38 author_id: 2
38 author_id: 2
39 attachments_004:
39 attachments_004:
40 created_on: 2006-07-19 21:07:27 +02:00
40 created_on: 2006-07-19 21:07:27 +02:00
41 container_type: Issue
41 container_type: Issue
42 container_id: 3
42 container_id: 3
43 downloads: 0
43 downloads: 0
44 disk_filename: 060719210727_source.rb
44 disk_filename: 060719210727_source.rb
45 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
45 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
46 id: 4
46 id: 4
47 filesize: 153
47 filesize: 153
48 filename: source.rb
48 filename: source.rb
49 author_id: 2
49 author_id: 2
50 description: This is a Ruby source file
50 description: This is a Ruby source file
51 content_type: application/x-ruby
51 content_type: application/x-ruby
52 attachments_005:
52 attachments_005:
53 created_on: 2006-07-19 21:07:27 +02:00
53 created_on: 2006-07-19 21:07:27 +02:00
54 container_type: Issue
54 container_type: Issue
55 container_id: 3
55 container_id: 3
56 downloads: 0
56 downloads: 0
57 disk_filename: 060719210727_changeset_iso8859-1.diff
57 disk_filename: 060719210727_changeset_iso8859-1.diff
58 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
58 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
59 id: 5
59 id: 5
60 filesize: 687
60 filesize: 687
61 filename: changeset_iso8859-1.diff
61 filename: changeset_iso8859-1.diff
62 author_id: 2
62 author_id: 2
63 content_type: text/x-diff
63 content_type: text/x-diff
64 attachments_006:
64 attachments_006:
65 created_on: 2006-07-19 21:07:27 +02:00
65 created_on: 2006-07-19 21:07:27 +02:00
66 container_type: Issue
66 container_type: Issue
67 container_id: 3
67 container_id: 3
68 downloads: 0
68 downloads: 0
69 disk_filename: 060719210727_archive.zip
69 disk_filename: 060719210727_archive.zip
70 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
70 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
71 id: 6
71 id: 6
72 filesize: 157
72 filesize: 157
73 filename: archive.zip
73 filename: archive.zip
74 author_id: 2
74 author_id: 2
75 content_type: application/octet-stream
75 content_type: application/octet-stream
76 attachments_007:
76 attachments_007:
77 created_on: 2006-07-19 21:07:27 +02:00
77 created_on: 2006-07-19 21:07:27 +02:00
78 container_type: Issue
78 container_type: Issue
79 container_id: 4
79 container_id: 4
80 downloads: 0
80 downloads: 0
81 disk_filename: 060719210727_archive.zip
81 disk_filename: 060719210727_archive.zip
82 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
82 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
83 id: 7
83 id: 7
84 filesize: 157
84 filesize: 157
85 filename: archive.zip
85 filename: archive.zip
86 author_id: 1
86 author_id: 1
87 content_type: application/octet-stream
87 content_type: application/octet-stream
88 attachments_008:
88 attachments_008:
89 created_on: 2006-07-19 21:07:27 +02:00
89 created_on: 2006-07-19 21:07:27 +02:00
90 container_type: Project
90 container_type: Project
91 container_id: 1
91 container_id: 1
92 downloads: 0
92 downloads: 0
93 disk_filename: 060719210727_project_file.zip
93 disk_filename: 060719210727_project_file.zip
94 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
94 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
95 id: 8
95 id: 8
96 filesize: 320
96 filesize: 320
97 filename: project_file.zip
97 filename: project_file.zip
98 author_id: 2
98 author_id: 2
99 content_type: application/octet-stream
99 content_type: application/octet-stream
100 attachments_009:
100 attachments_009:
101 created_on: 2006-07-19 21:07:27 +02:00
101 created_on: 2006-07-19 21:07:27 +02:00
102 container_type: Version
102 container_type: Version
103 container_id: 1
103 container_id: 1
104 downloads: 0
104 downloads: 0
105 disk_filename: 060719210727_version_file.zip
105 disk_filename: 060719210727_version_file.zip
106 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
106 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
107 id: 9
107 id: 9
108 filesize: 452
108 filesize: 452
109 filename: version_file.zip
109 filename: version_file.zip
110 author_id: 2
110 author_id: 2
111 content_type: application/octet-stream
111 content_type: application/octet-stream
112 attachments_010:
112 attachments_010:
113 created_on: 2006-07-19 21:07:27 +02:00
113 created_on: 2006-07-19 21:07:27 +02:00
114 container_type: Issue
114 container_type: Issue
115 container_id: 2
115 container_id: 2
116 downloads: 0
116 downloads: 0
117 disk_filename: 060719210727_picture.jpg
117 disk_filename: 060719210727_picture.jpg
118 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
118 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
119 id: 10
119 id: 10
120 filesize: 452
120 filesize: 452
121 filename: picture.jpg
121 filename: picture.jpg
122 author_id: 2
122 author_id: 2
123 content_type: image/jpeg
123 content_type: image/jpeg
124 attachments_011:
124 attachments_011:
125 created_on: 2007-02-12 15:08:27 +01:00
125 created_on: 2007-02-12 15:08:27 +01:00
126 container_type: Document
126 container_type: Document
127 container_id: 1
127 container_id: 1
128 downloads: 0
128 downloads: 0
129 disk_filename: 060719210727_picture.jpg
129 disk_filename: 060719210727_picture.jpg
130 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
130 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
131 id: 11
131 id: 11
132 filesize: 452
132 filesize: 452
133 filename: picture.jpg
133 filename: picture.jpg
134 author_id: 2
134 author_id: 2
135 content_type: image/jpeg
135 content_type: image/jpeg
136 attachments_012:
136 attachments_012:
137 created_on: 2006-07-19 21:07:27 +02:00
137 created_on: 2006-07-19 21:07:27 +02:00
138 container_type: Version
138 container_type: Version
139 container_id: 1
139 container_id: 1
140 downloads: 0
140 downloads: 0
141 disk_filename: 060719210727_version_file.zip
141 disk_filename: 060719210727_version_file.zip
142 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
142 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
143 id: 12
143 id: 12
144 filesize: 452
144 filesize: 452
145 filename: version_file.zip
145 filename: version_file.zip
146 author_id: 2
146 author_id: 2
147 content_type: application/octet-stream
147 content_type: application/octet-stream
148 attachments_013:
148 attachments_013:
149 created_on: 2006-07-19 21:07:27 +02:00
149 created_on: 2006-07-19 21:07:27 +02:00
150 container_type: Message
150 container_type: Message
151 container_id: 1
151 container_id: 1
152 downloads: 0
152 downloads: 0
153 disk_filename: 060719210727_foo.zip
153 disk_filename: 060719210727_foo.zip
154 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
154 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
155 id: 13
155 id: 13
156 filesize: 452
156 filesize: 452
157 filename: foo.zip
157 filename: foo.zip
158 author_id: 2
158 author_id: 2
159 content_type: application/octet-stream
159 content_type: application/octet-stream
160 attachments_014:
160 attachments_014:
161 created_on: 2006-07-19 21:07:27 +02:00
161 created_on: 2006-07-19 21:07:27 +02:00
162 container_type: Issue
162 container_type: Issue
163 container_id: 3
163 container_id: 3
164 downloads: 0
164 downloads: 0
165 disk_filename: 060719210727_changeset_utf8.diff
165 disk_filename: 060719210727_changeset_utf8.diff
166 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
166 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
167 id: 14
167 id: 14
168 filesize: 687
168 filesize: 687
169 filename: changeset_utf8.diff
169 filename: changeset_utf8.diff
170 author_id: 2
170 author_id: 2
171 content_type: text/x-diff
171 content_type: text/x-diff
172 attachments_015:
172 attachments_015:
173 id: 15
173 id: 15
174 created_on: 2010-07-19 21:07:27 +02:00
174 created_on: 2010-07-19 21:07:27 +02:00
175 container_type: Issue
175 container_type: Issue
176 container_id: 14
176 container_id: 14
177 downloads: 0
177 downloads: 0
178 disk_filename: 060719210727_changeset_utf8.diff
178 disk_filename: 060719210727_changeset_utf8.diff
179 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
179 digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
180 filesize: 687
180 filesize: 687
181 filename: private.diff
181 filename: private.diff
182 author_id: 2
182 author_id: 2
183 content_type: text/x-diff
183 content_type: text/x-diff
184 description: attachement of a private issue
184 description: attachement of a private issue
185 attachments_016:
185 attachments_016:
186 content_type: image/png
186 content_type: image/png
187 downloads: 0
187 downloads: 0
188 created_on: 2010-11-23 16:14:50 +09:00
188 created_on: 2010-11-23 16:14:50 +09:00
189 disk_filename: 101123161450_testfile_1.png
189 disk_filename: 101123161450_testfile_1.png
190 container_id: 14
190 container_id: 14
191 digest: 8e0294de2441577c529f170b6fb8f638
191 digest: 8e0294de2441577c529f170b6fb8f638
192 id: 16
192 id: 16
193 container_type: Issue
193 container_type: Issue
194 description: ""
194 description: ""
195 filename: testfile.png
195 filename: testfile.png
196 filesize: 2654
196 filesize: 2654
197 author_id: 2
197 author_id: 2
198 attachments_017:
198 attachments_017:
199 content_type: image/png
199 content_type: image/png
200 downloads: 0
200 downloads: 0
201 created_on: 2010-12-23 16:14:50 +09:00
201 created_on: 2010-12-23 16:14:50 +09:00
202 disk_filename: 101223161450_testfile_2.png
202 disk_filename: 101223161450_testfile_2.png
203 container_id: 14
203 container_id: 14
204 digest: 6bc2963e8d7ea0d3e68d12d1fba3d6ca
204 digest: 6bc2963e8d7ea0d3e68d12d1fba3d6ca
205 id: 17
205 id: 17
206 container_type: Issue
206 container_type: Issue
207 description: ""
207 description: ""
208 filename: testfile.PNG
208 filename: testfile.PNG
209 filesize: 3582
209 filesize: 3582
210 author_id: 2
210 author_id: 2
211 attachments_018:
212 content_type: image/png
213 downloads: 0
214 created_on: 2011-01-23 16:14:50 +09:00
215 disk_filename: 101123161450_testfile_1.png
216 container_id: 14
217 digest: 8e0294de2441577c529f170b6fb8f638
218 id: 18
219 container_type: Issue
220 description: ""
221 filename: testγƒ†γ‚Ήγƒˆ.png
222 filesize: 2654
223 author_id: 2
224 attachments_019:
225 content_type: image/png
226 downloads: 0
227 created_on: 2011-02-23 16:14:50 +09:00
228 disk_filename: 101223161450_testfile_2.png
229 container_id: 14
230 digest: 6bc2963e8d7ea0d3e68d12d1fba3d6ca
231 id: 19
232 container_type: Issue
233 description: ""
234 filename: Testγƒ†γ‚Ήγƒˆ.PNG
235 filesize: 3582
236 author_id: 2
@@ -1,90 +1,127
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 fixtures :users, :projects, :roles, :members, :member_roles,
23 :enabled_modules, :issues, :trackers, :attachments
22
24
23 def test_fix_text_encoding_nil
25 def test_fix_text_encoding_nil
24 assert_equal '', Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(nil, "UTF-8")
26 assert_equal '', Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(nil, "UTF-8")
25 assert_equal '', Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(nil, "ISO-8859-1")
27 assert_equal '', Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(nil, "ISO-8859-1")
26 end
28 end
27
29
28 def test_rdm_pdf_iconv_cannot_convert_ja_cp932
30 def test_rdm_pdf_iconv_cannot_convert_ja_cp932
29 encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
31 encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
30 utf8_txt_1 = "\xe7\x8b\x80\xe6\x85\x8b"
32 utf8_txt_1 = "\xe7\x8b\x80\xe6\x85\x8b"
31 utf8_txt_2 = "\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
33 utf8_txt_2 = "\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
32 utf8_txt_3 = "\xe7\x8b\x80\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
34 utf8_txt_3 = "\xe7\x8b\x80\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
33 if utf8_txt_1.respond_to?(:force_encoding)
35 if utf8_txt_1.respond_to?(:force_encoding)
34 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_1, encoding)
36 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_1, encoding)
35 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_2, encoding)
37 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_2, encoding)
36 txt_3 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_3, encoding)
38 txt_3 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_3, encoding)
37 assert_equal "?\x91\xd4", txt_1
39 assert_equal "?\x91\xd4", txt_1
38 assert_equal "?\x91\xd4?", txt_2
40 assert_equal "?\x91\xd4?", txt_2
39 assert_equal "??\x91\xd4?", txt_3
41 assert_equal "??\x91\xd4?", txt_3
40 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
42 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
41 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
43 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
42 assert_equal "ASCII-8BIT", txt_3.encoding.to_s
44 assert_equal "ASCII-8BIT", txt_3.encoding.to_s
43 elsif RUBY_PLATFORM == 'java'
45 elsif RUBY_PLATFORM == 'java'
44 assert_equal "??",
46 assert_equal "??",
45 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_1, encoding)
47 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_1, encoding)
46 assert_equal "???",
48 assert_equal "???",
47 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_2, encoding)
49 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_2, encoding)
48 assert_equal "????",
50 assert_equal "????",
49 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_3, encoding)
51 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_3, encoding)
50 else
52 else
51 assert_equal "???\x91\xd4",
53 assert_equal "???\x91\xd4",
52 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_1, encoding)
54 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_1, encoding)
53 assert_equal "???\x91\xd4???",
55 assert_equal "???\x91\xd4???",
54 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_2, encoding)
56 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_2, encoding)
55 assert_equal "??????\x91\xd4???",
57 assert_equal "??????\x91\xd4???",
56 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_3, encoding)
58 Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_3, encoding)
57 end
59 end
58 end
60 end
59
61
60 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_en
62 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_en
61 str1 = "Texte encod\xe9 en ISO-8859-1"
63 str1 = "Texte encod\xe9 en ISO-8859-1"
62 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
64 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
63 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
65 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
64 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
66 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
65 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, 'UTF-8')
67 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, 'UTF-8')
66 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, 'UTF-8')
68 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, 'UTF-8')
67 if txt_1.respond_to?(:force_encoding)
69 if txt_1.respond_to?(:force_encoding)
68 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
70 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
69 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
71 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
70 end
72 end
71 assert_equal "Texte encod? en ISO-8859-1", txt_1
73 assert_equal "Texte encod? en ISO-8859-1", txt_1
72 assert_equal "?a?b?c?d?e test", txt_2
74 assert_equal "?a?b?c?d?e test", txt_2
73 end
75 end
74
76
75 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_ja
77 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_ja
76 str1 = "Texte encod\xe9 en ISO-8859-1"
78 str1 = "Texte encod\xe9 en ISO-8859-1"
77 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
79 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
78 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
80 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
79 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
81 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
80 encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
82 encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
81 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, encoding)
83 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, encoding)
82 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, encoding)
84 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, encoding)
83 if txt_1.respond_to?(:force_encoding)
85 if txt_1.respond_to?(:force_encoding)
84 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
86 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
85 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
87 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
86 end
88 end
87 assert_equal "Texte encod? en ISO-8859-1", txt_1
89 assert_equal "Texte encod? en ISO-8859-1", txt_1
88 assert_equal "?a?b?c?d?e test", txt_2
90 assert_equal "?a?b?c?d?e test", txt_2
89 end
91 end
92
93 def test_attach
94 Attachment.storage_path = "#{Rails.root}/test/fixtures/files"
95
96 str2 = "\x83e\x83X\x83g"
97 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
98 encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
99
100 a1 = Attachment.find(17)
101 a2 = Attachment.find(19)
102
103 User.current = User.find(1)
104 assert a1.readable?
105 assert a1.visible?
106 assert a2.readable?
107 assert a2.visible?
108
109 aa1 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "Testfile.PNG", "UTF-8")
110 assert_equal 17, aa1.id
111 aa2 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "test#{str2}.png", encoding)
112 assert_equal 19, aa2.id
113
114 User.current = nil
115 assert a1.readable?
116 assert (! a1.visible?)
117 assert a2.readable?
118 assert (! a2.visible?)
119
120 aa1 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "Testfile.PNG", "UTF-8")
121 assert_equal nil, aa1
122 aa2 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "test#{str2}.png", encoding)
123 assert_equal nil, aa2
124
125 set_tmp_attachments_directory
126 end
90 end
127 end
General Comments 0
You need to be logged in to leave comments. Login now