| 26 | 26 |     module PDF | 
  | 27 | 27 |       include ActionView::Helpers::TextHelper | 
  | 28 | 28 |       include ActionView::Helpers::NumberHelper | 
  | 29 |  |        | 
  |  | 29 |  | 
  | 30 | 30 |       class IFPDF < FPDF | 
  | 31 | 31 |         include Redmine::I18n | 
  | 32 | 32 |         attr_accessor :footer_date | 
  | 33 |  |          | 
  |  | 33 |  | 
  | 34 | 34 |         def initialize(lang) | 
  | 35 | 35 |           super() | 
  | 36 | 36 |           set_language_if_valid lang | 
  | 37 | 37 |           case current_language.to_s.downcase | 
  | 38 |  |           when 'ko' | 
  | 39 |  |             extend(PDF_Korean) | 
  | 40 |  |             AddUHCFont() | 
  | 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 | 
  | 59 |  |             @font_for_content = 'Arial' | 
  | 60 |  |             @font_for_footer = 'Helvetica'               | 
  |  | 38 |             when 'ko' | 
  |  | 39 |               extend(PDF_Korean) | 
  |  | 40 |               AddUHCFont() | 
  |  | 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 | 
  |  | 59 |               @font_for_content = 'Arial' | 
  |  | 60 |               @font_for_footer = 'Helvetica' | 
  | 61 | 61 |           end | 
  | 62 | 62 |           SetCreator(Redmine::Info.app_name) | 
  | 63 | 63 |           SetFont(@font_for_content) | 
  | 64 | 64 |         end | 
  | 65 |  |          | 
  |  | 65 |  | 
  | 66 | 66 |         def SetFontStyle(style, size) | 
  | 67 | 67 |           SetFont(@font_for_content, style, size) | 
  | 68 | 68 |         end | 
  | 69 |  |          | 
  |  | 69 |  | 
  | 70 | 70 |         def SetTitle(txt) | 
  | 71 | 71 |           txt = begin | 
  | 72 | 72 |             utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt) | 
  | 73 |  |             hextxt = "<FEFF"  # FEFF is BOM | 
  | 74 |  |             hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join | 
  |  | 73 |             hextxt = "<FEFF" # FEFF is BOM | 
  |  | 74 |             hextxt << utf16txt.unpack("C*").map { |x| sprintf("%02X", x) }.join | 
  | 75 | 75 |             hextxt << ">" | 
  | 76 | 76 |           rescue | 
  | 77 | 77 |             txt | 
  | 78 | 78 |           end || '' | 
  | 79 | 79 |           super(txt) | 
  | 80 | 80 |         end | 
  | 81 |  |      | 
  |  | 81 |  | 
  | 82 | 82 |         def textstring(s) | 
  | 83 | 83 |           # Format a text string | 
  | 84 |  |           if s =~ /^</  # This means the string is hex-dumped. | 
  |  | 84 |           if s =~ /^</ # This means the string is hex-dumped. | 
  | 85 | 85 |             return s | 
  | 86 | 86 |           else | 
  | 87 | 87 |             return '('+escape(s)+')' | 
  | 88 | 88 |           end | 
  | 89 | 89 |         end | 
  | 90 |  |            | 
  | 91 |  |         def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='') | 
  |  | 90 |  | 
  |  | 91 |         def fix_text_encoding(txt) | 
  | 92 | 92 |           @ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8') | 
  | 93 | 93 |           # these quotation marks are not correctly rendered in the pdf | 
  | 94 | 94 |           txt = txt.gsub(/[“�]/, '"') if txt | 
  | ... | ... |  | 
  | 96 | 96 |             # 0x5c char handling | 
  | 97 | 97 |             txtar = txt.split('\\') | 
  | 98 | 98 |             txtar << '' if txt[-1] == ?\\ | 
  | 99 |  |             txtar.collect {|x| @ic.iconv(x)}.join('\\').gsub(/\\/, "\\\\\\\\") | 
  |  | 99 |             txtar.collect { |x| @ic.iconv(x) }.join('\\').gsub(/\\/, "\\\\\\\\") | 
  | 100 | 100 |           rescue | 
  | 101 | 101 |             txt | 
  | 102 | 102 |           end || '' | 
  | 103 |  |           super w,h,txt,border,ln,align,fill,link | 
  |  | 103 |           return txt | 
  | 104 | 104 |         end | 
  | 105 |  |          | 
  |  | 105 |  | 
  |  | 106 |         def Cell(w, h=0, txt='', border=0, ln=0, align='', fill=0, link='') | 
  |  | 107 |           super w, h, fix_text_encoding(txt), border, ln, align, fill, link | 
  |  | 108 |         end | 
  |  | 109 |  | 
  |  | 110 |         def MultiCell(w, h=0, txt='', border=0, align='', fill=0) | 
  |  | 111 |           super w, h, fix_text_encoding(txt), border, align, fill | 
  |  | 112 |         end | 
  |  | 113 |  | 
  | 106 | 114 |         def Footer | 
  | 107 | 115 |           SetFont(@font_for_footer, 'I', 8) | 
  | 108 | 116 |           SetY(-15) | 
  | ... | ... |  | 
  | 113 | 121 |           Cell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C') | 
  | 114 | 122 |         end | 
  | 115 | 123 |       end | 
  | 116 |  |        | 
  |  | 124 |  | 
  | 117 | 125 |       # Returns a PDF string of a list of issues | 
  | 118 | 126 |       def issues_to_pdf(issues, project, query) | 
  | 119 | 127 |         pdf = IFPDF.new(current_language) | 
  | ... | ... |  | 
  | 122 | 130 |         pdf.SetTitle(title) | 
  | 123 | 131 |         pdf.AliasNbPages | 
  | 124 | 132 |         pdf.footer_date = format_date(Date.today) | 
  |  | 133 |         pdf.SetAutoPageBreak(false) | 
  | 125 | 134 |         pdf.AddPage("L") | 
  | 126 |  |          | 
  | 127 |  |         row_height = 6 | 
  |  | 135 |  | 
  |  | 136 |         # Landscape A4 = 210 x 297 mm | 
  |  | 137 |         page_height = 210 | 
  |  | 138 |         page_width = 297 | 
  |  | 139 |         right_margin = 10 | 
  |  | 140 |         bottom_margin = 20 | 
  |  | 141 |         col_id_width = 10 | 
  |  | 142 |         row_height = 5 | 
  |  | 143 |  | 
  |  | 144 |         # column widths | 
  |  | 145 |         table_width = page_width - right_margin - 10 # fixed left margin | 
  | 128 | 146 |         col_width = [] | 
  | 129 | 147 |         unless query.columns.empty? | 
  | 130 |  |           col_width = query.columns.collect {|column| column.name == :subject ? 4.0 : 1.0 } | 
  | 131 |  |           ratio = 262.0 / col_width.inject(0) {|s,w| s += w} | 
  | 132 |  |           col_width = col_width.collect {|w| w * ratio} | 
  |  | 148 |           col_width = query.columns.collect do |c| | 
  |  | 149 |             (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) && ['string', 'text'].include?(c.custom_field.field_format))) ? 4.0 : 1.0 | 
  |  | 150 |           end | 
  |  | 151 |           ratio = (table_width - col_id_width) / col_width.inject(0) { |s, w| s += w } | 
  |  | 152 |           col_width = col_width.collect { |w| w * ratio } | 
  | 133 | 153 |         end | 
  | 134 |  |          | 
  |  | 154 |  | 
  | 135 | 155 |         # title | 
  | 136 |  |         pdf.SetFontStyle('B',11)     | 
  | 137 |  |         pdf.Cell(190,10, title) | 
  |  | 156 |         pdf.SetFontStyle('B', 11) | 
  |  | 157 |         pdf.Cell(190, 10, title) | 
  | 138 | 158 |         pdf.Ln | 
  | 139 |  |          | 
  |  | 159 |  | 
  | 140 | 160 |         # headers | 
  | 141 |  |         pdf.SetFontStyle('B',8) | 
  |  | 161 |         pdf.SetFontStyle('B', 8) | 
  | 142 | 162 |         pdf.SetFillColor(230, 230, 230) | 
  | 143 |  |         pdf.Cell(15, row_height, "#", 1, 0, 'L', 1) | 
  |  | 163 |         pdf.Cell(col_id_width, row_height, "#", 1, 0, 'C', 1) | 
  | 144 | 164 |         query.columns.each_with_index do |column, i| | 
  | 145 | 165 |           pdf.Cell(col_width[i], row_height, column.caption, 1, 0, 'L', 1) | 
  | 146 | 166 |         end | 
  | 147 | 167 |         pdf.Ln | 
  | 148 |  |          | 
  |  | 168 |  | 
  | 149 | 169 |         # rows | 
  | 150 |  |         pdf.SetFontStyle('',8) | 
  |  | 170 |         pdf.SetFontStyle('', 8) | 
  | 151 | 171 |         pdf.SetFillColor(255, 255, 255) | 
  | 152 | 172 |         previous_group = false | 
  | 153 | 173 |         issues.each do |issue| | 
  | 154 | 174 |           if query.grouped? && (group = query.group_by_column.value(issue)) != previous_group | 
  | 155 |  |             pdf.SetFontStyle('B',9) | 
  | 156 |  |             pdf.Cell(277, row_height,  | 
  | 157 |  |               (group.blank? ? 'None' : group.to_s) + " (#{query.issue_count_by_group[group]})", | 
  | 158 |  |               1, 1, 'L') | 
  | 159 |  |             pdf.SetFontStyle('',8) | 
  |  | 175 |             pdf.SetFontStyle('B', 9) | 
  |  | 176 |             pdf.Cell(277, row_height, | 
  |  | 177 |                      (group.blank? ? 'None' : group.to_s) + " (#{query.issue_count_by_group[group]})", | 
  |  | 178 |                      1, 1, 'L') | 
  |  | 179 |             pdf.SetFontStyle('', 8) | 
  | 160 | 180 |             previous_group = group | 
  | 161 | 181 |           end | 
  | 162 |  |           pdf.Cell(15, row_height, issue.id.to_s, 1, 0, 'L', 1) | 
  | 163 |  |           query.columns.each_with_index do |column, i| | 
  |  | 182 |  | 
  |  | 183 |           # fetch all the row values | 
  |  | 184 |           col_values = query.columns.collect do |column| | 
  | 164 | 185 |             s = if column.is_a?(QueryCustomFieldColumn) | 
  | 165 |  |               cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id} | 
  | 166 |  |               show_value(cv) | 
  | 167 |  |             else | 
  | 168 |  |               value = issue.send(column.name) | 
  | 169 |  |               if value.is_a?(Date) | 
  | 170 |  |                 format_date(value) | 
  | 171 |  |               elsif value.is_a?(Time) | 
  | 172 |  |                 format_time(value) | 
  | 173 |  |               else | 
  | 174 |  |                 value | 
  | 175 |  |               end | 
  | 176 |  |             end | 
  | 177 |  |             pdf.Cell(col_width[i], row_height, s.to_s, 1, 0, 'L', 1) | 
  |  | 186 |                   cv = issue.custom_values.detect { |v| v.custom_field_id == column.custom_field.id } | 
  |  | 187 |                   show_value(cv) | 
  |  | 188 |                 else | 
  |  | 189 |                   value = issue.send(column.name) | 
  |  | 190 |                   if value.is_a?(Date) | 
  |  | 191 |                     format_date(value) | 
  |  | 192 |                   elsif value.is_a?(Time) | 
  |  | 193 |                     format_time(value) | 
  |  | 194 |                   else | 
  |  | 195 |                     value | 
  |  | 196 |                   end | 
  |  | 197 |                 end | 
  |  | 198 |             s.to_s | 
  | 178 | 199 |           end | 
  | 179 |  |           pdf.Ln | 
  |  | 200 |           # render it off-page to find the max height used | 
  |  | 201 |           base_x = pdf.GetX | 
  |  | 202 |           base_y = pdf.GetY | 
  |  | 203 |           pdf.SetY(2 * page_height) | 
  |  | 204 |           max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height) | 
  |  | 205 |           pdf.SetXY(base_x, base_y) | 
  |  | 206 |  | 
  |  | 207 |           # make new page if it doesn't fit on the current one | 
  |  | 208 |           space_left = page_height - base_y - bottom_margin | 
  |  | 209 |           if max_height > space_left | 
  |  | 210 |             pdf.AddPage("L") | 
  |  | 211 |             base_x = pdf.GetX | 
  |  | 212 |             base_y = pdf.GetY | 
  |  | 213 |           end | 
  |  | 214 |  | 
  |  | 215 |           # write the cells on page | 
  |  | 216 |           pdf.Cell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1) | 
  |  | 217 |           issues_to_pdf_write_cells(pdf, col_values, col_width, row_height) | 
  |  | 218 |           issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width) | 
  |  | 219 |           pdf.SetY(base_y + max_height); | 
  | 180 | 220 |         end | 
  |  | 221 |  | 
  | 181 | 222 |         if issues.size == Setting.issues_export_limit.to_i | 
  | 182 |  |           pdf.SetFontStyle('B',10) | 
  |  | 223 |           pdf.SetFontStyle('B', 10) | 
  | 183 | 224 |           pdf.Cell(0, row_height, '...') | 
  | 184 | 225 |         end | 
  | 185 | 226 |         pdf.Output | 
  | 186 | 227 |       end | 
  | 187 |  |        | 
  |  | 228 |  | 
  |  | 229 |  | 
  |  | 230 |       # Renders MultiCells and returns the maximum height used | 
  |  | 231 |       def issues_to_pdf_write_cells(pdf, col_values, col_widths, row_height) | 
  |  | 232 |         base_y = pdf.GetY | 
  |  | 233 |         max_height = row_height | 
  |  | 234 |         col_values.each_with_index do |column, i| | 
  |  | 235 |           col_x = pdf.GetX | 
  |  | 236 |           pdf.MultiCell(col_widths[i], row_height, col_values[i], "T", 'L', 1) | 
  |  | 237 |           max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height | 
  |  | 238 |           pdf.SetXY(col_x + col_widths[i], base_y); | 
  |  | 239 |         end | 
  |  | 240 |         return max_height | 
  |  | 241 |       end | 
  |  | 242 |  | 
  |  | 243 |       # Draw lines to close the row (MultiCell border drawing in not uniform) | 
  |  | 244 |       def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y, id_width, col_widths) | 
  |  | 245 |         col_x = top_x + id_width | 
  |  | 246 |         pdf.Line(col_x, top_y, col_x, lower_y) # id right border | 
  |  | 247 |         col_widths.each do |width| | 
  |  | 248 |           col_x += width | 
  |  | 249 |           pdf.Line(col_x, top_y, col_x, lower_y) # columns right border | 
  |  | 250 |         end | 
  |  | 251 |         pdf.Line(top_x, top_y, top_x, lower_y) # left border | 
  |  | 252 |         pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border | 
  |  | 253 |       end | 
  |  | 254 |  | 
  |  | 255 |  | 
  | 188 | 256 |       # Returns a PDF string of a single issue | 
  | 189 | 257 |       def issue_to_pdf(issue) | 
  | 190 | 258 |         pdf = IFPDF.new(current_language) | 
  | ... | ... |  | 
  | 192 | 260 |         pdf.AliasNbPages | 
  | 193 | 261 |         pdf.footer_date = format_date(Date.today) | 
  | 194 | 262 |         pdf.AddPage | 
  | 195 |  |          | 
  | 196 |  |         pdf.SetFontStyle('B',11)     | 
  | 197 |  |         pdf.Cell(190,10, "#{issue.project} - #{issue.tracker} # #{issue.id}: #{issue.subject}") | 
  |  | 263 |  | 
  |  | 264 |         pdf.SetFontStyle('B', 11) | 
  |  | 265 |         pdf.MultiCell(190, 5, "#{issue.project} - #{issue.tracker} # #{issue.id}: #{issue.subject}") | 
  | 198 | 266 |         pdf.Ln | 
  | 199 |  |          | 
  |  | 267 |  | 
  | 200 | 268 |         y0 = pdf.GetY | 
  | 201 |  |          | 
  | 202 |  |         pdf.SetFontStyle('B',9) | 
  | 203 |  |         pdf.Cell(35,5, l(:field_status) + ":","LT") | 
  | 204 |  |         pdf.SetFontStyle('',9) | 
  | 205 |  |         pdf.Cell(60,5, issue.status.to_s,"RT") | 
  | 206 |  |         pdf.SetFontStyle('B',9) | 
  | 207 |  |         pdf.Cell(35,5, l(:field_priority) + ":","LT") | 
  | 208 |  |         pdf.SetFontStyle('',9) | 
  | 209 |  |         pdf.Cell(60,5, issue.priority.to_s,"RT")         | 
  |  | 269 |  | 
  |  | 270 |         pdf.SetFontStyle('B', 9) | 
  |  | 271 |         pdf.Cell(35, 5, l(:field_status) + ":", "LT") | 
  |  | 272 |         pdf.SetFontStyle('', 9) | 
  |  | 273 |         pdf.Cell(60, 5, issue.status.to_s, "RT") | 
  |  | 274 |         pdf.SetFontStyle('B', 9) | 
  |  | 275 |         pdf.Cell(35, 5, l(:field_priority) + ":", "LT") | 
  |  | 276 |         pdf.SetFontStyle('', 9) | 
  |  | 277 |         pdf.Cell(60, 5, issue.priority.to_s, "RT") | 
  | 210 | 278 |         pdf.Ln | 
  | 211 |  |            | 
  | 212 |  |         pdf.SetFontStyle('B',9) | 
  | 213 |  |         pdf.Cell(35,5, l(:field_author) + ":","L") | 
  | 214 |  |         pdf.SetFontStyle('',9) | 
  | 215 |  |         pdf.Cell(60,5, issue.author.to_s,"R") | 
  | 216 |  |         pdf.SetFontStyle('B',9) | 
  | 217 |  |         pdf.Cell(35,5, l(:field_category) + ":","L") | 
  | 218 |  |         pdf.SetFontStyle('',9) | 
  | 219 |  |         pdf.Cell(60,5, issue.category.to_s,"R") | 
  | 220 |  |         pdf.Ln    | 
  | 221 |  |          | 
  | 222 |  |         pdf.SetFontStyle('B',9) | 
  | 223 |  |         pdf.Cell(35,5, l(:field_created_on) + ":","L") | 
  | 224 |  |         pdf.SetFontStyle('',9) | 
  | 225 |  |         pdf.Cell(60,5, format_date(issue.created_on),"R") | 
  | 226 |  |         pdf.SetFontStyle('B',9) | 
  | 227 |  |         pdf.Cell(35,5, l(:field_assigned_to) + ":","L") | 
  | 228 |  |         pdf.SetFontStyle('',9) | 
  | 229 |  |         pdf.Cell(60,5, issue.assigned_to.to_s,"R") | 
  |  | 279 |  | 
  |  | 280 |         pdf.SetFontStyle('B', 9) | 
  |  | 281 |         pdf.Cell(35, 5, l(:field_author) + ":", "L") | 
  |  | 282 |         pdf.SetFontStyle('', 9) | 
  |  | 283 |         pdf.Cell(60, 5, issue.author.to_s, "R") | 
  |  | 284 |         pdf.SetFontStyle('B', 9) | 
  |  | 285 |         pdf.Cell(35, 5, l(:field_category) + ":", "L") | 
  |  | 286 |         pdf.SetFontStyle('', 9) | 
  |  | 287 |         pdf.Cell(60, 5, issue.category.to_s, "R") | 
  |  | 288 |         pdf.Ln | 
  |  | 289 |  | 
  |  | 290 |         pdf.SetFontStyle('B', 9) | 
  |  | 291 |         pdf.Cell(35, 5, l(:field_created_on) + ":", "L") | 
  |  | 292 |         pdf.SetFontStyle('', 9) | 
  |  | 293 |         pdf.Cell(60, 5, format_date(issue.created_on), "R") | 
  |  | 294 |         pdf.SetFontStyle('B', 9) | 
  |  | 295 |         pdf.Cell(35, 5, l(:field_assigned_to) + ":", "L") | 
  |  | 296 |         pdf.SetFontStyle('', 9) | 
  |  | 297 |         pdf.Cell(60, 5, issue.assigned_to.to_s, "R") | 
  | 230 | 298 |         pdf.Ln | 
  | 231 |  |          | 
  | 232 |  |         pdf.SetFontStyle('B',9) | 
  | 233 |  |         pdf.Cell(35,5, l(:field_updated_on) + ":","LB") | 
  | 234 |  |         pdf.SetFontStyle('',9) | 
  | 235 |  |         pdf.Cell(60,5, format_date(issue.updated_on),"RB") | 
  | 236 |  |         pdf.SetFontStyle('B',9) | 
  | 237 |  |         pdf.Cell(35,5, l(:field_due_date) + ":","LB") | 
  | 238 |  |         pdf.SetFontStyle('',9) | 
  | 239 |  |         pdf.Cell(60,5, format_date(issue.due_date),"RB") | 
  |  | 299 |  | 
  |  | 300 |         pdf.SetFontStyle('B', 9) | 
  |  | 301 |         pdf.Cell(35, 5, l(:field_updated_on) + ":", "LB") | 
  |  | 302 |         pdf.SetFontStyle('', 9) | 
  |  | 303 |         pdf.Cell(60, 5, format_date(issue.updated_on), "RB") | 
  |  | 304 |         pdf.SetFontStyle('B', 9) | 
  |  | 305 |         pdf.Cell(35, 5, l(:field_due_date) + ":", "LB") | 
  |  | 306 |         pdf.SetFontStyle('', 9) | 
  |  | 307 |         pdf.Cell(60, 5, format_date(issue.due_date), "RB") | 
  | 240 | 308 |         pdf.Ln | 
  | 241 |  |            | 
  |  | 309 |  | 
  | 242 | 310 |         for custom_value in issue.custom_field_values | 
  | 243 |  |           pdf.SetFontStyle('B',9) | 
  | 244 |  |           pdf.Cell(35,5, custom_value.custom_field.name + ":","L") | 
  | 245 |  |           pdf.SetFontStyle('',9) | 
  | 246 |  |           pdf.MultiCell(155,5, (show_value custom_value),"R") | 
  |  | 311 |           pdf.SetFontStyle('B', 9) | 
  |  | 312 |           pdf.Cell(35, 5, custom_value.custom_field.name + ":", "L") | 
  |  | 313 |           pdf.SetFontStyle('', 9) | 
  |  | 314 |           pdf.MultiCell(155, 5, (show_value custom_value), "R") | 
  | 247 | 315 |         end | 
  | 248 |  |            | 
  | 249 |  |         pdf.SetFontStyle('B',9) | 
  | 250 |  |         pdf.Cell(35,5, l(:field_subject) + ":","LTB") | 
  | 251 |  |         pdf.SetFontStyle('',9) | 
  | 252 |  |         pdf.Cell(155,5, issue.subject,"RTB") | 
  | 253 |  |         pdf.Ln     | 
  | 254 |  |          | 
  | 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 |  |          | 
  |  | 316 |  | 
  |  | 317 |         pdf.SetFontStyle('B', 9) | 
  |  | 318 |         pdf.Cell(35, 5, l(:field_subject) + ":", "LT") | 
  |  | 319 |         pdf.SetFontStyle('', 9) | 
  |  | 320 |         pdf.MultiCell(155, 5, issue.subject, "RT") | 
  |  | 321 |  | 
  |  | 322 |         pdf.SetFontStyle('B', 9) | 
  |  | 323 |         pdf.Cell(35, 5, l(:field_description) + ":", "LT") | 
  |  | 324 |         pdf.SetFontStyle('', 9) | 
  |  | 325 |         pdf.MultiCell(155, 5, @issue.description, "RT") | 
  |  | 326 |  | 
  | 260 | 327 |         pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY) | 
  | 261 |  |         pdf.Line(pdf.GetX, pdf.GetY, 170, pdf.GetY) | 
  |  | 328 |         pdf.Line(pdf.GetX, pdf.GetY, pdf.GetX + 190, pdf.GetY) | 
  | 262 | 329 |         pdf.Ln | 
  | 263 |  |          | 
  |  | 330 |  | 
  | 264 | 331 |         if issue.changesets.any? && User.current.allowed_to?(:view_changesets, issue.project) | 
  | 265 |  |           pdf.SetFontStyle('B',9) | 
  | 266 |  |           pdf.Cell(190,5, l(:label_associated_revisions), "B") | 
  |  | 332 |           pdf.SetFontStyle('B', 9) | 
  |  | 333 |           pdf.Cell(190, 5, l(:label_associated_revisions), "B") | 
  | 267 | 334 |           pdf.Ln | 
  | 268 | 335 |           for changeset in issue.changesets | 
  | 269 |  |             pdf.SetFontStyle('B',8) | 
  | 270 |  |             pdf.Cell(190,5, format_time(changeset.committed_on) + " - " + changeset.author.to_s) | 
  |  | 336 |             pdf.SetFontStyle('B', 8) | 
  |  | 337 |             pdf.Cell(190, 5, format_time(changeset.committed_on) + " - " + changeset.author.to_s) | 
  | 271 | 338 |             pdf.Ln | 
  | 272 | 339 |             unless changeset.comments.blank? | 
  | 273 |  |               pdf.SetFontStyle('',8) | 
  | 274 |  |               pdf.MultiCell(190,5, changeset.comments) | 
  | 275 |  |             end    | 
  |  | 340 |               pdf.SetFontStyle('', 8) | 
  |  | 341 |               pdf.MultiCell(190, 5, changeset.comments) | 
  |  | 342 |             end | 
  | 276 | 343 |             pdf.Ln | 
  | 277 | 344 |           end | 
  | 278 | 345 |         end | 
  | 279 |  |          | 
  | 280 |  |         pdf.SetFontStyle('B',9) | 
  | 281 |  |         pdf.Cell(190,5, l(:label_history), "B") | 
  | 282 |  |         pdf.Ln   | 
  |  | 346 |  | 
  |  | 347 |         pdf.SetFontStyle('B', 9) | 
  |  | 348 |         pdf.Cell(190, 5, l(:label_history), "B") | 
  |  | 349 |         pdf.Ln | 
  | 283 | 350 |         for journal in issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC") | 
  | 284 |  |           pdf.SetFontStyle('B',8) | 
  | 285 |  |           pdf.Cell(190,5, format_time(journal.created_on) + " - " + journal.user.name) | 
  |  | 351 |           pdf.SetFontStyle('B', 8) | 
  |  | 352 |           pdf.Cell(190, 5, format_time(journal.created_on) + " - " + journal.user.name) | 
  | 286 | 353 |           pdf.Ln | 
  | 287 |  |           pdf.SetFontStyle('I',8) | 
  |  | 354 |           pdf.SetFontStyle('I', 8) | 
  | 288 | 355 |           for detail in journal.details | 
  | 289 |  |             pdf.Cell(190,5, "- " + show_detail(detail, true)) | 
  | 290 |  |             pdf.Ln | 
  |  | 356 |             pdf.MultiCell(190, 5, "- " + show_detail(detail, true)) | 
  | 291 | 357 |           end | 
  | 292 | 358 |           if journal.notes? | 
  | 293 |  |             pdf.SetFontStyle('',8) | 
  | 294 |  |             pdf.MultiCell(190,5, journal.notes) | 
  | 295 |  |           end    | 
  |  | 359 |             pdf.Ln unless journal.details.empty? | 
  |  | 360 |             pdf.SetFontStyle('', 8) | 
  |  | 361 |             pdf.MultiCell(190, 5, journal.notes) | 
  |  | 362 |           end | 
  | 296 | 363 |           pdf.Ln | 
  | 297 | 364 |         end | 
  | 298 |  |          | 
  |  | 365 |  | 
  | 299 | 366 |         if issue.attachments.any? | 
  | 300 |  |           pdf.SetFontStyle('B',9) | 
  | 301 |  |           pdf.Cell(190,5, l(:label_attachment_plural), "B") | 
  |  | 367 |           pdf.SetFontStyle('B', 9) | 
  |  | 368 |           pdf.Cell(190, 5, l(:label_attachment_plural), "B") | 
  | 302 | 369 |           pdf.Ln | 
  | 303 | 370 |           for attachment in issue.attachments | 
  | 304 |  |             pdf.SetFontStyle('',8) | 
  | 305 |  |             pdf.Cell(80,5, attachment.filename) | 
  | 306 |  |             pdf.Cell(20,5, number_to_human_size(attachment.filesize),0,0,"R") | 
  | 307 |  |             pdf.Cell(25,5, format_date(attachment.created_on),0,0,"R") | 
  | 308 |  |             pdf.Cell(65,5, attachment.author.name,0,0,"R") | 
  |  | 371 |             pdf.SetFontStyle('', 8) | 
  |  | 372 |             pdf.Cell(80, 5, attachment.filename) | 
  |  | 373 |             pdf.Cell(20, 5, number_to_human_size(attachment.filesize), 0, 0, "R") | 
  |  | 374 |             pdf.Cell(25, 5, format_date(attachment.created_on), 0, 0, "R") | 
  |  | 375 |             pdf.Cell(65, 5, attachment.author.name, 0, 0, "R") | 
  | 309 | 376 |             pdf.Ln | 
  | 310 | 377 |           end | 
  | 311 | 378 |         end | 
  | 312 | 379 |         pdf.Output | 
  | 313 | 380 |       end | 
  | 314 |  |        | 
  |  | 381 |  | 
  | 315 | 382 |       # Returns a PDF string of a gantt chart | 
  | 316 | 383 |       def gantt_to_pdf(gantt, project) | 
  | 317 | 384 |         pdf = IFPDF.new(current_language) | 
  | ... | ... |  | 
  | 319 | 386 |         pdf.AliasNbPages | 
  | 320 | 387 |         pdf.footer_date = format_date(Date.today) | 
  | 321 | 388 |         pdf.AddPage("L") | 
  | 322 |  |         pdf.SetFontStyle('B',12) | 
  |  | 389 |         pdf.SetFontStyle('B', 12) | 
  | 323 | 390 |         pdf.SetX(15) | 
  | 324 | 391 |         pdf.Cell(70, 20, project.to_s) | 
  | 325 | 392 |         pdf.Ln | 
  | 326 |  |         pdf.SetFontStyle('B',9) | 
  | 327 |  |          | 
  |  | 393 |         pdf.SetFontStyle('B', 9) | 
  |  | 394 |  | 
  | 328 | 395 |         subject_width = 100 | 
  | 329 | 396 |         header_heigth = 5 | 
  | 330 |  |          | 
  |  | 397 |  | 
  | 331 | 398 |         headers_heigth = header_heigth | 
  | 332 | 399 |         show_weeks = false | 
  | 333 | 400 |         show_days = false | 
  | 334 |  |          | 
  |  | 401 |  | 
  | 335 | 402 |         if gantt.months < 7 | 
  | 336 | 403 |           show_weeks = true | 
  | 337 | 404 |           headers_heigth = 2*header_heigth | 
  | ... | ... |  | 
  | 340 | 407 |             headers_heigth = 3*header_heigth | 
  | 341 | 408 |           end | 
  | 342 | 409 |         end | 
  | 343 |  |          | 
  |  | 410 |  | 
  | 344 | 411 |         g_width = 280 - subject_width | 
  | 345 | 412 |         zoom = (g_width) / (gantt.date_to - gantt.date_from + 1) | 
  | 346 | 413 |         g_height = 120 | 
  | 347 | 414 |         t_height = g_height + headers_heigth | 
  | 348 |  |          | 
  |  | 415 |  | 
  | 349 | 416 |         y_start = pdf.GetY | 
  | 350 |  |          | 
  |  | 417 |  | 
  | 351 | 418 |         # Months headers | 
  | 352 | 419 |         month_f = gantt.date_from | 
  | 353 | 420 |         left = subject_width | 
  | 354 | 421 |         height = header_heigth | 
  | 355 |  |         gantt.months.times do  | 
  | 356 |  |           width = ((month_f >> 1) - month_f) * zoom  | 
  |  | 422 |         gantt.months.times do | 
  |  | 423 |           width = ((month_f >> 1) - month_f) * zoom | 
  | 357 | 424 |           pdf.SetY(y_start) | 
  | 358 | 425 |           pdf.SetX(left) | 
  | 359 | 426 |           pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C") | 
  | 360 | 427 |           left = left + width | 
  | 361 | 428 |           month_f = month_f >> 1 | 
  | 362 |  |         end   | 
  | 363 |  |          | 
  |  | 429 |         end | 
  |  | 430 |  | 
  | 364 | 431 |         # Weeks headers | 
  | 365 | 432 |         if show_weeks | 
  | 366 | 433 |           left = subject_width | 
  | ... | ... |  | 
  | 386 | 453 |             week_f = week_f+7 | 
  | 387 | 454 |           end | 
  | 388 | 455 |         end | 
  | 389 |  |          | 
  |  | 456 |  | 
  | 390 | 457 |         # Days headers | 
  | 391 | 458 |         if show_days | 
  | 392 | 459 |           left = subject_width | 
  | 393 | 460 |           height = header_heigth | 
  | 394 | 461 |           wday = gantt.date_from.cwday | 
  | 395 |  |           pdf.SetFontStyle('B',7) | 
  | 396 |  |           (gantt.date_to - gantt.date_from + 1).to_i.times do  | 
  |  | 462 |           pdf.SetFontStyle('B', 7) | 
  |  | 463 |           (gantt.date_to - gantt.date_from + 1).to_i.times do | 
  | 397 | 464 |             width = zoom | 
  | 398 | 465 |             pdf.SetY(y_start + 2 * header_heigth) | 
  | 399 | 466 |             pdf.SetX(left) | 
  | ... | ... |  | 
  | 403 | 470 |             wday = 1 if wday > 7 | 
  | 404 | 471 |           end | 
  | 405 | 472 |         end | 
  | 406 |  |          | 
  |  | 473 |  | 
  | 407 | 474 |         pdf.SetY(y_start) | 
  | 408 | 475 |         pdf.SetX(15) | 
  | 409 | 476 |         pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1) | 
  | 410 |  |          | 
  |  | 477 |  | 
  | 411 | 478 |         # Tasks | 
  | 412 | 479 |         top = headers_heigth + y_start | 
  | 413 |  |         pdf.SetFontStyle('B',7) | 
  |  | 480 |         pdf.SetFontStyle('B', 7) | 
  | 414 | 481 |         gantt.events.each do |i| | 
  | 415 | 482 |           pdf.SetY(top) | 
  | 416 | 483 |           pdf.SetX(15) | 
  | 417 |  |            | 
  |  | 484 |  | 
  | 418 | 485 |           text = "" | 
  | 419 | 486 |           if i.is_a? Issue | 
  | 420 | 487 |             text = "#{i.tracker} #{i.id}: #{i.subject}" | 
  | ... | ... |  | 
  | 423 | 490 |           end | 
  | 424 | 491 |           text = "#{i.project} - #{text}" unless project && project == i.project | 
  | 425 | 492 |           pdf.Cell(subject_width-15, 5, text, "LR") | 
  | 426 |  |          | 
  |  | 493 |  | 
  | 427 | 494 |           pdf.SetY(top + 0.2) | 
  | 428 | 495 |           pdf.SetX(subject_width) | 
  | 429 | 496 |           pdf.SetFillColor(255, 255, 255) | 
  | 430 | 497 |           pdf.Cell(g_width, 4.6, "", "LR", 0, "", 1) | 
  | 431 | 498 |           pdf.SetY(top+1.5) | 
  | 432 |  |            | 
  |  | 499 |  | 
  | 433 | 500 |           if i.is_a? Issue | 
  | 434 |  |             i_start_date = (i.start_date >= gantt.date_from ? i.start_date : gantt.date_from ) | 
  | 435 |  |             i_end_date = (i.due_before <= gantt.date_to ? i.due_before : gantt.date_to ) | 
  | 436 |  |              | 
  |  | 501 |             i_start_date = (i.start_date >= gantt.date_from ? i.start_date : gantt.date_from) | 
  |  | 502 |             i_end_date = (i.due_before <= gantt.date_to ? i.due_before : gantt.date_to) | 
  |  | 503 |  | 
  | 437 | 504 |             i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor | 
  | 438 |  |             i_done_date = (i_done_date <= gantt.date_from ? gantt.date_from : i_done_date ) | 
  | 439 |  |             i_done_date = (i_done_date >= gantt.date_to ? gantt.date_to : i_done_date ) | 
  | 440 |  |              | 
  |  | 505 |             i_done_date = (i_done_date <= gantt.date_from ? gantt.date_from : i_done_date) | 
  |  | 506 |             i_done_date = (i_done_date >= gantt.date_to ? gantt.date_to : i_done_date) | 
  |  | 507 |  | 
  | 441 | 508 |             i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today | 
  | 442 |  |              | 
  | 443 |  |             i_left = ((i_start_date - gantt.date_from)*zoom)  | 
  |  | 509 |  | 
  |  | 510 |             i_left = ((i_start_date - gantt.date_from)*zoom) | 
  | 444 | 511 |             i_width = ((i_end_date - i_start_date + 1)*zoom) | 
  | 445 | 512 |             d_width = ((i_done_date - i_start_date)*zoom) | 
  | 446 | 513 |             l_width = ((i_late_date - i_start_date+1)*zoom) if i_late_date | 
  | 447 | 514 |             l_width ||= 0 | 
  | 448 |  |            | 
  |  | 515 |  | 
  | 449 | 516 |             pdf.SetX(subject_width + i_left) | 
  | 450 |  |             pdf.SetFillColor(200,200,200) | 
  |  | 517 |             pdf.SetFillColor(200, 200, 200) | 
  | 451 | 518 |             pdf.Cell(i_width, 2, "", 0, 0, "", 1) | 
  | 452 |  |            | 
  |  | 519 |  | 
  | 453 | 520 |             if l_width > 0 | 
  | 454 | 521 |               pdf.SetY(top+1.5) | 
  | 455 | 522 |               pdf.SetX(subject_width + i_left) | 
  | 456 |  |               pdf.SetFillColor(255,100,100) | 
  |  | 523 |               pdf.SetFillColor(255, 100, 100) | 
  | 457 | 524 |               pdf.Cell(l_width, 2, "", 0, 0, "", 1) | 
  | 458 |  |             end  | 
  |  | 525 |             end | 
  | 459 | 526 |             if d_width > 0 | 
  | 460 | 527 |               pdf.SetY(top+1.5) | 
  | 461 | 528 |               pdf.SetX(subject_width + i_left) | 
  | 462 |  |               pdf.SetFillColor(100,100,255) | 
  |  | 529 |               pdf.SetFillColor(100, 100, 255) | 
  | 463 | 530 |               pdf.Cell(d_width, 2, "", 0, 0, "", 1) | 
  | 464 | 531 |             end | 
  | 465 |  |              | 
  |  | 532 |  | 
  | 466 | 533 |             pdf.SetY(top+1.5) | 
  | 467 | 534 |             pdf.SetX(subject_width + i_left + i_width) | 
  | 468 | 535 |             pdf.Cell(30, 2, "#{i.status} #{i.done_ratio}%") | 
  | 469 | 536 |           else | 
  | 470 |  |             i_left = ((i.start_date - gantt.date_from)*zoom)  | 
  | 471 |  |              | 
  |  | 537 |             i_left = ((i.start_date - gantt.date_from)*zoom) | 
  |  | 538 |  | 
  | 472 | 539 |             pdf.SetX(subject_width + i_left) | 
  | 473 |  |             pdf.SetFillColor(50,200,50) | 
  | 474 |  |             pdf.Cell(2, 2, "", 0, 0, "", 1)  | 
  | 475 |  |          | 
  |  | 540 |             pdf.SetFillColor(50, 200, 50) | 
  |  | 541 |             pdf.Cell(2, 2, "", 0, 0, "", 1) | 
  |  | 542 |  | 
  | 476 | 543 |             pdf.SetY(top+1.5) | 
  | 477 | 544 |             pdf.SetX(subject_width + i_left + 3) | 
  | 478 | 545 |             pdf.Cell(30, 2, "#{i.name}") | 
  | 479 | 546 |           end | 
  | 480 |  |            | 
  |  | 547 |  | 
  | 481 | 548 |           top = top + 5 | 
  | 482 | 549 |           pdf.SetDrawColor(200, 200, 200) | 
  | 483 | 550 |           pdf.Line(15, top, subject_width+g_width, top) | 
  | ... | ... |  | 
  | 488 | 555 |           end | 
  | 489 | 556 |           pdf.SetDrawColor(0, 0, 0) | 
  | 490 | 557 |         end | 
  | 491 |  |          | 
  |  | 558 |  | 
  | 492 | 559 |         pdf.Line(15, top, subject_width+g_width, top) | 
  | 493 | 560 |         pdf.Output | 
  | 494 | 561 |       end |