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.
Files changed (173) hide show
  1. data/.yardopts +1 -0
  2. data/CHANGELOG.md +18 -3
  3. data/README.md +189 -55
  4. data/examples/example.csv +1000 -0
  5. data/examples/example.rb +264 -142
  6. data/examples/example.xlsx +0 -0
  7. data/examples/example_streamed.xlsx +0 -0
  8. data/examples/no-use_autowidth.xlsx +0 -0
  9. data/examples/shared_strings_example.xlsx +0 -0
  10. data/lib/axlsx/content_type/content_type.rb +13 -12
  11. data/lib/axlsx/content_type/default.rb +10 -6
  12. data/lib/axlsx/content_type/override.rb +13 -8
  13. data/lib/axlsx/doc_props/app.rb +38 -41
  14. data/lib/axlsx/doc_props/core.rb +13 -17
  15. data/lib/axlsx/drawing/axis.rb +39 -19
  16. data/lib/axlsx/drawing/bar_3D_chart.rb +34 -32
  17. data/lib/axlsx/drawing/bar_series.rb +14 -14
  18. data/lib/axlsx/drawing/cat_axis.rb +16 -14
  19. data/lib/axlsx/drawing/cat_axis_data.rb +17 -18
  20. data/lib/axlsx/drawing/chart.rb +40 -41
  21. data/lib/axlsx/drawing/drawing.rb +16 -12
  22. data/lib/axlsx/drawing/graphic_frame.rb +22 -21
  23. data/lib/axlsx/drawing/hyperlink.rb +13 -12
  24. data/lib/axlsx/drawing/line_3D_chart.rb +31 -28
  25. data/lib/axlsx/drawing/line_series.rb +12 -11
  26. data/lib/axlsx/drawing/marker.rb +11 -8
  27. data/lib/axlsx/drawing/named_axis_data.rb +36 -0
  28. data/lib/axlsx/drawing/one_cell_anchor.rb +18 -16
  29. data/lib/axlsx/drawing/pic.rb +26 -38
  30. data/lib/axlsx/drawing/picture_locking.rb +22 -18
  31. data/lib/axlsx/drawing/pie_3D_chart.rb +11 -8
  32. data/lib/axlsx/drawing/pie_series.rb +16 -12
  33. data/lib/axlsx/drawing/scaling.rb +11 -10
  34. data/lib/axlsx/drawing/scatter_chart.rb +69 -0
  35. data/lib/axlsx/drawing/scatter_series.rb +39 -0
  36. data/lib/axlsx/drawing/ser_axis.rb +11 -10
  37. data/lib/axlsx/drawing/series.rb +16 -15
  38. data/lib/axlsx/drawing/series_title.rb +15 -14
  39. data/lib/axlsx/drawing/title.rb +27 -26
  40. data/lib/axlsx/drawing/two_cell_anchor.rb +19 -20
  41. data/lib/axlsx/drawing/val_axis.rb +9 -7
  42. data/lib/axlsx/drawing/val_axis_data.rb +18 -17
  43. data/lib/axlsx/drawing/view_3D.rb +23 -20
  44. data/lib/axlsx/package.rb +116 -51
  45. data/lib/axlsx/rels/relationship.rb +12 -8
  46. data/lib/axlsx/rels/relationships.rb +8 -1
  47. data/lib/axlsx/stylesheet/#num_fmt.rb# +69 -0
  48. data/lib/axlsx/stylesheet/border.rb +28 -23
  49. data/lib/axlsx/stylesheet/border_pr.rb +17 -15
  50. data/lib/axlsx/stylesheet/cell_alignment.rb +24 -21
  51. data/lib/axlsx/stylesheet/cell_protection.rb +11 -7
  52. data/lib/axlsx/stylesheet/cell_style.rb +9 -5
  53. data/lib/axlsx/stylesheet/color.rb +21 -14
  54. data/lib/axlsx/stylesheet/fill.rb +8 -5
  55. data/lib/axlsx/stylesheet/font.rb +22 -14
  56. data/lib/axlsx/stylesheet/gradient_fill.rb +20 -17
  57. data/lib/axlsx/stylesheet/gradient_stop.rb +10 -6
  58. data/lib/axlsx/stylesheet/num_fmt.rb +13 -6
  59. data/lib/axlsx/stylesheet/pattern_fill.rb +26 -10
  60. data/lib/axlsx/stylesheet/styles.rb +53 -41
  61. data/lib/axlsx/stylesheet/table_style.rb +10 -4
  62. data/lib/axlsx/stylesheet/table_style_element.rb +11 -7
  63. data/lib/axlsx/stylesheet/table_styles.rb +12 -8
  64. data/lib/axlsx/stylesheet/xf.rb +30 -25
  65. data/lib/axlsx/util/cbf.rb +1 -0
  66. data/lib/axlsx/util/constants.rb +27 -14
  67. data/lib/axlsx/util/ms_off_crypto.rb +1 -1
  68. data/lib/axlsx/util/parser.rb +1 -0
  69. data/lib/axlsx/util/simple_typed_list.rb +20 -10
  70. data/lib/axlsx/util/storage.rb +1 -0
  71. data/lib/axlsx/util/validators.rb +23 -7
  72. data/lib/axlsx/version.rb +2 -1
  73. data/lib/axlsx/workbook/shared_strings_table.rb +69 -0
  74. data/lib/axlsx/workbook/shared_strings_table.rb~ +69 -0
  75. data/lib/axlsx/workbook/workbook.rb +81 -35
  76. data/lib/axlsx/workbook/worksheet/cell.rb +172 -117
  77. data/lib/axlsx/workbook/worksheet/col.rb +114 -0
  78. data/lib/axlsx/workbook/worksheet/col.rb~ +0 -0
  79. data/lib/axlsx/workbook/worksheet/date_time_converter.rb +29 -0
  80. data/lib/axlsx/workbook/worksheet/page_margins.rb +97 -0
  81. data/lib/axlsx/workbook/worksheet/row.rb +53 -21
  82. data/lib/axlsx/workbook/worksheet/shared_strings_table.rb~ +0 -0
  83. data/lib/axlsx/workbook/worksheet/table.rb +96 -0
  84. data/lib/axlsx/workbook/worksheet/table.rb~ +97 -0
  85. data/lib/axlsx/workbook/worksheet/worksheet.rb +269 -121
  86. data/lib/axlsx.rb +41 -14
  87. data/lib/schema/dc.xsd +5 -5
  88. data/lib/schema/dcmitype.xsd +5 -3
  89. data/lib/schema/dcterms.xsd +15 -15
  90. data/lib/schema/opc-coreProperties.xsd +6 -2
  91. data/lib/schema/xml.xsd +7 -8
  92. data/test/#benchmark.txt# +7 -0
  93. data/test/#tc_helper.rb# +3 -0
  94. data/test/benchmark.rb +81 -0
  95. data/test/benchmark.rb~ +0 -0
  96. data/test/benchmark.txt +6 -0
  97. data/test/benchmark.txt~ +6 -0
  98. data/test/content_type/tc_content_type.rb +31 -31
  99. data/test/content_type/tc_default.rb +9 -22
  100. data/test/content_type/tc_override.rb +8 -21
  101. data/test/doc_props/tc_app.rb +2 -8
  102. data/test/doc_props/tc_core.rb +6 -7
  103. data/test/drawing/tc_axis.rb +7 -3
  104. data/test/drawing/tc_bar_3D_chart.rb +6 -7
  105. data/test/drawing/tc_bar_series.rb +4 -5
  106. data/test/drawing/tc_cat_axis.rb +2 -3
  107. data/test/drawing/tc_cat_axis_data.rb +2 -3
  108. data/test/drawing/tc_chart.rb +13 -14
  109. data/test/drawing/tc_drawing.rb +7 -8
  110. data/test/drawing/tc_graphic_frame.rb +3 -4
  111. data/test/drawing/tc_hyperlink.rb +2 -3
  112. data/test/drawing/tc_line_3d_chart.rb +5 -6
  113. data/test/drawing/tc_line_series.rb +3 -4
  114. data/test/drawing/tc_marker.rb +3 -4
  115. data/test/drawing/tc_one_cell_anchor.rb +6 -7
  116. data/test/drawing/tc_pic.rb +8 -9
  117. data/test/drawing/tc_picture_locking.rb +2 -3
  118. data/test/drawing/tc_pie_3D_chart.rb +5 -6
  119. data/test/drawing/tc_pie_series.rb +4 -5
  120. data/test/drawing/tc_scaling.rb +3 -4
  121. data/test/drawing/tc_scatter_chart.rb +43 -0
  122. data/test/drawing/tc_scatter_series.rb +20 -0
  123. data/test/drawing/tc_ser_axis.rb +2 -3
  124. data/test/drawing/tc_series.rb +4 -5
  125. data/test/drawing/tc_series_title.rb +4 -5
  126. data/test/drawing/tc_title.rb +4 -5
  127. data/test/drawing/tc_two_cell_anchor.rb +4 -5
  128. data/test/drawing/tc_val_axis.rb +2 -3
  129. data/test/drawing/tc_val_axis_data.rb +2 -3
  130. data/test/drawing/tc_view_3D.rb +6 -7
  131. data/test/example.csv +1000 -0
  132. data/test/example.xlsx +0 -0
  133. data/test/example_streamed.xlsx +0 -0
  134. data/test/profile.rb +33 -0
  135. data/test/rels/tc_relationship.rb +5 -6
  136. data/test/rels/tc_relationships.rb +4 -5
  137. data/test/stylesheet/tc_border.rb +3 -4
  138. data/test/stylesheet/tc_border_pr.rb +3 -4
  139. data/test/stylesheet/tc_cell_alignment.rb +10 -6
  140. data/test/stylesheet/tc_cell_protection.rb +2 -3
  141. data/test/stylesheet/tc_cell_style.rb +2 -3
  142. data/test/stylesheet/tc_color.rb +2 -3
  143. data/test/stylesheet/tc_fill.rb +1 -2
  144. data/test/stylesheet/tc_font.rb +12 -5
  145. data/test/stylesheet/tc_gradient_fill.rb +1 -2
  146. data/test/stylesheet/tc_gradient_stop.rb +1 -2
  147. data/test/stylesheet/tc_num_fmt.rb +1 -2
  148. data/test/stylesheet/tc_pattern_fill.rb +3 -4
  149. data/test/stylesheet/tc_styles.rb +16 -6
  150. data/test/stylesheet/tc_table_style.rb +2 -3
  151. data/test/stylesheet/tc_table_style_element.rb +2 -3
  152. data/test/stylesheet/tc_table_styles.rb +3 -4
  153. data/test/stylesheet/tc_xf.rb +16 -17
  154. data/test/tc_axlsx.rb +39 -0
  155. data/test/tc_axlsx.rb~ +0 -0
  156. data/test/tc_helper.rb +3 -0
  157. data/test/tc_helper.rb~ +3 -0
  158. data/test/tc_package.rb +43 -9
  159. data/test/util/tc_simple_typed_list.rb +8 -9
  160. data/test/util/tc_validators.rb +7 -8
  161. data/test/workbook/tc_shared_strings_table.rb +38 -0
  162. data/test/workbook/tc_workbook.rb +32 -6
  163. data/test/workbook/worksheet/table/tc_table.rb +71 -0
  164. data/test/workbook/worksheet/table/tc_table.rb~ +72 -0
  165. data/test/workbook/worksheet/tc_cell.rb +70 -13
  166. data/test/workbook/worksheet/tc_col.rb +59 -0
  167. data/test/workbook/worksheet/tc_col.rb~ +10 -0
  168. data/test/workbook/worksheet/tc_date_time_converter.rb +126 -0
  169. data/test/workbook/worksheet/tc_page_margins.rb +97 -0
  170. data/test/workbook/worksheet/tc_row.rb +54 -4
  171. data/test/workbook/worksheet/tc_worksheet.rb +191 -31
  172. metadata +162 -109
  173. 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]).pluralize
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
 
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  module Axlsx
2
3
 
