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