axlsx 1.0.18 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. data/CHANGELOG.md +11 -3
  2. data/README.md +93 -18
  3. data/examples/example.csv +1000 -0
  4. data/examples/example.rb +97 -5
  5. data/examples/example.xlsx +0 -0
  6. data/examples/example_streamed.xlsx +0 -0
  7. data/examples/no-use_autowidth.xlsx +0 -0
  8. data/examples/shared_strings_example.xlsx +0 -0
  9. data/lib/axlsx.rb +30 -9
  10. data/lib/axlsx/content_type/content_type.rb +9 -9
  11. data/lib/axlsx/content_type/default.rb +9 -6
  12. data/lib/axlsx/content_type/override.rb +12 -8
  13. data/lib/axlsx/doc_props/app.rb +37 -40
  14. data/lib/axlsx/doc_props/core.rb +12 -17
  15. data/lib/axlsx/drawing/axis.rb +38 -19
  16. data/lib/axlsx/drawing/bar_3D_chart.rb +33 -32
  17. data/lib/axlsx/drawing/bar_series.rb +13 -14
  18. data/lib/axlsx/drawing/cat_axis.rb +15 -14
  19. data/lib/axlsx/drawing/cat_axis_data.rb +16 -18
  20. data/lib/axlsx/drawing/chart.rb +37 -38
  21. data/lib/axlsx/drawing/drawing.rb +15 -12
  22. data/lib/axlsx/drawing/graphic_frame.rb +21 -21
  23. data/lib/axlsx/drawing/hyperlink.rb +12 -11
  24. data/lib/axlsx/drawing/line_3D_chart.rb +30 -28
  25. data/lib/axlsx/drawing/line_series.rb +11 -11
  26. data/lib/axlsx/drawing/marker.rb +10 -8
  27. data/lib/axlsx/drawing/named_axis_data.rb +36 -0
  28. data/lib/axlsx/drawing/one_cell_anchor.rb +17 -16
  29. data/lib/axlsx/drawing/pic.rb +24 -37
  30. data/lib/axlsx/drawing/picture_locking.rb +21 -18
  31. data/lib/axlsx/drawing/pie_3D_chart.rb +10 -8
  32. data/lib/axlsx/drawing/pie_series.rb +15 -12
  33. data/lib/axlsx/drawing/scaling.rb +10 -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 +10 -10
  37. data/lib/axlsx/drawing/series.rb +15 -15
  38. data/lib/axlsx/drawing/series_title.rb +14 -14
  39. data/lib/axlsx/drawing/title.rb +26 -26
  40. data/lib/axlsx/drawing/two_cell_anchor.rb +18 -20
  41. data/lib/axlsx/drawing/val_axis.rb +8 -7
  42. data/lib/axlsx/drawing/val_axis_data.rb +17 -17
  43. data/lib/axlsx/drawing/view_3D.rb +22 -20
  44. data/lib/axlsx/package.rb +32 -15
  45. data/lib/axlsx/rels/relationship.rb +9 -6
  46. data/lib/axlsx/rels/relationships.rb +7 -1
  47. data/lib/axlsx/stylesheet/#num_fmt.rb# +69 -0
  48. data/lib/axlsx/stylesheet/border.rb +27 -23
  49. data/lib/axlsx/stylesheet/border_pr.rb +16 -15
  50. data/lib/axlsx/stylesheet/cell_alignment.rb +23 -21
  51. data/lib/axlsx/stylesheet/cell_protection.rb +10 -7
  52. data/lib/axlsx/stylesheet/cell_style.rb +8 -5
  53. data/lib/axlsx/stylesheet/color.rb +20 -14
  54. data/lib/axlsx/stylesheet/fill.rb +7 -5
  55. data/lib/axlsx/stylesheet/font.rb +14 -14
  56. data/lib/axlsx/stylesheet/gradient_fill.rb +19 -16
  57. data/lib/axlsx/stylesheet/gradient_stop.rb +9 -5
  58. data/lib/axlsx/stylesheet/num_fmt.rb +12 -6
  59. data/lib/axlsx/stylesheet/pattern_fill.rb +25 -10
  60. data/lib/axlsx/stylesheet/styles.rb +41 -32
  61. data/lib/axlsx/stylesheet/table_style.rb +9 -4
  62. data/lib/axlsx/stylesheet/table_style_element.rb +10 -7
  63. data/lib/axlsx/stylesheet/table_styles.rb +11 -8
  64. data/lib/axlsx/stylesheet/xf.rb +29 -25
  65. data/lib/axlsx/util/constants.rb +4 -0
  66. data/lib/axlsx/util/simple_typed_list.rb +18 -9
  67. data/lib/axlsx/util/validators.rb +13 -6
  68. data/lib/axlsx/version.rb +1 -1
  69. data/lib/axlsx/workbook/shared_strings_table.rb +19 -21
  70. data/lib/axlsx/workbook/workbook.rb +43 -19
  71. data/lib/axlsx/workbook/worksheet/cell.rb +93 -91
  72. data/lib/axlsx/workbook/worksheet/col.rb +114 -0
  73. data/lib/axlsx/workbook/worksheet/col.rb~ +0 -0
  74. data/lib/axlsx/workbook/worksheet/page_margins.rb +16 -13
  75. data/lib/axlsx/workbook/worksheet/row.rb +13 -13
  76. data/lib/axlsx/workbook/worksheet/table.rb +96 -0
  77. data/lib/axlsx/workbook/worksheet/table.rb~ +97 -0
  78. data/lib/axlsx/workbook/worksheet/worksheet.rb +152 -118
  79. data/lib/schema/dc.xsd +5 -5
  80. data/lib/schema/dcmitype.xsd +5 -3
  81. data/lib/schema/dcterms.xsd +15 -15
  82. data/lib/schema/opc-coreProperties.xsd +6 -2
  83. data/lib/schema/xml.xsd +7 -8
  84. data/test/#benchmark.txt# +7 -0
  85. data/test/#tc_helper.rb# +3 -0
  86. data/test/benchmark.rb +81 -0
  87. data/test/benchmark.rb~ +0 -0
  88. data/test/benchmark.txt +6 -0
  89. data/test/benchmark.txt~ +6 -0
  90. data/test/content_type/tc_content_type.rb +30 -32
  91. data/test/content_type/tc_default.rb +8 -23
  92. data/test/content_type/tc_override.rb +7 -21
  93. data/test/doc_props/tc_app.rb +2 -8
  94. data/test/doc_props/tc_core.rb +6 -7
  95. data/test/drawing/tc_axis.rb +7 -3
  96. data/test/drawing/tc_bar_3D_chart.rb +6 -7
  97. data/test/drawing/tc_bar_series.rb +4 -5
  98. data/test/drawing/tc_cat_axis.rb +2 -3
  99. data/test/drawing/tc_cat_axis_data.rb +2 -3
  100. data/test/drawing/tc_chart.rb +11 -12
  101. data/test/drawing/tc_drawing.rb +7 -8
  102. data/test/drawing/tc_graphic_frame.rb +3 -4
  103. data/test/drawing/tc_hyperlink.rb +2 -3
  104. data/test/drawing/tc_line_3d_chart.rb +5 -6
  105. data/test/drawing/tc_line_series.rb +3 -4
  106. data/test/drawing/tc_marker.rb +3 -4
  107. data/test/drawing/tc_one_cell_anchor.rb +6 -7
  108. data/test/drawing/tc_pic.rb +8 -9
  109. data/test/drawing/tc_picture_locking.rb +2 -3
  110. data/test/drawing/tc_pie_3D_chart.rb +5 -6
  111. data/test/drawing/tc_pie_series.rb +4 -5
  112. data/test/drawing/tc_scaling.rb +3 -4
  113. data/test/drawing/tc_scatter_chart.rb +43 -0
  114. data/test/drawing/tc_scatter_series.rb +20 -0
  115. data/test/drawing/tc_ser_axis.rb +2 -3
  116. data/test/drawing/tc_series.rb +4 -5
  117. data/test/drawing/tc_series_title.rb +4 -5
  118. data/test/drawing/tc_title.rb +4 -5
  119. data/test/drawing/tc_two_cell_anchor.rb +4 -5
  120. data/test/drawing/tc_val_axis.rb +2 -3
  121. data/test/drawing/tc_val_axis_data.rb +2 -3
  122. data/test/drawing/tc_view_3D.rb +6 -7
  123. data/test/example.csv +1000 -0
  124. data/test/example.xlsx +0 -0
  125. data/test/example_streamed.xlsx +0 -0
  126. data/test/profile.rb +33 -0
  127. data/test/rels/tc_relationship.rb +5 -6
  128. data/test/rels/tc_relationships.rb +4 -5
  129. data/test/stylesheet/tc_border.rb +3 -4
  130. data/test/stylesheet/tc_border_pr.rb +3 -4
  131. data/test/stylesheet/tc_cell_alignment.rb +4 -5
  132. data/test/stylesheet/tc_cell_protection.rb +2 -3
  133. data/test/stylesheet/tc_cell_style.rb +2 -3
  134. data/test/stylesheet/tc_color.rb +2 -3
  135. data/test/stylesheet/tc_fill.rb +1 -2
  136. data/test/stylesheet/tc_font.rb +5 -6
  137. data/test/stylesheet/tc_gradient_fill.rb +1 -2
  138. data/test/stylesheet/tc_gradient_stop.rb +1 -2
  139. data/test/stylesheet/tc_num_fmt.rb +1 -2
  140. data/test/stylesheet/tc_pattern_fill.rb +3 -4
  141. data/test/stylesheet/tc_styles.rb +15 -9
  142. data/test/stylesheet/tc_table_style.rb +2 -3
  143. data/test/stylesheet/tc_table_style_element.rb +2 -3
  144. data/test/stylesheet/tc_table_styles.rb +3 -4
  145. data/test/stylesheet/tc_xf.rb +16 -17
  146. data/test/tc_axlsx.rb +39 -0
  147. data/test/tc_axlsx.rb~ +0 -0
  148. data/test/tc_helper.rb +3 -0
  149. data/test/tc_helper.rb~ +3 -0
  150. data/test/tc_package.rb +13 -10
  151. data/test/util/tc_simple_typed_list.rb +8 -9
  152. data/test/util/tc_validators.rb +7 -8
  153. data/test/workbook/tc_shared_strings_table.rb +5 -6
  154. data/test/workbook/tc_workbook.rb +24 -6
  155. data/test/workbook/worksheet/table/tc_table.rb +71 -0
  156. data/test/workbook/worksheet/table/tc_table.rb~ +72 -0
  157. data/test/workbook/worksheet/tc_cell.rb +24 -10
  158. data/test/workbook/worksheet/tc_col.rb +59 -0
  159. data/test/workbook/worksheet/tc_col.rb~ +10 -0
  160. data/test/workbook/worksheet/tc_date_time_converter.rb +1 -2
  161. data/test/workbook/worksheet/tc_page_margins.rb +6 -9
  162. data/test/workbook/worksheet/tc_row.rb +26 -12
  163. data/test/workbook/worksheet/tc_worksheet.rb +134 -68
  164. metadata +150 -90
  165. data/test/drawing/tc_hyperlink.rb~ +0 -71
  166. data/test/workbook/tc_shared_strings_table.rb~ +0 -8
  167. data/test/workbook/worksheet/tc_date_time_converter.rb~ +0 -69
