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