From 03d16b3a31a6c5195e38e0a99596aa6ae4b07911 Mon Sep 17 00:00:00 2001 From: Hartwig Brandl Date: Tue, 19 Nov 2013 16:02:54 +0100 Subject: [PATCH] fixes colspan width calculations, e.g. #407 --- lib/prawn/table.rb | 31 ++++-------- lib/prawn/table/column_width_calculator.rb | 55 ++++++++++++++++++++++ spec/table_spec.rb | 25 +++++++++- 3 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 lib/prawn/table/column_width_calculator.rb diff --git a/lib/prawn/table.rb b/lib/prawn/table.rb index 4e0fa811b..d10b310f1 100644 --- a/lib/prawn/table.rb +++ b/lib/prawn/table.rb @@ -6,13 +6,14 @@ # # This is free software. Please see the LICENSE and COPYING files for details. -require 'prawn/table/cells' -require 'prawn/table/cell' -require 'prawn/table/cell/in_table' -require 'prawn/table/cell/text' -require 'prawn/table/cell/subtable' -require 'prawn/table/cell/image' -require 'prawn/table/cell/span_dummy' +require_relative 'table/column_width_calculator' +require_relative 'table/cells' +require_relative 'table/cell' +require_relative 'table/cell/in_table' +require_relative 'table/cell/text' +require_relative 'table/cell/subtable' +require_relative 'table/cell/image' +require_relative 'table/cell/span_dummy' module Prawn @@ -549,21 +550,7 @@ def assert_proper_table_data(data) # Returns an array of each column's natural (unconstrained) width. # def natural_column_widths - @natural_column_widths ||= - begin - widths_by_column = Hash.new(0) - cells.each do |cell| - next if cell.is_a?(Cell::SpanDummy) - - # Split the width of colspanned cells evenly by columns - width_per_column = cell.width.to_f / cell.colspan - cell.colspan.times do |i| - widths_by_column[cell.column + i] = - [widths_by_column[cell.column + i], width_per_column].max - end - end - widths_by_column.sort_by { |col, _| col }.map { |_, w| w } - end + @natural_column_widths ||= ColumnWidthCalculator.new(cells).natural_widths end # Returns the "natural" (unconstrained) width of the table. This may be diff --git a/lib/prawn/table/column_width_calculator.rb b/lib/prawn/table/column_width_calculator.rb new file mode 100644 index 000000000..fbdd06c71 --- /dev/null +++ b/lib/prawn/table/column_width_calculator.rb @@ -0,0 +1,55 @@ +module Prawn + class Table + class ColumnWidthCalculator + def initialize(cells) + @cells = cells + + @widths_by_column = Hash.new(0) + @rows_with_a_span_dummy = Hash.new(false) + end + + def natural_widths + @cells.each do |cell| + @rows_with_a_span_dummy[cell.row] = true if cell.is_a?(Cell::SpanDummy) + end + + #calculate natural column width for all rows that do not include a span dummy + @cells.each do |cell| + unless @rows_with_a_span_dummy[cell.row] + @widths_by_column[cell.column] = + [@widths_by_column[cell.column], cell.width.to_f].max + end + end + + #integrate natural column widths for all rows that do include a span dummy + @cells.each do |cell| + next unless @rows_with_a_span_dummy[cell.row] + #the width of a SpanDummy cell will be calculated by the "mother" cell + next if cell.is_a?(Cell::SpanDummy) + + if cell.colspan == 1 + @widths_by_column[cell.column] = + [@widths_by_column[cell.column], cell.width.to_f].max + else + #calculate the current with of all cells that will be spanned by the current cell + current_width_of_spanned_cells = + @widths_by_column.to_a[cell.column..(cell.column + cell.colspan - 1)] + .collect{|key, value| value}.inject(0, :+) + + #update the Hash only if the new with is at least equal to the old one + if cell.width.to_f > current_width_of_spanned_cells + # Split the width of colspanned cells evenly by columns + width_per_column = cell.width.to_f / cell.colspan + # Update the Hash + cell.colspan.times do |i| + @widths_by_column[cell.column + i] = width_per_column + end + end + end + end + + @widths_by_column.sort_by { |col, _| col }.map { |_, w| w } + end + end + end +end diff --git a/spec/table_spec.rb b/spec/table_spec.rb index cf3a3796e..7edf4ff3a 100644 --- a/spec/table_spec.rb +++ b/spec/table_spec.rb @@ -91,15 +91,36 @@ pdf = Prawn::Document.new first = {:content=>"Foooo fo foooooo",:width=>50,:align=>:center} second = {:content=>"Foooo",:colspan=>2,:width=>70,:align=>:center} - third = {:content=>"fooooooooooo, fooooooooooooo, fooo, foooooo fooooo",:width=>55,:align=>:center} - fourth = {:content=>"Bar",:width=>15,:align=>:center} + third = {:content=>"fooooooooooo, fooooooooooooo, fooo, foooooo fooooo",:width=>50,:align=>:center} + fourth = {:content=>"Bar",:width=>20,:align=>:center} table_content = [[ first, [[second],[third,fourth]] ]] pdf.move_down(20) + table = Prawn::Table.new table_content, pdf pdf.table(table_content) end + + #https://github.com/prawnpdf/prawn/issues/407#issuecomment-28556698 + it "correctly computes column widths with empty cells + colspan" do + data = [['', ''], + [{:content => '', :colspan => 2}] + ] + pdf = Prawn::Document.new + + table = Prawn::Table.new data, pdf, :column_widths => [50, 200] + table.column_widths.should == [50.0, 200.0] + end + + it "illustrates a variant of problem in issue #407 - comment 28556698" do + pdf = Prawn::Document.new + table_data = [["a", "b", "c"], [{:content=>"d", :colspan=>3}]] + column_widths = [50, 60, 400] + + # Before we fixed #407, this line incorrectly raise a CannotFit error + pdf.table(table_data, :column_widths => column_widths) + end end describe "#initialize" do