@@ -172,6 +172,9 @@ module Axlsx
172
172
  # drawing rels part
173
173
  DRAWING_RELS_PN = "drawings/_rels/drawing%d.xml.rels"
174
174
 
175
+ # drawing part
176
+ TABLE_PN = "tables/table%d.xml"
177
+
175
178
  # chart part
176
179
  CHART_PN = "charts/chart%d.xml"
177
180
 
@@ -228,4 +231,5 @@ module Axlsx
228
231
 
229
232
  # error message for duplicate sheet names
230
233
  ERR_DUPLICATE_SHEET_NAME = "There is already a worksheet in this workbook named '%s'. Please use a unique name"
234
+
231
235
  end
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
  module Axlsx
3
- # 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.
4
4
  # @private
5
5
  class SimpleTypedList
6
6
  # The class constants of allowed types
@@ -12,14 +12,14 @@ module Axlsx
12
12
  attr_reader :locked_at
13
13
 
14
14
  # The tag name to use when serializing this object
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
+ # 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.
16
16
  # @return [String]
17
17
  attr_reader :serialize_as
18
18
 
19
19
  # Creats a new typed list
20
20
  # @param [Array, Class] type An array of Class objects or a single Class object
21
21
  # @param [String] serialize The tag name to use in serialization
22
- # @raise [ArgumentError] if all members of type are not Class objects
22
+ # @raise [ArgumentError] if all members of type are not Class objects
23
23
  def initialize type, serialize_as=nil
