axlsx 1.3.4 → 1.3.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +27 -5
- data/examples/example.rb +31 -9
- data/examples/ios_preview.rb +14 -0
- data/examples/pivot_table.rb +39 -0
- data/examples/styles.rb +4 -0
- data/lib/axlsx.rb +28 -0
- data/lib/axlsx/drawing/bar_3D_chart.rb +15 -10
- data/lib/axlsx/drawing/chart.rb +13 -1
- data/lib/axlsx/drawing/drawing.rb +12 -11
- data/lib/axlsx/drawing/graphic_frame.rb +6 -1
- data/lib/axlsx/drawing/hyperlink.rb +5 -0
- data/lib/axlsx/drawing/line_3D_chart.rb +3 -2
- data/lib/axlsx/drawing/pic.rb +6 -0
- data/lib/axlsx/drawing/pie_3D_chart.rb +2 -1
- data/lib/axlsx/drawing/scatter_chart.rb +2 -1
- data/lib/axlsx/package.rb +13 -0
- data/lib/axlsx/rels/relationship.rb +1 -0
- data/lib/axlsx/stylesheet/styles.rb +22 -18
- data/lib/axlsx/util/accessors.rb +15 -0
- data/lib/axlsx/util/constants.rb +25 -5
- data/lib/axlsx/util/validators.rb +10 -8
- data/lib/axlsx/version.rb +1 -1
- data/lib/axlsx/workbook/shared_strings_table.rb +1 -1
- data/lib/axlsx/workbook/workbook.rb +27 -3
- data/lib/axlsx/workbook/worksheet/cell.rb +26 -64
- data/lib/axlsx/workbook/worksheet/cell_serializer.rb +144 -0
- data/lib/axlsx/workbook/worksheet/col.rb +9 -2
- data/lib/axlsx/workbook/worksheet/pivot_table.rb +259 -0
- data/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb +65 -0
- data/lib/axlsx/workbook/worksheet/pivot_tables.rb +24 -0
- data/lib/axlsx/workbook/worksheet/row.rb +10 -1
- data/lib/axlsx/workbook/worksheet/sheet_format_pr.rb +60 -0
- data/lib/axlsx/workbook/worksheet/worksheet.rb +55 -6
- data/test/benchmark.rb +1 -0
- data/test/drawing/tc_chart.rb +7 -0
- data/test/drawing/tc_graphic_frame.rb +5 -0
- data/test/example.xlsx +0 -0
- data/test/profile.rb +1 -0
- data/test/tc_axlsx.rb +15 -0
- data/test/tc_package.rb +13 -5
- data/test/workbook/worksheet/tc_cell.rb +5 -1
- data/test/workbook/worksheet/tc_pivot_table.rb +102 -0
- data/test/workbook/worksheet/tc_pivot_table_cache_definition.rb +46 -0
- data/test/workbook/worksheet/tc_sheet_format_pr.rb +88 -0
- data/test/workbook/worksheet/tc_worksheet.rb +45 -1
- 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
|
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="
|
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?
|
data/lib/axlsx/drawing/pic.rb
CHANGED
@@ -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="
|
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="
|
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 << '"/>'
|
data/lib/axlsx/package.rb
CHANGED
@@ -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}",
|
@@ -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
|
157
|
-
# ws.add_row
|
158
|
-
# ws.add_row
|
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
|
187
|
-
# ws.add_row
|
188
|
-
# ws.add_row
|
189
|
-
# ws.add_row
|
190
|
-
# ws.add_row
|
191
|
-
# ws.add_row
|
192
|
-
# ws.add_row
|
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
|
211
|
-
# ws.add_row
|
212
|
-
# ws.add_row
|
213
|
-
# ws.add_row
|
214
|
-
# ws.add_row
|
215
|
-
# ws.add_row
|
216
|
-
# ws.add_row
|
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
|
-
|
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
|
data/lib/axlsx/util/accessors.rb
CHANGED
@@ -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.
|
data/lib/axlsx/util/constants.rb
CHANGED
@@ -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
|
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=
|
54
|
+
def self.validate(name, types, v, other=false)
|
54
55
|
types = [types] unless types.is_a? Array
|
55
|
-
|
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|
|
60
|
+
types.each { |t| return if v.ancestors.include?(t) }
|
58
61
|
else
|
59
|
-
types.each { |t|
|
62
|
+
types.each { |t| return if v.is_a?(t) }
|
60
63
|
end
|
61
|
-
raise ArgumentError, (ERR_TYPE % [v.inspect, name, types.inspect])
|
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
|
data/lib/axlsx/version.rb
CHANGED
@@ -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>' <<
|
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 =
|
35
|
-
@
|
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 [
|
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
|
-
|
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
|
372
|
+
elsif v.to_s =~ /\A[+-]?\d+?\Z/ #numeric
|
419
373
|
:integer
|
420
|
-
elsif v.to_s
|
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,
|
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
|