From 14beaaac13637f2153faa3b851f3e849974df556 Mon Sep 17 00:00:00 2001 From: Randy Morgan Date: Sun, 15 Jul 2012 13:29:18 +0900 Subject: refactoring auto width calculation to improve code quality. --- lib/axlsx/workbook/worksheet/cell.rb | 14 ++++++++++ lib/axlsx/workbook/worksheet/col.rb | 15 +++++++++++ lib/axlsx/workbook/worksheet/row.rb | 2 +- lib/axlsx/workbook/worksheet/worksheet.rb | 45 +++++++++++-------------------- test/workbook/worksheet/tc_row.rb | 4 +-- test/workbook/worksheet/tc_worksheet.rb | 2 +- 6 files changed, 48 insertions(+), 34 deletions(-) diff --git a/lib/axlsx/workbook/worksheet/cell.rb b/lib/axlsx/workbook/worksheet/cell.rb index 407aef4d..7b302865 100644 --- a/lib/axlsx/workbook/worksheet/cell.rb +++ b/lib/axlsx/workbook/worksheet/cell.rb @@ -321,8 +321,22 @@ module Axlsx @type == :string && @value.start_with?('=') end + # This is still not perfect... + # - scaling is not linear as font sizes increst + # - different fonts have different mdw and char widths + def autowidth + return if is_formula? || value == nil + mdw = 1.78 #This is the widest width of 0..9 in arial@10px) + font_scale = (font_size/10.0).to_f + ((value.to_s.count(Worksheet.thin_chars) * mdw + 5) / mdw * 256) / 256.0 * font_scale + end + private + def font_size + sz || @styles.fonts[@styles.cellXfs[style].fontId].sz + end + # Utility method for setting inline style attributes def set_run_style( validator, attr, value) return unless INLINE_STYLES.include?(attr.to_s) diff --git a/lib/axlsx/workbook/worksheet/col.rb b/lib/axlsx/workbook/worksheet/col.rb index fb19a1bd..18414b62 100644 --- a/lib/axlsx/workbook/worksheet/col.rb +++ b/lib/axlsx/workbook/worksheet/col.rb @@ -102,6 +102,21 @@ module Axlsx self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}=" end end + + # updates the width for this col based on the cells autowidth and + # an optionally specified fixed width + # @param [Cell] cell The cell to use in updating this col's width + # @param [Integer] fixed_width If this is specified the width is set + # to this value and the cell's attributes are ignored. + # @param [Boolean] use_autowidth If this is false, the cell's + # autowidth value will be ignored. + def update_width(cell, fixed_width=nil, use_autowidth=true) + if fixed_width.is_a? Numeric + self.width = fixed_width + elsif use_autowidth + self.width = [width || 0, cell.autowidth || 0].max + end + end # Serialize this columns data to an xml string # @param [String] str diff --git a/lib/axlsx/workbook/worksheet/row.rb b/lib/axlsx/workbook/worksheet/row.rb index c15b2064..b10d8403 100644 --- a/lib/axlsx/workbook/worksheet/row.rb +++ b/lib/axlsx/workbook/worksheet/row.rb @@ -134,7 +134,7 @@ module Axlsx # @return [Cell] def add_cell(value="", options={}) c = Cell.new(self, value, options) - worksheet.send(:update_column_info, self.cells, [], self.cells.map(&:style)) + worksheet.send(:update_column_info, self.cells, []) c end diff --git a/lib/axlsx/workbook/worksheet/worksheet.rb b/lib/axlsx/workbook/worksheet/worksheet.rb index 54f42553..41b50a4a 100644 --- a/lib/axlsx/workbook/worksheet/worksheet.rb +++ b/lib/axlsx/workbook/worksheet/worksheet.rb @@ -274,10 +274,10 @@ module Axlsx # but at least a few other document readers expect this for conversion # @return [String] the A1:B2 style reference for the first and last row column intersection in the workbook def dimension - "#{dimension_reference(rows.first.cells.first, 'A1')}:#{dimension_reference(rows.last.cells.last, 'AA200')}" + "#{dimension_reference(rows.first.cells.first, 'A1')}:#{dimension_reference(rows.last.cells.last, 'AA200')}" end - # + # # Indicates if gridlines should be shown in the sheet. # This is true by default. # @return [Boolean] @@ -415,8 +415,7 @@ module Axlsx # @option options [Float] height the row's height (in points) def add_row(values=[], options={}) Row.new(self, values, options) - update_column_info @rows.last.cells, options.delete(:widths) ||[], options.delete(:style) || [] - # update_auto_fit_data @rows.last.cells, options.delete(:widths) || [] + update_column_info @rows.last.cells, options.delete(:widths) || [] yield @rows.last if block_given? @rows.last end @@ -470,7 +469,7 @@ module Axlsx # This is a helper method that Lets you specify a fixed width for multiple columns in a worksheet in one go. # Axlsx is sparse, so if you have not set data for a column, you cannot set the width. - # Setting a fixed column width to nil will revert the behaviour back to calculating the width for you. + # Setting a fixed column width to nil will revert the behaviour back to calculating the width for you on the next call to add_row. # @example This would set the first and third column widhts but leave the second column in autofit state. # ws.column_widths 7.2, nil, 3 # @note For updating only a single column it is probably easier to just set the width of the ws.column_info[col_index].width directly @@ -709,36 +708,22 @@ module Axlsx # assigns the owner workbook for this worksheet def workbook=(v) DataTypeValidator.validate "Worksheet.workbook", Workbook, v; @workbook = v; end - # TODO this needs cleanup! - def update_column_info(cells, widths=[], style=[]) - styles = self.workbook.styles - cellXfs, fonts = styles.cellXfs, styles.fonts - sz = 11 + def styles + @styles ||= self.workbook.styles + end + def update_column_info(cells, widths=[]) cells.each_with_index do |cell, index| - @column_info[index] ||= Col.new index+1, index+1 - col = @column_info[index] - width = widths[index] - col.width = width if [Integer, Float, Fixnum].include?(width.class) - c_style = style[index] if [Integer, Fixnum].include?(style[index].class) - next if width == :ignore || (cell.value.is_a?(String) && cell.value.start_with?('=') || cell.value == nil) - if self.workbook.use_autowidth - cell_xf = cellXfs[(c_style || 0)] - font = fonts[(cell_xf.fontId || 0)] - sz = cell.sz || font.sz || sz - col.width = [(col.width || 0), calculate_width(cell.value.to_s, sz)].max - end + col = find_or_create_column_info(index) + next if widths[index] == :ignore + col.update_width(cell, widths[index], workbook.use_autowidth) end end - - # This is still not perfect... - # - scaling is not linear as font sizes increst - # - different fonts have different mdw and char widths - def calculate_width(text, sz) - mdw = 1.78 #This is the widest width of 0..9 in arial@10px) - font_scale = (sz/10.0).to_f - ((text.count(Worksheet.thin_chars) * mdw + 5) / mdw * 256) / 256.0 * font_scale + def find_or_create_column_info(index, fixed_width=nil) + col = @column_info[index] || Col.new(index + 1, index + 1) + @column_info[index] = col if index == @column_info.size + col end def dimension_reference(cell, default) diff --git a/test/workbook/worksheet/tc_row.rb b/test/workbook/worksheet/tc_row.rb index bfd5ae9f..b126845a 100644 --- a/test/workbook/worksheet/tc_row.rb +++ b/test/workbook/worksheet/tc_row.rb @@ -38,8 +38,8 @@ class TestRow < Test::Unit::TestCase end def test_add_cell_autowidth_info - width = @ws.send :calculate_width, 'this is the cell of cells', @ws.workbook.styles.fonts.first.sz - @row.add_cell("this is the cell of cells") + cell = @row.add_cell("this is the cell of cells") + width = cell.send(:autowidth) assert_equal(@ws.column_info.last.width, width) end diff --git a/test/workbook/worksheet/tc_worksheet.rb b/test/workbook/worksheet/tc_worksheet.rb index 3ab80ed2..9f7529c4 100644 --- a/test/workbook/worksheet/tc_worksheet.rb +++ b/test/workbook/worksheet/tc_worksheet.rb @@ -365,7 +365,7 @@ class TestWorksheet < Test::Unit::TestCase def test_set_fixed_width_column @ws.add_row ["mule", "donkey", "horse"], :widths => [20, :ignore, nil] assert(@ws.column_info.size == 3, "a data item for each column") - assert_equal(@ws.column_info[0].width, 20, "adding a row with fixed width updates :fixed attribute") + assert_equal(20, @ws.column_info[0].width, "adding a row with fixed width updates :fixed attribute") assert_equal(@ws.column_info[1].width, nil, ":ignore does not set any data") end -- cgit v1.2.3