3
4
  # The Storage class represents a storage object or stream in a compound file.
@@ -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.16" unless Axlsx.const_defined? :VERSION
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
- # A collection of worksheets associated with this workbook.
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
- # lets come back to this later when we are ready for parsing.
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, we try to determine if the platform is bsd/darwin and set date1904 to true automatically.
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
- self.date1904= options[:date1904].nil? ? is_bsd? : options[:date1904]
95
- yield self if block_given?
96
- end
120
+ @tables = SimpleTypedList.new Table
121
+ @use_autowidth = true
97
122
 
98
- # Uses RUBY_PLATFORM constant to determine if the OS is freebsd or darwin
99
- # based on this value we attempt to set date1904.
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
- # Serializes the workbook document
192
+ # Serialize the workbook
193
+ # @param [String] str
154
194
  # @return [String]
155
- def to_xml()
195
+ def to_xml_string(str='')
156
196
  add_worksheet unless worksheets.size > 0
157
- builder = Nokogiri::XML::Builder.new(:encoding => ENCODING) do |xml|
158
- xml.workbook(:xmlns => XML_NS, :'xmlns:r' => XML_NS_R) {
159
- xml.workbookPr(:date1904=>@@date1904)
160
- #<x:workbookProtection workbookPassword="xsd:hexBinary data" lockStructure="1" lockWindows="1" />
161
- xml.sheets {
162
- @worksheets.each_with_index do |sheet, index|
163
- xml.sheet(:name=>sheet.name, :sheetId=>index+1, :"r:id"=>sheet.rId)
164
- end
165
- }
166
- }
167
- end
168
- builder.to_xml(:save_with => 0)
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