lib/prawn/table/cells.rb in prawn-1.0.0.rc2 vs lib/prawn/table/cells.rb in prawn-1.0.0

- old
+ new

@@ -6,11 +6,10 @@ # # This is free software. Please see the LICENSE and COPYING files for details. module Prawn class Table - # Selects the given rows (0-based) for styling. Returns a Cells object -- # see the documentation on Cells for things you can do with cells. # def rows(row_spec) cells.rows(row_spec) @@ -36,30 +35,32 @@ # # table.rows(1..3).columns(2..4).background_color = 'ff0000' # class Cells < Array + # @group Experimental API + # Limits selection to the given row or rows. +row_spec+ can be anything # that responds to the === operator selecting a set of 0-based row # numbers; most commonly a number or a range. # # table.row(0) # selects first row # table.rows(3..4) # selects rows four and five # def rows(row_spec) - index_cells unless @indexed + index_cells unless defined?(@indexed) && @indexed row_spec = transform_spec(row_spec, @first_row, @row_count) Cells.new(@rows[row_spec] ||= select { |c| row_spec.respond_to?(:include?) ? row_spec.include?(c.row) : row_spec === c.row }) end alias_method :row, :rows # Returns the number of rows in the list. # def row_count - index_cells unless @indexed + index_cells unless defined?(@indexed) && @indexed @row_count end # Limits selection to the given column or columns. +col_spec+ can be # anything that responds to the === operator selecting a set of 0-based @@ -67,22 +68,22 @@ # # table.column(0) # selects first column # table.columns(3..4) # selects columns four and five # def columns(col_spec) - index_cells unless @indexed + index_cells unless defined?(@indexed) && @indexed col_spec = transform_spec(col_spec, @first_column, @column_count) Cells.new(@columns[col_spec] ||= select { |c| - col_spec.respond_to?(:include?) ? + col_spec.respond_to?(:include?) ? col_spec.include?(c.column) : col_spec === c.column }) end alias_method :column, :columns # Returns the number of columns in the list. # def column_count - index_cells unless @indexed + index_cells unless defined?(@indexed) && @indexed @column_count end # Allows you to filter the given cells by arbitrary properties. # @@ -93,16 +94,16 @@ Cells.new(select(&block)) end # Retrieves a cell based on its 0-based row and column. Returns an # individual Cell, not a Cells collection. - # + # # table.cells[0, 0].content # => "First cell content" # def [](row, col) return nil if empty? - index_cells unless @indexed + index_cells unless defined?(@indexed) && @indexed row_array, col_array = @rows[@first_row + row] || [], @columns[@first_column + col] || [] if row_array.length < col_array.length row_array.find { |c| c.column == @first_column + col } else col_array.find { |c| c.row == @first_row + row } @@ -114,11 +115,11 @@ def []=(row, col, cell) # :nodoc: cell.extend(Cell::InTable) cell.row = row cell.column = col - if @indexed + if defined?(@indexed) && @indexed (@rows[row] ||= []) << cell (@columns[col] ||= []) << cell @first_row = row if !@first_row || row < @first_row @first_column = col if !@first_column || col < @first_column @row_count = @rows.size @@ -152,11 +153,10 @@ # Returns the total width of all columns in the selected set. # def width widths = {} each do |cell| - index = cell.column per_cell_width = cell.width_ignoring_span.to_f / cell.colspan cell.colspan.times do |n| widths[cell.column+n] = [widths[cell.column+n], per_cell_width]. compact.max end @@ -171,11 +171,11 @@ end # Returns maximum width that can contain cells in the set. # def max_width - aggregate_cell_values(:column, :max_width_ignoring_span, :min) + aggregate_cell_values(:column, :max_width_ignoring_span, :max) end # Returns the total height of all rows in the selected set. # def height @@ -193,17 +193,17 @@ super end end protected - + # Defers indexing until rows() or columns() is actually called on the # Cells object. Without this, we would needlessly index the leaf nodes of # the object graph, the ones that are only there to be iterated over. # # Make sure to call this before using @rows or @columns. - # + # def index_cells @rows = {} @columns = {} each do |cell| @@ -227,12 +227,56 @@ # Takes the min/max (per +aggregate+) of the result of sending +meth+ to # each cell, grouped by +row_or_column+. # def aggregate_cell_values(row_or_column, meth, aggregate) values = {} + + #calculate values for all cells that do not span accross multiple cells + #this ensures that we don't have a problem if the first line includes + #a cell that spans across multiple cells each do |cell| + #don't take spanned cells + if cell.colspan == 1 and cell.class != Prawn::Table::Cell::SpanDummy + index = cell.send(row_or_column) + values[index] = [values[index], cell.send(meth)].compact.send(aggregate) + end + end + + #if there are only colspanned or rowspanned cells in a table + spanned_width_needs_fixing = true + + each do |cell| index = cell.send(row_or_column) - values[index] = [values[index], cell.send(meth)].compact.send(aggregate) + if cell.colspan > 1 + #calculate current (old) return value before we do anything + old_sum = 0 + cell.colspan.times { |i| + old_sum += values[index+i] unless values[index+i].nil? + } + + #calculate future return value + new_sum = cell.send(meth) * cell.colspan + + #due to float rounding errors we need to ignore a small difference in the new + #and the old sum the same had to be done in + #the column_width_calculator#natural_width + spanned_width_needs_fixing = ((new_sum - old_sum) > Prawn::FLOAT_PRECISION) + + if spanned_width_needs_fixing + #not entirely sure why we need this line, but with it the tests pass + values[index] = [values[index], cell.send(meth)].compact.send(aggregate) + #overwrite the old values with the new ones, but only if all entries existed + entries_exist = true + cell.colspan.times { |i| entries_exist = false if values[index+i].nil? } + cell.colspan.times { |i| + values[index+i] = cell.send(meth) if entries_exist + } + end + else + if spanned_width_needs_fixing && cell.class == Prawn::Table::Cell::SpanDummy + values[index] = [values[index], cell.send(meth)].compact.send(aggregate) + end + end end values.values.inject(0, &:+) end # Transforms +spec+, a column / row specification, into an object that