24
24
  if type.is_a? Array
25
25
  type.each { |item| raise ArgumentError, "All members of type must be Class objects" unless item.is_a? Class }
@@ -39,7 +39,7 @@ module Axlsx
39
39
  @locked_at = @list.size
40
40
  self
41
41
  end
42
-
42
+
43
43
  def to_ary
44
44
  @list
45
45
  end
@@ -58,7 +58,7 @@ module Axlsx
58
58
  def <<(v)
59
59
  DataTypeValidator.validate "SimpleTypedList.<<", @allowed_types, v
60
60
  @list << v
61
- @list.size - 1
61
+ @list.size - 1
62
62
  end
63
63
 
64
64
  # alternate of << method
@@ -104,14 +104,14 @@ module Axlsx
104
104
  return false unless @locked_at.is_a? Fixnum
105
105
  index < @locked_at
106
106
  end
107
-
107
+
108
108
  # override the equality method so that this object can be compared to a simple array.
109
109
  # if this object's list is equal to the specifiec array, we return true.
110
110
  def ==(v)
111
111
  v == @list
112
112
  end
113
113
  # method_mission override to pass allowed methods to the list.
114
- # @note
114
+ # @note
115
115
  # the following methods are not allowed
116
116
  # :replace
117
117
  # :insert
@@ -140,15 +140,23 @@ module Axlsx
140
140
  DELEGATES = Array.instance_methods - self.instance_methods - DESTRUCTIVE
141
141
 
142
142
  DELEGATES.each do |method|
143
- class_eval %{
143
+ class_eval %{
144
144
  def #{method}(*args, &block)
145
145
  @list.send(:#{method}, *args, &block)
146
146
  end
147
147
  }
148
148
  end
149
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
+
150
158
  # Serializes the list
151
- # 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.
152
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.
153
161
  # @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
154
162
  # @return [String]
@@ -159,6 +167,7 @@ module Axlsx
159
167
  @list.each { |item| item.to_xml(xml) }
160
168
  }
161
169
  end
170
+
162
171
  end
163
172
 
164
173
 
@@ -5,11 +5,11 @@ module Axlsx
5
5
  # Perform validation
6
6
  # @param [String] name The name of what is being validatied. This is included in the error message
7
7
  # @param [Array] choices The list of choices to validate against
8
- # @param [Any] v The value to be validated
8
+ # @param [Any] v The value to be validated
9
9
  # @raise [ArgumentError] Raised if the value provided is not in the list of choices.
10
10
  # @return [Boolean] true if validation succeeds.
11
11
  def self.validate(name, choices, v)
12
- 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)
13
13
  true
14
14
  end
15
15
  end
@@ -55,7 +55,7 @@ module Axlsx
55
55
 
56
56
  # Requires that the value is a Fixnum Integer or Float and is greater or equal to 0
57
57
  # @param [Any] v The value validated
