Revision 1298:4f746d8966dd lib/redmine/export
| lib/redmine/export/pdf.rb | ||
|---|---|---|
| 1 | 1 |
# encoding: utf-8 |
| 2 | 2 |
# |
| 3 | 3 |
# Redmine - project management software |
| 4 |
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
|
| 4 |
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
| 5 | 5 |
# |
| 6 | 6 |
# This program is free software; you can redistribute it and/or |
| 7 | 7 |
# modify it under the terms of the GNU General Public License |
| ... | ... | |
| 17 | 17 |
# along with this program; if not, write to the Free Software |
| 18 | 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 19 | 19 |
|
| 20 |
require 'iconv' |
|
| 21 | 20 |
require 'tcpdf' |
| 22 | 21 |
require 'fpdf/chinese' |
| 23 | 22 |
require 'fpdf/japanese' |
| 24 | 23 |
require 'fpdf/korean' |
| 25 | 24 |
|
| 25 |
if RUBY_VERSION < '1.9' |
|
| 26 |
require 'iconv' |
|
| 27 |
end |
|
| 28 |
|
|
| 26 | 29 |
module Redmine |
| 27 | 30 |
module Export |
| 28 | 31 |
module PDF |
| ... | ... | |
| 86 | 89 |
|
| 87 | 90 |
def SetTitle(txt) |
| 88 | 91 |
txt = begin |
| 89 |
utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
|
|
| 92 |
utf16txt = to_utf16(txt)
|
|
| 90 | 93 |
hextxt = "<FEFF" # FEFF is BOM |
| 91 | 94 |
hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
|
| 92 | 95 |
hextxt << ">" |
| ... | ... | |
| 116 | 119 |
html |
| 117 | 120 |
end |
| 118 | 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 |
|
| 129 |
end |
|
| 130 |
|
|
| 119 | 131 |
def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='') |
| 120 | 132 |
Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link) |
| 121 | 133 |
end |
| ... | ... | |
| 160 | 172 |
|
| 161 | 173 |
def bookmark_title(txt) |
| 162 | 174 |
txt = begin |
| 163 |
utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
|
|
| 175 |
utf16txt = to_utf16(txt)
|
|
| 164 | 176 |
hextxt = "<FEFF" # FEFF is BOM |
| 165 | 177 |
hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
|
| 166 | 178 |
hextxt << ">" |
| ... | ... | |
| 368 | 380 |
col_width |
| 369 | 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 | 384 |
# headers |
| 373 | 385 |
pdf.SetFontStyle('B',8)
|
| 374 | 386 |
pdf.SetFillColor(230, 230, 230) |
| ... | ... | |
| 377 | 389 |
base_x = pdf.GetX |
| 378 | 390 |
base_y = pdf.GetY |
| 379 | 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 | 393 |
pdf.SetXY(base_x, base_y); |
| 382 | 394 |
|
| 383 | 395 |
# write the cells on page |
| 384 |
pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1) |
|
| 385 | 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 | 398 |
pdf.SetY(base_y + max_height); |
| 388 | 399 |
|
| 389 | 400 |
# rows |
| ... | ... | |
| 405 | 416 |
# Landscape A4 = 210 x 297 mm |
| 406 | 417 |
page_height = 210 |
| 407 | 418 |
page_width = 297 |
| 419 |
left_margin = 10 |
|
| 408 | 420 |
right_margin = 10 |
| 409 | 421 |
bottom_margin = 20 |
| 410 |
col_id_width = 10 |
|
| 411 | 422 |
row_height = 4 |
| 412 | 423 |
|
| 413 | 424 |
# column widths |
| 414 |
table_width = page_width - right_margin - 10 # fixed left margin
|
|
| 425 |
table_width = page_width - right_margin - left_margin
|
|
| 415 | 426 |
col_width = [] |
| 416 | 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 | 429 |
table_width = col_width.inject(0) {|s,v| s += v}
|
| 419 | 430 |
end |
| 420 | 431 |
|
| 421 |
# use full width if the description is displayed
|
|
| 432 |
# use full width if the description is displayed
|
|
| 422 | 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 | 435 |
table_width = col_width.inject(0) {|s,v| s += v}
|
| 425 | 436 |
end |
| 426 | 437 |
|
| ... | ... | |
| 428 | 439 |
pdf.SetFontStyle('B',11)
|
| 429 | 440 |
pdf.RDMCell(190,10, title) |
| 430 | 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 | 443 |
previous_group = false |
| 433 | 444 |
issue_list(issues) do |issue, level| |
| 434 | 445 |
if query.grouped? && |
| ... | ... | |
| 437 | 448 |
group_label = group.blank? ? 'None' : group.to_s.dup |
| 438 | 449 |
group_label << " (#{query.issue_count_by_group[group]})"
|
| 439 | 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 | 452 |
pdf.SetFontStyle('',8)
|
| 442 | 453 |
previous_group = group |
| 443 | 454 |
end |
| ... | ... | |
| 456 | 467 |
space_left = page_height - base_y - bottom_margin |
| 457 | 468 |
if max_height > space_left |
| 458 | 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 | 471 |
base_x = pdf.GetX |
| 461 | 472 |
base_y = pdf.GetY |
| 462 | 473 |
end |
| 463 | 474 |
|
| 464 | 475 |
# write the cells on page |
| 465 |
pdf.RDMCell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1) |
|
| 466 | 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 | 478 |
pdf.SetY(base_y + max_height); |
| 469 | 479 |
|
| 470 | 480 |
if query.has_column?(:description) && issue.description? |
| ... | ... | |
| 501 | 511 |
end |
| 502 | 512 |
|
| 503 | 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 | 516 |
def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y, |
| 505 |
id_width, col_widths) |
|
| 506 |
col_x = top_x + id_width
|
|
| 517 |
col_id_width, col_widths)
|
|
| 518 |
col_x = top_x |
|
| 507 | 519 |
pdf.Line(col_x, top_y, col_x, lower_y) # id right border |
| 508 | 520 |
col_widths.each do |width| |
| 509 | 521 |
col_x += width |
Also available in: Unified diff