--- lib/redmine/export/pdf.rb | 106 ++++++++++++++++++++++++++++++++++++-------- 1 files changed, 86 insertions(+), 20 deletions(-) diff --git a/lib/redmine/export/pdf.rb b/lib/redmine/export/pdf.rb index c98b25f..85b51cd 100644 --- a/lib/redmine/export/pdf.rb +++ b/lib/redmine/export/pdf.rb @@ -88,7 +88,7 @@ module Redmine end end - def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='') + def fix_text_encoding(txt) @ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8') # these quotation marks are not correctly rendered in the pdf txt = txt.gsub(/[“�]/, '"') if txt @@ -100,7 +100,15 @@ module Redmine rescue txt end || '' - super w,h,txt,border,ln,align,fill,link + return txt + end + + def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='') + super w,h,fix_text_encoding(txt),border,ln,align,fill,link + end + + def MultiCell(w,h=0,txt='',border=0,align='',fill=0) + super w,h,fix_text_encoding(txt),border,align,fill end def Footer @@ -122,13 +130,25 @@ module Redmine pdf.SetTitle(title) pdf.AliasNbPages pdf.footer_date = format_date(Date.today) + pdf.SetAutoPageBreak(false) pdf.AddPage("L") - row_height = 6 + # Landscape A4 = 210 x 297 mm + page_height = 210 + page_width = 297 + right_margin = 10 + bottom_margin = 20 + col_id_width = 10 + row_height = 5 + + # column widths + table_width = page_width - right_margin - 10 # fixed left margin col_width = [] unless query.columns.empty? - col_width = query.columns.collect {|column| column.name == :subject ? 4.0 : 1.0 } - ratio = 262.0 / col_width.inject(0) {|s,w| s += w} + col_width = query.columns.collect do |c| + (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) && ['string', 'text'].include?(c.custom_field.field_format)))? 4.0 : 1.0 + end + ratio = (table_width - col_id_width) / col_width.inject(0) {|s,w| s += w} col_width = col_width.collect {|w| w * ratio} end @@ -140,7 +160,7 @@ module Redmine # headers pdf.SetFontStyle('B',8) pdf.SetFillColor(230, 230, 230) - pdf.Cell(15, row_height, "#", 1, 0, 'L', 1) + pdf.Cell(col_id_width, row_height, "#", 1, 0, 'C', 1) query.columns.each_with_index do |column, i| pdf.Cell(col_width[i], row_height, column.caption, 1, 0, 'L', 1) end @@ -159,8 +179,9 @@ module Redmine pdf.SetFontStyle('',8) previous_group = group end - pdf.Cell(15, row_height, issue.id.to_s, 1, 0, 'L', 1) - query.columns.each_with_index do |column, i| + + # fetch all the row values + col_values = query.columns.collect do |column| s = if column.is_a?(QueryCustomFieldColumn) cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id} show_value(cv) @@ -174,17 +195,63 @@ module Redmine value end end - pdf.Cell(col_width[i], row_height, s.to_s, 1, 0, 'L', 1) + s.to_s end - pdf.Ln + + # render it off-page to find the max height used + base_x = pdf.GetX + base_y = pdf.GetY + pdf.SetY(2 * page_height) + max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height) + pdf.SetXY(base_x, base_y) + + # make new page if it doesn't fit on the current one + space_left = page_height - base_y - bottom_margin + if max_height > space_left + pdf.AddPage("L") + base_x = pdf.GetX + base_y = pdf.GetY + end + + # write the cells on page + pdf.Cell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1) + issues_to_pdf_write_cells(pdf, col_values, col_width, row_height) + issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width) + pdf.SetY(base_y + max_height); end + if issues.size == Setting.issues_export_limit.to_i pdf.SetFontStyle('B',10) pdf.Cell(0, row_height, '...') end pdf.Output end - + + # Renders MultiCells and returns the maximum height used + def issues_to_pdf_write_cells(pdf, col_values, col_widths, row_height) + base_y = pdf.GetY + max_height = row_height + col_values.each_with_index do |column, i| + col_x = pdf.GetX + pdf.MultiCell(col_widths[i], row_height, col_values[i], "T", 'L', 1) + max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height + pdf.SetXY(col_x + col_widths[i], base_y); + end + return max_height + end + + # Draw lines to close the row (MultiCell border drawing in not uniform) + def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y, id_width, col_widths) + col_x = top_x + id_width + pdf.Line(col_x, top_y, col_x, lower_y) # id right border + col_widths.each do |width| + col_x += width + pdf.Line(col_x, top_y, col_x, lower_y) # columns right border + end + pdf.Line(top_x, top_y, top_x, lower_y) # left border + pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border + end + # Returns a PDF string of a single issue def issue_to_pdf(issue) pdf = IFPDF.new(current_language) @@ -194,7 +261,7 @@ module Redmine pdf.AddPage pdf.SetFontStyle('B',11) - pdf.Cell(190,10, "#{issue.project} - #{issue.tracker} # #{issue.id}: #{issue.subject}") + pdf.MultiCell(190,5, "#{issue.project} - #{issue.tracker} # #{issue.id}: #{issue.subject}") pdf.Ln y0 = pdf.GetY @@ -247,18 +314,17 @@ module Redmine end pdf.SetFontStyle('B',9) - pdf.Cell(35,5, l(:field_subject) + ":","LTB") + pdf.Cell(35,5, l(:field_subject) + ":","LT") pdf.SetFontStyle('',9) - pdf.Cell(155,5, issue.subject,"RTB") - pdf.Ln + pdf.MultiCell(155,5, issue.subject,"RT") pdf.SetFontStyle('B',9) - pdf.Cell(35,5, l(:field_description) + ":") + pdf.Cell(35,5, l(:field_description) + ":","LT") pdf.SetFontStyle('',9) - pdf.MultiCell(155,5, issue.description.to_s,"BR") + pdf.MultiCell(155,5, issue.description.to_s,"RT") pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY) - pdf.Line(pdf.GetX, pdf.GetY, 170, pdf.GetY) + pdf.Line(pdf.GetX, pdf.GetY, pdf.GetX + 190, pdf.GetY) pdf.Ln if issue.changesets.any? && User.current.allowed_to?(:view_changesets, issue.project) @@ -286,10 +352,10 @@ module Redmine pdf.Ln pdf.SetFontStyle('I',8) for detail in journal.details - pdf.Cell(190,5, "- " + show_detail(detail, true)) - pdf.Ln + pdf.MultiCell(190,5, "- " + show_detail(detail, true)) end if journal.notes? + pdf.Ln unless journal.details.empty? pdf.SetFontStyle('',8) pdf.MultiCell(190,5, journal.notes.to_s) end