58
- # @raise [ArgumentError] raised if the value is not a Fixnum or Integer value greater or equal to 0
58
+ # @raise [ArgumentError] raised if the value is not a Fixnun, Integer, Float value greater or equal to 0
59
59
  # @return [Boolean] true if the data is valid
60
60
  def self.validate_unsigned_numeric(v)
61
61
  DataTypeValidator.validate("Invalid column width", [Fixnum, Integer, Float], v, lambda { |arg| arg.respond_to?(:>=) && arg >= 0 })
@@ -68,7 +68,7 @@ module Axlsx
68
68
  end
69
69
 
70
70
  # Requires that the value is a form that can be evaluated as a boolean in an xml document.
71
- # 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
72
72
  # it must be one of 0, 1, "true", "false", :true, :false, true, false, "0", or "1"
73
73
  # @param [Any] v The value validated
74
74
  def self.validate_boolean(v)
@@ -79,13 +79,13 @@ module Axlsx
79
79
  # @param [Any] v The value validated
80
80
  def self.validate_string(v)
81
81
  DataTypeValidator.validate :string, String, v
82
- end
82
+ end
83
83
 
84
84
  # Requires that the value is a Float
85
85
  # @param [Any] v The value validated
86
86
  def self.validate_float(v)
87
87
  DataTypeValidator.validate :float, Float, v
88
- end
88
+ end
89
89
 
90
90
  # Requires that the value is valid pattern type.
91
91
  # valid pattern types must be one of :none, :solid, :mediumGray, :darkGray, :lightGray, :darkHorizontal, :darkVertical, :darkDown,
@@ -103,6 +103,13 @@ module Axlsx
103
103
  RestrictionValidator.validate :gradient_type, [:linear, :path], v
104
104
  end
105
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
106
113
  # Requires that the value is a valid horizontal_alignment
107
114
  # :general, :left, :center, :right, :fill, :justify, :centerContinuous, :distributed are allowed
108
115
  # @param [Any] v The value validated
@@ -5,6 +5,6 @@ module Axlsx
5
5
  # When using bunle exec rake and referencing the gem on github or locally
6
6
  # it will use the gemspec, which preloads this constant for the gem's version.
7
7
  # We check to make sure that it has not already been loaded
8
- VERSION="1.0.18" unless Axlsx.const_defined? :VERSION
8
+ VERSION="1.1.0" unless defined? Axlsx::VERSION
9
9
 
10
10
  end
@@ -3,7 +3,7 @@ module Axlsx
3
3
 
4
4
  # The Shared String Table class is responsible for managing and serializing common strings in a workbook.
5
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.
6
+ # and Google Docs require that the shared string table is populated in order to interoperate properly.
7
7
  # As a developer, you should never need to directly work against this class. Simply set 'use_shared_strings'
8
8
  # on the package or workbook to generate a package that uses the shared strings table instead of inline strings.
9
9
  # @note Serialization performance is affected by using this serialization method so if you do not need interoperability
@@ -15,57 +15,55 @@ module Axlsx
15
15
  # @return [Integer]
16
16
  attr_reader :count
17
17
 
18
- # The total number of unique strings in the workbook.
18
+ # The total number of unique strings in the workbook.
19
19
  # @return [Integer]
20
20
  def unique_count
21
21
  @unique_cells.size
22
22
  end
23
23
 
24
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.
25
+ # each of these unique cells is parsed into the shared string table.
26
26
  # @see Cell#sharable
27
27
  attr_reader :unique_cells
28
28
 
29
29
  # Creates a new Shared Strings Table agains an array of cells
30
30
  # @param [Array] cells This is an array of all of the cells in the workbook
31
31
  def initialize(cells)
32
- cells = cells.flatten.reject { |c| c.type != :string || c.value.start_with?('=') }
32
+ cells = cells.flatten.reject { |c| c.type != :string || c.value.nil? || c.value.start_with?('=') }
33
33
  @count = cells.size
34
34
  @unique_cells = []
35
+ @shared_xml_string = ""
35
36
  resolve(cells)
36
37
  end
37
38
 
38
-
39
- # Generate the xml document for the Shared Strings Table
39
+ # Serializes the object
40
+ # @param [String] str
40
41
  # @return [String]
41
- def to_xml
42
- builder = Nokogiri::XML::Builder.new(:encoding => ENCODING) do |xml|
43
- xml.sst(:xmlns => Axlsx::XML_NS, :count => count, :uniqueCount => unique_count) {
44
- @unique_cells.each do |cell|
45
- xml.si { cell.run_xml(xml) }
46
- end
47
- }
48
- end
49
- builder.to_xml(:save_with => 0)
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>'
50
44
  end
51
45
 
52
46
  private
53
-
54
- # Interate over all of the cells in the array.
55
- # if our unique cells array does not contain a sharable cell,
47
+
48
+ # Interate over all of the cells in the array.
49
+ # if our unique cells array does not contain a sharable cell,
56
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
57
51
  # if a sharable cell already exists in our unique_cells array, set the ssti attribute of the cell and move on.
58
52
  # @return [Array] unique cells
59
53
  def resolve(cells)
60
54
  cells.each do |cell|
61
- index = @unique_cells.index { |item| item.shareable(cell) }
55
+ cell_hash = cell.shareable_hash
56
+ index = @unique_cells.index do |item|
57
+ item == cell_hash
58
+ end
62
59
  if index == nil
