##// END OF EJS Templates
Use selected columns in the issues PDF export (#1190)....
Jean-Philippe Lang -
r2736:a49506ce5fa9
parent child
Show More
@@ -1,474 +1,485
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'iconv'
19 19 require 'rfpdf/fpdf'
20 20 require 'rfpdf/chinese'
21 21
22 22 module Redmine
23 23 module Export
24 24 module PDF
25 25 include ActionView::Helpers::TextHelper
26 26 include ActionView::Helpers::NumberHelper
27 27
28 28 class IFPDF < FPDF
29 29 include Redmine::I18n
30 30 attr_accessor :footer_date
31 31
32 32 def initialize(lang)
33 33 super()
34 34 set_language_if_valid lang
35 35 case current_language.to_s.downcase
36 36 when 'ja'
37 37 extend(PDF_Japanese)
38 38 AddSJISFont()
39 39 @font_for_content = 'SJIS'
40 40 @font_for_footer = 'SJIS'
41 41 when 'zh'
42 42 extend(PDF_Chinese)
43 43 AddGBFont()
44 44 @font_for_content = 'GB'
45 45 @font_for_footer = 'GB'
46 46 when 'zh-tw'
47 47 extend(PDF_Chinese)
48 48 AddBig5Font()
49 49 @font_for_content = 'Big5'
50 50 @font_for_footer = 'Big5'
51 51 else
52 52 @font_for_content = 'Arial'
53 53 @font_for_footer = 'Helvetica'
54 54 end
55 55 SetCreator(Redmine::Info.app_name)
56 56 SetFont(@font_for_content)
57 57 end
58 58
59 59 def SetFontStyle(style, size)
60 60 SetFont(@font_for_content, style, size)
61 61 end
62 62
63 63 def SetTitle(txt)
64 64 txt = begin
65 65 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
66 66 hextxt = "<FEFF" # FEFF is BOM
67 67 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
68 68 hextxt << ">"
69 69 rescue
70 70 txt
71 71 end || ''
72 72 super(txt)
73 73 end
74 74
75 75 def textstring(s)
76 76 # Format a text string
77 77 if s =~ /^</ # This means the string is hex-dumped.
78 78 return s
79 79 else
80 80 return '('+escape(s)+')'
81 81 end
82 82 end
83 83
84 84 def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
85 85 @ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
86 86 # these quotation marks are not correctly rendered in the pdf
87 87 txt = txt.gsub(/[Ò€œÒ€�]/, '"') if txt
88 88 txt = begin
89 89 # 0x5c char handling
90 90 txtar = txt.split('\\')
91 91 txtar << '' if txt[-1] == ?\\
92 92 txtar.collect {|x| @ic.iconv(x)}.join('\\').gsub(/\\/, "\\\\\\\\")
93 93 rescue
94 94 txt
95 95 end || ''
96 96 super w,h,txt,border,ln,align,fill,link
97 97 end
98 98
99 99 def Footer
100 100 SetFont(@font_for_footer, 'I', 8)
101 101 SetY(-15)
102 102 SetX(15)
103 103 Cell(0, 5, @footer_date, 0, 0, 'L')
104 104 SetY(-15)
105 105 SetX(-30)
106 106 Cell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
107 107 end
108 108 end
109 109
110 110 # Returns a PDF string of a list of issues
111 111 def issues_to_pdf(issues, project, query)
112 112 pdf = IFPDF.new(current_language)
113 title = project ? "#{project} - #{l(:label_issue_plural)}" : "#{l(:label_issue_plural)}"
113 title = query.new_record? ? l(:label_issue_plural) : query.name
114 title = "#{project} - #{title}" if project
114 115 pdf.SetTitle(title)
115 116 pdf.AliasNbPages
116 117 pdf.footer_date = format_date(Date.today)
117 118 pdf.AddPage("L")
118 row_height = 7
119
120 row_height = 6
121 col_width = []
122 unless query.columns.empty?
123 col_width = query.columns.collect {|column| column.name == :subject ? 4.0 : 1.0 }
124 ratio = 262.0 / col_width.inject(0) {|s,w| s += w}
125 col_width = col_width.collect {|w| w * ratio}
126 end
119 127
120 128 # title
121 129 pdf.SetFontStyle('B',11)
122 130 pdf.Cell(190,10, title)
123 131 pdf.Ln
124 132
125 133 # headers
126 pdf.SetFontStyle('B',10)
134 pdf.SetFontStyle('B',8)
127 135 pdf.SetFillColor(230, 230, 230)
128 pdf.Cell(15, row_height, "#", 0, 0, 'L', 1)
129 pdf.Cell(30, row_height, l(:field_tracker), 0, 0, 'L', 1)
130 pdf.Cell(30, row_height, l(:field_status), 0, 0, 'L', 1)
131 pdf.Cell(30, row_height, l(:field_priority), 0, 0, 'L', 1)
132 pdf.Cell(40, row_height, l(:field_assigned_to), 0, 0, 'L', 1)
133 pdf.Cell(25, row_height, l(:field_updated_on), 0, 0, 'L', 1)
134 pdf.Cell(0, row_height, l(:field_subject), 0, 0, 'L', 1)
135 pdf.Line(10, pdf.GetY, 287, pdf.GetY)
136 pdf.Cell(15, row_height, "#", 1, 0, 'L', 1)
137 query.columns.each_with_index do |column, i|
138 pdf.Cell(col_width[i], row_height, column.caption, 1, 0, 'L', 1)
139 end
136 140 pdf.Ln
137 pdf.Line(10, pdf.GetY, 287, pdf.GetY)
138 pdf.SetY(pdf.GetY() + 1)
139 141
140 142 # rows
141 pdf.SetFontStyle('',9)
143 pdf.SetFontStyle('',8)
142 144 pdf.SetFillColor(255, 255, 255)
143 145 group = false
144 146 issues.each do |issue|
145 147 if query.grouped? && issue.send(query.group_by) != group
146 148 group = issue.send(query.group_by)
147 pdf.SetFontStyle('B',10)
148 pdf.Cell(0, row_height, "#{group.blank? ? 'None' : group.to_s}", 0, 1, 'L')
149 pdf.Line(10, pdf.GetY, 287, pdf.GetY)
150 pdf.SetY(pdf.GetY() + 0.5)
151 pdf.Line(10, pdf.GetY, 287, pdf.GetY)
152 pdf.SetY(pdf.GetY() + 1)
153 pdf.SetFontStyle('',9)
149 pdf.SetFontStyle('B',9)
150 pdf.Cell(277, row_height, "#{group.blank? ? 'None' : group.to_s}", 1, 1, 'L')
151 pdf.SetFontStyle('',8)
152 end
153 pdf.Cell(15, row_height, issue.id.to_s, 1, 0, 'L', 1)
154 query.columns.each_with_index do |column, i|
155 s = if column.is_a?(QueryCustomFieldColumn)
156 cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
157 show_value(cv)
158 else
159 value = issue.send(column.name)
160 if value.is_a?(Date)
161 format_date(value)
162 elsif value.is_a?(Time)
163 format_time(value)
164 else
165 value
166 end
154 167 end
155 pdf.Cell(15, row_height, issue.id.to_s, 0, 0, 'L', 1)
156 pdf.Cell(30, row_height, issue.tracker.name, 0, 0, 'L', 1)
157 pdf.Cell(30, row_height, issue.status.name, 0, 0, 'L', 1)
158 pdf.Cell(30, row_height, issue.priority.name, 0, 0, 'L', 1)
159 pdf.Cell(40, row_height, issue.assigned_to ? issue.assigned_to.to_s : '', 0, 0, 'L', 1)
160 pdf.Cell(25, row_height, format_date(issue.updated_on), 0, 0, 'L', 1)
161 pdf.MultiCell(0, row_height, (project == issue.project ? issue.subject : "#{issue.project} - #{issue.subject}"))
162 pdf.Line(10, pdf.GetY, 287, pdf.GetY)
163 pdf.SetY(pdf.GetY() + 1)
168 pdf.Cell(col_width[i], row_height, s.to_s, 1, 0, 'L', 1)
169 end
170 pdf.Ln
171 end
172 if issues.size == Setting.issues_export_limit.to_i
173 pdf.SetFontStyle('B',10)
174 pdf.Cell(0, row_height, '...')
164 175 end
165 176 pdf.Output
166 177 end
167 178
168 179 # Returns a PDF string of a single issue
169 180 def issue_to_pdf(issue)
170 181 pdf = IFPDF.new(current_language)
171 182 pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
172 183 pdf.AliasNbPages
173 184 pdf.footer_date = format_date(Date.today)
174 185 pdf.AddPage
175 186
176 187 pdf.SetFontStyle('B',11)
177 188 pdf.Cell(190,10, "#{issue.project} - #{issue.tracker} # #{issue.id}: #{issue.subject}")
178 189 pdf.Ln
179 190
180 191 y0 = pdf.GetY
181 192
182 193 pdf.SetFontStyle('B',9)
183 194 pdf.Cell(35,5, l(:field_status) + ":","LT")
184 195 pdf.SetFontStyle('',9)
185 196 pdf.Cell(60,5, issue.status.to_s,"RT")
186 197 pdf.SetFontStyle('B',9)
187 198 pdf.Cell(35,5, l(:field_priority) + ":","LT")
188 199 pdf.SetFontStyle('',9)
189 200 pdf.Cell(60,5, issue.priority.to_s,"RT")
190 201 pdf.Ln
191 202
192 203 pdf.SetFontStyle('B',9)
193 204 pdf.Cell(35,5, l(:field_author) + ":","L")
194 205 pdf.SetFontStyle('',9)
195 206 pdf.Cell(60,5, issue.author.to_s,"R")
196 207 pdf.SetFontStyle('B',9)
197 208 pdf.Cell(35,5, l(:field_category) + ":","L")
198 209 pdf.SetFontStyle('',9)
199 210 pdf.Cell(60,5, issue.category.to_s,"R")
200 211 pdf.Ln
201 212
202 213 pdf.SetFontStyle('B',9)
203 214 pdf.Cell(35,5, l(:field_created_on) + ":","L")
204 215 pdf.SetFontStyle('',9)
205 216 pdf.Cell(60,5, format_date(issue.created_on),"R")
206 217 pdf.SetFontStyle('B',9)
207 218 pdf.Cell(35,5, l(:field_assigned_to) + ":","L")
208 219 pdf.SetFontStyle('',9)
209 220 pdf.Cell(60,5, issue.assigned_to.to_s,"R")
210 221 pdf.Ln
211 222
212 223 pdf.SetFontStyle('B',9)
213 224 pdf.Cell(35,5, l(:field_updated_on) + ":","LB")
214 225 pdf.SetFontStyle('',9)
215 226 pdf.Cell(60,5, format_date(issue.updated_on),"RB")
216 227 pdf.SetFontStyle('B',9)
217 228 pdf.Cell(35,5, l(:field_due_date) + ":","LB")
218 229 pdf.SetFontStyle('',9)
219 230 pdf.Cell(60,5, format_date(issue.due_date),"RB")
220 231 pdf.Ln
221 232
222 233 for custom_value in issue.custom_field_values
223 234 pdf.SetFontStyle('B',9)
224 235 pdf.Cell(35,5, custom_value.custom_field.name + ":","L")
225 236 pdf.SetFontStyle('',9)
226 237 pdf.MultiCell(155,5, (show_value custom_value),"R")
227 238 end
228 239
229 240 pdf.SetFontStyle('B',9)
230 241 pdf.Cell(35,5, l(:field_subject) + ":","LTB")
231 242 pdf.SetFontStyle('',9)
232 243 pdf.Cell(155,5, issue.subject,"RTB")
233 244 pdf.Ln
234 245
235 246 pdf.SetFontStyle('B',9)
236 247 pdf.Cell(35,5, l(:field_description) + ":")
237 248 pdf.SetFontStyle('',9)
238 249 pdf.MultiCell(155,5, @issue.description,"BR")
239 250
240 251 pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
241 252 pdf.Line(pdf.GetX, pdf.GetY, 170, pdf.GetY)
242 253 pdf.Ln
243 254
244 255 if issue.changesets.any? && User.current.allowed_to?(:view_changesets, issue.project)
245 256 pdf.SetFontStyle('B',9)
246 257 pdf.Cell(190,5, l(:label_associated_revisions), "B")
247 258 pdf.Ln
248 259 for changeset in issue.changesets
249 260 pdf.SetFontStyle('B',8)
250 261 pdf.Cell(190,5, format_time(changeset.committed_on) + " - " + changeset.author.to_s)
251 262 pdf.Ln
252 263 unless changeset.comments.blank?
253 264 pdf.SetFontStyle('',8)
254 265 pdf.MultiCell(190,5, changeset.comments)
255 266 end
256 267 pdf.Ln
257 268 end
258 269 end
259 270
260 271 pdf.SetFontStyle('B',9)
261 272 pdf.Cell(190,5, l(:label_history), "B")
262 273 pdf.Ln
263 274 for journal in issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
264 275 pdf.SetFontStyle('B',8)
265 276 pdf.Cell(190,5, format_time(journal.created_on) + " - " + journal.user.name)
266 277 pdf.Ln
267 278 pdf.SetFontStyle('I',8)
268 279 for detail in journal.details
269 280 pdf.Cell(190,5, "- " + show_detail(detail, true))
270 281 pdf.Ln
271 282 end
272 283 if journal.notes?
273 284 pdf.SetFontStyle('',8)
274 285 pdf.MultiCell(190,5, journal.notes)
275 286 end
276 287 pdf.Ln
277 288 end
278 289
279 290 if issue.attachments.any?
280 291 pdf.SetFontStyle('B',9)
281 292 pdf.Cell(190,5, l(:label_attachment_plural), "B")
282 293 pdf.Ln
283 294 for attachment in issue.attachments
284 295 pdf.SetFontStyle('',8)
285 296 pdf.Cell(80,5, attachment.filename)
286 297 pdf.Cell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
287 298 pdf.Cell(25,5, format_date(attachment.created_on),0,0,"R")
288 299 pdf.Cell(65,5, attachment.author.name,0,0,"R")
289 300 pdf.Ln
290 301 end
291 302 end
292 303 pdf.Output
293 304 end
294 305
295 306 # Returns a PDF string of a gantt chart
296 307 def gantt_to_pdf(gantt, project)
297 308 pdf = IFPDF.new(current_language)
298 309 pdf.SetTitle("#{l(:label_gantt)} #{project}")
299 310 pdf.AliasNbPages
300 311 pdf.footer_date = format_date(Date.today)
301 312 pdf.AddPage("L")
302 313 pdf.SetFontStyle('B',12)
303 314 pdf.SetX(15)
304 315 pdf.Cell(70, 20, project.to_s)
305 316 pdf.Ln
306 317 pdf.SetFontStyle('B',9)
307 318
308 319 subject_width = 70
309 320 header_heigth = 5
310 321
311 322 headers_heigth = header_heigth
312 323 show_weeks = false
313 324 show_days = false
314 325
315 326 if gantt.months < 7
316 327 show_weeks = true
317 328 headers_heigth = 2*header_heigth
318 329 if gantt.months < 3
319 330 show_days = true
320 331 headers_heigth = 3*header_heigth
321 332 end
322 333 end
323 334
324 335 g_width = 210
325 336 zoom = (g_width) / (gantt.date_to - gantt.date_from + 1)
326 337 g_height = 120
327 338 t_height = g_height + headers_heigth
328 339
329 340 y_start = pdf.GetY
330 341
331 342 # Months headers
332 343 month_f = gantt.date_from
333 344 left = subject_width
334 345 height = header_heigth
335 346 gantt.months.times do
336 347 width = ((month_f >> 1) - month_f) * zoom
337 348 pdf.SetY(y_start)
338 349 pdf.SetX(left)
339 350 pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C")
340 351 left = left + width
341 352 month_f = month_f >> 1
342 353 end
343 354
344 355 # Weeks headers
345 356 if show_weeks
346 357 left = subject_width
347 358 height = header_heigth
348 359 if gantt.date_from.cwday == 1
349 360 # gantt.date_from is monday
350 361 week_f = gantt.date_from
351 362 else
352 363 # find next monday after gantt.date_from
353 364 week_f = gantt.date_from + (7 - gantt.date_from.cwday + 1)
354 365 width = (7 - gantt.date_from.cwday + 1) * zoom-1
355 366 pdf.SetY(y_start + header_heigth)
356 367 pdf.SetX(left)
357 368 pdf.Cell(width + 1, height, "", "LTR")
358 369 left = left + width+1
359 370 end
360 371 while week_f <= gantt.date_to
361 372 width = (week_f + 6 <= gantt.date_to) ? 7 * zoom : (gantt.date_to - week_f + 1) * zoom
362 373 pdf.SetY(y_start + header_heigth)
363 374 pdf.SetX(left)
364 375 pdf.Cell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C")
365 376 left = left + width
366 377 week_f = week_f+7
367 378 end
368 379 end
369 380
370 381 # Days headers
371 382 if show_days
372 383 left = subject_width
373 384 height = header_heigth
374 385 wday = gantt.date_from.cwday
375 386 pdf.SetFontStyle('B',7)
376 387 (gantt.date_to - gantt.date_from + 1).to_i.times do
377 388 width = zoom
378 389 pdf.SetY(y_start + 2 * header_heigth)
379 390 pdf.SetX(left)
380 391 pdf.Cell(width, height, day_name(wday).first, "LTR", 0, "C")
381 392 left = left + width
382 393 wday = wday + 1
383 394 wday = 1 if wday > 7
384 395 end
385 396 end
386 397
387 398 pdf.SetY(y_start)
388 399 pdf.SetX(15)
389 400 pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1)
390 401
391 402 # Tasks
392 403 top = headers_heigth + y_start
393 404 pdf.SetFontStyle('B',7)
394 405 gantt.events.each do |i|
395 406 pdf.SetY(top)
396 407 pdf.SetX(15)
397 408
398 409 if i.is_a? Issue
399 410 pdf.Cell(subject_width-15, 5, "#{i.tracker} #{i.id}: #{i.subject}".sub(/^(.{30}[^\s]*\s).*$/, '\1 (...)'), "LR")
400 411 else
401 412 pdf.Cell(subject_width-15, 5, "#{l(:label_version)}: #{i.name}", "LR")
402 413 end
403 414
404 415 pdf.SetY(top)
405 416 pdf.SetX(subject_width)
406 417 pdf.Cell(g_width, 5, "", "LR")
407 418
408 419 pdf.SetY(top+1.5)
409 420
410 421 if i.is_a? Issue
411 422 i_start_date = (i.start_date >= gantt.date_from ? i.start_date : gantt.date_from )
412 423 i_end_date = (i.due_before <= gantt.date_to ? i.due_before : gantt.date_to )
413 424
414 425 i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor
415 426 i_done_date = (i_done_date <= gantt.date_from ? gantt.date_from : i_done_date )
416 427 i_done_date = (i_done_date >= gantt.date_to ? gantt.date_to : i_done_date )
417 428
418 429 i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
419 430
420 431 i_left = ((i_start_date - gantt.date_from)*zoom)
421 432 i_width = ((i_end_date - i_start_date + 1)*zoom)
422 433 d_width = ((i_done_date - i_start_date)*zoom)
423 434 l_width = ((i_late_date - i_start_date+1)*zoom) if i_late_date
424 435 l_width ||= 0
425 436
426 437 pdf.SetX(subject_width + i_left)
427 438 pdf.SetFillColor(200,200,200)
428 439 pdf.Cell(i_width, 2, "", 0, 0, "", 1)
429 440
430 441 if l_width > 0
431 442 pdf.SetY(top+1.5)
432 443 pdf.SetX(subject_width + i_left)
433 444 pdf.SetFillColor(255,100,100)
434 445 pdf.Cell(l_width, 2, "", 0, 0, "", 1)
435 446 end
436 447 if d_width > 0
437 448 pdf.SetY(top+1.5)
438 449 pdf.SetX(subject_width + i_left)
439 450 pdf.SetFillColor(100,100,255)
440 451 pdf.Cell(d_width, 2, "", 0, 0, "", 1)
441 452 end
442 453
443 454 pdf.SetY(top+1.5)
444 455 pdf.SetX(subject_width + i_left + i_width)
445 456 pdf.Cell(30, 2, "#{i.status} #{i.done_ratio}%")
446 457 else
447 458 i_left = ((i.start_date - gantt.date_from)*zoom)
448 459
449 460 pdf.SetX(subject_width + i_left)
450 461 pdf.SetFillColor(50,200,50)
451 462 pdf.Cell(2, 2, "", 0, 0, "", 1)
452 463
453 464 pdf.SetY(top+1.5)
454 465 pdf.SetX(subject_width + i_left + 3)
455 466 pdf.Cell(30, 2, "#{i.name}")
456 467 end
457 468
458 469 top = top + 5
459 470 pdf.SetDrawColor(200, 200, 200)
460 471 pdf.Line(15, top, subject_width+g_width, top)
461 472 if pdf.GetY() > 180
462 473 pdf.AddPage("L")
463 474 top = 20
464 475 pdf.Line(15, top, subject_width+g_width, top)
465 476 end
466 477 pdf.SetDrawColor(0, 0, 0)
467 478 end
468 479
469 480 pdf.Line(15, top, subject_width+g_width, top)
470 481 pdf.Output
471 482 end
472 483 end
473 484 end
474 485 end
General Comments 0
You need to be logged in to leave comments. Login now