axlsx 1.3.4 → 1.3.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/README.md +27 -5
  2. data/examples/example.rb +31 -9
  3. data/examples/ios_preview.rb +14 -0
  4. data/examples/pivot_table.rb +39 -0
  5. data/examples/styles.rb +4 -0
  6. data/lib/axlsx.rb +28 -0
  7. data/lib/axlsx/drawing/bar_3D_chart.rb +15 -10
  8. data/lib/axlsx/drawing/chart.rb +13 -1
  9. data/lib/axlsx/drawing/drawing.rb +12 -11
  10. data/lib/axlsx/drawing/graphic_frame.rb +6 -1
  11. data/lib/axlsx/drawing/hyperlink.rb +5 -0
  12. data/lib/axlsx/drawing/line_3D_chart.rb +3 -2
  13. data/lib/axlsx/drawing/pic.rb +6 -0
  14. data/lib/axlsx/drawing/pie_3D_chart.rb +2 -1
  15. data/lib/axlsx/drawing/scatter_chart.rb +2 -1
  16. data/lib/axlsx/package.rb +13 -0
  17. data/lib/axlsx/rels/relationship.rb +1 -0
  18. data/lib/axlsx/stylesheet/styles.rb +22 -18
  19. data/lib/axlsx/util/accessors.rb +15 -0
  20. data/lib/axlsx/util/constants.rb +25 -5
  21. data/lib/axlsx/util/validators.rb +10 -8
  22. data/lib/axlsx/version.rb +1 -1
  23. data/lib/axlsx/workbook/shared_strings_table.rb +1 -1
  24. data/lib/axlsx/workbook/workbook.rb +27 -3
  25. data/lib/axlsx/workbook/worksheet/cell.rb +26 -64
  26. data/lib/axlsx/workbook/worksheet/cell_serializer.rb +144 -0
  27. data/lib/axlsx/workbook/worksheet/col.rb +9 -2
  28. data/lib/axlsx/workbook/worksheet/pivot_table.rb +259 -0
  29. data/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb +65 -0
  30. data/lib/axlsx/workbook/worksheet/pivot_tables.rb +24 -0
  31. data/lib/axlsx/workbook/worksheet/row.rb +10 -1
  32. data/lib/axlsx/workbook/worksheet/sheet_format_pr.rb +60 -0
  33. data/lib/axlsx/workbook/worksheet/worksheet.rb +55 -6
  34. data/test/benchmark.rb +1 -0
  35. data/test/drawing/tc_chart.rb +7 -0
  36. data/test/drawing/tc_graphic_frame.rb +5 -0
  37. data/test/example.xlsx +0 -0
  38. data/test/profile.rb +1 -0
  39. data/test/tc_axlsx.rb +15 -0
  40. data/test/tc_package.rb +13 -5
  41. data/test/workbook/worksheet/tc_cell.rb +5 -1
  42. data/test/workbook/worksheet/tc_pivot_table.rb +102 -0
  43. data/test/workbook/worksheet/tc_pivot_table_cache_definition.rb +46 -0
  44. data/test/workbook/worksheet/tc_sheet_format_pr.rb +88 -0
  45. data/test/workbook/worksheet/tc_worksheet.rb +45 -1
  46. metadata +23 -9
@@ -9,7 +9,7 @@ module Axlsx
9
9
  #
10
10
  # p = Axlsx::Package.new
11
11
  # ws = p.workbook.add_worksheet
12
- # ws.add_row :values => ["This is a chart with no data in the sheet"]
12
+ # ws.add_row ["This is a chart with no data in the sheet"]
13
13
  #
14
14
  # chart = ws.add_chart(Axlsx::Line3DChart, :start_at=> [0,1], :end_at=>[0,6], :title=>"Most Popular Pets")
15
15
  # chart.add_series :data => [1, 9, 10], :labels => ["Slimy Reptiles", "Fuzzy Bunnies", "Rottweiler"]
@@ -60,6 +60,7 @@ module Axlsx
60
60
  # @see Chart
61
61
  # @see View3D
62
62
  def initialize(frame, options={})
63
+ @vary_colors = false
63
64
  @gapDepth = nil
64
65
  @grouping = :standard
65
66
  @catAxId = rand(8 ** 8)
@@ -93,7 +94,7 @@ module Axlsx
93
94
  super(str) do |str_inner|
94
95
  str_inner << '<c:line3DChart>'
95
96
  str_inner << '<c:grouping val="' << grouping.to_s << '"/>'
96
- str_inner << '<c:varyColors val="1"/>'
97
+ str_inner << '<c:varyColors val="' << vary_colors.to_s << '"/>'
97
98
  @series.each { |ser| ser.to_xml_string(str_inner) }