63
60
  cell.send :ssti=, @unique_cells.size
64
- @unique_cells << cell
61
+ @shared_xml_string << '<si>' << cell.run_xml_string << '</si>'
62
+ @unique_cells << cell_hash
65
63
  else
66
64
  cell.send :ssti=, index
67
65
  end
68
66
  end
69
- end
67
+ end
70
68
  end
71
69
  end
@@ -5,8 +5,10 @@ require 'axlsx/workbook/worksheet/date_time_converter.rb'
5
5
  require 'axlsx/workbook/worksheet/cell.rb'
6
6
  require 'axlsx/workbook/worksheet/page_margins.rb'
7
7
  require 'axlsx/workbook/worksheet/row.rb'
8
+ require 'axlsx/workbook/worksheet/col.rb'
8
9
  require 'axlsx/workbook/worksheet/worksheet.rb'
9
10
  require 'axlsx/workbook/shared_strings_table.rb'
11
+ require 'axlsx/workbook/worksheet/table.rb'
10
12
 
11
13
  # The Workbook class is an xlsx workbook that manages worksheets, charts, drawings and styles.
12
14
  # The following parts of the Office Open XML spreadsheet specification are not implimented in this version.
@@ -46,7 +48,7 @@ require 'axlsx/workbook/shared_strings_table.rb'
46
48
  end
47
49
 
48
50
 
49
- # A collection of worksheets associated with this workbook.
51
+ # A collection of worksheets associated with this workbook.
50
52
  # @note The recommended way to manage worksheets is add_worksheet
51
53
  # @see Workbook#add_worksheet
52
54
  # @see Worksheet
@@ -74,6 +76,14 @@ require 'axlsx/workbook/shared_strings_table.rb'
74
76
  # @return [SimpleTypedList]
75
77
  attr_reader :drawings
76
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
+
77
87
  # The styles associated with this workbook
78
88
  # @note The recommended way to manage styles is Styles#add_style
79
89
  # @see Style#add_style
@@ -88,6 +98,7 @@ require 'axlsx/workbook/shared_strings_table.rb'
88
98
  # Indicates if the epoc date for serialization should be 1904. If false, 1900 is used.
89
99
  @@date1904 = false
90
100
 
101
+
91
102
  # lets come back to this later when we are ready for parsing.
92
103
  #def self.parse entry
93
104
  # io = entry.get_input_stream
@@ -106,6 +117,9 @@ require 'axlsx/workbook/shared_strings_table.rb'
106
117
  @drawings = SimpleTypedList.new Drawing
107
118
  @charts = SimpleTypedList.new Chart
108
119
  @images = SimpleTypedList.new Pic
120
+ @tables = SimpleTypedList.new Table
121
+ @use_autowidth = true
122
+
109
123
  self.date1904= !options[:date1904].nil? && options[:date1904]
110
124
  yield self if block_given?
111
125
  end
@@ -125,6 +139,14 @@ require 'axlsx/workbook/shared_strings_table.rb'
125
139
  # @return [Boolean]
126
140
  def self.date1904() @@date1904; end
127
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
+
128
150
  # Adds a worksheet to this workbook
129
151
  # @return [Worksheet]
130
152
  # @option options [String] name The name of the worksheet.
@@ -167,27 +189,29 @@ require 'axlsx/workbook/shared_strings_table.rb'
167
189
  worksheet[cell_def.gsub(/.+!/,"")]
168
190
  end
169
191
 
170
- # Serializes the workbook document
192
+ # Serialize the workbook
193
+ # @param [String] str
171
194
  # @return [String]
172
- def to_xml()
195
+ def to_xml_string(str='')
173
196
  add_worksheet unless worksheets.size > 0
174
- builder = Nokogiri::XML::Builder.new(:encoding => ENCODING) do |xml|
175
- xml.workbook(:xmlns => XML_NS, :'xmlns:r' => XML_NS_R) {
176
- xml.workbookPr(:date1904=>@@date1904)
177
- #<x:workbookProtection workbookPassword="xsd:hexBinary data" lockStructure="1" lockWindows="1" />
178
- # Required to support rubyXL parsing as it requires sheetView, which requires this.
179
- # and removed because it seems to cause some odd [Grouped] behaviour in excel.
180
- # xml.bookViews {
181
- # xml.workbookView :activeTab=>0
182
- # }
183
- xml.sheets {
184
- @worksheets.each_with_index do |sheet, index|
185
- xml.sheet(:name=>sheet.name, :sheetId=>index+1, :"r:id"=>sheet.rId)
186
- end
187
- }
188
- }
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 << '"/>'
189
203
  end
190
- builder.to_xml(:save_with => 0)
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>'
191
214
  end
215
+
192
216
  end
193
217
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: UTF-8
2
+ require 'cgi'
2
3
  module Axlsx
3
4
  # A cell in a worksheet.
4
5
  # Cell stores inforamation requried to serialize a single worksheet cell to xml. You must provde the Row that the cell belongs to and the cells value. The data type will automatically be determed if you do not specify the :type option. The default style will be applied if you do not supply the :style option. Changing the cell's type will recast the value to the type specified. Altering the cell's value via the property accessor will also automatically cast the provided value to the cell's type.
@@ -25,12 +26,16 @@ module Axlsx
25
26
 
26
27
 
27
28
  # An array of available inline styes.
