diff options
57 files changed, 948 insertions, 310 deletions
diff --git a/.travis.yml b/.travis.yml index 0c7b6ffc..e7627047 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ dist: trusty group: beta cache: bundler bundler_args: --without profile - + notifications: irc: "irc.freenode.org#axlsx" email: @@ -15,10 +15,9 @@ notifications: matrix: include: - rvm: 2.1 - - rvm: 2.2.7 - - rvm: 2.3.4 - - rvm: 2.0.0 - - rvm: 2.4.1 + - rvm: 2.2.8 + - rvm: 2.3.5 + - rvm: 2.4.2 - rvm: rbx-3 - rvm: jruby-19mode - rvm: jruby-9.1.17.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0639f3ef..901ab193 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ CHANGELOG --------- +- **April.1.17**:3.0.0-pre + - Support for ruby versions limited to officially supported version + - Updates to dependency gems, especially nokogiri and ruby-zip + - Patch options parsing for two cell anchor + - Remove support for depreciated worksheet members - **November.25.12**:1.3.4 - Support for headers and footers for worksheets - bug fix: Properly escape hyperlink urls @@ -8,7 +8,7 @@ group :test do else gem 'rake' end - gem 'simplecov' + gem 'simplecov', '>= 0.14.1' gem 'test-unit' end @@ -5,8 +5,6 @@ Axlsx: Office Open XML Spreadsheet Generation If you are using axlsx for commercial purposes, or just want to show your appreciation for the gem, please don't hesitate to make a donation. -[](http://www.pledgie.com/campaigns/17814) - **IRC**:[irc.freenode.net / #axlsx](irc://irc.freenode.net/axlsx) **Git**:[http://github.com/randym/axlsx](http://github.com/randym/axlsx) @@ -17,17 +15,17 @@ appreciation for the gem, please don't hesitate to make a donation. **Author**: Randy Morgan -**Copyright**: 2011 - 2013 +**Copyright**: 2011 - 2017 **License**: MIT License -**Latest Version**: 2.0.1 +**Latest Version**: 3.0.0 -**Ruby Version**: 1.9.2, 1.9.3, 2.0.0 +**Ruby Version**: 2.2.7, 2.3.4, 2.4.1 **JRuby Version**: 1.9 modes -**Rubinius Version**: rubinius 2.0.0dev * lower versions may run, this gem always tests against head. +**Rubinius Version**: rubinius 3 * lower versions may run, this gem always tests against head. **Release Date**: September 12th 2013 @@ -9,7 +9,6 @@ task :benchmark do end task :gendoc do - #puts 'yard doc generation disabled until JRuby build native extensions for redcarpet or yard removes the dependency.' system "yardoc" system "yard stats --list-undoc" end diff --git a/axlsx.gemspec b/axlsx.gemspec index 228d5a98..d1a25429 100644 --- a/axlsx.gemspec +++ b/axlsx.gemspec @@ -3,7 +3,7 @@ require File.expand_path('../lib/axlsx/version', __FILE__) Gem::Specification.new do |s| s.name = 'axlsx' s.version = Axlsx::VERSION - s.author = ["Randy Morgan", "Jurriaan Pruis"] + s.authors = ["Randy Morgan", "Jurriaan Pruis"] s.email = '[email protected]' s.homepage = 'https://github.com/randym/axlsx' s.platform = Gem::Platform::RUBY @@ -17,14 +17,14 @@ Gem::Specification.new do |s| s.files = Dir.glob("{lib/**/*,examples/**/*.rb,examples/**/*.jpeg}") + %w{ LICENSE README.md Rakefile CHANGELOG.md .yardopts .yardopts_guide } s.test_files = Dir.glob("{test/**/*}") - s.add_runtime_dependency 'nokogiri', '>= 1.6.6' - s.add_runtime_dependency 'rubyzip', '>= 1.2.1' - s.add_runtime_dependency "htmlentities", "~> 4.3.4" - s.add_runtime_dependency "mimemagic", "~> 0.3" + s.add_runtime_dependency 'nokogiri', '~> 1.8', '>= 1.8.2' + s.add_runtime_dependency 'rubyzip','~> 1.2', '>= 1.2.1' + s.add_runtime_dependency "htmlentities", "~> 4.3", '>= 4.3.4' + s.add_runtime_dependency "mimemagic", '~> 0.3' - s.add_development_dependency 'yard' - s.add_development_dependency 'kramdown' - s.add_development_dependency 'timecop', "~> 0.6.1" + s.add_development_dependency 'yard', "~> 0.9.8" + s.add_development_dependency 'kramdown', '~> 1.16', '>= 1.16.2' + s.add_development_dependency 'timecop', "~> 0.8.1" s.required_ruby_version = '>= 1.9.2' s.require_path = 'lib' end diff --git a/examples/data_validation.rb b/examples/data_validation.rb index 56248ec3..b95a6397 100644 --- a/examples/data_validation.rb +++ b/examples/data_validation.rb @@ -4,47 +4,64 @@ $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" require 'axlsx' p = Axlsx::Package.new -p.workbook.add_worksheet do |ws| - ws.add_data_validation("A10", { - :type => :whole, - :operator => :between, - :formula1 => '5', - :formula2 => '10', - :showErrorMessage => true, - :errorTitle => 'Wrong input', - :error => 'Only values between 5 and 10', - :errorStyle => :information, - :showInputMessage => true, - :promptTitle => 'Be carful!', - :prompt => 'Only values between 5 and 10'}) - - ws.add_data_validation("B10", { - :type => :textLength, - :operator => :greaterThan, - :formula1 => '10', - :showErrorMessage => true, - :errorTitle => 'Text is too long', - :error => 'Max text length is 10 characters', - :errorStyle => :stop, - :showInputMessage => true, - :promptTitle => 'Text length', - :prompt => 'Max text length is 10 characters'}) - - 8.times do |i| - ws.add_row [nil, nil, i*2] +p.workbook.add_worksheet do |ws| + + ws.add_row ["between", "lessThan", "bound list", "raw list"] + + 4.times do |i| + ws.add_row [nil, nil, nil, nil, (i+1) * 2] end - - ws.add_data_validation("C10", { - :type => :list, - :formula1 => 'C1:C8', + + ws.add_data_validation("A2:A5", { + :type => :whole, + :operator => :between, + :formula1 => '5', + :formula2 => '10', + :showErrorMessage => true, + :errorTitle => 'Wrong input', + :error => 'Only values between 5 and 10', + :errorStyle => :information, + :showInputMessage => true, + :promptTitle => 'Be careful!', + :prompt => %{We really want a value between 5 and 10, +but it is OK if you want to break the rules. +}}) + + ws.add_data_validation("B1:B5", { + :type => :textLength, + :operator => :lessThan, + :formula1 => '10', + :showErrorMessage => true, + :errorTitle => 'Text is too long', + :error => 'Max text length is 10 characters', + :errorStyle => :stop, + :showInputMessage => true, + :promptTitle => 'Text length', + :prompt => 'Max text length is 10 characters'}) + + ws.add_data_validation("C2:C5", { + :type => :list, + :formula1 => 'E2:E5', + :showDropDown => false, + :showErrorMessage => true, + :errorTitle => '', + :error => 'Only values from E2:E5', + :errorStyle => :stop, + :showInputMessage => true, + :promptTitle => '', + :prompt => 'Only values from E2:E5'}) + + ws.add_data_validation("D2:D5", { + :type => :list, + :formula1 => '"Red, Orange, NavyBlue"', :showDropDown => false, - :showErrorMessage => true, - :errorTitle => '', - :error => 'Only values from C1:C8', - :errorStyle => :stop, - :showInputMessage => true, - :promptTitle => '', - :prompt => 'Only values from C1:C8'}) + :showErrorMessage => true, + :errorTitle => '', + :error => 'Please use the dropdown selector to choose the value', + :errorStyle => :stop, + :showInputMessage => true, + :prompt => '& Choose the value from the dropdown'}) + end -p.serialize 'data_validation.xlsx'
\ No newline at end of file +p.serialize 'data_validation.xlsx' diff --git a/examples/example.rb b/examples/example.rb index 4d4c2630..9266f830 100755 --- a/examples/example.rb +++ b/examples/example.rb @@ -22,6 +22,7 @@ examples << :format_dates examples << :mbcs examples << :formula examples << :auto_filter +examples << :sheet_protection examples << :data_types examples << :override_data_types examples << :hyperlinks @@ -585,6 +586,7 @@ if examples.include? :defined_name wb.add_worksheet(:name => 'defined name') do |sheet| sheet.add_row [1, 2, 17, '=FOOBAR'] wb.add_defined_name("'defined name'!$C1", :local_sheet_id => sheet.index, :name => 'FOOBAR') + wb.add_defined_name("'defined name'!$A$1:$C$1", :local_sheet_id => sheet.index, :name => '_xlnm.Print_Area') end end @@ -592,10 +594,17 @@ end if examples.include? :sheet_protection unlocked = wb.styles.add_style :locked => false wb.add_worksheet(:name => 'Sheet Protection') do |sheet| - sheet.sheet_protection.password = 'fish' - sheet.add_row [1, 2 ,3] # These cells will be locked - sheet.add_row [4, 5, 6], :style => unlocked # these cells will not! + sheet.sheet_protection do |protection| + protection.password = 'fish' + protection.auto_filter = false + end + + sheet.add_row [1, 2 ,3], :style => unlocked # These cells will be locked + sheet.add_row [4, 5, 6] + sheet.add_row [7, 8, 9] + sheet.auto_filter = "A1:C3" end + end ##Specify page margins and other options for printing @@ -873,3 +882,4 @@ if examples.include? :tab_color p.serialize 'tab_color.xlsx' end ##``` + diff --git a/lib/axlsx.rb b/lib/axlsx.rb index c5d26c2b..23fc6c8b 100644 --- a/lib/axlsx.rb +++ b/lib/axlsx.rb @@ -11,7 +11,6 @@ require 'axlsx/util/serialized_attributes' require 'axlsx/util/options_parser' # to be included with parsable intitites. #require 'axlsx/util/parser.rb' -require 'axlsx/util/string' require 'axlsx/util/mime_type_utils' require 'axlsx/stylesheet/styles.rb' @@ -136,10 +135,14 @@ module Axlsx # @param [String] str The string to process # @return [String] def self.sanitize(str) - str.delete!(CONTROL_CHARS) - str + if str.frozen? + str.delete(CONTROL_CHARS) + else + str.delete!(CONTROL_CHARS) + str + end end - + # If value is boolean return 1 or 0 # else return the value # @param [Object] value The value to process diff --git a/lib/axlsx/drawing/area_chart.rb b/lib/axlsx/drawing/area_chart.rb new file mode 100644 index 00000000..2002840c --- /dev/null +++ b/lib/axlsx/drawing/area_chart.rb @@ -0,0 +1,99 @@ +# encoding: UTF-8 +module Axlsx + + # The AreaChart is a two dimentional line chart (who would have guessed?) that you can add to your worksheet. + # @example Creating a chart + # # This example creates a line in a single sheet. + # require "rubygems" # if that is your preferred way to manage gems! + # require "axlsx" + # + # p = Axlsx::Package.new + # ws = p.workbook.add_worksheet + # ws.add_row ["This is a chart with no data in the sheet"] + # + # chart = ws.add_chart(Axlsx::AreaChart, :start_at=> [0,1], :end_at=>[0,6], :title=>"Most Popular Pets") + # chart.add_series :data => [1, 9, 10], :labels => ["Slimy Reptiles", "Fuzzy Bunnies", "Rottweiler"] + # + # @see Worksheet#add_chart + # @see Worksheet#add_row + # @see Chart#add_series + # @see Series + # @see Package#serialize + class AreaChart < Chart + + # the category axis + # @return [CatAxis] + def cat_axis + axes[:cat_axis] + end + alias :catAxis :cat_axis + + # the category axis + # @return [ValAxis] + def val_axis + axes[:val_axis] + end + alias :valAxis :val_axis + + # must be one of [:percentStacked, :clustered, :standard, :stacked] + # @return [Symbol] + attr_reader :grouping + + # Creates a new line chart object + # @param [GraphicFrame] frame The workbook that owns this chart. + # @option options [Cell, String] title + # @option options [Boolean] show_legend + # @option options [Symbol] grouping + # @see Chart + def initialize(frame, options={}) + @vary_colors = false + @grouping = :standard + super(frame, options) + @series_type = AreaSeries + @d_lbls = nil + end + + # @see grouping + def grouping=(v) + RestrictionValidator.validate "AreaChart.grouping", [:percentStacked, :standard, :stacked], v + @grouping = v + end + + # The node name to use in serialization. As AreaChart is used as the + # base class for Liine3DChart we need to be sure to serialize the + # chart based on the actual class type and not a fixed node name. + # @return [String] + def node_name + path = self.class.to_s + if i = path.rindex('::') + path = path[(i+2)..-1] + end + path[0] = path[0].chr.downcase + path + end + + # Serializes the object + # @param [String] str + # @return [String] + def to_xml_string(str = '') + super(str) do + str << ("<c:" << node_name << ">") + str << ('<c:grouping val="' << grouping.to_s << '"/>') + str << ('<c:varyColors val="' << vary_colors.to_s << '"/>') + @series.each { |ser| ser.to_xml_string(str) } + @d_lbls.to_xml_string(str) if @d_lbls + yield if block_given? + axes.to_xml_string(str, :ids => true) + str << ("</c:" << node_name << ">") + axes.to_xml_string(str) + end + end + + # The axes for this chart. AreaCharts have a category and value + # axis. + # @return [Axes] + def axes + @axes ||= Axes.new(:cat_axis => CatAxis, :val_axis => ValAxis) + end + end +end diff --git a/lib/axlsx/drawing/area_series.rb b/lib/axlsx/drawing/area_series.rb new file mode 100644 index 00000000..c039869e --- /dev/null +++ b/lib/axlsx/drawing/area_series.rb @@ -0,0 +1,110 @@ +# encoding: UTF-8 +module Axlsx + # A AreaSeries defines the title, data and labels for line charts + # @note The recommended way to manage series is to use Chart#add_series + # @see Worksheet#add_chart + # @see Chart#add_series + class AreaSeries < Series + + # The data for this series. + # @return [ValAxisData] + attr_reader :data + + # The labels for this series. + # @return [CatAxisData] + attr_reader :labels + + # The fill color for this series. + # Red, green, and blue is expressed as sequence of hex digits, RRGGBB. A perceptual gamma of 2.2 is used. + # @return [String] + attr_reader :color + + # show markers on values + # @return [Boolean] + attr_reader :show_marker + + # custom marker symbol + # @return [String] + attr_reader :marker_symbol + + # line smoothing on values + # @return [Boolean] + attr_reader :smooth + + # Creates a new series + # @option options [Array, SimpleTypedList] data + # @option options [Array, SimpleTypedList] labels + # @param [Chart] chart + def initialize(chart, options={}) + @show_marker = false + @marker_symbol = options[:marker_symbol] ? options[:marker_symbol] : :default + @smooth = false + @labels, @data = nil, nil + super(chart, options) + @labels = AxDataSource.new(:data => options[:labels]) unless options[:labels].nil? + @data = NumDataSource.new(options) unless options[:data].nil? + end + + # @see color + def color=(v) + @color = v + end + + # @see show_marker + def show_marker=(v) + Axlsx::validate_boolean(v) + @show_marker = v + end + + # @see marker_symbol + def marker_symbol=(v) + Axlsx::validate_marker_symbol(v) + @marker_symbol = v + end + + # @see smooth + def smooth=(v) + Axlsx::validate_boolean(v) + @smooth = v + end + + # Serializes the object + # @param [String] str + # @return [String] + def to_xml_string(str = '') + super(str) do + if color + str << '<c:spPr><a:solidFill>' + str << ('<a:srgbClr val="' << color << '"/>') + str << '</a:solidFill>' + str << '<a:ln w="28800">' + str << '<a:solidFill>' + str << ('<a:srgbClr val="' << color << '"/>') + str << '</a:solidFill>' + str << '</a:ln>' + str << '<a:round/>' + str << '</c:spPr>' + end + + if !@show_marker + str << '<c:marker><c:symbol val="none"/></c:marker>' + elsif @marker_symbol != :default + str << '<c:marker><c:symbol val="' + @marker_symbol.to_s + '"/></c:marker>' + end + + @labels.to_xml_string(str) unless @labels.nil? + @data.to_xml_string(str) unless @data.nil? + str << ('<c:smooth val="' << ((smooth) ? '1' : '0') << '"/>') + end + end + + private + + # assigns the data for this series + def data=(v) DataTypeValidator.validate "Series.data", [NumDataSource], v; @data = v; end + + # assigns the labels for this series + def labels=(v) DataTypeValidator.validate "Series.labels", [AxDataSource], v; @labels = v; end + + end +end diff --git a/lib/axlsx/drawing/bar_chart.rb b/lib/axlsx/drawing/bar_chart.rb new file mode 100644 index 00000000..7efd0ec5 --- /dev/null +++ b/lib/axlsx/drawing/bar_chart.rb @@ -0,0 +1,143 @@ +# encoding: UTF-8 +module Axlsx + + # The BarChart is a three dimentional barchart (who would have guessed?) that you can add to your worksheet. + # @see Worksheet#add_chart + # @see Chart#add_series + # @see Package#serialize + # @see README for an example + class BarChart < Chart + + # the category axis + # @return [CatAxis] + def cat_axis + axes[:cat_axis] + end + alias :catAxis :cat_axis + + # the value axis + # @return [ValAxis] + def val_axis + axes[:val_axis] + end + alias :valAxis :val_axis + + # The direction of the bars in the chart + # must be one of [:bar, :col] + # @return [Symbol] + def bar_dir + @bar_dir ||= :bar + end + alias :barDir :bar_dir + + # space between bar or column clusters, as a percentage of the bar or column width. + # @return [String] + attr_reader :gap_depth + alias :gapDepth :gap_depth + + # space between bar or column clusters, as a percentage of the bar or column width. + # @return [String] + def gap_width + @gap_width ||= 150 + end + alias :gapWidth :gap_width + + #grouping for a column, line, or area chart. + # must be one of [:percentStacked, :clustered, :standard, :stacked] + # @return [Symbol] + def grouping + @grouping ||= :clustered + end + + # The shabe of the bars or columns + # must be one of [:cone, :coneToMax, :box, :cylinder, :pyramid, :pyramidToMax] + # @return [Symbol] + def shape + @shape ||= :box + end + + # validation regex for gap amount percent + GAP_AMOUNT_PERCENT = /0*(([0-9])|([1-9][0-9])|([1-4][0-9][0-9])|500)%/ + + # Creates a new bar chart object + # @param [GraphicFrame] frame The workbook that owns this chart. + # @option options [Cell, String] title + # @option options [Boolean] show_legend + # @option options [Symbol] bar_dir + # @option options [Symbol] grouping + # @option options [String] gap_width + # @option options [String] gap_depth + # @option options [Symbol] shape + # @see Chart + def initialize(frame, options={}) + @vary_colors = true + @gap_width, @gap_depth, @shape = nil, nil, nil + super(frame, options) + @series_type = BarSeries + @d_lbls = nil + end + + # The direction of the bars in the chart + # must be one of [:bar, :col] + def bar_dir=(v) + RestrictionValidator.validate "BarChart.bar_dir", [:bar, :col], v + @bar_dir = v + end + alias :barDir= :bar_dir= + + #grouping for a column, line, or area chart. + # must be one of [:percentStacked, :clustered, :standard, :stacked] + def grouping=(v) + RestrictionValidator.validate "BarChart.grouping", [:percentStacked, :clustered, :standard, :stacked], v + @grouping = v + end + + # space between bar or column clusters, as a percentage of the bar or column width. + def gap_width=(v) + RegexValidator.validate "BarChart.gap_width", GAP_AMOUNT_PERCENT, v + @gap_width=(v) + end + alias :gapWidth= :gap_width= + + # space between bar or column clusters, as a percentage of the bar or column width. + def gap_depth=(v) + RegexValidator.validate "BarChart.gap_didth", GAP_AMOUNT_PERCENT, v + @gap_depth=(v) + end + alias :gapDepth= :gap_depth= + + # The shabe of the bars or columns + # must be one of [:cone, :coneToMax, :box, :cylinder, :pyramid, :pyramidToMax] + def shape=(v) + RestrictionValidator.validate "BarChart.shape", [:cone, :coneToMax, :box, :cylinder, :pyramid, :pyramidToMax], v + @shape = v + end + + # Serializes the object + # @param [String] str + # @return [String] + def to_xml_string(str = '') + super(str) do + str << '<c:barChart>' + str << ('<c:barDir val="' << bar_dir.to_s << '"/>') + str << ('<c:grouping val="' << grouping.to_s << '"/>') + str << ('<c:varyColors val="' << vary_colors.to_s << '"/>') + @series.each { |ser| ser.to_xml_string(str) } + @d_lbls.to_xml_string(str) if @d_lbls + str << ('<c:gapWidth val="' << @gap_width.to_s << '"/>') unless @gap_width.nil? + str << ('<c:gapDepth val="' << @gap_depth.to_s << '"/>') unless @gap_depth.nil? + str << ('<c:shape val="' << @shape.to_s << '"/>') unless @shape.nil? + axes.to_xml_string(str, :ids => true) + str << '</c:barChart>' + axes.to_xml_string(str) + end + end + + # A hash of axes used by this chart. Bar charts have a value and + # category axes specified via axes[:val_axes] and axes[:cat_axis] + # @return [Axes] + def axes + @axes ||= Axes.new(:cat_axis => CatAxis, :val_axis => ValAxis) + end + end +end diff --git a/lib/axlsx/drawing/chart.rb b/lib/axlsx/drawing/chart.rb index de87b91a..d3a7e9ff 100644 --- a/lib/axlsx/drawing/chart.rb +++ b/lib/axlsx/drawing/chart.rb @@ -56,7 +56,7 @@ module Axlsx # Indicates that colors should be varied by datum # @return [Boolean] attr_reader :vary_colors - + # Configures the vary_colors options for this chart # @param [Boolean] v The value to set def vary_colors=(v) Axlsx::validate_boolean(v); @vary_colors = v; end @@ -129,6 +129,8 @@ module Axlsx end # The size of the Title object of the chart. + # @param [String] v The size for the title object + # @see Title def title_size=(v) @title.text_size = v unless v.to_s.empty? end diff --git a/lib/axlsx/drawing/drawing.rb b/lib/axlsx/drawing/drawing.rb index 2b593606..e9da37a5 100644 --- a/lib/axlsx/drawing/drawing.rb +++ b/lib/axlsx/drawing/drawing.rb @@ -9,6 +9,7 @@ module Axlsx require 'axlsx/drawing/line_series.rb' require 'axlsx/drawing/scatter_series.rb' require 'axlsx/drawing/bubble_series.rb' + require 'axlsx/drawing/area_series.rb' require 'axlsx/drawing/scaling.rb' require 'axlsx/drawing/axis.rb' @@ -35,10 +36,12 @@ module Axlsx require 'axlsx/drawing/chart.rb' require 'axlsx/drawing/pie_3D_chart.rb' require 'axlsx/drawing/bar_3D_chart.rb' + require 'axlsx/drawing/bar_chart.rb' require 'axlsx/drawing/line_chart.rb' require 'axlsx/drawing/line_3D_chart.rb' require 'axlsx/drawing/scatter_chart.rb' require 'axlsx/drawing/bubble_chart.rb' + require 'axlsx/drawing/area_chart.rb' require 'axlsx/drawing/picture_locking.rb' require 'axlsx/drawing/pic.rb' diff --git a/lib/axlsx/drawing/one_cell_anchor.rb b/lib/axlsx/drawing/one_cell_anchor.rb index 1ae7c1b9..ffa10a7d 100644 --- a/lib/axlsx/drawing/one_cell_anchor.rb +++ b/lib/axlsx/drawing/one_cell_anchor.rb @@ -7,7 +7,7 @@ module Axlsx class OneCellAnchor include Axlsx::OptionsParser - + # Creates a new OneCellAnchor object and an Pic associated with it. # @param [Drawing] drawing # @option options [Array] start_at the col, row to start at @@ -23,6 +23,7 @@ module Axlsx drawing.anchors << self @from = Marker.new parse_options options + start_at(*options[:start_at]) if options[:start_at] @object = Pic.new(self, options) end diff --git a/lib/axlsx/drawing/pic.rb b/lib/axlsx/drawing/pic.rb index 5a418df0..e8d0ebd7 100644 --- a/lib/axlsx/drawing/pic.rb +++ b/lib/axlsx/drawing/pic.rb @@ -115,7 +115,6 @@ module Axlsx end # providing access to the anchor's width attribute - # @param [Integer] v # @see OneCellAnchor.width def width return unless @anchor.is_a?(OneCellAnchor) @@ -129,7 +128,6 @@ module Axlsx end # providing access to update the anchor's height attribute - # @param [Integer] v # @see OneCellAnchor.width # @note this is a noop if you are using a TwoCellAnchor def height diff --git a/lib/axlsx/drawing/scatter_series.rb b/lib/axlsx/drawing/scatter_series.rb index 6b71227c..b9ca2c1d 100644 --- a/lib/axlsx/drawing/scatter_series.rb +++ b/lib/axlsx/drawing/scatter_series.rb @@ -27,7 +27,7 @@ module Axlsx # Line smoothing between data points # @return [Boolean] attr_reader :smooth - + # Creates a new ScatterSeries def initialize(chart, options={}) @xData, @yData = nil @@ -55,7 +55,7 @@ module Axlsx Axlsx::validate_boolean(v) @smooth = v end - + # @see ln_width def ln_width=(v) @ln_width = v @@ -85,7 +85,7 @@ module Axlsx end if ln_width str << '<c:spPr>' - str << '<a:ln w="' << ln_width << '"/>' + str << '<a:ln w="' << ln_width.to_s << '"/>' str << '</c:spPr>' end @xData.to_xml_string(str) unless @xData.nil? diff --git a/lib/axlsx/drawing/two_cell_anchor.rb b/lib/axlsx/drawing/two_cell_anchor.rb index b18268f7..d422c0ce 100644 --- a/lib/axlsx/drawing/two_cell_anchor.rb +++ b/lib/axlsx/drawing/two_cell_anchor.rb @@ -37,11 +37,16 @@ module Axlsx drawing.anchors << self @from, @to = Marker.new, Marker.new(:col => 5, :row=>10) parse_options options + + # bit of a hack to work around the fact that the coords for start at and end at + # are passed in as an array when specified in intialization options - however + start_at(*options[:start_at]) if options[:start_at] + end_at(*options[:end_at]) if options[:end_at] end # sets the col, row attributes for the from marker. # @note The recommended way to set the start position for graphical - # objects is directly thru the object. + # objects is directly thru the object. # @see Chart#start_at def start_at(x, y=nil) from.coord x, y diff --git a/lib/axlsx/package.rb b/lib/axlsx/package.rb index 8a4f29dc..da661b34 100644 --- a/lib/axlsx/package.rb +++ b/lib/axlsx/package.rb @@ -1,4 +1,4 @@ -# encoding: UTF-8 +# encoding: utf-8 module Axlsx # Package is responsible for managing all the bits and peices that Open Office XML requires to make a valid # xlsx document including valdation and serialization. @@ -147,11 +147,7 @@ module Axlsx errors = [] parts.each do |part| unless part[:schema].nil? - if part[:doc].is_a? String - errors.concat validate_single_doc(part[:schema], part[:doc]) - else - errors.concat validate_single_doc(part[:schema], part[:doc].to_xml_string) - end + errors.concat validate_single_doc(part[:schema], part[:doc].to_xml_string) end end errors @@ -165,20 +161,13 @@ module Axlsx def write_parts(zip) p = parts p.each do |part| - #next unless part[:entry] == CORE_PN unless part[:doc].nil? zip.put_next_entry(zip_entry_for_part(part)) - if part[:doc].is_a? String - entry = ['1.9.2', '1.9.3'].include?(RUBY_VERSION) ? part[:doc].force_encoding('BINARY') : part[:doc] - zip.puts(entry) - else - part[:doc].to_xml_string(zip) - end + part[:doc].to_xml_string(zip) end unless part[:path].nil? zip.put_next_entry(zip_entry_for_part(part)) - # binread for 1.9.3 - zip.write IO.respond_to?(:binread) ? IO.binread(part[:path]) : IO.read(part[:path]) + zip.write IO.read(part[:path]) end end zip @@ -253,7 +242,9 @@ module Axlsx parts << {:entry => "xl/#{sheet.rels_pn}", :doc => sheet.relationships, :schema => RELS_XSD} parts << {:entry => "xl/#{sheet.pn}", :doc => sheet, :schema => SML_XSD} end - parts + + # Sort parts for correct MIME detection + parts.sort_by { |part| part[:entry] } end # Performs xsd validation for a signle document diff --git a/lib/axlsx/rels/relationships.rb b/lib/axlsx/rels/relationships.rb index b855abcd..5d07f612 100644 --- a/lib/axlsx/rels/relationships.rb +++ b/lib/axlsx/rels/relationships.rb @@ -10,14 +10,17 @@ require 'axlsx/rels/relationship.rb' def initialize super Relationship end - + # The relationship instance for the given source object, or nil if none exists. # @see Relationship#source_obj # @return [Relationship] def for(source_obj) find{ |rel| rel.source_obj == source_obj } end - + + # serialize relationships + # @param [String] str + # @return [String] def to_xml_string(str = '') str << '<?xml version="1.0" encoding="UTF-8"?>' str << ('<Relationships xmlns="' << RELS_R << '">') diff --git a/lib/axlsx/stylesheet/styles.rb b/lib/axlsx/stylesheet/styles.rb index c7283324..2460ab10 100644 --- a/lib/axlsx/stylesheet/styles.rb +++ b/lib/axlsx/stylesheet/styles.rb @@ -128,7 +128,7 @@ module Axlsx # @option options [Boolean] i Indicates if the text should be italicised # @option options [Boolean] u Indicates if the text should be underlined # @option options [Boolean] strike Indicates if the text should be rendered with a strikethrough - # @option options [Boolean] strike Indicates if the text should be rendered with a shadow + # @option options [Boolean] shadow Indicates if the text should be rendered with a shadow # @option options [Integer] charset The character set to use. # @option options [Integer] family The font family to use. # @option options [String] font_name The name of the font to use diff --git a/lib/axlsx/util/constants.rb b/lib/axlsx/util/constants.rb index 64bae270..aa8eb626 100644 --- a/lib/axlsx/util/constants.rb +++ b/lib/axlsx/util/constants.rb @@ -385,13 +385,16 @@ module Axlsx # @see http://www.codetable.net/asciikeycodes pattern = "\x0-\x08\x0B\x0C\x0E-\x1F" pattern = pattern.respond_to?(:encode) ? pattern.encode('UTF-8') : pattern - + # The regular expression used to remove control characters from worksheets CONTROL_CHARS = pattern.freeze - + + # ISO 8601 date recognition ISO_8601_REGEX = /\A(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[0-1]|0[1-9]|[1-2][0-9])T(2[0-3]|[0-1][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[0-1][0-9]):[0-5][0-9])?\Z/.freeze - + + # FLOAT recognition FLOAT_REGEX = /\A[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\Z/.freeze - + + # Numeric recognition NUMERIC_REGEX = /\A[+-]?\d+?\Z/.freeze end diff --git a/lib/axlsx/util/string.rb b/lib/axlsx/util/string.rb deleted file mode 100644 index 8def6e5b..00000000 --- a/lib/axlsx/util/string.rb +++ /dev/null @@ -1,7 +0,0 @@ -unless String.method_defined? :prepend - class String - def prepend(other_str) - insert(0, other_str) - end - end -end
\ No newline at end of file diff --git a/lib/axlsx/util/validators.rb b/lib/axlsx/util/validators.rb index 2b4afd8d..71652ef2 100644 --- a/lib/axlsx/util/validators.rb +++ b/lib/axlsx/util/validators.rb @@ -56,13 +56,13 @@ module Axlsx raise ArgumentError, (ERR_TYPE % [v.inspect, name, types.inspect]) unless other.call(v) end v_class = v.is_a?(Class) ? v : v.class - Array(types).each do |t| + Array(types).each do |t| return if v_class <= t end raise ArgumentError, (ERR_TYPE % [v.inspect, name, types.inspect]) end end - + # Requires that the value can be converted to an integer # @para, [Any] v the value to validate @@ -78,7 +78,8 @@ module Axlsx def self.validate_angle(v) raise ArgumentError, (ERR_ANGLE % v.inspect) unless (v.to_i >= -5400000 && v.to_i <= 5400000) end - + + # Validates an unsigned intger UINT_VALIDATOR = lambda { |arg| arg.respond_to?(:>=) && arg >= 0 } # Requires that the value is a Integer and is greater or equal to 0 @@ -148,7 +149,7 @@ module Axlsx RestrictionValidator.validate "cell run style u", [:none, :single, :double, :singleAccounting, :doubleAccounting], v end - # validates cell style family which must be between 1 and 5 + # validates cell style family which must be between 1 and 5 def self.validate_family(v) RestrictionValidator.validate "cell run style family", 1..5, v end diff --git a/lib/axlsx/version.rb b/lib/axlsx/version.rb index df4bd9ed..10f78875 100644 --- a/lib/axlsx/version.rb +++ b/lib/axlsx/version.rb @@ -1,5 +1,5 @@ module Axlsx # The current version - VERSION = "2.1.0.pre" + VERSION = "3.0.0.pre" end diff --git a/lib/axlsx/workbook/workbook.rb b/lib/axlsx/workbook/workbook.rb index 4ba44adb..7572e51f 100644 --- a/lib/axlsx/workbook/workbook.rb +++ b/lib/axlsx/workbook/workbook.rb @@ -280,6 +280,10 @@ require 'axlsx/workbook/worksheet/selection.rb' worksheet end + # Adds a new WorkbookView + # @return WorkbookViews + # @option options [Hash] options passed into the added WorkbookView + # @see WorkbookView#initialize def add_view(options={}) views << WorkbookView.new(options) end diff --git a/lib/axlsx/workbook/workbook_view.rb b/lib/axlsx/workbook/workbook_view.rb index 11eae571..e0d38cae 100644 --- a/lib/axlsx/workbook/workbook_view.rb +++ b/lib/axlsx/workbook/workbook_view.rb @@ -33,7 +33,7 @@ module Axlsx # Creates a new BookView object - # @params [Hash] options A hash of key/value pairs that will be mapped to this instances attributes. + # @param [Hash] options A hash of key/value pairs that will be mapped to this instances attributes. # @option [Symbol] visibility Specifies visible state of the workbook window. The default value for this attribute is :visible. # @option [Boolean] minimized Specifies a boolean value that indicates whether the workbook window is minimized. # @option [Boolean] show_horizontal_scroll Specifies a boolean value that indicates whether to display the horizontal scroll bar in the user interface. @@ -41,7 +41,7 @@ module Axlsx # @option [Boolean] show_sheet_tabs Specifies a boolean value that indicates whether to display the sheet tabs in the user interface. # @option [Integer] tab_ratio Specifies ratio between the workbook tabs bar and the horizontal scroll bar. # @option [Integer] first_sheet Specifies the index to the first sheet in this book view. - # @option [Integer] active_tab Specifies an unsignedInt that contains the index to the active sheet in this book view. + # @option [Integer] active_tab Specifies an unsignedInt that contains the index to the active sheet in this book view. # @option [Integer] x_window Specifies the X coordinate for the upper left corner of the workbook window. The unit of measurement for this value is twips. # @option [Integer] y_window Specifies the Y coordinate for the upper left corner of the workbook window. The unit of measurement for this value is twips. # @option [Integer] window_width Specifies the width of the workbook window. The unit of measurement for this value is twips. @@ -68,7 +68,9 @@ module Axlsx :show_sheet_tabs, :auto_filter_date_grouping - + # Serialize the WorkbookView + # @param [String] str + # @return [String] def to_xml_string(str = '') str << '<workbookView ' serialized_attributes str diff --git a/lib/axlsx/workbook/worksheet/cell.rb b/lib/axlsx/workbook/worksheet/cell.rb index 777b7812..fb10ad36 100644 --- a/lib/axlsx/workbook/worksheet/cell.rb +++ b/lib/axlsx/workbook/worksheet/cell.rb @@ -65,6 +65,7 @@ module Axlsx :shadow, :condense, :extend, :u, :vertAlign, :sz, :color, :scheme].freeze + # An array of valid cell types CELL_TYPES = [:date, :time, :float, :integer, :richtext, :string, :boolean, :iso_8601, :text].freeze @@ -79,7 +80,7 @@ module Axlsx # @return [Row] attr_reader :row - # The cell's data type. Currently only six types are supported, :date, :time, :float, :integer, :string and :boolean. + # The cell's data type. # Changing the type for a cell will recast the value into that type. If no type option is specified in the constructor, the type is # automatically determed. # @see Cell#cell_type_from_value @@ -348,6 +349,8 @@ module Axlsx # returns the name of the cell attr_reader :name + # Attempts to determine the correct width for this cell's content + # @return [Float] def autowidth return if is_formula? || value.nil? if contains_rich_text? diff --git a/lib/axlsx/workbook/worksheet/cell_serializer.rb b/lib/axlsx/workbook/worksheet/cell_serializer.rb index 76a3c386..eb834b08 100644 --- a/lib/axlsx/workbook/worksheet/cell_serializer.rb +++ b/lib/axlsx/workbook/worksheet/cell_serializer.rb @@ -159,8 +159,6 @@ module Axlsx str << ('t="' << serialization_type.to_s << '"') if serialization_type str << ('><v>' << serialization_value.to_s << '</v>') end - - end end end diff --git a/lib/axlsx/workbook/worksheet/cfvos.rb b/lib/axlsx/workbook/worksheet/cfvos.rb index 0b5a21b3..eb44f9d8 100644 --- a/lib/axlsx/workbook/worksheet/cfvos.rb +++ b/lib/axlsx/workbook/worksheet/cfvos.rb @@ -8,6 +8,9 @@ module Axlsx super(Cfvo) end + # Serialize the Cfvo object + # @param [String] str + # @return [String] def to_xml_string(str='') each { |cfvo| cfvo.to_xml_string(str) } end diff --git a/lib/axlsx/workbook/worksheet/cols.rb b/lib/axlsx/workbook/worksheet/cols.rb index d4595f29..c0704343 100644 --- a/lib/axlsx/workbook/worksheet/cols.rb +++ b/lib/axlsx/workbook/worksheet/cols.rb @@ -1,6 +1,6 @@ module Axlsx - # The cols class manages the col object used to manage column widths. + # The cols class manages the col object used to manage column widths. # This is where the magic happens with autowidth class Cols < SimpleTypedList @@ -10,11 +10,14 @@ module Axlsx @worksheet = worksheet end + # Serialize the Cols object + # @param [String] str + # @return [String] def to_xml_string(str = '') return if empty? str << '<cols>' each { |item| item.to_xml_string(str) } - str << '</cols>' + str << '</cols>' end end end diff --git a/lib/axlsx/workbook/worksheet/outline_pr.rb b/lib/axlsx/workbook/worksheet/outline_pr.rb index ae9ccf38..f25ef4fa 100644 --- a/lib/axlsx/workbook/worksheet/outline_pr.rb +++ b/lib/axlsx/workbook/worksheet/outline_pr.rb @@ -18,7 +18,7 @@ module Axlsx :apply_styles # Creates a new OutlinePr object - # @param [Worksheet] worksheet The worksheet that owns this OutlinePr object + # @param [Hash] options used to create the outline_pr def initialize(options = {}) parse_options options end diff --git a/lib/axlsx/workbook/worksheet/pivot_table.rb b/lib/axlsx/workbook/worksheet/pivot_table.rb index fffced0b..55174968 100644 --- a/lib/axlsx/workbook/worksheet/pivot_table.rb +++ b/lib/axlsx/workbook/worksheet/pivot_table.rb @@ -170,12 +170,12 @@ module Axlsx def to_xml_string(str = '') str << '<?xml version="1.0" encoding="UTF-8"?>' str << ('<pivotTableDefinition xmlns="' << XML_NS << '" name="' << name << '" cacheId="' << cache_definition.cache_id.to_s << '" dataOnRows="1" applyNumberFormats="0" applyBorderFormats="0" applyFontFormats="0" applyPatternFormats="0" applyAlignmentFormats="0" applyWidthHeightFormats="1" dataCaption="Data" showMultipleLabel="0" showMemberPropertyTips="0" useAutoFormatting="1" indent="0" compact="0" compactData="0" gridDropZones="1" multipleFieldFilters="0">') - str << ( '<location firstDataCol="1" firstDataRow="1" firstHeaderRow="1" ref="' << ref << '"/>') - str << ( '<pivotFields count="' << header_cells_count.to_s << '">') + str << ('<location firstDataCol="1" firstDataRow="1" firstHeaderRow="1" ref="' << ref << '"/>') + str << ('<pivotFields count="' << header_cells_count.to_s << '">') header_cell_values.each do |cell_value| - str << pivot_field_for(cell_value,!no_subtotals_on_headers.include?(cell_value)) + str << pivot_field_for(cell_value, !no_subtotals_on_headers.include?(cell_value)) end - str << '</pivotFields>' + str << '</pivotFields>' if rows.empty? str << '<rowFields count="1"><field x="-2"/></rowFields>' str << '<rowItems count="2"><i><x/></i> <i i="1"><x v="1"/></i></rowItems>' @@ -263,25 +263,25 @@ module Axlsx def pivot_field_for(cell_ref, subtotal=true) if rows.include? cell_ref if subtotal - '<pivotField axis="axisRow" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1">' + '<items count="1"><item t="default"/></items>' + '</pivotField>' + '<pivotField axis="axisRow" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1"><items count="1"><item t="default"/></items></pivotField>' else - '<pivotField axis="axisRow" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1" defaultSubtotal="0">' + '</pivotField>' + '<pivotField axis="axisRow" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1" defaultSubtotal="0"></pivotField>' end elsif columns.include? cell_ref - '<pivotField axis="axisCol" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1">' + '<items count="1"><item t="default"/></items>' + '</pivotField>' + '<pivotField axis="axisCol" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1"><items count="1"><item t="default"/></items></pivotField>' elsif pages.include? cell_ref - '<pivotField axis="axisPage" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1">' + '<items count="1"><item t="default"/></items>' + '</pivotField>' + '<pivotField axis="axisPage" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1"><items count="1"><item t="default"/></items></pivotField>' elsif data_refs.include? cell_ref - '<pivotField dataField="1" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1">' + '</pivotField>' + '<pivotField dataField="1" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1"></pivotField>' else - '<pivotField compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1">' + '</pivotField>' + '<pivotField compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1"></pivotField>' end end - + def data_refs data.map { |hash| hash[:ref] } end - + def header_range range.gsub(/^(\w+?)(\d+)\:(\w+?)\d+$/, '\1\2:\3\2') end diff --git a/lib/axlsx/workbook/worksheet/protected_ranges.rb b/lib/axlsx/workbook/worksheet/protected_ranges.rb index 46a2546b..d274cf80 100644 --- a/lib/axlsx/workbook/worksheet/protected_ranges.rb +++ b/lib/axlsx/workbook/worksheet/protected_ranges.rb @@ -24,11 +24,14 @@ module Axlsx last end + # Serializes the protected ranges + # @param [String] str + # @return [String] def to_xml_string(str = '') return if empty? str << '<protectedRanges>' each { |range| range.to_xml_string(str) } - str << '</protectedRanges>' + str << '</protectedRanges>' end end end diff --git a/lib/axlsx/workbook/worksheet/rich_text.rb b/lib/axlsx/workbook/worksheet/rich_text.rb index 9124dfbe..81af32b2 100644 --- a/lib/axlsx/workbook/worksheet/rich_text.rb +++ b/lib/axlsx/workbook/worksheet/rich_text.rb @@ -1,32 +1,52 @@ module Axlsx + + # A simple, self serializing class for storing TextRuns class RichText < SimpleTypedList + + # creates a new RichText collection + # @param [String] text -optional The text to use in creating the first RichTextRun + # @param [Object] options -optional The options to use in creating the first RichTextRun + # @yield [RichText] self def initialize(text = nil, options={}) super(RichTextRun) add_run(text, options) unless text.nil? yield self if block_given? end + # The cell that owns this RichText collection attr_reader :cell - + + # Assign the cell for this RichText collection + # @param [Cell] cell The cell which all RichTextRuns in the collection will belong to def cell=(cell) @cell = cell each { |run| run.cell = cell } end - + + # Calculates the longest autowidth of the RichTextRuns in this collection + # @return [Number] def autowidth widtharray = [0] # Are arrays the best way of solving this problem? each { |run| run.autowidth(widtharray) } widtharray.max end + # Creates and adds a RichTextRun to this collectino + # @param [String] text The text to use in creating a new RichTextRun + # @param [Object] options The options to use in creating the new RichTextRun def add_run(text, options={}) self << RichTextRun.new(text, options) end - + + # The RichTextRuns we own + # @return [RichText] def runs self end + # renders the RichTextRuns in this collection + # @param [String] str + # @return [String] def to_xml_string(str='') each{ |run| run.to_xml_string(str) } str diff --git a/lib/axlsx/workbook/worksheet/rich_text_run.rb b/lib/axlsx/workbook/worksheet/rich_text_run.rb index e56dc3ae..40d99bb0 100644 --- a/lib/axlsx/workbook/worksheet/rich_text_run.rb +++ b/lib/axlsx/workbook/worksheet/rich_text_run.rb @@ -1,26 +1,29 @@ module Axlsx + + # The RichTextRun class creates and self serializing text run. class RichTextRun - + include Axlsx::OptionsParser - + attr_reader :value - + + # A list of allowed inline style attributes used for validation INLINE_STYLES = [:font_name, :charset, :family, :b, :i, :strike, :outline, :shadow, :condense, :extend, :u, :vertAlign, :sz, :color, :scheme].freeze - + def initialize(value, options={}) self.value = value - parse_options(options) + parse_options(options) end - + def value=(value) @value = value end - + attr_accessor :cell - + # The inline font_name property for the cell # @return [String] attr_reader :font_name @@ -154,19 +157,9 @@ module Axlsx set_run_style nil, :scheme, v end - # The Shared Strings Table index for this cell - # @return [Integer] - attr_reader :ssti - - # @return [Integer] The cellXfs item index applied to this cell. - # @raise [ArgumentError] Invalid cellXfs id if the value provided is not within cellXfs items range. - def style=(v) - Axlsx::validate_unsigned_int(v) - count = styles.cellXfs.size - raise ArgumentError, "Invalid cellXfs id" unless v < count - @style = v - end - + # Tries to work out the width of the longest line in the run + # @param [Array] widtharray this array is populated with the widths of each line in the run. + # @return [Array] def autowidth(widtharray) return if value.nil? if styles.cellXfs[style].alignment && styles.cellXfs[style].alignment.wrap_text @@ -184,19 +177,22 @@ module Axlsx end widtharray end - + # Utility method for setting inline style attributes def set_run_style(validator, attr, value) return unless INLINE_STYLES.include?(attr.to_sym) Axlsx.send(validator, value) unless validator.nil? self.instance_variable_set :"@#{attr.to_s}", value end - + + # Serializes the RichTextRun + # @param [String] str + # @return [String] def to_xml_string(str = '') valid = RichTextRun::INLINE_STYLES - data = Hash[self.instance_values.map{ |k, v| [k.to_sym, v] }] + data = Hash[self.instance_values.map{ |k, v| [k.to_sym, v] }] data = data.select { |key, value| valid.include?(key) && !value.nil? } - + str << '<r><rPr>' data.keys.each do |key| case key @@ -211,9 +207,9 @@ module Axlsx clean_value = Axlsx::trust_input ? @value.to_s : ::CGI.escapeHTML(Axlsx::sanitize(@value.to_s)) str << ('</rPr><t>' << clean_value << '</t></r>') end - + private - + # Returns the width of a string according to the current style # This is still not perfect... # - scaling is not linear as font sizes increase @@ -221,7 +217,7 @@ module Axlsx font_scale = font_size / 10.0 string.count(Worksheet::THIN_CHARS) * font_scale end - + # we scale the font size if bold style is applied to either the style font or # the cell itself. Yes, it is a bit of a hack, but it is much better than using # imagemagick and loading metrics for every character. @@ -230,15 +226,15 @@ module Axlsx font = styles.fonts[styles.cellXfs[style].fontId] || styles.fonts[0] (font.b || (defined?(@b) && @b)) ? (font.sz * 1.5) : font.sz end - + def style cell.style end - + def styles - cell.row.worksheet.styles + cell.row.worksheet.styles end - + # Converts the value to the correct XML representation (fixes issues with # Numbers) def xml_value value diff --git a/lib/axlsx/workbook/worksheet/tables.rb b/lib/axlsx/workbook/worksheet/tables.rb index 624c96c4..d033212c 100644 --- a/lib/axlsx/workbook/worksheet/tables.rb +++ b/lib/axlsx/workbook/worksheet/tables.rb @@ -20,6 +20,9 @@ module Axlsx map{ |table| Relationship.new(table, TABLE_R, "../#{table.pn}") } end + # renders the tables xml + # @param [String] str + # @return [String] def to_xml_string(str = "") return if empty? str << "<tableParts count='#{size}'>" diff --git a/lib/axlsx/workbook/worksheet/worksheet.rb b/lib/axlsx/workbook/worksheet/worksheet.rb index d39bf069..6fbd384e 100644 --- a/lib/axlsx/workbook/worksheet/worksheet.rb +++ b/lib/axlsx/workbook/worksheet/worksheet.rb @@ -8,7 +8,7 @@ module Axlsx # definition of characters which are less than the maximum width of 0-9 in the default font for use in String#count. # This is used for autowidth calculations THIN_CHARS = '^.acfijklrstxzFIJL()-'.freeze - + # Creates a new worksheet. # @note the recommended way to manage worksheets is Workbook#add_worksheet # @see Workbook#add_worksheet @@ -152,7 +152,7 @@ module Axlsx # cell at a specific index. The block will be called with the row and column # index in the missing cell was found. # @example - # cols { |row_index, column_index| p "warn - row #{row_index} is does not have a cell at #{column_index} + # cols { |row_index, column_index| puts "warn - row #{row_index} does not have a cell at #{column_index}" } def cols(&block) @rows.transpose(&block) end @@ -305,50 +305,6 @@ module Axlsx @sheet_pr ||= SheetPr.new self end - # Indicates if gridlines should be shown in the sheet. - # This is true by default. - # @return [Boolean] - # @deprecated Use SheetView#show_grid_lines= instead. - def show_gridlines=(v) - warn('axlsx::DEPRECIATED: Worksheet#show_gridlines= has been depreciated. This value can be set over SheetView#show_grid_lines=.') - Axlsx::validate_boolean v - sheet_view.show_grid_lines = v - end - - # @see selected - # @return [Boolean] - # @deprecated Use SheetView#tab_selected= instead. - def selected=(v) - warn('axlsx::DEPRECIATED: Worksheet#selected= has been depreciated. This value can be set over SheetView#tab_selected=.') - Axlsx::validate_boolean v - sheet_view.tab_selected = v - end - - # Indicates if the worksheet should show gridlines or not - # @return Boolean - # @deprecated Use SheetView#show_grid_lines instead. - def show_gridlines - warn('axlsx::DEPRECIATED: Worksheet#show_gridlines has been depreciated. This value can get over SheetView#show_grid_lines.') - sheet_view.show_grid_lines - end - - # Indicates if the worksheet is selected in the workbook - # It is possible to have more than one worksheet selected, however it might cause issues - # in some older versions of excel when using copy and paste. - # @return Boolean - # @deprecated Use SheetView#tab_selected instead. - def selected - warn('axlsx::DEPRECIATED: Worksheet#selected has been depreciated. This value can get over SheetView#tab_selected.') - sheet_view.tab_selected - end - - # (see #fit_to_page) - # @return [Boolean] - def fit_to_page=(v) - warn('axlsx::DEPRECIATED: Worksheet#fit_to_page has been depreciated. This value will automatically be set for you when you use PageSetup#fit_to.') - fit_to_page? - end - # The name of the worksheet # The name of a worksheet must be unique in the workbook, and must not exceed 31 characters # @param [String] name @@ -630,7 +586,7 @@ module Axlsx # Returns the cell or cells defined using excel style A1:B3 references. # @param [String|Integer] cell_def the string defining the cell or range of cells, or the rownumber # @return [Cell, Array] - def [] (cell_def) + def [](cell_def) return rows[cell_def] if cell_def.is_a?(Integer) parts = cell_def.split(':').map{ |part| name_to_cell part } if parts.size == 1 diff --git a/lib/axlsx/workbook/worksheet/worksheet_drawing.rb b/lib/axlsx/workbook/worksheet/worksheet_drawing.rb index 08cad1f7..ae03ac0f 100644 --- a/lib/axlsx/workbook/worksheet/worksheet_drawing.rb +++ b/lib/axlsx/workbook/worksheet/worksheet_drawing.rb @@ -1,5 +1,5 @@ module Axlsx - + # This is a utility class for serialing the drawing node in a # worksheet. Drawing objects have their own serialization that exports # a drawing document. This is only for the single node in the @@ -17,7 +17,7 @@ module Axlsx attr_reader :worksheet attr_reader :drawing - + # adds a chart to the drawing object # @param [Class] chart_type The type of chart to add # @param [Hash] options Options to pass on to the drawing and chart @@ -26,17 +26,17 @@ module Axlsx @drawing ||= Drawing.new worksheet drawing.add_chart(chart_type, options) end - + # adds an image to the drawing object - # @param [Hash] options Options to pass on to the drawing and image + # @param [Hash] options Options to pass on to the drawing and image # @see Worksheet#add_image def add_image(options) - @drawing ||= Drawing.new worksheet + @drawing ||= Drawing.new(worksheet) drawing.add_image(options) - end - + end + # helper method to tell us if the drawing has something in it or not - # @return [Boolean] + # @return [Boolean] def has_drawing? @drawing.is_a? Drawing end @@ -45,13 +45,13 @@ module Axlsx # @return [Relationship] def relationship return unless has_drawing? - Relationship.new(self, DRAWING_R, "../#{drawing.pn}") + Relationship.new(self, DRAWING_R, "../#{drawing.pn}") end # Serialize the drawing for the worksheet # @param [String] str def to_xml_string(str = '') - return unless has_drawing? + return unless has_drawing? str << "<drawing r:id='#{relationship.Id}'/>" end end diff --git a/test/drawing/tc_area_chart.rb b/test/drawing/tc_area_chart.rb new file mode 100644 index 00000000..b3013c12 --- /dev/null +++ b/test/drawing/tc_area_chart.rb @@ -0,0 +1,39 @@ +require 'tc_helper.rb' + +class TestAreaChart < Test::Unit::TestCase + + def setup + @p = Axlsx::Package.new + ws = @p.workbook.add_worksheet + @row = ws.add_row ["one", 1, Time.now] + @chart = ws.add_chart Axlsx::AreaChart, :title => "fishery" + end + + def teardown + end + + def test_initialization + assert_equal(@chart.grouping, :standard, "grouping defualt incorrect") + assert_equal(@chart.series_type, Axlsx::AreaSeries, "series type incorrect") + assert(@chart.cat_axis.is_a?(Axlsx::CatAxis), "category axis not created") + assert(@chart.val_axis.is_a?(Axlsx::ValAxis), "value access not created") + end + + def test_grouping + assert_raise(ArgumentError, "require valid grouping") { @chart.grouping = :inverted } + assert_nothing_raised("allow valid grouping") { @chart.grouping = :stacked } + assert(@chart.grouping == :stacked) + end + + def test_to_xml + schema = Nokogiri::XML::Schema(File.open(Axlsx::DRAWING_XSD)) + doc = Nokogiri::XML(@chart.to_xml_string) + errors = [] + schema.validate(doc).each do |error| + errors.push error + puts error.message + end + assert(errors.empty?, "error free validation") + end + +end diff --git a/test/drawing/tc_area_series.rb b/test/drawing/tc_area_series.rb new file mode 100644 index 00000000..00229a39 --- /dev/null +++ b/test/drawing/tc_area_series.rb @@ -0,0 +1,71 @@ +require 'tc_helper.rb' + +class TestAreaSeries < Test::Unit::TestCase + + def setup + p = Axlsx::Package.new + @ws = p.workbook.add_worksheet :name=>"hmmm" + chart = @ws.add_chart Axlsx::AreaChart, :title => "fishery" + @series = chart.add_series( + :data => [0,1,2], + :labels => ["zero", "one", "two"], + :title => "bob", + :color => "#FF0000", + :show_marker => true, + :smooth => true + ) + end + + def test_initialize + assert_equal(@series.title.text, "bob", "series title has been applied") + assert_equal(@series.labels.class, Axlsx::AxDataSource) + assert_equal(@series.data.class, Axlsx::NumDataSource) + end + + def test_show_marker + assert_equal(true, @series.show_marker) + @series.show_marker = false + assert_equal(false, @series.show_marker) + end + + def test_smooth + assert_equal(true, @series.smooth) + @series.smooth = false + assert_equal(false, @series.smooth) + end + + def test_marker_symbol + assert_equal(:default, @series.marker_symbol) + @series.marker_symbol = :circle + assert_equal(:circle, @series.marker_symbol) + end + + def test_to_xml_string + doc = Nokogiri::XML(wrap_with_namespaces(@series)) + assert(doc.xpath("//srgbClr[@val='#{@series.color}']")) + assert_equal(xpath_with_namespaces(doc, "//c:marker").size, 0) + assert(doc.xpath("//smooth")) + + @series.marker_symbol = :diamond + doc = Nokogiri::XML(wrap_with_namespaces(@series)) + assert_equal(xpath_with_namespaces(doc, "//c:marker/c:symbol[@val='diamond']").size, 1) + + @series.show_marker = false + doc = Nokogiri::XML(wrap_with_namespaces(@series)) + assert_equal(xpath_with_namespaces(doc, "//c:marker/c:symbol[@val='none']").size, 1) + end + + def wrap_with_namespaces(series) + '<c:chartSpace xmlns:c="' << + Axlsx::XML_NS_C << + '" xmlns:a="' << + Axlsx::XML_NS_A << + '">' << + series.to_xml_string << + '</c:chartSpace>' + end + + def xpath_with_namespaces(doc, xpath) + doc.xpath(xpath, "a" => Axlsx::XML_NS_A, "c" => Axlsx::XML_NS_C) + end +end diff --git a/test/drawing/tc_bar_chart.rb b/test/drawing/tc_bar_chart.rb new file mode 100644 index 00000000..ca1ca016 --- /dev/null +++ b/test/drawing/tc_bar_chart.rb @@ -0,0 +1,71 @@ +require 'tc_helper.rb' + +class TestBarChart < Test::Unit::TestCase + + def setup + @p = Axlsx::Package.new + ws = @p.workbook.add_worksheet + @row = ws.add_row ["one", 1, Time.now] + @chart = ws.add_chart Axlsx::BarChart, :title => "fishery" + end + + def teardown + end + + def test_initialization + assert_equal(@chart.grouping, :clustered, "grouping defualt incorrect") + assert_equal(@chart.series_type, Axlsx::BarSeries, "series type incorrect") + assert_equal(@chart.bar_dir, :bar, " bar direction incorrect") + assert(@chart.cat_axis.is_a?(Axlsx::CatAxis), "category axis not created") + assert(@chart.val_axis.is_a?(Axlsx::ValAxis), "value access not created") + end + + def test_bar_direction + assert_raise(ArgumentError, "require valid bar direction") { @chart.bar_dir = :left } + assert_nothing_raised("allow valid bar direction") { @chart.bar_dir = :col } + assert(@chart.bar_dir == :col) + end + + def test_grouping + assert_raise(ArgumentError, "require valid grouping") { @chart.grouping = :inverted } + assert_nothing_raised("allow valid grouping") { @chart.grouping = :standard } + assert(@chart.grouping == :standard) + end + + + def test_gapWidth + assert_raise(ArgumentError, "require valid gap width") { @chart.gap_width = 200 } + assert_nothing_raised("allow valid gapWidth") { @chart.gap_width = "200%" } + assert(@chart.gap_width == "200%") + end + + def test_gapDepth + assert_raise(ArgumentError, "require valid gap_depth") { @chart.gap_depth = 200 } + assert_nothing_raised("allow valid gap_depth") { @chart.gap_depth = "200%" } + assert(@chart.gap_depth == "200%") + end + + def test_shape + assert_raise(ArgumentError, "require valid shape") { @chart.shape = :star } + assert_nothing_raised("allow valid shape") { @chart.shape = :cone } + assert(@chart.shape == :cone) + end + + def test_to_xml_string + schema = Nokogiri::XML::Schema(File.open(Axlsx::DRAWING_XSD)) + doc = Nokogiri::XML(@chart.to_xml_string) + errors = [] + schema.validate(doc).each do |error| + errors.push error + puts error.message + end + assert(errors.empty?, "error free validation") + end + + def test_to_xml_string_has_axes_in_correct_order + str = @chart.to_xml_string + cat_axis_position = str.index(@chart.axes[:cat_axis].id.to_s) + val_axis_position = str.index(@chart.axes[:val_axis].id.to_s) + assert(cat_axis_position < val_axis_position, "cat_axis must occur earlier than val_axis in the XML") + end +end diff --git a/test/drawing/tc_chart.rb b/test/drawing/tc_chart.rb index 8b69e73a..64a5435b 100644 --- a/test/drawing/tc_chart.rb +++ b/test/drawing/tc_chart.rb @@ -6,7 +6,7 @@ class TestChart < Test::Unit::TestCase @p = Axlsx::Package.new ws = @p.workbook.add_worksheet @row = ws.add_row ["one", 1, Time.now] - @chart = ws.add_chart Axlsx::Bar3DChart, :title => "fishery" + @chart = ws.add_chart Axlsx::Bar3DChart, :title => "fishery", :bg_color => "000000" end def teardown @@ -27,17 +27,30 @@ class TestChart < Test::Unit::TestCase assert_equal(@chart.title.cell, @row.cells.first) end + def test_style + assert_raise(ArgumentError) { @chart.style = 49 } + assert_nothing_raised { @chart.style = 2 } + assert_equal(@chart.style, 2) + end + def test_to_from_marker_access assert(@chart.to.is_a?(Axlsx::Marker)) assert(@chart.from.is_a?(Axlsx::Marker)) end - def test_style - assert_raise(ArgumentError) { @chart.style = 49 } - assert_nothing_raised { @chart.style = 2 } - assert_equal(@chart.style, 2) + def test_bg_color + assert_raise(ArgumentError) { @chart.bg_color = 2 } + assert_nothing_raised { @chart.bg_color = "FFFFFF" } + assert_equal(@chart.bg_color, "FFFFFF") + end - + + def test_title_size + assert_raise(ArgumentError) { @chart.title_size = 2 } + assert_nothing_raised { @chart.title_size = "100" } + assert_equal(@chart.title.text_size, "100") + end + def test_vary_colors assert_equal(true, @chart.vary_colors) assert_raise(ArgumentError) { @chart.vary_colors = 7 } @@ -63,7 +76,7 @@ class TestChart < Test::Unit::TestCase @chart.start_at [5,6] assert_equal(@chart.graphic_frame.anchor.from.col, 5) assert_equal(@chart.graphic_frame.anchor.from.row, 6) - + end def test_end_at @@ -76,7 +89,7 @@ class TestChart < Test::Unit::TestCase @chart.end_at [10,11] assert_equal(@chart.graphic_frame.anchor.to.col, 10) assert_equal(@chart.graphic_frame.anchor.to.row, 11) - + end def test_add_series @@ -88,13 +101,13 @@ class TestChart < Test::Unit::TestCase def test_pn assert_equal(@chart.pn, "charts/chart1.xml") end - + def test_d_lbls assert_equal(nil, @chart.instance_values[:d_lbls]) @chart.d_lbls.d_lbl_pos = :t assert(@chart.d_lbls.is_a?(Axlsx::DLbls), 'DLbls instantiated on access') end - + def test_to_xml_string schema = Nokogiri::XML::Schema(File.open(Axlsx::DRAWING_XSD)) doc = Nokogiri::XML(@chart.to_xml_string) diff --git a/test/drawing/tc_drawing.rb b/test/drawing/tc_drawing.rb index 7014f1b2..c9095fe7 100644 --- a/test/drawing/tc_drawing.rb +++ b/test/drawing/tc_drawing.rb @@ -59,9 +59,9 @@ class TestDrawing < Test::Unit::TestCase end def test_relationships - chart = @ws.add_chart(Axlsx::Pie3DChart, :title=>"bob", :start_at=>[0,0], :end_at=>[1,1]) + @ws.add_chart(Axlsx::Pie3DChart, :title=>"bob", :start_at=>[0,0], :end_at=>[1,1]) assert_equal(@ws.drawing.relationships.size, 1, "adding a chart adds a relationship") - chart = @ws.add_chart(Axlsx::Pie3DChart, :title=>"nancy", :start_at=>[1,5], :end_at=>[5,10]) + @ws.add_chart(Axlsx::Pie3DChart, :title=>"nancy", :start_at=>[1,5], :end_at=>[5,10]) assert_equal(@ws.drawing.relationships.size, 2, "adding a chart adds a relationship") end diff --git a/test/drawing/tc_pic.rb b/test/drawing/tc_pic.rb index 2c58fcfd..67e6c416 100644 --- a/test/drawing/tc_pic.rb +++ b/test/drawing/tc_pic.rb @@ -9,10 +9,7 @@ class TestPic < Test::Unit::TestCase @test_img_png = File.dirname(__FILE__) + "/../../examples/image1.png" @test_img_gif = File.dirname(__FILE__) + "/../../examples/image1.gif" @test_img_fake = File.dirname(__FILE__) + "/../../examples/image1_fake.jpg" - @image = ws.add_image :image_src => @test_img, :hyperlink => 'https://github.com/randym', :tooltip => "What's up doc?" - end - - def teardown + @image = ws.add_image :image_src => @test_img, :hyperlink => 'https://github.com/randym', :tooltip => "What's up doc?", :opacity => 5 end def test_initialization @@ -40,6 +37,7 @@ class TestPic < Test::Unit::TestCase assert_equal(200, @image.width) end + def test_hyperlink assert_equal(@image.hyperlink.href, "https://github.com/randym") @image.hyperlink = "http://axlsx.blogspot.com" @@ -102,5 +100,4 @@ class TestPic < Test::Unit::TestCase doc = Nokogiri::XML(@image.anchor.drawing.to_xml_string) assert_equal r_id, doc.xpath("//a:blip").first["r:embed"] end - end diff --git a/test/drawing/tc_scatter_series.rb b/test/drawing/tc_scatter_series.rb index 62a338b1..ea3611ab 100644 --- a/test/drawing/tc_scatter_series.rb +++ b/test/drawing/tc_scatter_series.rb @@ -18,28 +18,39 @@ class TestScatterSeries < Test::Unit::TestCase @series = @chart.add_series :xData=>[1,2,4], :yData=>[1,3,9], :title=>"smoothed exponents" assert(@series.smooth, "series is smooth by default on smooth charts") end - + def test_unsmoothed_chart_default_smoothing @chart = @ws.add_chart Axlsx::ScatterChart, :title => "Unsmooth Chart", :scatter_style => :line @series = @chart.add_series :xData=>[1,2,4], :yData=>[1,3,9], :title=>"unsmoothed exponents" assert([email protected], "series is not smooth by default on non-smooth charts") end - + def test_explicit_smoothing @chart = @ws.add_chart Axlsx::ScatterChart, :title => "Unsmooth Chart, Smooth Series", :scatter_style => :line @series = @chart.add_series :xData=>[1,2,4], :yData=>[1,3,9], :title=>"smoothed exponents", :smooth => true assert(@series.smooth, "series is smooth when overriding chart default") - end - + end + def test_explicit_unsmoothing @chart = @ws.add_chart Axlsx::ScatterChart, :title => "Smooth Chart, Unsmooth Series", :scatter_style => :smoothMarker @series = @chart.add_series :xData=>[1,2,4], :yData=>[1,3,9], :title=>"unsmoothed exponents", :smooth => false assert([email protected], "series is not smooth when overriding chart default") - end + end + + def test_ln_width + @chart = @ws.add_chart Axlsx::ScatterChart, :title => "ln width", :scatter_style => :line + @series = @chart.add_series :xData=>[1,2,4], :yData=>[1,3,9], :title=>"ln_width" + @series.ln_width = 12700 + assert_equal(@series.ln_width, 12700, 'line width assigment is allowed') + end def test_to_xml_string + @chart.scatter_style = :line + @series.ln_width = 12700 doc = Nokogiri::XML(@chart.to_xml_string) + assert_equal(12700, @series.ln_width) assert_equal(doc.xpath("//a:srgbClr[@val='#{@series.color}']").size,4) + assert_equal(doc.xpath("//a:ln[@w='#{@series.ln_width}']").length, 1) end end diff --git a/test/drawing/tc_title.rb b/test/drawing/tc_title.rb index 84345150..95e44602 100644 --- a/test/drawing/tc_title.rb +++ b/test/drawing/tc_title.rb @@ -19,6 +19,11 @@ class TestTitle < Test::Unit::TestCase assert(@title.cell == nil) end + def test_initialize_title_size + title = Axlsx::Title.new 'bob', 90 + assert_equal "90", title.text_size + end + def test_text assert_raise(ArgumentError, "text must be a string") { @title.text = 123 } @title.cell = @row.cells.first diff --git a/test/tc_axlsx.rb b/test/tc_axlsx.rb index 99832f63..3d6a2e70 100644 --- a/test/tc_axlsx.rb +++ b/test/tc_axlsx.rb @@ -79,4 +79,25 @@ class TestAxlsx < Test::Unit::TestCase assert_equal([['Z5', 'AA5', 'AB5'], ['Z6', 'AA6', 'AB6']], Axlsx::range_to_a('Z5:AB6')) end + def test_sanitize_frozen_control_strippped + needs_sanitize = "legit\x08".freeze # Backspace control char + + assert_equal(Axlsx.sanitize(needs_sanitize), 'legit', 'should strip control chars') + end + + def test_sanitize_unfrozen_control_strippped + needs_sanitize = "legit\x08" # Backspace control char + sanitized_str = Axlsx.sanitize(needs_sanitize) + + assert_equal(sanitized_str, 'legit', 'should strip control chars') + assert_equal(sanitized_str.object_id, sanitized_str.object_id, 'should preserve object') + end + + def test_sanitize_unfrozen_no_sanitize + legit_str = 'legit' + sanitized_str = Axlsx.sanitize(legit_str) + + assert_equal(sanitized_str, legit_str, 'should preserve value') + assert_equal(sanitized_str.object_id, legit_str.object_id, 'should preserve object') + end end diff --git a/test/tc_helper.rb b/test/tc_helper.rb index 96f545cd..396f19b2 100644 --- a/test/tc_helper.rb +++ b/test/tc_helper.rb @@ -7,4 +7,6 @@ end require 'test/unit' require "timecop" -require "axlsx.rb"
\ No newline at end of file +require "axlsx.rb" +# MIME detection for Microsoft Office 2007+ formats +require 'mimemagic/overlay' diff --git a/test/tc_package.rb b/test/tc_package.rb index 0ad27f7d..d89f35b0 100644 --- a/test/tc_package.rb +++ b/test/tc_package.rb @@ -7,6 +7,12 @@ class TestPackage < Test::Unit::TestCase ws = @package.workbook.add_worksheet ws.add_row ['Can', 'we', 'build it?'] ws.add_row ['Yes!', 'We', 'can!'] + @rt = Axlsx::RichText.new + @rt.add_run "run 1", :b => true, :i => false + ws.add_row [@rt] + + ws.rows.last.add_cell('b', :type => :text) + ws.outline_level_rows 0, 1 ws.outline_level_columns 0, 1 ws.add_hyperlink :ref => ws.rows.first.cells.last, :location => 'https://github.com/randym' @@ -154,6 +160,11 @@ class TestPackage < Test::Unit::TestCase assert package_1.to_stream.string == package_2.to_stream.string, "zip files are not identical" end + def test_serialization_creates_files_with_excel_mime_type + assert_equal(MimeMagic.by_magic(@package.to_stream).type, + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + end + def test_validation assert_equal(@package.validate.size, 0, @package.validate) Axlsx::Workbook.send(:class_variable_set, :@@date1904, 9900) @@ -188,6 +199,7 @@ class TestPackage < Test::Unit::TestCase def test_shared_strings_requires_part @package.use_shared_strings = true + @package.to_stream #ensure all cell_serializer paths are hit p = @package.send(:parts) assert_equal(p.select{ |part| part[:entry] =~/xl\/sharedStrings.xml/}.size, 1, "shared strings table missing") end diff --git a/test/workbook/tc_shared_strings_table.rb b/test/workbook/tc_shared_strings_table.rb index 25a0a0b2..b9347903 100644 --- a/test/workbook/tc_shared_strings_table.rb +++ b/test/workbook/tc_shared_strings_table.rb @@ -4,10 +4,12 @@ class TestSharedStringsTable < Test::Unit::TestCase def setup @p = Axlsx::Package.new :use_shared_strings=>true + ws = @p.workbook.add_worksheet ws.add_row ['a', 1, 'b'] ws.add_row ['b', 1, 'c'] ws.add_row ['c', 1, 'd'] + ws.rows.last.add_cell('b', :type => :text) end def test_workbook_has_shared_strings @@ -16,7 +18,7 @@ class TestSharedStringsTable < Test::Unit::TestCase def test_count sst = @p.workbook.shared_strings - assert_equal(sst.count, 6) + assert_equal(sst.count, 7) end def test_unique_count @@ -54,5 +56,4 @@ class TestSharedStringsTable < Test::Unit::TestCase # assert that the shared string was normalized to remove the control characters assert_not_nil @p.workbook.shared_strings.to_xml_string.index("helloworld") end - end diff --git a/test/workbook/tc_workbook.rb b/test/workbook/tc_workbook.rb index ab400db1..c2f8ebd6 100644 --- a/test/workbook/tc_workbook.rb +++ b/test/workbook/tc_workbook.rb @@ -33,6 +33,12 @@ class TestWorkbook < Test::Unit::TestCase assert_equal(@wb.use_autowidth, false) end + def test_is_reversed + assert_equal(@wb.is_reversed, nil) + assert_raise(ArgumentError) {@wb.is_reversed = 0.1} + assert_nothing_raised {@wb.is_reversed = true } + assert_equal(@wb.use_autowidth, true) + end def test_sheet_by_name_retrieval @wb.add_worksheet(:name=>'foo') @@ -98,7 +104,16 @@ class TestWorkbook < Test::Unit::TestCase end assert(errors.empty?, "error free validation") end - def test_range_requires__valid_sheet + + def test_to_xml_reversed + @wb.is_reversed = true + @wb.add_worksheet(:name => 'first') + second = @wb.add_worksheet(:name => 'second') + doc = Nokogiri::XML(@wb.to_xml_string) + assert_equal second.name, doc.xpath('//xmlns:workbook/xmlns:sheets/*[1]/@name').to_s + end + + def test_range_requires_valid_sheet ws = @wb.add_worksheet :name=>'fish' ws.add_row [1,2,3] ws.add_row [4,5,6] @@ -136,7 +151,7 @@ class TestWorkbook < Test::Unit::TestCase doc = Nokogiri::XML(@wb.to_xml_string) assert_equal pivot_table.cache_definition.rId, doc.xpath("//xmlns:pivotCache").first["r:id"] end - + def test_worksheet_name_is_intact_after_serialized_into_xml sheet = @wb.add_worksheet(:name => '_Example') wb_xml = Nokogiri::XML(@wb.to_xml_string) diff --git a/test/workbook/worksheet/tc_cell.rb b/test/workbook/worksheet/tc_cell.rb index f61a254f..11b54f44 100644 --- a/test/workbook/worksheet/tc_cell.rb +++ b/test/workbook/worksheet/tc_cell.rb @@ -4,6 +4,7 @@ class TestCell < Test::Unit::TestCase def setup p = Axlsx::Package.new + p.use_shared_strings = true @ws = p.workbook.add_worksheet :name=>"hmmm" p.workbook.styles.add_style :sz=>20 @row = @ws.add_row @@ -56,6 +57,19 @@ class TestCell < Test::Unit::TestCase assert_equal('foo', @ws.workbook.defined_names.last.name) end + def test_autowidth + style = @c.row.worksheet.workbook.styles.add_style({:alignment => {:horizontal => :center, :vertical => :center, :wrap_text => true}} ) + @c.style = style + assert_equal(@c.autowidth, 5.5) + end + + def test_time + @c.type = :time + now = DateTime.now + @c.value = now + assert_equal(@c.value, now.to_time) + end + def test_style assert_raise(ArgumentError, "must reject invalid style indexes") { @[email protected] } assert_nothing_raised("must allow valid style index changes") {@c.style=1} diff --git a/test/workbook/worksheet/tc_pivot_table.rb b/test/workbook/worksheet/tc_pivot_table.rb index ff7f8b92..6d7f4d1c 100644 --- a/test/workbook/worksheet/tc_pivot_table.rb +++ b/test/workbook/worksheet/tc_pivot_table.rb @@ -117,12 +117,7 @@ class TestPivotTable < Test::Unit::TestCase end def test_to_xml_string - pivot_table = @ws.add_pivot_table('G5:G6', 'A1:D5') - shared_test_pivot_table_xml_validity(pivot_table) - end - - def test_to_xml_string_with_configuration - pivot_table = @ws.add_pivot_table('G5:G6', 'A1:E5') do |pt| + pivot_table = @ws.add_pivot_table('G5:G6', 'A1:E5', {:no_subtotals_on_headers=>['Year']}) do |pt| pt.rows = ['Year', 'Month'] pt.columns = ['Type'] pt.data = ['Sales'] diff --git a/test/workbook/worksheet/tc_rich_text_run.rb b/test/workbook/worksheet/tc_rich_text_run.rb index f8b11f6c..e4a5ef80 100644 --- a/test/workbook/worksheet/tc_rich_text_run.rb +++ b/test/workbook/worksheet/tc_rich_text_run.rb @@ -7,7 +7,7 @@ class RichTextRun < Test::Unit::TestCase @p.workbook.styles.add_style :sz => 20 @rtr = Axlsx::RichTextRun.new('hihihi', b: true, i: false) @rtr2 = Axlsx::RichTextRun.new('hihi2hi2', b: false, i: true) - @rt = Axlsx::RichText.new + @rt = Axlsx::RichText.new @rt.runs << @rtr @rt.runs << @rtr2 @row = @ws.add_row [@rt] @@ -19,7 +19,7 @@ class RichTextRun < Test::Unit::TestCase assert_equal(@rtr.b, true) assert_equal(@rtr.i, false) end - + def test_font_size_with_custom_style_and_no_sz @c.style = @c.row.worksheet.workbook.styles.add_style :bg_color => 'FF00FF' sz = @rtr.send(:font_size) @@ -41,12 +41,12 @@ class RichTextRun < Test::Unit::TestCase sz2 = @rtr2.send(:font_size) assert_equal(sz2, 52) end - + def test_rtr_with_sz @rtr.sz = 25 assert_equal(25, @rtr.send(:font_size)) end - + def test_color assert_raise(ArgumentError) { @rtr.color = -1.1 } assert_nothing_raised { @rtr.color = "FF00FF00" } @@ -129,9 +129,9 @@ class RichTextRun < Test::Unit::TestCase end def test_family - assert_raise(ArgumentError) { @c.family = -1.1 } - assert_nothing_raised { @c.family = 5 } - assert_equal(@c.family, 5) + assert_raise(ArgumentError) { @rtr.family = 0 } + assert_nothing_raised { @rtr.family = 1 } + assert_equal(@rtr.family, 1) end def test_b @@ -139,11 +139,11 @@ class RichTextRun < Test::Unit::TestCase assert_nothing_raised { @c.b = false } assert_equal(@c.b, false) end - + def test_multiline_autowidth wrap = @p.workbook.styles.add_style({:alignment => {:wrap_text => true}}) awtr = Axlsx::RichTextRun.new('I\'m bold' + "\n", :b => true) - rt = Axlsx::RichText.new + rt = Axlsx::RichText.new rt.runs << awtr @ws.add_row [rt], :style => wrap ar = [0] @@ -151,7 +151,7 @@ class RichTextRun < Test::Unit::TestCase assert_equal(ar.length, 2) assert_equal(ar.last, 0) end - + def test_to_xml schema = Nokogiri::XML::Schema(File.open(Axlsx::SML_XSD)) doc = Nokogiri::XML(@ws.to_xml_string) @@ -161,7 +161,7 @@ class RichTextRun < Test::Unit::TestCase errors.push error end assert(errors.empty?, "error free validation") - + assert(doc.xpath('//rPr/b[@val=1]')) assert(doc.xpath('//rPr/i[@val=0]')) assert(doc.xpath('//rPr/b[@val=0]')) diff --git a/test/workbook/worksheet/tc_table.rb b/test/workbook/worksheet/tc_table.rb index 6f39bb13..8edfc099 100644 --- a/test/workbook/worksheet/tc_table.rb +++ b/test/workbook/worksheet/tc_table.rb @@ -47,9 +47,9 @@ class TestTable < Test::Unit::TestCase def test_relationships assert(@ws.relationships.empty?) - table = @ws.add_table("A1:D5") + @ws.add_table("A1:D5") assert_equal(@ws.relationships.size, 1, "adding a table adds a relationship") - table = @ws.add_table("F1:J5") + @ws.add_table("F1:J5") assert_equal(@ws.relationships.size, 2, "adding a table adds a relationship") end @@ -64,5 +64,4 @@ class TestTable < Test::Unit::TestCase end assert(errors.empty?, "error free validation") end - end diff --git a/test/workbook/worksheet/tc_worksheet.rb b/test/workbook/worksheet/tc_worksheet.rb index 166850e5..fa8832d8 100644 --- a/test/workbook/worksheet/tc_worksheet.rb +++ b/test/workbook/worksheet/tc_worksheet.rb @@ -93,7 +93,7 @@ class TestWorksheet < Test::Unit::TestCase page_setup = {:fit_to_height => 1, :fit_to_width => 1, :orientation => :landscape, :paper_width => "210mm", :paper_height => "297mm", :scale => 80} print_options = {:grid_lines => true, :headings => true, :horizontal_centered => true, :vertical_centered => true} header_footer = {:different_first => false, :different_odd_even => false, :odd_header => 'Header'} - optioned = @ws.workbook.add_worksheet(:name => 'bob', :page_margins => page_margins, :page_setup => page_setup, :print_options => print_options, :header_footer => header_footer, :selected => true, :show_gridlines => false) + optioned = @ws.workbook.add_worksheet(:name => 'bob', :page_margins => page_margins, :page_setup => page_setup, :print_options => print_options, :header_footer => header_footer) page_margins.keys.each do |key| assert_equal(page_margins[key], optioned.page_margins.send(key)) end @@ -107,23 +107,21 @@ class TestWorksheet < Test::Unit::TestCase assert_equal(header_footer[key], optioned.header_footer.send(key)) end assert_equal(optioned.name, 'bob') - assert_equal(optioned.selected, true) - assert_equal(optioned.show_gridlines, false) end - def test_use_gridlines - assert_raise(ArgumentError) { @ws.show_gridlines = -1.1 } - assert_nothing_raised { @ws.show_gridlines = false } - assert_equal(@ws.show_gridlines, false) - end + # def test_use_gridlines + # assert_raise(ArgumentError) { @ws.show_gridlines = -1.1 } + # assert_nothing_raised { @ws.show_gridlines = false } + # assert_equal(@ws.show_gridlines, false) + # end - def test_selected - assert_raise(ArgumentError) { @ws.selected = -1.1 } - assert_nothing_raised { @ws.selected = true } - assert_equal(@ws.selected, true) - end + # def test_selected + # assert_raise(ArgumentError) { @ws.selected = -1.1 } + # assert_nothing_raised { @ws.selected = true } + # assert_equal(@ws.selected, true) + # end def test_rels_pn assert_equal(@ws.rels_pn, "worksheets/_rels/sheet1.xml.rels") @@ -266,11 +264,11 @@ class TestWorksheet < Test::Unit::TestCase end end - def test_to_xml_string_fit_to_page - @ws.page_setup.fit_to_width = 1 - doc = Nokogiri::XML(@ws.to_xml_string) - assert_equal(doc.xpath('//xmlns:worksheet/xmlns:sheetPr/xmlns:pageSetUpPr[@fitToPage=1]').size, 1) - end + # def test_to_xml_string_fit_to_page + # @ws.page_setup.fit_to_width = 1 + # doc = Nokogiri::XML(@ws.to_xml_string) + # assert_equal(doc.xpath('//xmlns:worksheet/xmlns:sheetPr/xmlns:pageSetUpPr[@fitToPage=1]').size, 1) + # end def test_to_xml_string_dimensions @ws.add_row [1,2,3] @@ -278,22 +276,22 @@ class TestWorksheet < Test::Unit::TestCase assert_equal(doc.xpath('//xmlns:worksheet/xmlns:dimension[@ref="A1:C1"]').size, 1) end - def test_fit_to_page_assignation_does_nothing - @ws.fit_to_page = true - assert_equal(@ws.fit_to_page?, false) - end + # def test_fit_to_page_assignation_does_nothing + # @ws.fit_to_page = true + # assert_equal(@ws.fit_to_page?, false) + # end - def test_to_xml_string_selected - @ws.selected = true - doc = Nokogiri::XML(@ws.to_xml_string) - assert_equal(doc.xpath('//xmlns:worksheet/xmlns:sheetViews/xmlns:sheetView[@tabSelected=1]').size, 1) - end + # def test_to_xml_string_selected + # @ws.selected = true + # doc = Nokogiri::XML(@ws.to_xml_string) + # assert_equal(doc.xpath('//xmlns:worksheet/xmlns:sheetViews/xmlns:sheetView[@tabSelected=1]').size, 1) + # end - def test_to_xml_string_show_gridlines - @ws.show_gridlines = false - doc = Nokogiri::XML(@ws.to_xml_string) - assert_equal(doc.xpath('//xmlns:worksheet/xmlns:sheetViews/xmlns:sheetView[@showGridLines=0]').size, 1) - end + # def test_to_xml_string_show_gridlines + # @ws.show_gridlines = false + # doc = Nokogiri::XML(@ws.to_xml_string) + # assert_equal(doc.xpath('//xmlns:worksheet/xmlns:sheetViews/xmlns:sheetView[@showGridLines=0]').size, 1) + # end def test_to_xml_string_auto_fit_data @ws.add_row [1, "two"] @@ -439,15 +437,15 @@ class TestWorksheet < Test::Unit::TestCase def test_relationships @ws.add_row [1,2,3] assert(@ws.relationships.empty?, "No Drawing relationship until you add a chart") - c = @ws.add_chart Axlsx::Pie3DChart + @ws.add_chart Axlsx::Pie3DChart assert_equal(@ws.relationships.size, 1, "adding a chart creates the relationship") - c = @ws.add_chart Axlsx::Pie3DChart + @ws.add_chart Axlsx::Pie3DChart assert_equal(@ws.relationships.size, 1, "multiple charts still only result in one relationship") - c = @ws.add_comment :text => 'builder', :author => 'bob', :ref => @ws.rows.last.cells.last + @ws.add_comment :text => 'builder', :author => 'bob', :ref => @ws.rows.last.cells.last assert_equal(@ws.relationships.size, 3, "adding a comment adds 2 relationships") - c = @ws.add_comment :text => 'not that is a comment!', :author => 'travis', :ref => "A1" + @ws.add_comment :text => 'not that is a comment!', :author => 'travis', :ref => "A1" assert_equal(@ws.relationships.size, 3, "adding multiple comments in the same worksheet should not add any additional comment relationships") - c = @ws.add_pivot_table 'G5:G6', 'A1:D10' + @ws.add_pivot_table 'G5:G6', 'A1:D10' assert_equal(@ws.relationships.size, 4, "adding a pivot table adds 1 relationship") end @@ -580,7 +578,7 @@ class TestWorksheet < Test::Unit::TestCase assert_raise(ArgumentError) { @wb.add_worksheet(:name => 'Sheet1') } assert_equal(1, @wb.worksheets.size) end - + def test_worksheet_only_includes_outline_pr_when_set doc = Nokogiri::XML(@ws.to_xml_string) assert_equal(doc.xpath('//xmlns:worksheet/xmlns:sheetPr/xmlns:outlinePr').size, 0) |
