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