29
+ # TODO change this to a hash where each key defines attr name and validator (and any info the validator requires)
30
+ # then move it out to a module so we can re-use in in other classes.
31
+ # needs to define bla=(v) and bla methods on the class that hook into a
32
+ # set_attr method that kicks the suplied validator and updates the instance_variable
33
+ # for the key
28
34
  INLINE_STYLES = ['value', 'type', 'font_name', 'charset',
29
35
  'family', 'b', 'i', 'strike','outline',
30
36
  'shadow', 'condense', 'extend', 'u',
31
37
  'vertAlign', 'sz', 'color', 'scheme']
32
38
 
33
-
34
39
  # The index of the cellXfs item to be applied to this cell.
35
40
  # @return [Integer]
36
41
  # @see Axlsx::Styles
@@ -69,71 +74,79 @@ module Axlsx
69
74
  @value = cast_value(v)
70
75
  end
71
76
 
77
+
78
+ # Indicates that the cell has one or more of the custom cell styles applied.
79
+ # @return [Boolean]
80
+ def is_text_run?
81
+ @is_text_run ||= false
82
+ end
83
+
84
+
72
85
  # The inline font_name property for the cell
73
86
  # @return [String]
74
87
  attr_reader :font_name
75
88
  # @see font_name
76
- def font_name=(v) Axlsx::validate_string(v); @font_name = v; end
89
+ def font_name=(v) set_run_style :validate_string, :font_name, v; end
77
90
 
78
91
  # The inline charset property for the cell
79
92
  # @return [String]
80
93
  attr_reader :charset
81
94
  # @see charset
82
- def charset=(v) Axlsx::validate_unsigned_int(v); @charset = v; end
95
+ def charset=(v) set_run_style :validate_unsigned_int, :charset, v; end
83
96
 
84
97
  # The inline family property for the cell
85
98
  # @return [String]
86
99
  attr_reader :family
87
100
  # @see family
88
- def family=(v) Axlsx::validate_string(v); @family = v; end
101
+ def family=(v) set_run_style :validate_string, :family, v; end
89
102
 
90
103
  # The inline bold property for the cell
91
104
  # @return [Boolean]
92
105
  attr_reader :b
93
106
  # @see b
94
- def b=(v) Axlsx::validate_boolean(v); @b = v; end
107
+ def b=(v) set_run_style :validate_boolean, :b, v; end
95
108
 
96
109
  # The inline italic property for the cell
97
110
  # @return [Boolean]
98
111
  attr_reader :i
99
112
  # @see i
100
- def i=(v) Axlsx::validate_boolean(v); @i = v; end
113
+ def i=(v) set_run_style :validate_boolean, :i, v; end
101
114
 
102
115
  # The inline strike property for the cell
103
116
  # @return [Boolean]
104
117
  attr_reader :strike
105
118
  # @see strike
106
- def strike=(v) Axlsx::validate_boolean(v); @strike = v; end
119
+ def strike=(v) set_run_style :validate_boolean, :strike, v; end
107
120
 
108
121
  # The inline outline property for the cell
109
122
  # @return [Boolean]
110
123
  attr_reader :outline
111
124
  # @see outline
112
- def outline=(v) Axlsx::validate_boolean(v); @outline = v; end
125
+ def outline=(v) set_run_style :validate_boolean, :outline, v; end
113
126
 
114
127
  # The inline shadow property for the cell
115
128
  # @return [Boolean]
116
129
  attr_reader :shadow
117
130
  # @see shadow
118
- def shadow=(v) Axlsx::validate_boolean(v); @shadow = v; end
131
+ def shadow=(v) set_run_style :validate_boolean, :shadow, v; end
119
132
 
120
133
  # The inline condense property for the cell
121
134
  # @return [Boolean]
122
135
  attr_reader :condense
123
136
  # @see condense
124
- def condense=(v) Axlsx::validate_boolean(v); @condense = v; end
137
+ def condense=(v) set_run_style :validate_boolean, :condense, v; end
125
138
 
126
139
  # The inline extend property for the cell
127
140
  # @return [Boolean]
128
141
  attr_reader :extend
129
142
  # @see extend
130
- def extend=(v) Axlsx::validate_boolean(v); @extend = v; end
143
+ def extend=(v) set_run_style :validate_boolean, :extend, v; end
131
144
 
132
145
  # The inline underline property for the cell
133
146
  # @return [Boolean]
134
147
  attr_reader :u
135
148
  # @see u
136
- def u=(v) Axlsx::validate_boolean(v); @u = v; end
149
+ def u=(v) set_run_style :validate_boolean, :u, v; end
137
150
 
138
151
  # The inline color property for the cell
139
152
  # @return [Color]
@@ -141,27 +154,34 @@ module Axlsx
141
154
  # @param [String] The 8 character representation for an rgb color #FFFFFFFF"
142
155
  def color=(v)
143
156
  @color = v.is_a?(Color) ? v : Color.new(:rgb=>v)
157
+ @has_run_style = true
144
158
  end
145
159
 
146
160
  # The inline sz property for the cell
147
161
  # @return [Boolean]
148
162
  attr_reader :sz
149
163
  # @see sz
150
- def sz=(v) Axlsx::validate_unsigned_int(v); @sz = v; end
164
+ def sz=(v) set_run_style :validate_unsigned_int, :sz, v; end
151
165
 
