axlsx 1.3.4 → 1.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|