axlsx 1.0.16 → 1.1.0
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/.yardopts +1 -0
- data/CHANGELOG.md +18 -3
- data/README.md +189 -55
- data/examples/example.csv +1000 -0
- data/examples/example.rb +264 -142
- data/examples/example.xlsx +0 -0
- data/examples/example_streamed.xlsx +0 -0
- data/examples/no-use_autowidth.xlsx +0 -0
- data/examples/shared_strings_example.xlsx +0 -0
- data/lib/axlsx/content_type/content_type.rb +13 -12
- data/lib/axlsx/content_type/default.rb +10 -6
- data/lib/axlsx/content_type/override.rb +13 -8
- data/lib/axlsx/doc_props/app.rb +38 -41
- data/lib/axlsx/doc_props/core.rb +13 -17
- data/lib/axlsx/drawing/axis.rb +39 -19
- data/lib/axlsx/drawing/bar_3D_chart.rb +34 -32
- data/lib/axlsx/drawing/bar_series.rb +14 -14
- data/lib/axlsx/drawing/cat_axis.rb +16 -14
- data/lib/axlsx/drawing/cat_axis_data.rb +17 -18
- data/lib/axlsx/drawing/chart.rb +40 -41
- data/lib/axlsx/drawing/drawing.rb +16 -12
- data/lib/axlsx/drawing/graphic_frame.rb +22 -21
- data/lib/axlsx/drawing/hyperlink.rb +13 -12
- data/lib/axlsx/drawing/line_3D_chart.rb +31 -28
- data/lib/axlsx/drawing/line_series.rb +12 -11
- data/lib/axlsx/drawing/marker.rb +11 -8
- data/lib/axlsx/drawing/named_axis_data.rb +36 -0
- data/lib/axlsx/drawing/one_cell_anchor.rb +18 -16
- data/lib/axlsx/drawing/pic.rb +26 -38
- data/lib/axlsx/drawing/picture_locking.rb +22 -18
- data/lib/axlsx/drawing/pie_3D_chart.rb +11 -8
- data/lib/axlsx/drawing/pie_series.rb +16 -12
- data/lib/axlsx/drawing/scaling.rb +11 -10
- data/lib/axlsx/drawing/scatter_chart.rb +69 -0
- data/lib/axlsx/drawing/scatter_series.rb +39 -0
- data/lib/axlsx/drawing/ser_axis.rb +11 -10
- data/lib/axlsx/drawing/series.rb +16 -15
- data/lib/axlsx/drawing/series_title.rb +15 -14
- data/lib/axlsx/drawing/title.rb +27 -26
- data/lib/axlsx/drawing/two_cell_anchor.rb +19 -20
- data/lib/axlsx/drawing/val_axis.rb +9 -7
- data/lib/axlsx/drawing/val_axis_data.rb +18 -17
- data/lib/axlsx/drawing/view_3D.rb +23 -20
- data/lib/axlsx/package.rb +116 -51
- data/lib/axlsx/rels/relationship.rb +12 -8
- data/lib/axlsx/rels/relationships.rb +8 -1
- data/lib/axlsx/stylesheet/#num_fmt.rb# +69 -0
- data/lib/axlsx/stylesheet/border.rb +28 -23
- data/lib/axlsx/stylesheet/border_pr.rb +17 -15
- data/lib/axlsx/stylesheet/cell_alignment.rb +24 -21
- data/lib/axlsx/stylesheet/cell_protection.rb +11 -7
- data/lib/axlsx/stylesheet/cell_style.rb +9 -5
- data/lib/axlsx/stylesheet/color.rb +21 -14
- data/lib/axlsx/stylesheet/fill.rb +8 -5
- data/lib/axlsx/stylesheet/font.rb +22 -14
- data/lib/axlsx/stylesheet/gradient_fill.rb +20 -17
- data/lib/axlsx/stylesheet/gradient_stop.rb +10 -6
- data/lib/axlsx/stylesheet/num_fmt.rb +13 -6
- data/lib/axlsx/stylesheet/pattern_fill.rb +26 -10
- data/lib/axlsx/stylesheet/styles.rb +53 -41
- data/lib/axlsx/stylesheet/table_style.rb +10 -4
- data/lib/axlsx/stylesheet/table_style_element.rb +11 -7
- data/lib/axlsx/stylesheet/table_styles.rb +12 -8
- data/lib/axlsx/stylesheet/xf.rb +30 -25
- data/lib/axlsx/util/cbf.rb +1 -0
- data/lib/axlsx/util/constants.rb +27 -14
- data/lib/axlsx/util/ms_off_crypto.rb +1 -1
- data/lib/axlsx/util/parser.rb +1 -0
- data/lib/axlsx/util/simple_typed_list.rb +20 -10
- data/lib/axlsx/util/storage.rb +1 -0
- data/lib/axlsx/util/validators.rb +23 -7
- data/lib/axlsx/version.rb +2 -1
- data/lib/axlsx/workbook/shared_strings_table.rb +69 -0
- data/lib/axlsx/workbook/shared_strings_table.rb~ +69 -0
- data/lib/axlsx/workbook/workbook.rb +81 -35
- data/lib/axlsx/workbook/worksheet/cell.rb +172 -117
- data/lib/axlsx/workbook/worksheet/col.rb +114 -0
- data/lib/axlsx/workbook/worksheet/col.rb~ +0 -0
- data/lib/axlsx/workbook/worksheet/date_time_converter.rb +29 -0
- data/lib/axlsx/workbook/worksheet/page_margins.rb +97 -0
- data/lib/axlsx/workbook/worksheet/row.rb +53 -21
- data/lib/axlsx/workbook/worksheet/shared_strings_table.rb~ +0 -0
- data/lib/axlsx/workbook/worksheet/table.rb +96 -0
- data/lib/axlsx/workbook/worksheet/table.rb~ +97 -0
- data/lib/axlsx/workbook/worksheet/worksheet.rb +269 -121
- data/lib/axlsx.rb +41 -14
- data/lib/schema/dc.xsd +5 -5
- data/lib/schema/dcmitype.xsd +5 -3
- data/lib/schema/dcterms.xsd +15 -15
- data/lib/schema/opc-coreProperties.xsd +6 -2
- data/lib/schema/xml.xsd +7 -8
- data/test/#benchmark.txt# +7 -0
- data/test/#tc_helper.rb# +3 -0
- data/test/benchmark.rb +81 -0
- data/test/benchmark.rb~ +0 -0
- data/test/benchmark.txt +6 -0
- data/test/benchmark.txt~ +6 -0
- data/test/content_type/tc_content_type.rb +31 -31
- data/test/content_type/tc_default.rb +9 -22
- data/test/content_type/tc_override.rb +8 -21
- data/test/doc_props/tc_app.rb +2 -8
- data/test/doc_props/tc_core.rb +6 -7
- data/test/drawing/tc_axis.rb +7 -3
- data/test/drawing/tc_bar_3D_chart.rb +6 -7
- data/test/drawing/tc_bar_series.rb +4 -5
- data/test/drawing/tc_cat_axis.rb +2 -3
- data/test/drawing/tc_cat_axis_data.rb +2 -3
- data/test/drawing/tc_chart.rb +13 -14
- data/test/drawing/tc_drawing.rb +7 -8
- data/test/drawing/tc_graphic_frame.rb +3 -4
- data/test/drawing/tc_hyperlink.rb +2 -3
- data/test/drawing/tc_line_3d_chart.rb +5 -6
- data/test/drawing/tc_line_series.rb +3 -4
- data/test/drawing/tc_marker.rb +3 -4
- data/test/drawing/tc_one_cell_anchor.rb +6 -7
- data/test/drawing/tc_pic.rb +8 -9
- data/test/drawing/tc_picture_locking.rb +2 -3
- data/test/drawing/tc_pie_3D_chart.rb +5 -6
- data/test/drawing/tc_pie_series.rb +4 -5
- data/test/drawing/tc_scaling.rb +3 -4
- data/test/drawing/tc_scatter_chart.rb +43 -0
- data/test/drawing/tc_scatter_series.rb +20 -0
- data/test/drawing/tc_ser_axis.rb +2 -3
- data/test/drawing/tc_series.rb +4 -5
- data/test/drawing/tc_series_title.rb +4 -5
- data/test/drawing/tc_title.rb +4 -5
- data/test/drawing/tc_two_cell_anchor.rb +4 -5
- data/test/drawing/tc_val_axis.rb +2 -3
- data/test/drawing/tc_val_axis_data.rb +2 -3
- data/test/drawing/tc_view_3D.rb +6 -7
- data/test/example.csv +1000 -0
- data/test/example.xlsx +0 -0
- data/test/example_streamed.xlsx +0 -0
- data/test/profile.rb +33 -0
- data/test/rels/tc_relationship.rb +5 -6
- data/test/rels/tc_relationships.rb +4 -5
- data/test/stylesheet/tc_border.rb +3 -4
- data/test/stylesheet/tc_border_pr.rb +3 -4
- data/test/stylesheet/tc_cell_alignment.rb +10 -6
- data/test/stylesheet/tc_cell_protection.rb +2 -3
- data/test/stylesheet/tc_cell_style.rb +2 -3
- data/test/stylesheet/tc_color.rb +2 -3
- data/test/stylesheet/tc_fill.rb +1 -2
- data/test/stylesheet/tc_font.rb +12 -5
- data/test/stylesheet/tc_gradient_fill.rb +1 -2
- data/test/stylesheet/tc_gradient_stop.rb +1 -2
- data/test/stylesheet/tc_num_fmt.rb +1 -2
- data/test/stylesheet/tc_pattern_fill.rb +3 -4
- data/test/stylesheet/tc_styles.rb +16 -6
- data/test/stylesheet/tc_table_style.rb +2 -3
- data/test/stylesheet/tc_table_style_element.rb +2 -3
- data/test/stylesheet/tc_table_styles.rb +3 -4
- data/test/stylesheet/tc_xf.rb +16 -17
- data/test/tc_axlsx.rb +39 -0
- data/test/tc_axlsx.rb~ +0 -0
- data/test/tc_helper.rb +3 -0
- data/test/tc_helper.rb~ +3 -0
- data/test/tc_package.rb +43 -9
- data/test/util/tc_simple_typed_list.rb +8 -9
- data/test/util/tc_validators.rb +7 -8
- data/test/workbook/tc_shared_strings_table.rb +38 -0
- data/test/workbook/tc_workbook.rb +32 -6
- data/test/workbook/worksheet/table/tc_table.rb +71 -0
- data/test/workbook/worksheet/table/tc_table.rb~ +72 -0
- data/test/workbook/worksheet/tc_cell.rb +70 -13
- data/test/workbook/worksheet/tc_col.rb +59 -0
- data/test/workbook/worksheet/tc_col.rb~ +10 -0
- data/test/workbook/worksheet/tc_date_time_converter.rb +126 -0
- data/test/workbook/worksheet/tc_page_margins.rb +97 -0
- data/test/workbook/worksheet/tc_row.rb +54 -4
- data/test/workbook/worksheet/tc_worksheet.rb +191 -31
- metadata +162 -109
- data/test/drawing/tc_hyperlink.rb~ +0 -71
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
1
2
|
module Axlsx
|
|
2
|
-
# A SimpleTypedList is a type restrictive collection that allows some of the methods from Array and supports basic xml serialization.
|
|
3
|
+
# A SimpleTypedList is a type restrictive collection that allows some of the methods from Array and supports basic xml serialization.
|
|
3
4
|
# @private
|
|
4
5
|
class SimpleTypedList
|
|
5
6
|
# The class constants of allowed types
|
|
@@ -11,14 +12,14 @@ module Axlsx
|
|
|
11
12
|
attr_reader :locked_at
|
|
12
13
|
|
|
13
14
|
# The tag name to use when serializing this object
|
|
14
|
-
# by default the parent node for all items in the list is the classname of the first allowed type with the first letter in lowercase.
|
|
15
|
+
# by default the parent node for all items in the list is the classname of the first allowed type with the first letter in lowercase.
|
|
15
16
|
# @return [String]
|
|
16
17
|
attr_reader :serialize_as
|
|
17
18
|
|
|
18
19
|
# Creats a new typed list
|
|
19
20
|
# @param [Array, Class] type An array of Class objects or a single Class object
|
|
20
21
|
# @param [String] serialize The tag name to use in serialization
|
|
21
|
-
# @raise [ArgumentError] if all members of type are not Class objects
|
|
22
|
+
# @raise [ArgumentError] if all members of type are not Class objects
|
|
22
23
|
def initialize type, serialize_as=nil
|
|
23
24
|
if type.is_a? Array
|
|
24
25
|
type.each { |item| raise ArgumentError, "All members of type must be Class objects" unless item.is_a? Class }
|
|
@@ -38,7 +39,7 @@ module Axlsx
|
|
|
38
39
|
@locked_at = @list.size
|
|
39
40
|
self
|
|
40
41
|
end
|
|
41
|
-
|
|
42
|
+
|
|
42
43
|
def to_ary
|
|
43
44
|
@list
|
|
44
45
|
end
|
|
@@ -57,7 +58,7 @@ module Axlsx
|
|
|
57
58
|
def <<(v)
|
|
58
59
|
DataTypeValidator.validate "SimpleTypedList.<<", @allowed_types, v
|
|
59
60
|
@list << v
|
|
60
|
-
@list.size - 1
|
|
61
|
+
@list.size - 1
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
# alternate of << method
|
|
@@ -103,14 +104,14 @@ module Axlsx
|
|
|
103
104
|
return false unless @locked_at.is_a? Fixnum
|
|
104
105
|
index < @locked_at
|
|
105
106
|
end
|
|
106
|
-
|
|
107
|
+
|
|
107
108
|
# override the equality method so that this object can be compared to a simple array.
|
|
108
109
|
# if this object's list is equal to the specifiec array, we return true.
|
|
109
110
|
def ==(v)
|
|
110
111
|
v == @list
|
|
111
112
|
end
|
|
112
113
|
# method_mission override to pass allowed methods to the list.
|
|
113
|
-
# @note
|
|
114
|
+
# @note
|
|
114
115
|
# the following methods are not allowed
|
|
115
116
|
# :replace
|
|
116
117
|
# :insert
|
|
@@ -139,25 +140,34 @@ module Axlsx
|
|
|
139
140
|
DELEGATES = Array.instance_methods - self.instance_methods - DESTRUCTIVE
|
|
140
141
|
|
|
141
142
|
DELEGATES.each do |method|
|
|
142
|
-
class_eval %{
|
|
143
|
+
class_eval %{
|
|
143
144
|
def #{method}(*args, &block)
|
|
144
145
|
@list.send(:#{method}, *args, &block)
|
|
145
146
|
end
|
|
146
147
|
}
|
|
147
148
|
end
|
|
148
149
|
|
|
150
|
+
def to_xml_string(str = '')
|
|
151
|
+
classname = @allowed_types[0].name.split('::').last
|
|
152
|
+
el_name = serialize_as || (classname[0,1].downcase + classname[1..-1])
|
|
153
|
+
str << '<' << el_name << ' count="' << @list.size.to_s << '">'
|
|
154
|
+
@list.each { |item| item.to_xml_string(str) }
|
|
155
|
+
str << '</' << el_name << '>'
|
|
156
|
+
end
|
|
157
|
+
|
|
149
158
|
# Serializes the list
|
|
150
|
-
# If the serialize_as property is set, it is used as the parent node name.
|
|
159
|
+
# If the serialize_as property is set, it is used as the parent node name.
|
|
151
160
|
# If the serialize_as property is nil, the first item in the list of allowed_types will be used, having the first letter of the class changed to lower case.
|
|
152
161
|
# @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
|
|
153
162
|
# @return [String]
|
|
154
163
|
def to_xml(xml)
|
|
155
164
|
classname = @allowed_types[0].name.split('::').last
|
|
156
|
-
el_name = serialize_as || (classname[0,1].downcase + classname[1..-1])
|
|
165
|
+
el_name = serialize_as || (classname[0,1].downcase + classname[1..-1])
|
|
157
166
|
xml.send(el_name, :count=>@list.size) {
|
|
158
167
|
@list.each { |item| item.to_xml(xml) }
|
|
159
168
|
}
|
|
160
169
|
end
|
|
170
|
+
|
|
161
171
|
end
|
|
162
172
|
|
|
163
173
|
|
data/lib/axlsx/util/storage.rb
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
1
2
|
module Axlsx
|
|
2
3
|
# Validate a value against a specific list of allowed values.
|
|
3
4
|
class RestrictionValidator
|
|
4
5
|
# Perform validation
|
|
5
6
|
# @param [String] name The name of what is being validatied. This is included in the error message
|
|
6
7
|
# @param [Array] choices The list of choices to validate against
|
|
7
|
-
# @param [Any] v The value to be validated
|
|
8
|
+
# @param [Any] v The value to be validated
|
|
8
9
|
# @raise [ArgumentError] Raised if the value provided is not in the list of choices.
|
|
9
10
|
# @return [Boolean] true if validation succeeds.
|
|
10
11
|
def self.validate(name, choices, v)
|
|
11
|
-
raise ArgumentError, (ERR_RESTRICTION % [v.to_s, name, choices.inspect]) unless choices.include?(v)
|
|
12
|
+
raise ArgumentError, (ERR_RESTRICTION % [v.to_s, name, choices.inspect]) unless choices.include?(v)
|
|
12
13
|
true
|
|
13
14
|
end
|
|
14
15
|
end
|
|
@@ -52,6 +53,14 @@ module Axlsx
|
|
|
52
53
|
DataTypeValidator.validate(:unsigned_int, [Fixnum, Integer], v, lambda { |arg| arg.respond_to?(:>=) && arg >= 0 })
|
|
53
54
|
end
|
|
54
55
|
|
|
56
|
+
# Requires that the value is a Fixnum Integer or Float and is greater or equal to 0
|
|
57
|
+
# @param [Any] v The value validated
|
|
58
|
+
# @raise [ArgumentError] raised if the value is not a Fixnun, Integer, Float value greater or equal to 0
|
|
59
|
+
# @return [Boolean] true if the data is valid
|
|
60
|
+
def self.validate_unsigned_numeric(v)
|
|
61
|
+
DataTypeValidator.validate("Invalid column width", [Fixnum, Integer, Float], v, lambda { |arg| arg.respond_to?(:>=) && arg >= 0 })
|
|
62
|
+
end
|
|
63
|
+
|
|
55
64
|
# Requires that the value is a Fixnum or Integer
|
|
56
65
|
# @param [Any] v The value validated
|
|
57
66
|
def self.validate_int(v)
|
|
@@ -59,7 +68,7 @@ module Axlsx
|
|
|
59
68
|
end
|
|
60
69
|
|
|
61
70
|
# Requires that the value is a form that can be evaluated as a boolean in an xml document.
|
|
62
|
-
# The value must be an instance of Fixnum, String, Integer, Symbol, TrueClass or FalseClass and
|
|
71
|
+
# The value must be an instance of Fixnum, String, Integer, Symbol, TrueClass or FalseClass and
|
|
63
72
|
# it must be one of 0, 1, "true", "false", :true, :false, true, false, "0", or "1"
|
|
64
73
|
# @param [Any] v The value validated
|
|
65
74
|
def self.validate_boolean(v)
|
|
@@ -70,13 +79,13 @@ module Axlsx
|
|
|
70
79
|
# @param [Any] v The value validated
|
|
71
80
|
def self.validate_string(v)
|
|
72
81
|
DataTypeValidator.validate :string, String, v
|
|
73
|
-
end
|
|
82
|
+
end
|
|
74
83
|
|
|
75
84
|
# Requires that the value is a Float
|
|
76
85
|
# @param [Any] v The value validated
|
|
77
86
|
def self.validate_float(v)
|
|
78
87
|
DataTypeValidator.validate :float, Float, v
|
|
79
|
-
end
|
|
88
|
+
end
|
|
80
89
|
|
|
81
90
|
# Requires that the value is valid pattern type.
|
|
82
91
|
# valid pattern types must be one of :none, :solid, :mediumGray, :darkGray, :lightGray, :darkHorizontal, :darkVertical, :darkDown,
|
|
@@ -94,6 +103,13 @@ module Axlsx
|
|
|
94
103
|
RestrictionValidator.validate :gradient_type, [:linear, :path], v
|
|
95
104
|
end
|
|
96
105
|
|
|
106
|
+
# Requires that the value is a valid scatterStyle
|
|
107
|
+
# must be one of :none | :line | :lineMarker | :marker | :smooth | :smoothMarker
|
|
108
|
+
# must be one of "none" | "line" | "lineMarker" | "marker" | "smooth" | "smoothMarker"
|
|
109
|
+
# @param [Symbol|String] the value to validate
|
|
110
|
+
def self.validate_scatter_style(v)
|
|
111
|
+
Axlsx::RestrictionValidator.validate "ScatterChart.scatterStyle", [:none, :line, :lineMarker, :marker, :smooth, :smoothMarker], v.to_sym
|
|
112
|
+
end
|
|
97
113
|
# Requires that the value is a valid horizontal_alignment
|
|
98
114
|
# :general, :left, :center, :right, :fill, :justify, :centerContinuous, :distributed are allowed
|
|
99
115
|
# @param [Any] v The value validated
|
|
@@ -116,10 +132,10 @@ module Axlsx
|
|
|
116
132
|
end
|
|
117
133
|
|
|
118
134
|
# Requires that the value is a valid relationship_type
|
|
119
|
-
# XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R are allowed
|
|
135
|
+
# 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
|
|
120
136
|
# @param [Any] v The value validated
|
|
121
137
|
def self.validate_relationship_type(v)
|
|
122
|
-
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], v
|
|
138
|
+
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], v
|
|
123
139
|
end
|
|
124
140
|
|
|
125
141
|
# Requires that the value is a valid table element type
|
data/lib/axlsx/version.rb
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
1
2
|
module Axlsx
|
|
2
3
|
|
|
3
4
|
# The version of the gem
|
|
4
5
|
# When using bunle exec rake and referencing the gem on github or locally
|
|
5
6
|
# it will use the gemspec, which preloads this constant for the gem's version.
|
|
6
7
|
# We check to make sure that it has not already been loaded
|
|
7
|
-
VERSION="1.0
|
|
8
|
+
VERSION="1.1.0" unless defined? Axlsx::VERSION
|
|
8
9
|
|
|
9
10
|
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
module Axlsx
|
|
3
|
+
|
|
4
|
+
# The Shared String Table class is responsible for managing and serializing common strings in a workbook.
|
|
5
|
+
# While the ECMA-376 spec allows for both inline and shared strings it seems that at least some applications like iWorks Numbers
|
|
6
|
+
# and Google Docs require that the shared string table is populated in order to interoperate properly.
|
|
7
|
+
# As a developer, you should never need to directly work against this class. Simply set 'use_shared_strings'
|
|
8
|
+
# on the package or workbook to generate a package that uses the shared strings table instead of inline strings.
|
|
9
|
+
# @note Serialization performance is affected by using this serialization method so if you do not need interoperability
|
|
10
|
+
# it is recomended that you use the default inline string method of serialization.
|
|
11
|
+
class SharedStringsTable
|
|
12
|
+
|
|
13
|
+
# The total number of strings in the workbook including duplicates
|
|
14
|
+
# Empty cells are treated as blank strings
|
|
15
|
+
# @return [Integer]
|
|
16
|
+
attr_reader :count
|
|
17
|
+
|
|
18
|
+
# The total number of unique strings in the workbook.
|
|
19
|
+
# @return [Integer]
|
|
20
|
+
def unique_count
|
|
21
|
+
@unique_cells.size
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# An array of unique cells. Multiple attributes of the cell are used in comparison
|
|
25
|
+
# each of these unique cells is parsed into the shared string table.
|
|
26
|
+
# @see Cell#sharable
|
|
27
|
+
attr_reader :unique_cells
|
|
28
|
+
|
|
29
|
+
# Creates a new Shared Strings Table agains an array of cells
|
|
30
|
+
# @param [Array] cells This is an array of all of the cells in the workbook
|
|
31
|
+
def initialize(cells)
|
|
32
|
+
cells = cells.flatten.reject { |c| c.type != :string || c.value.nil? || c.value.start_with?('=') }
|
|
33
|
+
@count = cells.size
|
|
34
|
+
@unique_cells = []
|
|
35
|
+
@shared_xml_string = ""
|
|
36
|
+
resolve(cells)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Serializes the object
|
|
40
|
+
# @param [String] str
|
|
41
|
+
# @return [String]
|
|
42
|
+
def to_xml_string
|
|
43
|
+
'<?xml version="1.0" encoding="UTF-8"?><sst xmlns="' << XML_NS << '" count="' << @count.to_s << '" uniqueCount="' << unique_count.to_s << '">' << @shared_xml_string << '</sst>'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
# Interate over all of the cells in the array.
|
|
49
|
+
# if our unique cells array does not contain a sharable cell,
|
|
50
|
+
# add the cell to our unique cells array and set the ssti attribute on the index of this cell in the shared strings table
|
|
51
|
+
# if a sharable cell already exists in our unique_cells array, set the ssti attribute of the cell and move on.
|
|
52
|
+
# @return [Array] unique cells
|
|
53
|
+
def resolve(cells)
|
|
54
|
+
cells.each do |cell|
|
|
55
|
+
cell_hash = cell.shareable_hash
|
|
56
|
+
index = @unique_cells.index do |item|
|
|
57
|
+
item == cell_hash
|
|
58
|
+
end
|
|
59
|
+
if index == nil
|
|
60
|
+
cell.send :ssti=, @unique_cells.size
|
|
61
|
+
@shared_xml_string << '<si>' << cell.run_xml_string << '</si>'
|
|
62
|
+
@unique_cells << cell_hash
|
|
63
|
+
else
|
|
64
|
+
cell.send :ssti=, index
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# encoding: UTF--8
|
|
2
|
+
module Axlsx
|
|
3
|
+
|
|
4
|
+
# The Shared String Table class is responsible for managing and serializing common strings in a workbook.
|
|
5
|
+
# While the ECMA-376 spec allows for both inline and shared strings it seems that at least some applications like Numbers (Mac)
|
|
6
|
+
# and Google Docs require that the shared string table is populated in order to interoperate properly.
|
|
7
|
+
# As a developer, you should never need to directly work against this class. Simply set 'use_shared_strings'
|
|
8
|
+
# on the package or workbook to generate a package that uses the shared strings table instead of inline strings.
|
|
9
|
+
# @note Serialization performance is affected by using this serialization method so if you do not need interoperability
|
|
10
|
+
# it is recomended that you use the default inline string method of serialization.
|
|
11
|
+
class SharedStringsTable
|
|
12
|
+
|
|
13
|
+
# The total number of strings in the workbook including duplicates
|
|
14
|
+
# Empty cells are treated as blank strings
|
|
15
|
+
# @return [Integer]
|
|
16
|
+
attr_reader :count
|
|
17
|
+
|
|
18
|
+
# The total number of unique strings in the workbook.
|
|
19
|
+
# @return [Integer]
|
|
20
|
+
def unique_count
|
|
21
|
+
@unique_cells.size
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# An array of unique cells. Multiple attributes of the cell are used in comparison
|
|
25
|
+
# each of these unique cells is parsed into the shared string table.
|
|
26
|
+
# @see Cell#sharable
|
|
27
|
+
attr_reader :unique_cells
|
|
28
|
+
|
|
29
|
+
# Creates a new Shared Strings Table agains an array of cells
|
|
30
|
+
# @param [Array] cells This is an array of all of the cells in the workbook
|
|
31
|
+
def initialize(cells)
|
|
32
|
+
cells = cells.reject { |c| c.type != :string }
|
|
33
|
+
@count = cells.size
|
|
34
|
+
@unique_cells = []
|
|
35
|
+
resolve cells
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Interate over all of the cells in the array.
|
|
39
|
+
# if our unique cells array does not contain a sharable cell,
|
|
40
|
+
# add the cell to our unique cells array and set the ssti attribute on the index of this cell in the shared strings table
|
|
41
|
+
# if a sharable cell already exists in our unique_cells array, set the ssti attribute of the cell and move on.
|
|
42
|
+
# @return [Array] unique cells
|
|
43
|
+
def resolve(cells)
|
|
44
|
+
cells.each do |cell|
|
|
45
|
+
index = @unique_cells.index { |item| item.sharable(cell) }
|
|
46
|
+
if index == nil
|
|
47
|
+
cell.ssti = @unique_cells.size
|
|
48
|
+
@unique_cells << cell
|
|
49
|
+
else
|
|
50
|
+
cell.ssti = index
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Generate the xml document for the Shared Strings Table
|
|
56
|
+
# @return [String]
|
|
57
|
+
def to_xml
|
|
58
|
+
builder = Nokogiri::XML::Builder.new(:encoding => ENCODING) do |xml|
|
|
59
|
+
xml.sst(:xmlns => Axlsx::XML_NS, :count => count, :uniqueCount => unique_count) {
|
|
60
|
+
@unique_cells.each do |cell|
|
|
61
|
+
xml.si { xml.t cell.value.to_s }
|
|
62
|
+
end
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
builder.to_xml(:save_with => 0)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
module Axlsx
|
|
2
|
+
module Axlsx
|
|
3
3
|
|
|
4
|
+
require 'axlsx/workbook/worksheet/date_time_converter.rb'
|
|
4
5
|
require 'axlsx/workbook/worksheet/cell.rb'
|
|
6
|
+
require 'axlsx/workbook/worksheet/page_margins.rb'
|
|
5
7
|
require 'axlsx/workbook/worksheet/row.rb'
|
|
8
|
+
require 'axlsx/workbook/worksheet/col.rb'
|
|
6
9
|
require 'axlsx/workbook/worksheet/worksheet.rb'
|
|
10
|
+
require 'axlsx/workbook/shared_strings_table.rb'
|
|
11
|
+
require 'axlsx/workbook/worksheet/table.rb'
|
|
7
12
|
|
|
8
13
|
# The Workbook class is an xlsx workbook that manages worksheets, charts, drawings and styles.
|
|
9
14
|
# The following parts of the Office Open XML spreadsheet specification are not implimented in this version.
|
|
10
|
-
#
|
|
15
|
+
#
|
|
11
16
|
# bookViews
|
|
12
17
|
# calcPr
|
|
13
18
|
# customWorkbookViews
|
|
@@ -30,7 +35,20 @@ require 'axlsx/workbook/worksheet/worksheet.rb'
|
|
|
30
35
|
# *workbookPr is only supported to the extend of date1904
|
|
31
36
|
class Workbook
|
|
32
37
|
|
|
33
|
-
#
|
|
38
|
+
# When true, the Package will be generated with a shared string table. This may be required by some OOXML processors that do not
|
|
39
|
+
# adhere to the ECMA specification that dictates string may be inline in the sheet.
|
|
40
|
+
# Using this option will increase the time required to serialize the document as every string in every cell must be analzed and referenced.
|
|
41
|
+
# @return [Boolean]
|
|
42
|
+
attr_reader :use_shared_strings
|
|
43
|
+
|
|
44
|
+
# @see use_shared_strings
|
|
45
|
+
def use_shared_strings=(v)
|
|
46
|
+
Axlsx::validate_boolean(v)
|
|
47
|
+
@use_shared_strings = v
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# A collection of worksheets associated with this workbook.
|
|
34
52
|
# @note The recommended way to manage worksheets is add_worksheet
|
|
35
53
|
# @see Workbook#add_worksheet
|
|
36
54
|
# @see Worksheet
|
|
@@ -58,6 +76,14 @@ require 'axlsx/workbook/worksheet/worksheet.rb'
|
|
|
58
76
|
# @return [SimpleTypedList]
|
|
59
77
|
attr_reader :drawings
|
|
60
78
|
|
|
79
|
+
# A colllection of tables associated with this workbook
|
|
80
|
+
# @note The recommended way to manage drawings is Worksheet#add_table
|
|
81
|
+
# @see Worksheet#add_table
|
|
82
|
+
# @see Table
|
|
83
|
+
# @return [SimpleTypedList]
|
|
84
|
+
attr_reader :tables
|
|
85
|
+
|
|
86
|
+
|
|
61
87
|
# The styles associated with this workbook
|
|
62
88
|
# @note The recommended way to manage styles is Styles#add_style
|
|
63
89
|
# @see Style#add_style
|
|
@@ -72,40 +98,35 @@ require 'axlsx/workbook/worksheet/worksheet.rb'
|
|
|
72
98
|
# Indicates if the epoc date for serialization should be 1904. If false, 1900 is used.
|
|
73
99
|
@@date1904 = false
|
|
74
100
|
|
|
75
|
-
|
|
101
|
+
|
|
102
|
+
# lets come back to this later when we are ready for parsing.
|
|
76
103
|
#def self.parse entry
|
|
77
104
|
# io = entry.get_input_stream
|
|
78
105
|
# w = self.new
|
|
79
106
|
# w.parser_xml = Nokogiri::XML(io.read)
|
|
80
|
-
# w.parse_string :date1904, "//xmlns:workbookPr/@date1904"
|
|
107
|
+
# w.parse_string :date1904, "//xmlns:workbookPr/@date1904"
|
|
81
108
|
# w
|
|
82
109
|
#end
|
|
83
110
|
|
|
84
111
|
# Creates a new Workbook
|
|
85
|
-
#
|
|
86
|
-
# @option options [Boolean] date1904. If this is not specified,
|
|
87
|
-
#
|
|
112
|
+
# The recomended way to work with workbooks is via Package#workbook
|
|
113
|
+
# @option options [Boolean] date1904. If this is not specified, date1904 is set to false. Office 2011 for Mac defaults to false.
|
|
88
114
|
def initialize(options={})
|
|
89
115
|
@styles = Styles.new
|
|
90
116
|
@worksheets = SimpleTypedList.new Worksheet
|
|
91
117
|
@drawings = SimpleTypedList.new Drawing
|
|
92
118
|
@charts = SimpleTypedList.new Chart
|
|
93
119
|
@images = SimpleTypedList.new Pic
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
end
|
|
120
|
+
@tables = SimpleTypedList.new Table
|
|
121
|
+
@use_autowidth = true
|
|
97
122
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
# @return [Boolean]
|
|
101
|
-
def is_bsd?
|
|
102
|
-
platform = RUBY_PLATFORM.downcase
|
|
103
|
-
platform.include?('freebsd') || platform.include?('darwin')
|
|
123
|
+
self.date1904= !options[:date1904].nil? && options[:date1904]
|
|
124
|
+
yield self if block_given?
|
|
104
125
|
end
|
|
105
126
|
|
|
106
127
|
# Instance level access to the class variable 1904
|
|
107
128
|
# @return [Boolean]
|
|
108
|
-
def date1904() @@date1904; end
|
|
129
|
+
def date1904() @@date1904; end
|
|
109
130
|
|
|
110
131
|
# see @date1904
|
|
111
132
|
def date1904=(v) Axlsx::validate_boolean v; @@date1904 = v; end
|
|
@@ -118,9 +139,18 @@ require 'axlsx/workbook/worksheet/worksheet.rb'
|
|
|
118
139
|
# @return [Boolean]
|
|
119
140
|
def self.date1904() @@date1904; end
|
|
120
141
|
|
|
142
|
+
# Indicates if the workbook should use autowidths or not.
|
|
143
|
+
# this must be set before instantiating a worksheet to avoid Rmagix inclusion
|
|
144
|
+
# @return [Boolean]
|
|
145
|
+
def use_autowidth() @use_autowidth; end
|
|
146
|
+
|
|
147
|
+
# see @use_autowidth
|
|
148
|
+
def use_autowidth=(v) Axlsx::validate_boolean v; @use_autowidth = v; end
|
|
149
|
+
|
|
121
150
|
# Adds a worksheet to this workbook
|
|
122
151
|
# @return [Worksheet]
|
|
123
152
|
# @option options [String] name The name of the worksheet.
|
|
153
|
+
# @option options [Hash] page_margins The page margins for the worksheet.
|
|
124
154
|
# @see Worksheet#initialize
|
|
125
155
|
def add_worksheet(options={})
|
|
126
156
|
worksheet = Worksheet.new(self, options)
|
|
@@ -134,38 +164,54 @@ require 'axlsx/workbook/worksheet/worksheet.rb'
|
|
|
134
164
|
r = Relationships.new
|
|
135
165
|
@worksheets.each do |sheet|
|
|
136
166
|
r << Relationship.new(WORKSHEET_R, WORKSHEET_PN % (r.size+1))
|
|
137
|
-
end
|
|
167
|
+
end
|
|
138
168
|
r << Relationship.new(STYLES_R, STYLES_PN)
|
|
169
|
+
if use_shared_strings
|
|
170
|
+
r << Relationship.new(SHARED_STRINGS_R, SHARED_STRINGS_PN)
|
|
171
|
+
end
|
|
139
172
|
r
|
|
140
173
|
end
|
|
141
174
|
|
|
175
|
+
# generates a shared string object against all cells in all worksheets.
|
|
176
|
+
# @return [SharedStringTable]
|
|
177
|
+
def shared_strings
|
|
178
|
+
SharedStringsTable.new(worksheets.collect { |ws| ws.cells })
|
|
179
|
+
end
|
|
180
|
+
|
|
142
181
|
# returns a range of cells in a worksheet
|
|
143
|
-
# @param [String] cell_def The excel style reference defining the worksheet and cells. The range must specify the sheet to
|
|
182
|
+
# @param [String] cell_def The excel style reference defining the worksheet and cells. The range must specify the sheet to
|
|
144
183
|
# retrieve the cells from. e.g. range('Sheet1!A1:B2') will return an array of four cells [A1, A2, B1, B2] while range('Sheet1!A1') will return a single Cell.
|
|
145
184
|
# @return [Cell, Array]
|
|
146
185
|
def [](cell_def)
|
|
147
186
|
sheet_name = cell_def.split('!')[0] if cell_def.match('!')
|
|
148
187
|
worksheet = self.worksheets.select { |s| s.name == sheet_name }.first
|
|
149
|
-
raise ArgumentError, 'Unknown Sheet' unless sheet_name && worksheet.is_a?(Worksheet)
|
|
188
|
+
raise ArgumentError, 'Unknown Sheet' unless sheet_name && worksheet.is_a?(Worksheet)
|
|
150
189
|
worksheet[cell_def.gsub(/.+!/,"")]
|
|
151
190
|
end
|
|
152
191
|
|
|
153
|
-
#
|
|
192
|
+
# Serialize the workbook
|
|
193
|
+
# @param [String] str
|
|
154
194
|
# @return [String]
|
|
155
|
-
def
|
|
195
|
+
def to_xml_string(str='')
|
|
156
196
|
add_worksheet unless worksheets.size > 0
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
197
|
+
str << '<?xml version="1.0" encoding="UTF-8"?>'
|
|
198
|
+
str << '<workbook xmlns="' << XML_NS << '" xmlns:r="' << XML_NS_R << '">'
|
|
199
|
+
str << '<workbookPr date1904="' << @@date1904.to_s << '"/>'
|
|
200
|
+
str << '<sheets>'
|
|
201
|
+
@worksheets.each_with_index do |sheet, index|
|
|
202
|
+
str << '<sheet name="' << sheet.name << '" sheetId="' << (index+1).to_s << '" r:id="' << sheet.rId << '"/>'
|
|
203
|
+
end
|
|
204
|
+
str << '</sheets>'
|
|
205
|
+
str << '<definedNames>'
|
|
206
|
+
@worksheets.each_with_index do |sheet, index|
|
|
207
|
+
if sheet.auto_filter
|
|
208
|
+
str << '<definedName name="_xlnm._FilterDatabase" localSheetId="' << index.to_s << '" hidden="1">'
|
|
209
|
+
str << sheet.abs_auto_filter << '</definedName>'
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
str << '</definedNames>'
|
|
213
|
+
str << '</workbook>'
|
|
169
214
|
end
|
|
215
|
+
|
|
170
216
|
end
|
|
171
217
|
end
|