152
166
  # The inline vertical alignment property for the cell
153
167
  # this must be one of [:baseline, :subscript, :superscript]
154
168
  # @return [Symbol]
155
169
  attr_reader :vertAlign
156
170
  # @see vertAlign
157
- def vertAlign=(v) RestrictionValidator.validate "Cell.vertAlign", [:baseline, :subscript, :superscript], v; @vertAlign = v; end
171
+ def vertAlign=(v)
172
+ RestrictionValidator.validate "Cell.vertAlign", [:baseline, :subscript, :superscript], v
173
+ set_run_style nil, :vertAlign, v
174
+ end
158
175
 
159
176
  # The inline scheme property for the cell
160
177
  # this must be one of [:none, major, minor]
161
178
  # @return [Symbol]
162
179
  attr_reader :scheme
163
180
  # @see scheme
164
- def scheme=(v) RestrictionValidator.validate "Cell.schema", [:none, :major, :minor], v; @scheme = v; end
181
+ def scheme=(v)
182
+ RestrictionValidator.validate "Cell.schema", [:none, :major, :minor], v
183
+ set_run_style nil, :scheme, v
184
+ end
165
185
 
166
186
  # @param [Row] row The row this cell belongs to.
167
187
  # @param [Any] value The value associated with this cell.
@@ -202,16 +222,11 @@ module Axlsx
202
222
 
203
223
  # equality comparison to test value, type and inline style attributes
204
224
  # this is how we work out if the cell needs to be added or already exists in the shared strings table
205
- def shareable(v)
206
-
207
- #using reject becase 1.8.7 select returns an array...
208
- v_hash = v.instance_values.reject { |key, val| !INLINE_STYLES.include?(key) }
209
- self_hash = self.instance_values.reject { |key, val| !INLINE_STYLES.include?(key) }
210
- # required as color is an object, and the comparison will fail even though both use the same color.
211
- v_hash['color'] = v_hash['color'].instance_values if v_hash['color']
225
+ def shareable_hash
226
+ self_hash = {}
227
+ INLINE_STYLES.each { |style| self_hash[style] = self.instance_variable_get("@" + style) }
212
228
  self_hash['color'] = self_hash['color'].instance_values if self_hash['color']
213
-
214
- v_hash == self_hash
229
+ self_hash
215
230
  end
216
231
 
217
232
  # @return [Integer] The index of the cell in the containing row.
@@ -222,15 +237,16 @@ module Axlsx
222
237
  # @return [String] The alpha(column)numeric(row) reference for this sell.
223
238
  # @example Relative Cell Reference
224
239
  # ws.rows.first.cells.first.r #=> "A1"
240
+ # @note this will be discontinued in 1.1.0 - prefer Axlsx.cell_r
225
241
  def r
226
- "#{col_ref}#{@row.index+1}"
242
+ "#{Axlsx::col_ref(index)}#{@row.index+1}"
227
243
  end
228
244
 
229
245
  # @return [String] The absolute alpha(column)numeric(row) reference for this sell.
230
246
  # @example Absolute Cell Reference
231
247
  # ws.rows.first.cells.first.r #=> "$A$1"
232
248
  def r_abs
233
- "$#{r.split('').join('$')}"
249
+ "$#{r.match(%r{([A-Z]+)([0-9]+)})[1,2].join('$')}"
234
250
  end
235
251
 
236
252
  # @return [Integer] The cellXfs item index applied to this cell.
@@ -259,77 +275,76 @@ module Axlsx
259
275
  self.row.worksheet.merge_cells "#{self.r}:#{range_end}" unless range_end.nil?
260
276
  end
261
277
 
262
- # builds an xml text run based on this cells attributes. This is extracted from to_xml so that shared strings can use it.
263
- # @param [Nokogiri::XML::Builder] xml The document builder instance this output will be added to.
264
- # @return [String] the xml for this cell's text run
265
- def run_xml(xml)
266
- if (self.instance_values.keys & INLINE_STYLES).size > 0
267
- xml.r {
268
- xml.rPr {
269
- xml.rFont(:val=>@font_name) if @font_name
270
- xml.charset(:val=>@charset) if @charset
271
- xml.family(:val=>@family) if @family
272
- xml.b(:val=>@b) if @b
273
- xml.i(:val=>@i) if @i
274
- xml.strike(:val=>@strike) if @strike
275
- xml.outline(:val=>@outline) if @outline
276
- xml.shadow(:val=>@shadow) if @shadow
277
- xml.condense(:val=>@condense) if @condense
278
- xml.extend(:val=>@extend) if @extend
279
- @color.to_xml(xml) if @color
280
- xml.sz(:val=>@sz) if @sz
281
- xml.u(:val=>@u) if @u
282
- # :baseline, :subscript, :superscript
283
- xml.vertAlign(:val=>@vertAlign) if @vertAlign
284
- # :none, major, :minor
285
- xml.scheme(:val=>@scheme) if @scheme
286
- }
287
- xml.t @value.to_s
288
- }
278
+ # builds an xml text run based on this cells attributes.
279
+ # @param [String] str The string instance this run will be concated to.
280
+ # @return [String]
281
+ def run_xml_string(str = '')
282
+ if is_text_run?
283
+ data = self.instance_values.reject{|key, value| value == nil }
284
+ keys = data.keys & INLINE_STYLES
285
+ keys.delete ['value', 'type']
286
+ str << "<r><rPr>"
287
+ keys.each do |key|
288
+ case key
289
+ when 'font_name'
290
+ str << "<rFont val='"<< @font_name << "'/>"
291
+ when 'color'
292
+ str << data[key].to_xml_string
293
+ else
294
+ "<" << key.to_s << " val='" << data[key].to_s << "'/>"
295
+ end
296
+ end
297
+ str << "</rPr>" << "<t>" << value.to_s << "</t></r>"
289
298
  else
