Mercurial > hg > soundsoftware-site
comparison lib/redmine/export/pdf.rb @ 1298:4f746d8966dd redmine_2.3_integration
Merge from redmine-2.3 branch to create new branch redmine-2.3-integration
author | Chris Cannam |
---|---|
date | Fri, 14 Jun 2013 09:28:30 +0100 |
parents | 622f24f53b42 |
children |
comparison
equal
deleted
inserted
replaced
1297:0a574315af3e | 1298:4f746d8966dd |
---|---|
1 # encoding: utf-8 | 1 # encoding: utf-8 |
2 # | 2 # |
3 # Redmine - project management software | 3 # Redmine - project management software |
4 # Copyright (C) 2006-2012 Jean-Philippe Lang | 4 # Copyright (C) 2006-2013 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. |
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' | |
21 require 'tcpdf' | 20 require 'tcpdf' |
22 require 'fpdf/chinese' | 21 require 'fpdf/chinese' |
23 require 'fpdf/japanese' | 22 require 'fpdf/japanese' |
24 require 'fpdf/korean' | 23 require 'fpdf/korean' |
24 | |
25 if RUBY_VERSION < '1.9' | |
26 require 'iconv' | |
27 end | |
25 | 28 |
26 module Redmine | 29 module Redmine |
27 module Export | 30 module Export |
28 module PDF | 31 module PDF |
29 include ActionView::Helpers::TextHelper | 32 include ActionView::Helpers::TextHelper |
84 SetFont(@font_for_content, style, size) | 87 SetFont(@font_for_content, style, size) |
85 end | 88 end |
86 | 89 |
87 def SetTitle(txt) | 90 def SetTitle(txt) |
88 txt = begin | 91 txt = begin |
89 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt) | 92 utf16txt = to_utf16(txt) |
90 hextxt = "<FEFF" # FEFF is BOM | 93 hextxt = "<FEFF" # FEFF is BOM |
91 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join | 94 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join |
92 hextxt << ">" | 95 hextxt << ">" |
93 rescue | 96 rescue |
94 txt | 97 txt |
112 def formatted_text(text) | 115 def formatted_text(text) |
113 html = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) | 116 html = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) |
114 # Strip {{toc}} tags | 117 # Strip {{toc}} tags |
115 html.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i, '') | 118 html.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i, '') |
116 html | 119 html |
120 end | |
121 | |
122 # Encodes an UTF-8 string to UTF-16BE | |
123 def to_utf16(str) | |
124 if str.respond_to?(:encode) | |
125 str.encode('UTF-16BE') | |
126 else | |
127 Iconv.conv('UTF-16BE', 'UTF-8', str) | |
128 end | |
117 end | 129 end |
118 | 130 |
119 def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='') | 131 def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='') |
120 Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link) | 132 Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link) |
121 end | 133 end |
158 @outlines << {:t => txt, :l => level, :p => PageNo(), :y => (@h - y)*@k} | 170 @outlines << {:t => txt, :l => level, :p => PageNo(), :y => (@h - y)*@k} |
159 end | 171 end |
160 | 172 |
161 def bookmark_title(txt) | 173 def bookmark_title(txt) |
162 txt = begin | 174 txt = begin |
163 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt) | 175 utf16txt = to_utf16(txt) |
164 hextxt = "<FEFF" # FEFF is BOM | 176 hextxt = "<FEFF" # FEFF is BOM |
165 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join | 177 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join |
166 hextxt << ">" | 178 hextxt << ">" |
167 rescue | 179 rescue |
168 txt | 180 txt |
366 end | 378 end |
367 end | 379 end |
368 col_width | 380 col_width |
369 end | 381 end |
370 | 382 |
371 def render_table_header(pdf, query, col_width, row_height, col_id_width, table_width) | 383 def render_table_header(pdf, query, col_width, row_height, table_width) |
372 # headers | 384 # headers |
373 pdf.SetFontStyle('B',8) | 385 pdf.SetFontStyle('B',8) |
374 pdf.SetFillColor(230, 230, 230) | 386 pdf.SetFillColor(230, 230, 230) |
375 | 387 |
376 # render it background to find the max height used | 388 # render it background to find the max height used |
377 base_x = pdf.GetX | 389 base_x = pdf.GetX |
378 base_y = pdf.GetY | 390 base_y = pdf.GetY |
379 max_height = issues_to_pdf_write_cells(pdf, query.inline_columns, col_width, row_height, true) | 391 max_height = issues_to_pdf_write_cells(pdf, query.inline_columns, col_width, row_height, true) |
380 pdf.Rect(base_x, base_y, table_width + col_id_width, max_height, 'FD'); | 392 pdf.Rect(base_x, base_y, table_width, max_height, 'FD'); |
381 pdf.SetXY(base_x, base_y); | 393 pdf.SetXY(base_x, base_y); |
382 | 394 |
383 # write the cells on page | 395 # write the cells on page |
384 pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1) | |
385 issues_to_pdf_write_cells(pdf, query.inline_columns, col_width, row_height, true) | 396 issues_to_pdf_write_cells(pdf, query.inline_columns, col_width, row_height, true) |
386 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width) | 397 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, 0, col_width) |
387 pdf.SetY(base_y + max_height); | 398 pdf.SetY(base_y + max_height); |
388 | 399 |
389 # rows | 400 # rows |
390 pdf.SetFontStyle('',8) | 401 pdf.SetFontStyle('',8) |
391 pdf.SetFillColor(255, 255, 255) | 402 pdf.SetFillColor(255, 255, 255) |
403 pdf.AddPage("L") | 414 pdf.AddPage("L") |
404 | 415 |
405 # Landscape A4 = 210 x 297 mm | 416 # Landscape A4 = 210 x 297 mm |
406 page_height = 210 | 417 page_height = 210 |
407 page_width = 297 | 418 page_width = 297 |
419 left_margin = 10 | |
408 right_margin = 10 | 420 right_margin = 10 |
409 bottom_margin = 20 | 421 bottom_margin = 20 |
410 col_id_width = 10 | |
411 row_height = 4 | 422 row_height = 4 |
412 | 423 |
413 # column widths | 424 # column widths |
414 table_width = page_width - right_margin - 10 # fixed left margin | 425 table_width = page_width - right_margin - left_margin |
415 col_width = [] | 426 col_width = [] |
416 unless query.inline_columns.empty? | 427 unless query.inline_columns.empty? |
417 col_width = calc_col_width(issues, query, table_width - col_id_width, pdf) | 428 col_width = calc_col_width(issues, query, table_width, pdf) |
418 table_width = col_width.inject(0) {|s,v| s += v} | 429 table_width = col_width.inject(0) {|s,v| s += v} |
419 end | 430 end |
420 | 431 |
421 # use full width if the description is displayed | 432 # use full width if the description is displayed |
422 if table_width > 0 && query.has_column?(:description) | 433 if table_width > 0 && query.has_column?(:description) |
423 col_width = col_width.map {|w| w = w * (page_width - right_margin - 10 - col_id_width) / table_width} | 434 col_width = col_width.map {|w| w * (page_width - right_margin - left_margin) / table_width} |
424 table_width = col_width.inject(0) {|s,v| s += v} | 435 table_width = col_width.inject(0) {|s,v| s += v} |
425 end | 436 end |
426 | 437 |
427 # title | 438 # title |
428 pdf.SetFontStyle('B',11) | 439 pdf.SetFontStyle('B',11) |
429 pdf.RDMCell(190,10, title) | 440 pdf.RDMCell(190,10, title) |
430 pdf.Ln | 441 pdf.Ln |
431 render_table_header(pdf, query, col_width, row_height, col_id_width, table_width) | 442 render_table_header(pdf, query, col_width, row_height, table_width) |
432 previous_group = false | 443 previous_group = false |
433 issue_list(issues) do |issue, level| | 444 issue_list(issues) do |issue, level| |
434 if query.grouped? && | 445 if query.grouped? && |
435 (group = query.group_by_column.value(issue)) != previous_group | 446 (group = query.group_by_column.value(issue)) != previous_group |
436 pdf.SetFontStyle('B',10) | 447 pdf.SetFontStyle('B',10) |
437 group_label = group.blank? ? 'None' : group.to_s.dup | 448 group_label = group.blank? ? 'None' : group.to_s.dup |
438 group_label << " (#{query.issue_count_by_group[group]})" | 449 group_label << " (#{query.issue_count_by_group[group]})" |
439 pdf.Bookmark group_label, 0, -1 | 450 pdf.Bookmark group_label, 0, -1 |
440 pdf.RDMCell(table_width + col_id_width, row_height * 2, group_label, 1, 1, 'L') | 451 pdf.RDMCell(table_width, row_height * 2, group_label, 1, 1, 'L') |
441 pdf.SetFontStyle('',8) | 452 pdf.SetFontStyle('',8) |
442 previous_group = group | 453 previous_group = group |
443 end | 454 end |
444 | 455 |
445 # fetch row values | 456 # fetch row values |
454 | 465 |
455 # make new page if it doesn't fit on the current one | 466 # make new page if it doesn't fit on the current one |
456 space_left = page_height - base_y - bottom_margin | 467 space_left = page_height - base_y - bottom_margin |
457 if max_height > space_left | 468 if max_height > space_left |
458 pdf.AddPage("L") | 469 pdf.AddPage("L") |
459 render_table_header(pdf, query, col_width, row_height, col_id_width, table_width) | 470 render_table_header(pdf, query, col_width, row_height, table_width) |
460 base_x = pdf.GetX | 471 base_x = pdf.GetX |
461 base_y = pdf.GetY | 472 base_y = pdf.GetY |
462 end | 473 end |
463 | 474 |
464 # write the cells on page | 475 # write the cells on page |
465 pdf.RDMCell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1) | |
466 issues_to_pdf_write_cells(pdf, col_values, col_width, row_height) | 476 issues_to_pdf_write_cells(pdf, col_values, col_width, row_height) |
467 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width) | 477 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, 0, col_width) |
468 pdf.SetY(base_y + max_height); | 478 pdf.SetY(base_y + max_height); |
469 | 479 |
470 if query.has_column?(:description) && issue.description? | 480 if query.has_column?(:description) && issue.description? |
471 pdf.SetX(10) | 481 pdf.SetX(10) |
472 pdf.SetAutoPageBreak(true, 20) | 482 pdf.SetAutoPageBreak(true, 20) |
499 end | 509 end |
500 return max_height | 510 return max_height |
501 end | 511 end |
502 | 512 |
503 # Draw lines to close the row (MultiCell border drawing in not uniform) | 513 # Draw lines to close the row (MultiCell border drawing in not uniform) |
514 # | |
515 # parameter "col_id_width" is not used. it is kept for compatibility. | |
504 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y, | 516 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y, |
505 id_width, col_widths) | 517 col_id_width, col_widths) |
506 col_x = top_x + id_width | 518 col_x = top_x |
507 pdf.Line(col_x, top_y, col_x, lower_y) # id right border | 519 pdf.Line(col_x, top_y, col_x, lower_y) # id right border |
508 col_widths.each do |width| | 520 col_widths.each do |width| |
509 col_x += width | 521 col_x += width |
510 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border | 522 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border |
511 end | 523 end |