@@ -234,6 +234,157 module Redmine | |||||
234 | end |
|
234 | end | |
235 | end |
|
235 | end | |
236 |
|
236 | |||
|
237 | # fetch row values | |||
|
238 | def fetch_row_values(issue, query, level) | |||
|
239 | query.columns.collect do |column| | |||
|
240 | s = if column.is_a?(QueryCustomFieldColumn) | |||
|
241 | cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id} | |||
|
242 | show_value(cv) | |||
|
243 | else | |||
|
244 | value = issue.send(column.name) | |||
|
245 | if column.name == :subject | |||
|
246 | value = " " * level + value | |||
|
247 | end | |||
|
248 | if value.is_a?(Date) | |||
|
249 | format_date(value) | |||
|
250 | elsif value.is_a?(Time) | |||
|
251 | format_time(value) | |||
|
252 | else | |||
|
253 | value | |||
|
254 | end | |||
|
255 | end | |||
|
256 | s.to_s | |||
|
257 | end | |||
|
258 | end | |||
|
259 | ||||
|
260 | # calculate columns width | |||
|
261 | def calc_col_width(issues, query, table_width, pdf) | |||
|
262 | # calculate statistics | |||
|
263 | # by captions | |||
|
264 | pdf.SetFontStyle('B',8) | |||
|
265 | col_padding = pdf.GetStringWidth('OO') | |||
|
266 | col_width_min = query.columns.map {|v| pdf.GetStringWidth(v.caption) + col_padding} | |||
|
267 | col_width_max = Array.new(col_width_min) | |||
|
268 | col_width_avg = Array.new(col_width_min) | |||
|
269 | word_width_max = query.columns.map {|c| | |||
|
270 | n = 10 | |||
|
271 | c.caption.split.each {|w| | |||
|
272 | x = pdf.GetStringWidth(w) + col_padding | |||
|
273 | n = x if n < x | |||
|
274 | } | |||
|
275 | n | |||
|
276 | } | |||
|
277 | ||||
|
278 | # by properties of issues | |||
|
279 | pdf.SetFontStyle('',8) | |||
|
280 | col_padding = pdf.GetStringWidth('OO') | |||
|
281 | k = 1 | |||
|
282 | issue_list(issues) {|issue, level| | |||
|
283 | k += 1 | |||
|
284 | values = fetch_row_values(issue, query, level) | |||
|
285 | values.each_with_index {|v,i| | |||
|
286 | n = pdf.GetStringWidth(v) + col_padding | |||
|
287 | col_width_max[i] = n if col_width_max[i] < n | |||
|
288 | col_width_min[i] = n if col_width_min[i] > n | |||
|
289 | col_width_avg[i] += n | |||
|
290 | v.split.each {|w| | |||
|
291 | x = pdf.GetStringWidth(w) + col_padding | |||
|
292 | word_width_max[i] = x if word_width_max[i] < x | |||
|
293 | } | |||
|
294 | } | |||
|
295 | } | |||
|
296 | col_width_avg.map! {|x| x / k} | |||
|
297 | ||||
|
298 | # calculate columns width | |||
|
299 | ratio = table_width / col_width_avg.inject(0) {|s,w| s += w} | |||
|
300 | col_width = col_width_avg.map {|w| w * ratio} | |||
|
301 | ||||
|
302 | # correct max word width if too many columns | |||
|
303 | ratio = table_width / word_width_max.inject(0) {|s,w| s += w} | |||
|
304 | word_width_max.map! {|v| v * ratio} if ratio < 1 | |||
|
305 | ||||
|
306 | # correct and lock width of some columns | |||
|
307 | done = 1 | |||
|
308 | col_fix = [] | |||
|
309 | col_width.each_with_index do |w,i| | |||
|
310 | if w > col_width_max[i] | |||
|
311 | col_width[i] = col_width_max[i] | |||
|
312 | col_fix[i] = 1 | |||
|
313 | done = 0 | |||
|
314 | elsif w < word_width_max[i] | |||
|
315 | col_width[i] = word_width_max[i] | |||
|
316 | col_fix[i] = 1 | |||
|
317 | done = 0 | |||
|
318 | else | |||
|
319 | col_fix[i] = 0 | |||
|
320 | end | |||
|
321 | end | |||
|
322 | ||||
|
323 | # iterate while need to correct and lock coluns width | |||
|
324 | while done == 0 | |||
|
325 | # calculate free & locked columns width | |||
|
326 | done = 1 | |||
|
327 | fix_col_width = 0 | |||
|
328 | free_col_width = 0 | |||
|
329 | col_width.each_with_index do |w,i| | |||
|
330 | if col_fix[i] == 1 | |||
|
331 | fix_col_width += w | |||
|
332 | else | |||
|
333 | free_col_width += w | |||
|
334 | end | |||
|
335 | end | |||
|
336 | ||||
|
337 | # calculate column normalizing ratio | |||
|
338 | if free_col_width == 0 | |||
|
339 | ratio = table_width / col_width.inject(0) {|s,w| s += w} | |||
|
340 | else | |||
|
341 | ratio = (table_width - fix_col_width) / free_col_width | |||
|
342 | end | |||
|
343 | ||||
|
344 | # correct columns width | |||
|
345 | col_width.each_with_index do |w,i| | |||
|
346 | if col_fix[i] == 0 | |||
|
347 | col_width[i] = w * ratio | |||
|
348 | ||||
|
349 | # check if column width less then max word width | |||
|
350 | if col_width[i] < word_width_max[i] | |||
|
351 | col_width[i] = word_width_max[i] | |||
|
352 | col_fix[i] = 1 | |||
|
353 | done = 0 | |||
|
354 | elsif col_width[i] > col_width_max[i] | |||
|
355 | col_width[i] = col_width_max[i] | |||
|
356 | col_fix[i] = 1 | |||
|
357 | done = 0 | |||
|
358 | end | |||
|
359 | end | |||
|
360 | end | |||
|
361 | end | |||
|
362 | col_width | |||
|
363 | end | |||
|
364 | ||||
|
365 | def render_table_header(pdf, query, col_width, row_height, col_id_width, table_width) | |||
|
366 | # headers | |||
|
367 | pdf.SetFontStyle('B',8) | |||
|
368 | pdf.SetFillColor(230, 230, 230) | |||
|
369 | ||||
|
370 | # render it background to find the max height used | |||
|
371 | base_x = pdf.GetX | |||
|
372 | base_y = pdf.GetY | |||
|
373 | max_height = issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true) | |||
|
374 | pdf.Rect(base_x, base_y, table_width + col_id_width, max_height, 'FD'); | |||
|
375 | pdf.SetXY(base_x, base_y); | |||
|
376 | ||||
|
377 | # write the cells on page | |||
|
378 | pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1) | |||
|
379 | issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true) | |||
|
380 | issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width) | |||
|
381 | pdf.SetY(base_y + max_height); | |||
|
382 | ||||
|
383 | # rows | |||
|
384 | pdf.SetFontStyle('',8) | |||
|
385 | pdf.SetFillColor(255, 255, 255) | |||
|
386 | end | |||
|
387 | ||||
237 | # Returns a PDF string of a list of issues |
|
388 | # Returns a PDF string of a list of issues | |
238 | def issues_to_pdf(issues, project, query) |
|
389 | def issues_to_pdf(issues, project, query) | |
239 | pdf = ITCPDF.new(current_language) |
|
390 | pdf = ITCPDF.new(current_language) | |
@@ -251,77 +402,36 module Redmine | |||||
251 | right_margin = 10 |
|
402 | right_margin = 10 | |
252 | bottom_margin = 20 |
|
403 | bottom_margin = 20 | |
253 | col_id_width = 10 |
|
404 | col_id_width = 10 | |
254 |
row_height = |
|
405 | row_height = 4 | |
255 |
|
406 | |||
256 | # column widths |
|
407 | # column widths | |
257 | table_width = page_width - right_margin - 10 # fixed left margin |
|
408 | table_width = page_width - right_margin - 10 # fixed left margin | |
258 | col_width = [] |
|
409 | col_width = [] | |
259 | unless query.columns.empty? |
|
410 | unless query.columns.empty? | |
260 | col_width = query.columns.collect do |c| |
|
411 | col_width = calc_col_width(issues, query, table_width - col_id_width, pdf) | |
261 | (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) && |
|
412 | table_width = col_width.inject(0) {|s,v| s += v} | |
262 | ['string', 'text'].include?(c.custom_field.field_format))) ? 4.0 : 1.0 |
|
|||
263 | end |
|
|||
264 | ratio = (table_width - col_id_width) / col_width.inject(0) {|s,w| s += w} |
|
|||
265 | col_width = col_width.collect {|w| w * ratio} |
|
|||
266 | end |
|
413 | end | |
267 |
|
414 | |||
268 | # title |
|
415 | # title | |
269 | pdf.SetFontStyle('B',11) |
|
416 | pdf.SetFontStyle('B',11) | |
270 | pdf.RDMCell(190,10, title) |
|
417 | pdf.RDMCell(190,10, title) | |
271 | pdf.Ln |
|
418 | pdf.Ln | |
272 |
|
419 | render_table_header(pdf, query, col_width, row_height, col_id_width, table_width) | ||
273 | # headers |
|
|||
274 | pdf.SetFontStyle('B',8) |
|
|||
275 | pdf.SetFillColor(230, 230, 230) |
|
|||
276 |
|
||||
277 | # render it background to find the max height used |
|
|||
278 | base_x = pdf.GetX |
|
|||
279 | base_y = pdf.GetY |
|
|||
280 | max_height = issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true) |
|
|||
281 | pdf.Rect(base_x, base_y, table_width, max_height, 'FD'); |
|
|||
282 | pdf.SetXY(base_x, base_y); |
|
|||
283 |
|
||||
284 | # write the cells on page |
|
|||
285 | pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1) |
|
|||
286 | issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true) |
|
|||
287 | issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width) |
|
|||
288 | pdf.SetY(base_y + max_height); |
|
|||
289 |
|
||||
290 | # rows |
|
|||
291 | pdf.SetFontStyle('',8) |
|
|||
292 | pdf.SetFillColor(255, 255, 255) |
|
|||
293 | previous_group = false |
|
420 | previous_group = false | |
294 | issue_list(issues) do |issue, level| |
|
421 | issue_list(issues) do |issue, level| | |
295 | if query.grouped? && |
|
422 | if query.grouped? && | |
296 | (group = query.group_by_column.value(issue)) != previous_group |
|
423 | (group = query.group_by_column.value(issue)) != previous_group | |
297 |
pdf.SetFontStyle('B', |
|
424 | pdf.SetFontStyle('B',10) | |
298 | group_label = group.blank? ? 'None' : group.to_s |
|
425 | group_label = group.blank? ? 'None' : group.to_s | |
299 | group_label << " (#{query.issue_count_by_group[group]})" |
|
426 | group_label << " (#{query.issue_count_by_group[group]})" | |
300 | pdf.Bookmark group_label, 0, -1 |
|
427 | pdf.Bookmark group_label, 0, -1 | |
301 |
pdf.RDMCell( |
|
428 | pdf.RDMCell(table_width + col_id_width, row_height * 2, group_label, 1, 1, 'L') | |
302 | pdf.SetFontStyle('',8) |
|
429 | pdf.SetFontStyle('',8) | |
303 | previous_group = group |
|
430 | previous_group = group | |
304 | end |
|
431 | end | |
305 | # fetch all the row values |
|
432 | ||
306 | col_values = query.columns.collect do |column| |
|
433 | # fetch row values | |
307 | s = if column.is_a?(QueryCustomFieldColumn) |
|
434 | col_values = fetch_row_values(issue, query, level) | |
308 | cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id} |
|
|||
309 | show_value(cv) |
|
|||
310 | else |
|
|||
311 | value = issue.send(column.name) |
|
|||
312 | if column.name == :subject |
|
|||
313 | value = " " * level + value |
|
|||
314 | end |
|
|||
315 | if value.is_a?(Date) |
|
|||
316 | format_date(value) |
|
|||
317 | elsif value.is_a?(Time) |
|
|||
318 | format_time(value) |
|
|||
319 | else |
|
|||
320 | value |
|
|||
321 | end |
|
|||
322 | end |
|
|||
323 | s.to_s |
|
|||
324 | end |
|
|||
325 |
|
435 | |||
326 | # render it off-page to find the max height used |
|
436 | # render it off-page to find the max height used | |
327 | base_x = pdf.GetX |
|
437 | base_x = pdf.GetX | |
@@ -334,6 +444,7 module Redmine | |||||
334 | space_left = page_height - base_y - bottom_margin |
|
444 | space_left = page_height - base_y - bottom_margin | |
335 | if max_height > space_left |
|
445 | if max_height > space_left | |
336 | pdf.AddPage("L") |
|
446 | pdf.AddPage("L") | |
|
447 | render_table_header(pdf, query, col_width, row_height, col_id_width, table_width) | |||
337 | base_x = pdf.GetX |
|
448 | base_x = pdf.GetX | |
338 | base_y = pdf.GetY |
|
449 | base_y = pdf.GetY | |
339 | end |
|
450 | end |
General Comments 0
You need to be logged in to leave comments.
Login now