Project

General

Profile

Patch #10464 » pdf-r9240.diff

Toshi MARUYAMA, 2012-03-17 06:36

View differences:

lib/redmine/export/pdf.rb
234 234
        end
235 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_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
          col_width_min = query.columns.map {|v| pdf.GetStringWidth(v.caption)}
265
          col_width_max = Array.new(col_width_min)
266
          col_width_avg = Array.new(col_width_min)
267
          word_width_max = query.columns.map {|c| 
268
            n = 10
269
            c.caption.split.each {|w| 
270
              x = pdf.GetStringWidth(w)
271
              n = x if n < x
272
            }
273
            n
274
          }
275

  
276
          #  by properties of issues
277
          k = 1
278
          issue_list(issues) {|issue, level|
279
            k += 1
280
            values = fetch_row_values(issue, query, level)
281
            values.each_with_index {|v,i| 
282
              n = pdf.GetStringWidth(v.rstrip)
283
              col_width_max[i] = n if col_width_max[i] < n
284
              col_width_min[i] = n if col_width_min[i] > n
285
              col_width_avg[i] += n
286
              v.split.each {|w| 
287
                x = pdf.GetStringWidth(w); 
288
                word_width_max[i] = x if word_width_max[i] < x
289
              }
290
            }
291
          }
292
          col_width_avg.map! {|x| x / k}
293

  
294
          # calculate columns width
295
          ratio = table_width / col_width_avg.inject(0) {|s,w| s += w}
296
          col_width = col_width_avg.map {|w| w * ratio}
297

  
298
          # correct max word width if too many columns
299
          ratio = table_width / word_width_max.inject(0) {|s,w| s += w}
300
          word_width_max.map! {|v| v * ratio} if ratio < 1
301

  
302
          # correct and lock width of some columns
303
          done = 1
304
          col_fix = []
305
          col_width.each_with_index do |w,i| 
306
            if w > col_width_max[i]
307
              col_width[i] = col_width_max[i]
308
              col_fix[i] = 1
309
              done = 0
310
            elsif w < word_width_max[i]
311
              col_width[i] = word_width_max[i]
312
              col_fix[i] = 1
313
              done = 0
314
            else
315
              col_fix[i] = 0
316
            end
317
          end
318

  
319
          # iterate while need to correct and lock coluns width
320
          while done == 0 
321
            # calculate free & locked columns width
322
            done = 1
323
            fix_col_width = 0
324
            free_col_width = 0
325
            col_width.each_with_index do |w,i|
326
              if col_fix[i] == 1
327
                fix_col_width += w
328
              else
329
                free_col_width += w
330
              end
331
            end
332

  
333
            # calculate column normalizing ratio
334
            if free_col_width == 0
335
              ratio = table_width / col_width.inject(0) {|s,w| s += w}
336
            else
337
              ratio = (table_width - fix_col_width) / free_col_width
338
            end
339

  
340
            # correct columns width
341
            col_width.each_with_index do |w,i| 
342
              if col_fix[i] == 0
343
                col_width[i] = w * ratio
344

  
345
                # check if column width less then max word width
346
                if col_width[i] < word_width_max[i]
347
                  col_width[i] = word_width_max[i]
348
                  col_fix[i] = 1
349
                  done = 0
350
                end
351
              end
352
            end
353
          end 
354

  
355
          # calculate columns alignment
356
          @col_align = col_width.map {|x| x < table_width / col_width.count ? 'C' : 'L'}
357

  
358
          col_width
359
      end
360

  
361
      def render_table_header(pdf, query, col_width, row_height, col_id_width, table_width)
362
        # headers
363
        pdf.SetFontStyle('B',8)
364
        pdf.SetFillColor(230, 230, 230)
365

  
366
        # render it background to find the max height used
367
        base_x = pdf.GetX
368
        base_y = pdf.GetY
369
        max_height = issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
370
        pdf.Rect(base_x, base_y, table_width, max_height, 'FD');
371
        pdf.SetXY(base_x, base_y);
372

  
373
        # write the cells on page
374
        pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1)
375
        issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
376
        issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
377
        pdf.SetY(base_y + max_height);
378

  
379
        # rows
380
        pdf.SetFontStyle('',8)
381
        pdf.SetFillColor(255, 255, 255)
382
      end
383

  
237 384
      # Returns a PDF string of a list of issues
238 385
      def issues_to_pdf(issues, project, query)
239 386
        pdf = ITCPDF.new(current_language)
......
257 404
        table_width = page_width - right_margin - 10  # fixed left margin
258 405
        col_width = []
259 406
        unless query.columns.empty?
260
          col_width = query.columns.collect do |c|
261
            (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) &&
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}
407
          col_width = calc_col_width(issues, query, table_width - col_id_width, pdf)
408
          table_width = col_width.inject(0) {|s,v| s += v}
266 409
        end
267 410

  
268 411
        # title
......
270 413
        pdf.RDMCell(190,10, title)
271 414
        pdf.Ln
272 415

  
273
        # headers
274
        pdf.SetFontStyle('B',8)
275
        pdf.SetFillColor(230, 230, 230)
416
        render_table_header(pdf, query, col_width, row_height, col_id_width, table_width)
276 417

  
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 418
        previous_group = false
294 419
        issue_list(issues) do |issue, level|
295 420
          if query.grouped? &&
......
299 424
            group_label << " (#{query.issue_count_by_group[group]})"
300 425
            pdf.Bookmark group_label, 0, -1
301 426
            pdf.RDMCell(277, row_height, group_label, 1, 1, 'L')
427
            ## ***** conflict
428
            # pdf.SetFontStyle('B',10)
429
            # pdf.RDMCell(table_width + col_id_width, row_height * 2,
430
            #   (group.blank? ? '  None' : '  ' + group.to_s) + " (#{query.issue_count_by_group[group]})",
431
            #   1, 1, 'L')
302 432
            pdf.SetFontStyle('',8)
303 433
            previous_group = group
304 434
          end
305
          # fetch all the row values
306
          col_values = query.columns.collect do |column|
307
            s = if column.is_a?(QueryCustomFieldColumn)
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
435

  
436
          # fetch row values
437
          col_values = fetch_row_values(issue, query, level)
325 438

  
326 439
          # render it off-page to find the max height used
327 440
          base_x = pdf.GetX
......
336 449
            pdf.AddPage("L")
337 450
            base_x = pdf.GetX
338 451
            base_y = pdf.GetY
452
            render_table_header(pdf, query, col_width, row_height, col_id_width, table_width)
339 453
          end
340 454

  
341 455
          # write the cells on page
......
355 469
      # Renders MultiCells and returns the maximum height used
356 470
      def issues_to_pdf_write_cells(pdf, col_values, col_widths,
357 471
                                    row_height, head=false)
472
        @col_align ||= []
358 473
        base_y = pdf.GetY
359 474
        max_height = row_height
360 475
        col_values.each_with_index do |column, i|
361 476
          col_x = pdf.GetX
362 477
          if head == true
363
            pdf.RDMMultiCell(col_widths[i], row_height, column.caption, "T", 'L', 1)
478
            pdf.RDMMultiCell(col_widths[i], row_height, column.caption, "T", 'C', 1)
364 479
          else
365
            pdf.RDMMultiCell(col_widths[i], row_height, column, "T", 'L', 1)
480
            @col_align[i] = 'L' if @col_align[i].nil?
481
            pdf.RDMMultiCell(col_widths[i], row_height, column, "T", @col_align[i], 1)
366 482
          end
367 483
          max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height
368 484
          pdf.SetXY(col_x + col_widths[i], base_y);
(5-5/10)