98
99
  @d_lbls.to_xml_string(str) if @d_lbls
99
100
  str_inner << '<c:gapDepth val="' << @gapDepth.to_s << '"/>' unless @gapDepth.nil?
@@ -107,6 +107,12 @@ module Axlsx
107
107
  @anchor.drawing.charts.size + @anchor.drawing.images.index(self) + 1
108
108
  end
109
109
 
110
+ # Returns a relationship object for this object
111
+ # @return Axlsx::Relationship
112
+ def relationship
113
+ Relationship.new(IMAGE_R, "../#{pn}")
114
+ end
115
+
110
116
  # providing access to the anchor's width attribute
111
117
  # @param [Integer] v
112
118
  # @see OneCellAnchor.width
@@ -23,6 +23,7 @@ module Axlsx
23
23
  # @see Chart
24
24
  # @see View3D
25
25
  def initialize(frame, options={})
26
+ @vary_colors = true
26
27
  super(frame, options)
27
28
  @series_type = PieSeries
28
29
  @view_3D = View3D.new({:rot_x =>30, :perspective=>30}.merge(options))
@@ -36,7 +37,7 @@ module Axlsx
36
37
  super(str) do |str_inner|
37
38
 
38
39
  str_inner << '<c:pie3DChart>'
39
- str_inner << '<c:varyColors val="1"/>'
40
+ str_inner << '<c:varyColors val="' << vary_colors.to_s << '"/>'
40
41
  @series.each { |ser| ser.to_xml_string(str_inner) }
41
42
  d_lbls.to_xml_string(str) if @d_lbls
42
43
  str_inner << '</c:pie3DChart>'
@@ -24,6 +24,7 @@ module Axlsx
24
24
 
25
25
  # Creates a new scatter chart
26
26
  def initialize(frame, options={})
27
+ @vary_colors = 0
27
28
  @scatterStyle = :lineMarker
28
29
  @xValAxId = rand(8 ** 8)
29
30
  @yValAxId = rand(8 ** 8)
@@ -48,7 +49,7 @@ module Axlsx
48
49
  super(str) do |str_inner|
49
50
  str_inner << '<c:scatterChart>'
50
51
  str_inner << '<c:scatterStyle val="' << scatterStyle.to_s << '"/>'
51
- str_inner << '<c:varyColors val="1"/>'
52
+ str_inner << '<c:varyColors val="' << vary_colors.to_s << '"/>'
52
53
  @series.each { |ser| ser.to_xml_string(str_inner) }
53
54
  d_lbls.to_xml_string(str) if @d_lbls
54
55
  str_inner << '<c:axId val="' << @xValAxId.to_s << '"/>'
@@ -192,6 +192,12 @@ module Axlsx
192
192
  workbook.tables.each do |table|
193
193
  parts << {:entry => "xl/#{table.pn}", :doc => table.to_xml_string, :schema => SML_XSD}
194
194
  end
195
+ workbook.pivot_tables.each do |pivot_table|
196
+ cache_definition = pivot_table.cache_definition
197
+ parts << {:entry => "xl/#{pivot_table.rels_pn}", :doc => pivot_table.relationships.to_xml_string, :schema => RELS_XSD}
198
+ parts << {:entry => "xl/#{pivot_table.pn}", :doc => pivot_table.to_xml_string} #, :schema => SML_XSD}
199
+ parts << {:entry => "xl/#{cache_definition.pn}", :doc => cache_definition.to_xml_string} #, :schema => SML_XSD}
200
+ end
195
201
 
196
202
  workbook.comments.each do|comment|
197
203
  if comment.size > 0
@@ -255,6 +261,13 @@ module Axlsx
255
261
  :ContentType => TABLE_CT)
256
262
  end
257
263
 
264
+ workbook.pivot_tables.each do |pivot_table|
265
+ c_types << Axlsx::Override.new(:PartName => "/xl/#{pivot_table.pn}",
266
+ :ContentType => PIVOT_TABLE_CT)
267
+ c_types << Axlsx::Override.new(:PartName => "/xl/#{pivot_table.cache_definition.pn}",
268
+ :ContentType => PIVOT_TABLE_CACHE_DEFINITION_CT)
269
+ end
270
+
258
271
  workbook.comments.each do |comment|
259
272
  if comment.size > 0