290
- xml.t @value.to_s
299
+ str << "<t>" << value.to_s << "</t>"
291
300
  end
301
+ str
292
302
  end
293
303
 
294
304
  # Serializes the cell
295
- # @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
305
+ # @param [Integer] r_index The row index for the cell
306
+ # @param [Integer] c_index The cell index in the row.
307
+ # @param [String] str The string index the cell content will be appended to. Defaults to empty string.
296
308
  # @return [String] xml text for the cell
297
- def to_xml(xml)
298
- if @type == :string
309
+ def to_xml_string(r_index, c_index, str = '')
310
+ return str if @value.nil?
311
+ str << '<c r="' << Axlsx::cell_r(c_index, r_index) << '" s="' << @style.to_s << '" '
312
+ case @type
313
+ when :string
299
314
  #parse formula
300
315
  if @value.start_with?('=')
301
- xml.c(:r => r, :t=>:str, :s=>style) {
302
- xml.f @value.to_s.gsub('=', '')
303
- }
316
+ str << 't="str"><f>' << @value.to_s.gsub('=', '') << '</f>'
304
317
  else
305
318
  #parse shared
306
319
  if @ssti
307
- xml.c(:r => r, :s=>style, :t => :s) { xml.v ssti }
320
+ str << 't="s"><v>' << @ssti.to_s << '</v>'
308
321
  else
309
- #parse inline string
310
- xml.c(:r => r, :s=>style, :t => :inlineStr) {
311
- xml.is {
312
- run_xml(xml)
313
- }
314
- }
322
+ str << 't="inlineStr"><is>' << run_xml_string << '</is>'
315
323
  end
316
324
  end
317
- elsif @type == :date
325
+ when :date
318
326
  # TODO: See if this is subject to the same restriction as Time below
319
- v = DateTimeConverter::date_to_serial @value
320
- xml.c(:r => r, :s => style) { xml.v v }
321
- elsif @type == :time
322
- v = DateTimeConverter::time_to_serial @value
323
- xml.c(:r => r, :s => style) { xml.v v }
324
- elsif @type == :boolean
325
- xml.c(:r => r, :s => style, :t => :b) { xml.v value }
327
+ str << '><v>' << DateTimeConverter::date_to_serial(@value).to_s << '</v>'
328
+ when :time
329
+ str << '><v>' << DateTimeConverter::time_to_serial(@value).to_s << '</v>'
330
+ when :boolean
331
+ str << 't="b"><v>' << @value.to_s << '</v>'
326
332
  else
327
- xml.c(:r => r, :s => style) { xml.v value }
333
+ str << '><v>' << @value.to_s << '</v>'
328
334
  end
335
+ str << '</c>'
329
336
  end
330
337
 
331
338
  private
332
339
 
340
+ # Utility method for setting inline style attributes
341
+ def set_run_style( validator, attr, value)
342
+ return unless INLINE_STYLES.include?(attr.to_s)
343
+ Axlsx.send(validator, value) unless validator == nil
344
+ self.instance_variable_set :"@#{attr.to_s}", value
345
+ @is_text_run = true
346
+ end
347
+
333
348
  # @see ssti
334
349
  def ssti=(v)
335
350
  Axlsx::validate_unsigned_int(v)
@@ -339,20 +354,6 @@ module Axlsx
339
354
  # assigns the owning row for this cell.
340
355
  def row=(v) DataTypeValidator.validate "Cell.row", Row, v; @row=v end
341
356
 
342
- # converts the column index into alphabetical values.
343
- # @note This follows the standard spreadsheet convention of naming columns A to Z, followed by AA to AZ etc.
344
- # @return [String]
345
- def col_ref
346
- chars = []
347
- index = self.index
348
- while index >= 26 do
349
- chars << ((index % 26) + 65).chr
350
- index /= 26
351
- end
352
- chars << ((chars.empty? ? index : index-1) + 65).chr
353
- chars.reverse.join
354
- end
355
-
356
357
  # Determines the cell type based on the cell value.
357
358
  # @note This is only used when a cell is created but no :type option is specified, the following rules apply:
358
359
  # 1. If the value is an instance of Date, the type is set to :date
@@ -382,6 +383,7 @@ module Axlsx
382
383
  # 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.
383
384
  # @see Axlsx#date1904
384
385
  def cast_value(v)
386
+ return nil if v.nil?
385
387
  if @type == :date
386
388
  self.style = STYLE_DATE if self.style == 0
387
389
  v
@@ -396,7 +398,7 @@ module Axlsx
396
398
  v ? 1 : 0
397
399
  else
398
400
  @type = :string
399
- v.to_s
401
+ ::CGI.escapeHTML(v.to_s)
400
402
  end
401
403
  end
402
404
  end