260
273
  c_types << Axlsx::Override.new(:PartName => "/xl/#{comment.pn}",
@@ -12,6 +12,7 @@ module Axlsx
12
12
  # @note Supported types are defined as constants in Axlsx:
13
13
  # @see XML_NS_R
14
14
  # @see TABLE_R
15
+ # @see PIVOT_TABLE_R
15
16
  # @see WORKBOOK_R
16
17
  # @see WORKSHEET_R
17
18
  # @see APP_R
@@ -153,9 +153,9 @@ module Axlsx
153
153
  # # black text on a white background at 14pt with thin borders!
154
154
  # title = ws.style.add_style(:bg_color => "FFFF0000", :fg_color=>"#FF000000", :sz=>14, :border=> {:style => :thin, :color => "FFFF0000"}
155
155
  #
156
- # ws.add_row :values => ["Least Popular Pets"]
157
- # ws.add_row :values => ["", "Dry Skinned Reptiles", "Bald Cats", "Violent Parrots"], :style=>title
158
- # ws.add_row :values => ["Votes", 6, 4, 1], :style=>Axlsx::STYLE_THIN_BORDER
156
+ # ws.add_row ["Least Popular Pets"]
157
+ # ws.add_row ["", "Dry Skinned Reptiles", "Bald Cats", "Violent Parrots"], :style=>title
158
+ # ws.add_row ["Votes", 6, 4, 1], :style=>Axlsx::STYLE_THIN_BORDER
159
159
  # f = File.open('example_you_got_style.xlsx', 'w')
160
160
  # p.serialize(f)
161
161
  #
@@ -183,13 +183,13 @@ module Axlsx
183
183
  # :border=>Axlsx::STYLE_THIN_BORDER)
184
184
  #
185
185
  # # build your rows
186
- # ws.add_row :values => ["Genreated At:", Time.now], :styles=>[nil, date_time]
187
- # ws.add_row :values => ["Previous Year Quarterly Profits (JPY)"], :style=>title
188
- # ws.add_row :values => ["Quarter", "Profit", "% of Total"], :style=>title
189
- # ws.add_row :values => ["Q1", 4000, 40], :style=>[title, currency, percent]
190
- # ws.add_row :values => ["Q2", 3000, 30], :style=>[title, currency, percent]
191
- # ws.add_row :values => ["Q3", 1000, 10], :style=>[title, currency, percent]
192
- # ws.add_row :values => ["Q4", 2000, 20], :style=>[title, currency, percent]
186
+ # ws.add_row ["Genreated At:", Time.now], :styles=>[nil, date_time]
187
+ # ws.add_row ["Previous Year Quarterly Profits (JPY)"], :style=>title
188
+ # ws.add_row ["Quarter", "Profit", "% of Total"], :style=>title
189
+ # ws.add_row ["Q1", 4000, 40], :style=>[title, currency, percent]
190
+ # ws.add_row ["Q2", 3000, 30], :style=>[title, currency, percent]
191
+ # ws.add_row ["Q3", 1000, 10], :style=>[title, currency, percent]
192
+ # ws.add_row ["Q4", 2000, 20], :style=>[title, currency, percent]
193
193
  # f = File.open('example_you_got_style.xlsx', 'w')
194
194
  # p.serialize(f)
195
195
  #
@@ -207,13 +207,13 @@ module Axlsx
207
207
  # :fg_color=>"#FF000000",
208
208
  # :type => :dxf)
209
209
  #
210
- # ws.add_row :values => ["Genreated At:", Time.now], :styles=>[nil, date_time]
211
- # ws.add_row :values => ["Previous Year Quarterly Profits (JPY)"], :style=>title
212
- # ws.add_row :values => ["Quarter", "Profit", "% of Total"], :style=>title
213
- # ws.add_row :values => ["Q1", 4000, 40], :style=>[title, currency, percent]
214
- # ws.add_row :values => ["Q2", 3000, 30], :style=>[title, currency, percent]
215
- # ws.add_row :values => ["Q3", 1000, 10], :style=>[title, currency, percent]
216
- # ws.add_row :values => ["Q4", 2000, 20], :style=>[title, currency, percent]
210
+ # ws.add_row ["Genreated At:", Time.now], :styles=>[nil, date_time]
211
+ # ws.add_row ["Previous Year Quarterly Profits (JPY)"], :style=>title
212
+ # ws.add_row ["Quarter", "Profit", "% of Total"], :style=>title
213
+ # ws.add_row ["Q1", 4000, 40], :style=>[title, currency, percent]
214
+ # ws.add_row ["Q2", 3000, 30], :style=>[title, currency, percent]
215
+ # ws.add_row ["Q3", 1000, 10], :style=>[title, currency, percent]
216
+ # ws.add_row ["Q4", 2000, 20], :style=>[title, currency, percent]
217
217
  #
218
218
  # ws.add_conditional_formatting("A1:A7", { :type => :cellIs, :operator => :greaterThan, :formula => "2000", :dxfId => profitable, :priority => 1 })
219
219
  # f = File.open('example_differential_styling', 'w')
@@ -308,6 +308,8 @@ module Axlsx
308
308
  # may include an :edges entry that references an array of symbols identifying which border edges
309
309
  # you wish to apply the style or any other valid Border initializer options.
310
310
  # If the :edges entity is not provided the style is applied to all edges of cells that reference this style.
311
+ # Also available :border_top, :border_right, :border_bottom and :border_left options with :style and/or :color
312
+ # key-value entries, which override :border values.
311
313
  # @example
312
314
  # #apply a thick red border to the top and bottom
313
315
  # { :border => { :style => :thick, :color => "FFFF0000", :edges => [:top, :bottom] }
@@ -319,7 +321,9 @@ module Axlsx
319
321
  raise ArgumentError, (ERR_INVALID_BORDER_OPTIONS % b_opts) unless b_opts.keys.include?(:style) && b_opts.keys.include?(:color)
320
322
  border = Border.new b_opts
321
323
  (b_opts[:edges] || [:left, :right, :top, :bottom]).each do |edge|
322
- b_options = { :name => edge, :style => b_opts[:style], :color => Color.new(:rgb => b_opts[:color]) }
324
+ edge_options = options["border_#{edge}".to_sym] || {}
325
+ border_edge = b_opts.merge(edge_options)
326
+ b_options = { :name => edge, :style => border_edge[:style], :color => Color.new(:rgb => border_edge[:color]) }
323
327
  border.prs << BorderPr.new(b_options)
324
328
  end
325
329
  options[:type] == :dxf ? border : borders << border
@@ -22,6 +22,21 @@ module Axlsx
22
22
  validated_attr_accessor(symbols, 'validate_string')
23
23
  end
24
24
 
25
+
26
+ # Creates one or more usigned integer attr_accessors
27
+ # @param [Array] symbols An array of symbols representing the
28
+ # names of the attributes you will add to your class
29
+ def unsigned_int_attr_accessor(*symbols)
30
+ validated_attr_accessor(symbols, 'validate_unsigned_int')
31
+ end
32
+
33
+ # Creates one or more float (double?) attr_accessors
34
+ # @param [Array] symbols An array of symbols representing the
35
+ # names of the attributes you will add to your class
36
+ def float_attr_accessor(*symbols)
37
+ validated_attr_accessor(symbols, 'validate_float')
38
+ end
39
+
25
40
  # Creates on or more boolean validated attr_accessors
26
41
  # @param [Array] symbols An array of symbols representing the
27
42
  # names of the attributes you will add to your class.
@@ -51,6 +51,12 @@ module Axlsx
51
51
  # table rels namespace
52
52
  TABLE_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table"
53
53
 
54
+ # pivot table rels namespace
55
+ PIVOT_TABLE_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable"
56
+
57
+ # pivot table cache definition namespace
58
+ PIVOT_TABLE_CACHE_DEFINITION_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition"
59
+
54
60
  # workbook rels namespace
55
61
  WORKBOOK_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
56
62
 
@@ -99,6 +105,12 @@ module Axlsx
99
105
  # table content type
100
106
  TABLE_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"
101
107
 
108
+ # pivot table content type
109
+ PIVOT_TABLE_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"
110
+
111
+ # pivot table cache definition content type
112
+ PIVOT_TABLE_CACHE_DEFINITION_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"
113
+
102
114
  # workbook content type
103
115
  WORKBOOK_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"
104
116
 
@@ -208,6 +220,15 @@ module Axlsx
208
220
  # drawing part
209
221
  TABLE_PN = "tables/table%d.xml"
210
222
 
223
+ # pivot table parts
224
+ PIVOT_TABLE_PN = "pivotTables/pivotTable%d.xml"
225
+
226
+ # pivot table cache definition part name
227
+ PIVOT_TABLE_CACHE_DEFINITION_PN = "pivotCache/pivotCacheDefinition%d.xml"
228
+
229
+ # pivot table rels parts
230
+ PIVOT_TABLE_RELS_PN = "pivotTables/_rels/pivotTable%d.xml.rels"
231
+
211
232
  # chart part
212
233
  CHART_PN = "charts/chart%d.xml"
213
234
 
@@ -268,9 +289,8 @@ module Axlsx
268
289
  # error message for sheets that use a name which is longer than 31 bytes
269
290
  ERR_SHEET_NAME_TOO_LONG = "Your worksheet name '%s' is too long. Worksheet names must be 31 characters (bytes) or less"
270
291
 
271
- # error message for sheets that use a name which includes a colon
272
-
273
- ERR_SHEET_NAME_COLON_FORBIDDEN = "Your worksheet name '%s' contains a colon, which is not allowed by MS Excel and will cause repair warnings. Please change the name of your sheet."
292
+ # error message for sheets that use a name which include invalid characters
293
+ ERR_SHEET_NAME_CHARACTER_FORBIDDEN = "Your worksheet name '%s' contains a character which is not allowed by MS Excel and will cause repair warnings. Please change the name of your sheet."
274
294
 
275
295
  # error message for duplicate sheet names
276
296
  ERR_DUPLICATE_SHEET_NAME = "There is already a worksheet in this workbook named '%s'. Please use a unique name"
@@ -320,7 +340,7 @@ module Axlsx
320
340
  # x1E Information Separator Two
321
341
  # x1F Information Separator One
322
342
  #
323
- # The following are not dealt with.
343
+ # The following are not dealt with.
324
344
  # If you have this in your data, expect excel to blow up!
325
345
  #
326
346
  # x7F Delete
@@ -365,7 +385,7 @@ module Axlsx
365
385
  # @see http://www.codetable.net/asciikeycodes
366
386
  pattern = "[\x0-\x08\x0B\x0C\x0E-\x1F]"
367
387
  pattern= pattern.respond_to?(:encode) ? pattern.encode('UTF-8') : pattern
368
-
388
+
369
389
  # The regular expression used to remove control characters from worksheets
370
390
  CONTROL_CHAR_REGEX = Regexp.new(pattern, 'n')
371
391
 
@@ -41,6 +41,7 @@ module Axlsx
41
41
  raise ArgumentError, (ERR_REGEX % [v.inspect, regex.to_s]) unless (v.respond_to?(:to_s) && v.to_s.match(regex))
42
42
  end
43
43
  end
44
+
44
45
  # Validate that the class of the value provided is either an instance or the class of the allowed types and that any specified additional validation returns true.
45
46
  class DataTypeValidator
46
47
  # Perform validation
@@ -50,17 +51,18 @@ module Axlsx
50
51
  # @raise [ArugumentError] Raised if the class of the value provided is not in the specified array of types or the block passed returns false
51
52
  # @return [Boolean] true if validation succeeds.
52
53
  # @see validate_boolean
53
- def self.validate(name, types, v, other= lambda{|arg| true })
54
+ def self.validate(name, types, v, other=false)
54
55
  types = [types] unless types.is_a? Array
55
- valid_type = false
56
+ if other.is_a?(Proc)
57
+ raise ArgumentError, (ERR_TYPE % [v.inspect, name, types.inspect]) unless other.call(v)
58
+ end
56
59
  if v.class == Class
57
- types.each { |t| valid_type = true if v.ancestors.include?(t) }
60
+ types.each { |t| return if v.ancestors.include?(t) }
58
61
  else
59
- types.each { |t| valid_type = true if v.is_a?(t) }
62
+ types.each { |t| return if v.is_a?(t) }
60
63
  end
61
- raise ArgumentError, (ERR_TYPE % [v.inspect, name, types.inspect]) unless (other.call(v) && valid_type)
64
+ raise ArgumentError, (ERR_TYPE % [v.inspect, name, types.inspect])
62
65
  end
63
- true
64
66
  end
65
67
 
66
68
 
@@ -229,14 +231,14 @@ module Axlsx
229
231
  # TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, DRAWING_CT, COMMENT_CT are allowed
230
232
  # @param [Any] v The value validated
231
233
  def self.validate_content_type(v)
232
- RestrictionValidator.validate :content_type, [TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, JPEG_CT, GIF_CT, PNG_CT, DRAWING_CT, COMMENT_CT, VML_DRAWING_CT], v
234
+ RestrictionValidator.validate :content_type, [TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, JPEG_CT, GIF_CT, PNG_CT, DRAWING_CT, COMMENT_CT, VML_DRAWING_CT, PIVOT_TABLE_CT, PIVOT_TABLE_CACHE_DEFINITION_CT], v
233
235
  end
234
236
 
235
237
  # Requires that the value is a valid relationship_type
236
238
  # XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R, IMAGE_R, HYPERLINK_R, SHARED_STRINGS_R are allowed
237
239
  # @param [Any] v The value validated
238
240
  def self.validate_relationship_type(v)
239
- RestrictionValidator.validate :relationship_type, [XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R, IMAGE_R, HYPERLINK_R, SHARED_STRINGS_R, COMMENT_R, VML_DRAWING_R, COMMENT_R_NULL], v
241
+ RestrictionValidator.validate :relationship_type, [XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R, IMAGE_R, HYPERLINK_R, SHARED_STRINGS_R, COMMENT_R, VML_DRAWING_R, COMMENT_R_NULL, PIVOT_TABLE_R, PIVOT_TABLE_CACHE_DEFINITION_R], v
240
242
  end
241
243
 
242
244
  # Requires that the value is a valid table element type
@@ -1,5 +1,5 @@
1
1
  module Axlsx
2
2
 
3
3
  # The current version
4
- VERSION = "1.3.4"
4
+ VERSION = "1.3.5"
5
5
  end
@@ -58,7 +58,7 @@ module Axlsx
58
58
  cell.send :ssti=, index
59
59
  else
60
60
  cell.send :ssti=, @index
61
- @shared_xml_string << '<si>' << cell.run_xml_string << '</si>'
61
+ @shared_xml_string << '<si>' << CellSerializer.run_xml_string(cell) << '</si>'
62
62
  @unique_cells[cell_hash] = @index
63
63
  @index += 1
64
64
  end
@@ -5,6 +5,7 @@ require 'axlsx/workbook/worksheet/auto_filter/auto_filter.rb'
5
5
  require 'axlsx/workbook/worksheet/date_time_converter.rb'
6
6
  require 'axlsx/workbook/worksheet/protected_range.rb'
7
7
  require 'axlsx/workbook/worksheet/protected_ranges.rb'
8
+ require 'axlsx/workbook/worksheet/cell_serializer.rb'
8
9
  require 'axlsx/workbook/worksheet/cell.rb'
9
10
  require 'axlsx/workbook/worksheet/page_margins.rb'
10
11
  require 'axlsx/workbook/worksheet/page_set_up_pr.rb'
@@ -40,9 +41,13 @@ require 'axlsx/workbook/defined_names.rb'
40
41
  require 'axlsx/workbook/worksheet/table_style_info.rb'
41
42
  require 'axlsx/workbook/worksheet/table.rb'
42
43
  require 'axlsx/workbook/worksheet/tables.rb'
44
+ require 'axlsx/workbook/worksheet/pivot_table_cache_definition.rb'
45
+ require 'axlsx/workbook/worksheet/pivot_table.rb'
46
+ require 'axlsx/workbook/worksheet/pivot_tables.rb'
43
47
  require 'axlsx/workbook/worksheet/data_validation.rb'
44
48
  require 'axlsx/workbook/worksheet/data_validations.rb'
45
49
  require 'axlsx/workbook/worksheet/sheet_view.rb'
50
+ require 'axlsx/workbook/worksheet/sheet_format_pr.rb'
46
51
  require 'axlsx/workbook/worksheet/pane.rb'
47
52
  require 'axlsx/workbook/worksheet/selection.rb'
48
53
  # The Workbook class is an xlsx workbook that manages worksheets, charts, drawings and styles.
@@ -121,10 +126,17 @@ require 'axlsx/workbook/worksheet/selection.rb'
121
126
  # @return [SimpleTypedList]
122
127
  attr_reader :tables
123
128
 
129
+ # A colllection of pivot tables associated with this workbook
130
+ # @note The recommended way to manage drawings is Worksheet#add_table
131
+ # @see Worksheet#add_table
132
+ # @see Table
133
+ # @return [SimpleTypedList]
134
+ attr_reader :pivot_tables
135
+
124
136
 
125
137
  # A collection of defined names for this workbook
126
138
  # @note The recommended way to manage defined names is Workbook#add_defined_name
127
- # @see DefinedName
139
+ # @see DefinedName
128
140
  # @return [DefinedNames]
129
141
  def defined_names
130
142
  @defined_names ||= DefinedNames.new
@@ -170,7 +182,7 @@ require 'axlsx/workbook/worksheet/selection.rb'
170
182
  # w.parse_string :date1904, "//xmlns:workbookPr/@date1904"
171
183
  # w
172
184
  #end
173
-
185
+
174
186
  # Creates a new Workbook
175
187
  # The recomended way to work with workbooks is via Package#workbook
176
188
  # @option options [Boolean] date1904. If this is not specified, date1904 is set to false. Office 2011 for Mac defaults to false.
@@ -182,6 +194,7 @@ require 'axlsx/workbook/worksheet/selection.rb'
182
194
  @images = SimpleTypedList.new Pic
183
195
  # Are these even used????? Check package serialization parts
184
196
  @tables = SimpleTypedList.new Table
197
+ @pivot_tables = SimpleTypedList.new PivotTable
185
198
  @comments = SimpleTypedList.new Comments
186
199
 
187
200
 
@@ -217,7 +230,7 @@ require 'axlsx/workbook/worksheet/selection.rb'
217
230
  def use_autowidth=(v=true) Axlsx::validate_boolean v; @use_autowidth = v; end
218
231
 
219
232
  # inserts a worksheet into this workbook at the position specified.
220
- # It the index specified is out of range, the worksheet will be added to the end of the
233
+ # It the index specified is out of range, the worksheet will be added to the end of the
221
234
  # worksheets collection
222
235
  # @return [Worksheet]
223
236
  # @param index The zero based position to insert the newly created worksheet
@@ -259,6 +272,9 @@ require 'axlsx/workbook/worksheet/selection.rb'
259
272
  @worksheets.each do |sheet|
260
273
  r << Relationship.new(WORKSHEET_R, WORKSHEET_PN % (r.size+1))
261
274
  end
275
+ pivot_tables.each_with_index do |pivot_table, index|
276
+ r << Relationship.new(PIVOT_TABLE_CACHE_DEFINITION_R, PIVOT_TABLE_CACHE_DEFINITION_PN % (index+1))
277
+ end
262
278
  r << Relationship.new(STYLES_R, STYLES_PN)
263
279
  if use_shared_strings
264
280
  r << Relationship.new(SHARED_STRINGS_R, SHARED_STRINGS_PN)
@@ -300,6 +316,14 @@ require 'axlsx/workbook/worksheet/selection.rb'
300
316
  end
301
317
  str << '</sheets>'
302
318
  defined_names.to_xml_string(str)
319
+ unless pivot_tables.empty?
320
+ str << '<pivotCaches>'
321
+ pivot_tables.each_with_index do |pivot_table, index|
322
+ rId = "rId#{@worksheets.size + index + 1 }"
323
+ str << '<pivotCache cacheId="' << pivot_table.cache_definition.cache_id.to_s << '" r:id="' << rId << '"/>'
324
+ end
325
+ str << '</pivotCaches>'
326
+ end
303
327
  str << '</workbook>'
304
328
  end
305
329
 
@@ -28,11 +28,13 @@ module Axlsx
28
28
  # @option options [Symbol] vertAlign must be one of :baseline, :subscript, :superscript
29
29
  # @option options [Integer] sz
30
30
  # @option options [String] color an 8 letter rgb specification
31
+ # @option options [Number] formula_value The value to cache for a formula cell.
31
32
  # @option options [Symbol] scheme must be one of :none, major, :minor
32
33
  def initialize(row, value="", options={})
33
34
  self.row=row
34
- @value = @font_name = @charset = @family = @b = @i = @strike = @outline = @shadow = nil
35
- @condense = @u = @vertAlign = @sz = @color = @scheme = @extend = @ssti = nil
35
+ @value = nil
36
+ #@value = @font_name = @charset = @family = @b = @i = @strike = @outline = @shadow = nil
37
+ #@formula_value = @condense = @u = @vertAlign = @sz = @color = @scheme = @extend = @ssti = nil
36
38
  @styles = row.worksheet.workbook.styles
37
39
  @row.cells << self
38
40
  parse_options options
@@ -41,6 +43,10 @@ module Axlsx
41
43
  @value = cast_value(value)
42
44
  end
43
45
 
46
+ # this is the cached value for formula cells. If you want the values to render in iOS/Mac OSX preview
47
+ # you need to set this.
48
+ attr_accessor :formula_value
49
+
44
50
  # An array of available inline styes.
45
51
  # TODO change this to a hash where each key defines attr name and validator (and any info the validator requires)
46
52
  # then move it out to a module so we can re-use in in other classes.
@@ -75,14 +81,14 @@ module Axlsx
75
81
  attr_reader :type
76
82
  # @see type
77
83
  def type=(v)
78
- RestrictionValidator.validate "Cell.type", [:date, :time, :float, :integer, :string, :boolean], v
84
+ RestrictionValidator.validate "Cell.type", [:date, :time, :float, :integer, :string, :boolean, :iso_8601], v
79
85
  @type=v
80
86
  self.value = @value unless @value.nil?
81
87
  end
82
88
 
83
89
 
84
90
  # The value of this cell.
85
- # @return [String, Integer, Float, Time] casted value based on cell's type attribute.
91
+ # @return [String, Integer, Float, Time, Boolean] casted value based on cell's type attribute.
86
92
  attr_reader :value
87
93
  # @see value
88
94
  def value=(v)
@@ -214,7 +220,7 @@ module Axlsx
214
220
  end
215
221
 
216
222
  # The inline sz property for the cell
217
- # @return [Boolean]
223
+ # @return [Inteter]
218
224
  attr_reader :sz
219
225
  # @see sz
220
226
  def sz=(v) set_run_style :validate_unsigned_int, :sz, v; end
@@ -288,67 +294,15 @@ module Axlsx
288
294
  self.row.worksheet.merge_cells "#{self.r}:#{range_end}" unless range_end.nil?
289
295
  end
290
296
 
291
- # builds an xml text run based on this cells attributes.
292
- # @param [String] str The string instance this run will be concated to.
293
- # @return [String]
294
- def run_xml_string(str = '')
295
- if is_text_run?
296
- data = instance_values.reject{|key, value| value == nil || key == 'value' || key == 'type' }
297
- keys = data.keys & INLINE_STYLES
298
- str << "<r><rPr>"
299
- keys.each do |key|
300
- case key
301
- when 'font_name'
302
- str << "<rFont val='"<< @font_name << "'/>"
303
- when 'color'
304
- str << data[key].to_xml_string
305
- else
306
- str << "<" << key.to_s << " val='" << data[key].to_s << "'/>"
307
- end
308
- end
309
- str << "</rPr>" << "<t>" << value.to_s << "</t></r>"
310
- else
311
- str << "<t>" << value.to_s << "</t>"
312
- end
313
- str
314
- end
315
-
316
297
  # Serializes the cell
317
298
  # @param [Integer] r_index The row index for the cell
318
299
  # @param [Integer] c_index The cell index in the row.
319
300
  # @param [String] str The string index the cell content will be appended to. Defaults to empty string.
320
301
  # @return [String] xml text for the cell
321
302
  def to_xml_string(r_index, c_index, str = '')
322
- str << '<c r="' << Axlsx::cell_r(c_index, r_index) << '" s="' << @style.to_s << '" '
323
- return str << '/>' if @value.nil?
324
-
325
- case @type
326
-
327
- when :string
328
- #parse formula
329
- if @value.start_with?('=')
330
- str << 't="str"><f>' << @value.to_s.sub('=', '') << '</f>'
331
- else
332
- #parse shared
333
- if @ssti
334
- str << 't="s"><v>' << @ssti.to_s << '</v>'
335
- else
336
- str << 't="inlineStr"><is>' << run_xml_string << '</is>'
337
- end
338
- end
339
- when :date
340
- # TODO: See if this is subject to the same restriction as Time below
341
- str << '><v>' << DateTimeConverter::date_to_serial(@value).to_s << '</v>'
342
- when :time
343
- str << '><v>' << DateTimeConverter::time_to_serial(@value).to_s << '</v>'
344
- when :boolean
345
- str << 't="b"><v>' << @value.to_s << '</v>'
346
- else
347
- str << '><v>' << @value.to_s << '</v>'
348
- end
349
- str << '</c>'
303
+ CellSerializer.to_xml_string r_index, c_index, self, str
350
304
  end
351
-
305
+
352
306
  def is_formula?
353
307
  @type == :string && @value.to_s.start_with?('=')
354
308
  end
@@ -415,10 +369,15 @@ module Axlsx
415
369
  :time
416
370
  elsif v.is_a?(TrueClass) || v.is_a?(FalseClass)
417
371
  :boolean
418
- elsif v.to_s.match(/\A[+-]?\d+?\Z/) #numeric
372
+ elsif v.to_s =~ /\A[+-]?\d+?\Z/ #numeric
419
373
  :integer
420
- elsif v.to_s.match(/\A[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\Z/) #float
374
+ elsif v.to_s =~ /\A[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\Z/ #float
421
375
  :float
376
+ # \A(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[0-1]|0[1-9]|[1-2][0-9])
377
+ # T(2[0-3]|[0-1][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?
378
+ # (Z|[+-](?:2[0-3]|[0-1][0-9]):[0-5][0-9])?\Z
379
+ elsif v.to_s =~/\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/
380
+ :iso_8601
422
381
  else
423
382
  :string
424
383
  end
@@ -426,7 +385,7 @@ module Axlsx
426
385
 
427
386
  # Cast the value into this cells data type.
428
387
  # @note
429
- # About Time - Time in OOXML is *different* from what you might expect. The history as to why is interesting, but you can safely assume that if you are generating docs on a mac, you will want to specify Workbook.1904 as true when using time typed values.
388
+ # About Time - Time in OOXML is *different* from what you might expect. The history as to why is interesting, but you can safely assume that if you are generating docs on a mac, you will want to specify Workbook.1904 as true when using time typed values.
430
389
  # @see Axlsx#date1904
431
390
  def cast_value(v)
432
391
  return nil if v.nil?
@@ -442,13 +401,16 @@ module Axlsx
442
401
  v.to_i
443
402
  elsif @type == :boolean
444
403
  v ? 1 : 0
404
+ elsif @type == :iso_8601
405
+ #consumer is responsible for ensuring the iso_8601 format when specifying this type
406
+ v
445
407
  else
446
408
  @type = :string
447
- v.to_s
448
409
  # TODO find a better way to do this as it accounts for 30% of
449
410
  # processing time in benchmarking...
450
- ::CGI.escapeHTML(v.to_s)
411
+ Axlsx::trust_input ? v.to_s : ::CGI.escapeHTML(v.to_s)
451
412
  end
452
413
  end
414
+
453
415
  end
454
416
  end