axlsx 1.3.6 → 2.0.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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts_guide +19 -0
  3. data/CHANGELOG.md +8 -0
  4. data/README.md +52 -79
  5. data/Rakefile +0 -5
  6. data/examples/2010_comments.rb +17 -0
  7. data/examples/anchor_swapping.rb +28 -0
  8. data/examples/example.rb +16 -1
  9. data/examples/pivot_table.rb +2 -0
  10. data/examples/underline.rb +13 -0
  11. data/lib/axlsx.rb +8 -0
  12. data/lib/axlsx/doc_props/core.rb +6 -1
  13. data/lib/axlsx/drawing/axes.rb +7 -3
  14. data/lib/axlsx/drawing/bar_3D_chart.rb +2 -2
  15. data/lib/axlsx/drawing/chart.rb +20 -4
  16. data/lib/axlsx/drawing/drawing.rb +2 -17
  17. data/lib/axlsx/drawing/graphic_frame.rb +3 -8
  18. data/lib/axlsx/drawing/hyperlink.rb +5 -12
  19. data/lib/axlsx/drawing/marker.rb +25 -5
  20. data/lib/axlsx/drawing/one_cell_anchor.rb +9 -0
  21. data/lib/axlsx/drawing/pic.rb +17 -23
  22. data/lib/axlsx/drawing/two_cell_anchor.rb +7 -27
  23. data/lib/axlsx/package.rb +31 -11
  24. data/lib/axlsx/rels/relationship.rb +73 -8
  25. data/lib/axlsx/rels/relationships.rb +8 -1
  26. data/lib/axlsx/stylesheet/color.rb +1 -1
  27. data/lib/axlsx/stylesheet/num_fmt.rb +2 -2
  28. data/lib/axlsx/stylesheet/styles.rb +5 -3
  29. data/lib/axlsx/util/serialized_attributes.rb +11 -8
  30. data/lib/axlsx/util/simple_typed_list.rb +34 -13
  31. data/lib/axlsx/util/validators.rb +7 -0
  32. data/lib/axlsx/version.rb +1 -1
  33. data/lib/axlsx/workbook/defined_name.rb +1 -1
  34. data/lib/axlsx/workbook/shared_strings_table.rb +12 -3
  35. data/lib/axlsx/workbook/workbook.rb +31 -8
  36. data/lib/axlsx/workbook/worksheet/break.rb +37 -0
  37. data/lib/axlsx/workbook/worksheet/cell.rb +5 -5
  38. data/lib/axlsx/workbook/worksheet/cell_serializer.rb +1 -1
  39. data/lib/axlsx/workbook/worksheet/col_breaks.rb +35 -0
  40. data/lib/axlsx/workbook/worksheet/comment.rb +6 -5
  41. data/lib/axlsx/workbook/worksheet/comments.rb +3 -3
  42. data/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb +1 -1
  43. data/lib/axlsx/workbook/worksheet/date_time_converter.rb +6 -5
  44. data/lib/axlsx/workbook/worksheet/pivot_table.rb +32 -18
  45. data/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb +4 -3
  46. data/lib/axlsx/workbook/worksheet/pivot_tables.rb +1 -1
  47. data/lib/axlsx/workbook/worksheet/row.rb +1 -1
  48. data/lib/axlsx/workbook/worksheet/row_breaks.rb +33 -0
  49. data/lib/axlsx/workbook/worksheet/table.rb +3 -2
  50. data/lib/axlsx/workbook/worksheet/tables.rb +1 -1
  51. data/lib/axlsx/workbook/worksheet/worksheet.rb +61 -26
  52. data/lib/axlsx/workbook/worksheet/worksheet_comments.rb +6 -5
  53. data/lib/axlsx/workbook/worksheet/worksheet_drawing.rb +3 -9
  54. data/lib/axlsx/workbook/worksheet/worksheet_hyperlink.rb +5 -10
  55. data/lib/schema/sml.xsd +4 -0
  56. data/test/axlsx.qcachegrind +2226 -0
  57. data/test/doc_props/tc_core.rb +7 -0
  58. data/test/drawing/tc_axes.rb +8 -0
  59. data/test/drawing/tc_bar_3D_chart.rb +6 -0
  60. data/test/drawing/tc_chart.rb +13 -0
  61. data/test/drawing/tc_drawing.rb +0 -5
  62. data/test/drawing/tc_graphic_frame.rb +4 -7
  63. data/test/drawing/tc_hyperlink.rb +0 -4
  64. data/test/drawing/tc_pic.rb +14 -3
  65. data/test/drawing/tc_two_cell_anchor.rb +3 -3
  66. data/test/profile.rb +7 -3
  67. data/test/rels/tc_relationship.rb +29 -11
  68. data/test/rels/tc_relationships.rb +12 -1
  69. data/test/stylesheet/tc_color.rb +6 -0
  70. data/test/stylesheet/tc_styles.rb +2 -2
  71. data/test/tc_helper.rb +1 -0
  72. data/test/tc_package.rb +30 -0
  73. data/test/util/tc_serialized_attributes.rb +19 -0
  74. data/test/workbook/tc_shared_strings_table.rb +6 -0
  75. data/test/workbook/tc_workbook.rb +23 -1
  76. data/test/workbook/worksheet/tc_break.rb +49 -0
  77. data/test/workbook/worksheet/tc_comment.rb +17 -6
  78. data/test/workbook/worksheet/tc_conditional_formatting.rb +2 -2
  79. data/test/workbook/worksheet/tc_date_time_converter.rb +3 -11
  80. data/test/workbook/worksheet/tc_pivot_table.rb +40 -22
  81. data/test/workbook/worksheet/tc_pivot_table_cache_definition.rb +9 -1
  82. data/test/workbook/worksheet/tc_sheet_view.rb +39 -39
  83. data/test/workbook/worksheet/tc_table.rb +2 -2
  84. data/test/workbook/worksheet/tc_worksheet.rb +39 -7
  85. data/test/workbook/worksheet/tc_worksheet_hyperlink.rb +2 -11
  86. metadata +37 -10
  87. data/test/example.xlsx +0 -0
@@ -136,7 +136,7 @@ module Axlsx
136
136
  def value_serialization(serialization_type, serialization_value, str = '')
137
137
  str << 't="' << serialization_type << '"' if serialization_type
138
138
  str << '><v>' << serialization_value << '</v>'
139
- end
139
+ end
140
140
 
141
141
 
142
142
  end
@@ -0,0 +1,35 @@
1
+ module Axlsx
2
+
3
+ # A collection of Brake objects.
4
+ # Please do not use this class directly. Instead use
5
+ # Worksheet#add_break
6
+ class ColBreaks < SimpleTypedList
7
+
8
+ # Instantiates a new list restricted to Break types
9
+ def initialize
10
+ super Break
11
+ end
12
+
13
+ # A column break specific helper for adding a break.
14
+ # @param [Hash] options A list of options to pass into the Break object
15
+ # The max and man options are fixed, however any other valid option for
16
+ # Break will be passed to the created break object.
17
+ # @see Break
18
+ def add_break(options)
19
+ @list << Break.new(options.merge(:max => 1048575, :man => true))
20
+ last
21
+ end
22
+
23
+ # Serialize the collection to xml
24
+ # @param [String] str The string to append this lists xml to.
25
+ # <colBreaks count="1" manualBreakCount="1">
26
+ # <brk id="3" max="1048575" man="1"/>
27
+ # </colBreaks>
28
+ def to_xml_string(str='')
29
+ return if empty?
30
+ str << '<colBreaks count="' << @list.size.to_s << '" manualBreakCount="' << @list.size.to_s << '">'
31
+ each { |brk| brk.to_xml_string(str) }
32
+ str << '</colBreaks>'
33
+ end
34
+ end
35
+ end
@@ -64,13 +64,14 @@ module Axlsx
64
64
  def to_xml_string(str = "")
65
65
  author = @comments.authors[author_index]
66
66
  str << '<comment ref="' << ref << '" authorId="' << author_index.to_s << '">'
67
- str << '<text><r>'
68
- str << '<rPr> <b/><color indexed="81"/></rPr>'
69
- str << '<t>' << author.to_s << ':
70
- </t></r>'
67
+ str << '<text>'
68
+ unless author.to_s == ""
69
+ str << '<r><rPr><b/><color indexed="81"/></rPr>'
70
+ str << "<t>" << ::CGI.escapeHTML(author.to_s) << ":\n</t></r>"
71
+ end
71
72
  str << '<r>'
72
73
  str << '<rPr><color indexed="81"/></rPr>'
73
- str << '<t>' << text << '</t></r></text>'
74
+ str << '<t>' << ::CGI.escapeHTML(text) << '</t></r></text>'
74
75
  str << '</comment>'
75
76
  end
76
77
 
@@ -56,9 +56,9 @@ module Axlsx
56
56
  # The relationships required by this object
57
57
  # @return [Array]
58
58
  def relationships
59
- [Relationship.new(VML_DRAWING_R, "../#{vml_drawing.pn}"),
60
- Relationship.new(COMMENT_R, "../#{pn}"),
61
- Relationship.new(COMMENT_R_NULL, "NULL")]
59
+ [Relationship.new(self, VML_DRAWING_R, "../#{vml_drawing.pn}"),
60
+ Relationship.new(self, COMMENT_R, "../#{pn}"),
61
+ Relationship.new(self, COMMENT_R_NULL, "NULL")]
62
62
  end
63
63
 
64
64
  # serialize the object
@@ -182,7 +182,7 @@ module Axlsx
182
182
  # @see timePeriod
183
183
  def timePeriod=(v); Axlsx::validate_time_period_type(v); @timePeriod = v end
184
184
  # @see formula
185
- def formula=(v); [*v].each {|x| Axlsx::validate_string(x) }; @formula = v end
185
+ def formula=(v); [*v].each {|x| Axlsx::validate_string(x) }; @formula = [*v].map { |form| ::CGI.escapeHTML(form) } end
186
186
 
187
187
  # @see color_scale
188
188
  def color_scale=(v)
@@ -10,7 +10,8 @@ module Axlsx
10
10
  # @return [Numeric]
11
11
  def self.date_to_serial(date)
12
12
  epoch = Axlsx::Workbook::date1904 ? Date.new(1904) : Date.new(1899, 12, 30)
13
- (date - epoch).to_f
13
+ offset_date = date.respond_to?(:utc_offset) ? date + date.utc_offset.seconds : date
14
+ (offset_date - epoch).to_f
14
15
  end
15
16
 
16
17
  # The time_to_serial methond converts a Time object its excel serialized form.
@@ -19,11 +20,11 @@ module Axlsx
19
20
  def self.time_to_serial(time)
20
21
  # Using hardcoded offsets here as some operating systems will not except
21
22
  # a 'negative' offset from the ruby epoch.
22
- epoch1900 = -2209161600 # Time.utc(1899, 12, 30).to_i
23
- epoch1904 = -2082844800 # Time.utc(1904, 1, 1).to_i
24
- seconds_per_day = 86400 # 60*60*24
23
+ epoch1900 = -2209161600.0 # Time.utc(1899, 12, 30).to_i
24
+ epoch1904 = -2082844800.0 # Time.utc(1904, 1, 1).to_i
25
+ seconds_per_day = 86400.0 # 60*60*24
25
26
  epoch = Axlsx::Workbook::date1904 ? epoch1904 : epoch1900
26
- (time.to_f - epoch)/seconds_per_day
27
+ (time.utc_offset + time.to_f - epoch)/seconds_per_day
27
28
  end
28
29
  end
29
30
  end
@@ -19,10 +19,12 @@ module Axlsx
19
19
  @sheet = sheet
20
20
  @sheet.workbook.pivot_tables << self
21
21
  @name = "PivotTable#{index+1}"
22
+ @data_sheet = nil
22
23
  @rows = []
23
24
  @columns = []
24
25
  @data = []
25
26
  @pages = []
27
+ @subtotal = nil
26
28
  parse_options options
27
29
  yield self if block_given?
28
30
  end
@@ -39,6 +41,15 @@ module Axlsx
39
41
  # @return [String]
40
42
  attr_reader :sheet
41
43
 
44
+ # The sheet used as data source for the pivot table
45
+ # @return [Worksheet]
46
+ attr_writer :data_sheet
47
+
48
+ # @see #data_sheet
49
+ def data_sheet
50
+ @data_sheet || @sheet
51
+ end
52
+
42
53
  # The range where the data for this pivot table lives.
43
54
  # @return [String]
44
55
  attr_reader :range
@@ -85,10 +96,17 @@ module Axlsx
85
96
  # (see #data)
86
97
  def data=(v)
87
98
  DataTypeValidator.validate "#{self.class}.data", [Array], v
88
- v.each do |ref|
89
- DataTypeValidator.validate "#{self.class}.data[]", [String], ref
99
+ @data = []
100
+ v.each do |data_field|
101
+ if data_field.is_a? String
102
+ data_field = {:ref => data_field}
103
+ end
104
+ data_field.values.each do |value|
105
+ DataTypeValidator.validate "#{self.class}.data[]", [String], value
106
+ end
107
+ @data << data_field
90
108
  end
91
- @data = v
109
+ @data
92
110
  end
93
111
 
94
112
  # The pages
@@ -128,20 +146,14 @@ module Axlsx
128
146
  @cache_definition ||= PivotTableCacheDefinition.new(self)
129
147
  end
130
148
 
131
- # The worksheet relationships. This is managed automatically by the worksheet
149
+ # The relationships for this pivot table.
132
150
  # @return [Relationships]
133
151
  def relationships
134
152
  r = Relationships.new
135
- r << Relationship.new(PIVOT_TABLE_CACHE_DEFINITION_R, "../#{cache_definition.pn}")
153
+ r << Relationship.new(cache_definition, PIVOT_TABLE_CACHE_DEFINITION_R, "../#{cache_definition.pn}")
136
154
  r
137
155
  end
138
156
 
139
- # The relation reference id for this table
140
- # @return [String]
141
- def rId
142
- "rId#{index+1}"
143
- end
144
-
145
157
  # Serializes the object
146
158
  # @param [String] str
147
159
  # @return [String]
@@ -186,11 +198,11 @@ module Axlsx
186
198
  str << '</pageFields>'
187
199
  end
188
200
  unless data.empty?
189
- str << '<dataFields count="' << data.size.to_s << '">'
201
+ str << "<dataFields count=\"#{data.size}\">"
190
202
  data.each do |datum_value|
191
- str << '<dataField name="Sum of ' << datum_value << '" ' <<
192
- 'fld="' << header_index_of(datum_value).to_s << '" ' <<
193
- 'baseField="0" baseItem="0"/>'
203
+ str << "<dataField name='#{@subtotal} of #{datum_value[:ref]}' fld='#{header_index_of(datum_value[:ref])}' baseField='0' baseItem='0'"
204
+ str << " subtotal='#{datum_value[:subtotal]}' " if datum_value[:subtotal]
205
+ str << "/>"
194
206
  end
195
207
  str << '</dataFields>'
196
208
  end
@@ -206,7 +218,7 @@ module Axlsx
206
218
  # The header cells for the pivot table
207
219
  # @return [Array]
208
220
  def header_cells
209
- @sheet[header_range]
221
+ data_sheet[header_range]
210
222
  end
211
223
 
212
224
  # The values in the header cells collection
@@ -242,7 +254,7 @@ module Axlsx
242
254
  '<pivotField axis="axisCol" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1">' <<
243
255
  '<items count="1"><item t="default"/></items>' <<
244
256
  '</pivotField>'
245
- elsif data.include? cell_ref
257
+ elsif data_refs.include? cell_ref
246
258
  '<pivotField dataField="1" compact="0" outline="0" subtotalTop="0" showAll="0" includeNewItemsInFilter="1">' <<
247
259
  '</pivotField>'
248
260
  else
@@ -250,7 +262,9 @@ module Axlsx
250
262
  '</pivotField>'
251
263
  end
252
264
  end
253
-
265
+ def data_refs
266
+ data.map { |hash| hash[:ref] }
267
+ end
254
268
  def header_range
255
269
  range.gsub(/^(\w+?)(\d+)\:(\w+?)\d+$/, '\1\2:\3\2')
256
270
  end
@@ -35,10 +35,11 @@ module Axlsx
35
35
  index + 1
36
36
  end
37
37
 
38
- # The relation reference id for this table
38
+ # The relationship id for this pivot table cache definition.
39
+ # @see Relationship#Id
39
40
  # @return [String]
40
41
  def rId
41
- "rId#{index + 1}"
42
+ pivot_table.relationships.for(self).Id
42
43
  end
43
44
 
44
45
  # Serializes the object
@@ -48,7 +49,7 @@ module Axlsx
48
49
  str << '<?xml version="1.0" encoding="UTF-8"?>'
49
50
  str << '<pivotCacheDefinition xmlns="' << XML_NS << '" xmlns:r="' << XML_NS_R << '" invalid="1" refreshOnLoad="1" recordCount="0">'
50
51
  str << '<cacheSource type="worksheet">'
51
- str << '<worksheetSource ref="' << pivot_table.range << '" sheet="Data Sheet"/>'
52
+ str << '<worksheetSource ref="' << pivot_table.range << '" sheet="' << pivot_table.data_sheet.name << '"/>'
52
53
  str << '</cacheSource>'
53
54
  str << '<cacheFields count="' << pivot_table.header_cells_count.to_s << '">'
54
55
  pivot_table.header_cells.each do |cell|
@@ -17,7 +17,7 @@ module Axlsx
17
17
  # returns the relationships required by this collection
18
18
  def relationships
19
19
  return [] if empty?
20
- map{ |pivot_table| Relationship.new(PIVOT_TABLE_R, "../#{pivot_table.pn}") }
20
+ map{ |pivot_table| Relationship.new(pivot_table, PIVOT_TABLE_R, "../#{pivot_table.pn}") }
21
21
  end
22
22
  end
23
23
 
@@ -97,7 +97,7 @@ module Axlsx
97
97
  str << '</row>'
98
98
  end
99
99
 
100
- # Adds a singel sell to the row based on the data provided and updates the worksheet's autofit data.
100
+ # Adds a single sell to the row based on the data provided and updates the worksheet's autofit data.
101
101
  # @return [Cell]
102
102
  def add_cell(value="", options={})
103
103
  c = Cell.new(self, value, options)
@@ -0,0 +1,33 @@
1
+ module Axlsx
2
+
3
+ # A collection of break objects that define row breaks (page breaks) for printing and preview
4
+
5
+ class RowBreaks < SimpleTypedList
6
+
7
+ def initialize
8
+ super Break
9
+ end
10
+
11
+ # Adds a row break
12
+ # @param [Hash] options The options for the break to be created.
13
+ # max and man values are fixed.
14
+ # @see Break
15
+ def add_break(options)
16
+ # force feed the excel default
17
+ @list << Break.new(options.merge(:max => 16383, :man => true))
18
+ last
19
+ end
20
+
21
+ # <rowBreaks count="3" manualBreakCount="3">
22
+ # <brk id="1" max="16383" man="1"/>
23
+ # <brk id="7" max="16383" man="1"/>
24
+ # <brk id="13" max="16383" man="1"/>
25
+ # </rowBreaks>
26
+ def to_xml_string(str='')
27
+ return if empty?
28
+ str << '<rowBreaks count="' << @list.size.to_s << '" manualBreakCount="' << @list.size.to_s << '">'
29
+ each { |brk| brk.to_xml_string(str) }
30
+ str << '</rowBreaks>'
31
+ end
32
+ end
33
+ end
@@ -47,10 +47,11 @@ module Axlsx
47
47
  "#{TABLE_PN % (index+1)}"
48
48
  end
49
49
 
50
- # The relation reference id for this table
50
+ # The relationship id for this table.
51
+ # @see Relationship#Id
51
52
  # @return [String]
52
53
  def rId
53
- "rId#{index+1}"
54
+ @sheet.relationships.for(self).Id
54
55
  end
55
56
 
56
57
  # The name of the Table.
@@ -17,7 +17,7 @@ module Axlsx
17
17
  # returns the relationships required by this collection
18
18
  def relationships
19
19
  return [] if empty?
20
- map{ |table| Relationship.new(TABLE_R, "../#{table.pn}") }
20
+ map{ |table| Relationship.new(table, TABLE_R, "../#{table.pn}") }
21
21
  end
22
22
 
23
23
  def to_xml_string(str = "")
@@ -37,6 +37,8 @@ module Axlsx
37
37
  @page_setup = PageSetup.new options[:page_setup] if options[:page_setup]
38
38
  @print_options = PrintOptions.new options[:print_options] if options[:print_options]
39
39
  @header_footer = HeaderFooter.new options[:header_footer] if options[:header_footer]
40
+ @row_breaks = RowBreaks.new
41
+ @col_breaks = ColBreaks.new
40
42
  end
41
43
 
42
44
  # The name of the worksheet
@@ -94,6 +96,22 @@ module Axlsx
94
96
  @pivot_tables ||= PivotTables.new self
95
97
  end
96
98
 
99
+ # A collection of column breaks added to this worksheet
100
+ # @note Please do not use this directly. Instead use
101
+ # add_page_break
102
+ # @see Worksheet#add_page_break
103
+ def col_breaks
104
+ @col_breaks ||= ColBreaks.new
105
+ end
106
+
107
+ # A collection of row breaks added to this worksheet
108
+ # @note Please do not use this directly. Instead use
109
+ # add_page_break
110
+ # @see Worksheet#add_page_break
111
+ def row_breaks
112
+ @row_breaks ||= RowBreaks.new
113
+ end
114
+
97
115
  # A typed collection of hyperlinks associated with this worksheet
98
116
  # @return [WorksheetHyperlinks]
99
117
  def hyperlinks
@@ -114,14 +132,19 @@ module Axlsx
114
132
  @rows ||= SimpleTypedList.new Row
115
133
  end
116
134
 
117
- # returns the sheet data as columnw
118
- def cols
119
- @rows.transpose
135
+ # returns the sheet data as columns
136
+ # If you pass a block, it will be evaluated whenever a row does not have a
137
+ # cell at a specific index. The block will be called with the row and column
138
+ # index in the missing cell was found.
139
+ # @example
140
+ # cols { |row_index, column_index| p "warn - row #{row_index} is does not have a cell at #{column_index}
141
+ def cols(&block)
142
+ @rows.transpose(&block)
120
143
  end
121
144
 
122
- # An range that excel will apply an autfilter to "A1:B3"
145
+ # An range that excel will apply an auto-filter to "A1:B3"
123
146
  # This will turn filtering on for the cells in the range.
124
- # The first row is considered the header, while subsequent rows are considerd to be data.
147
+ # The first row is considered the header, while subsequent rows are considered to be data.
125
148
  # @return String
126
149
  def auto_filter
127
150
  @auto_filter ||= AutoFilter.new self
@@ -327,6 +350,10 @@ module Axlsx
327
350
  auto_filter.range = v
328
351
  end
329
352
 
353
+ # Accessor for controlling whether leading and trailing spaces in cells are
354
+ # preserved or ignored. The default is to preserve spaces.
355
+ attr_accessor :preserve_spaces
356
+
330
357
  # The part name of this worksheet
331
358
  # @return [String]
332
359
  def pn
@@ -339,10 +366,11 @@ module Axlsx
339
366
  "#{WORKSHEET_RELS_PN % (index+1)}"
340
367
  end
341
368
 
342
- # The relationship Id of thiw worksheet
369
+ # The relationship id of this worksheet.
343
370
  # @return [String]
371
+ # @see Relationship#Id
344
372
  def rId
345
- "rId#{index+1}"
373
+ @workbook.relationships.for(self).Id
346
374
  end
347
375
 
348
376
  # The index of this worksheet in the owning Workbook's worksheets list.
@@ -488,6 +516,24 @@ module Axlsx
488
516
  image
489
517
  end
490
518
 
519
+ # Adds a page break (row break) to the worksheet
520
+ # @param cell A Cell object or excel style string reference indicating where the break
521
+ # should be added to the sheet.
522
+ # @example
523
+ # ws.add_page_break("A4")
524
+ def add_page_break(cell)
525
+ DataTypeValidator.validate "Worksheet#add_page_break cell", [String, Cell], cell
526
+ column_index, row_index = if cell.is_a?(String)
527
+ Axlsx.name_to_indices(cell)
528
+ else
529
+ cell.pos
530
+ end
531
+ if column_index > 0
532
+ col_breaks.add_break(:id => column_index)
533
+ end
534
+ row_breaks.add_break(:id => row_index)
535
+ end
536
+
491
537
  # This is a helper method that Lets you specify a fixed width for multiple columns in a worksheet in one go.
492
538
  # Axlsx is sparse, so if you have not set data for a column, you cannot set the width.
493
539
  # Setting a fixed column width to nil will revert the behaviour back to calculating the width for you on the next call to add_row.
@@ -542,15 +588,7 @@ module Axlsx
542
588
  item.to_xml_string(str) if item
543
589
  end
544
590
  str << '</worksheet>'
545
- sanitize(str)
546
- end
547
-
548
- # returns the provided string with all invalid control charaters
549
- # removed.
550
- # @param [String] str The sting to process
551
- # @return [String]
552
- def sanitize(str)
553
- str.gsub(CONTROL_CHAR_REGEX, '')
591
+ Axlsx::sanitize(str)
554
592
  end
555
593
 
556
594
  # The worksheet relationships. This is managed automatically by the worksheet
@@ -565,14 +603,6 @@ module Axlsx
565
603
  r
566
604
  end
567
605
 
568
- # identifies the index of an object withing the collections used in generating relationships for the worksheet
569
- # @param [Any] object the object to search for
570
- # @return [Integer] The index of the object
571
- def relationships_index_of(object)
572
- objects = [tables.to_a, worksheet_comments.comments.to_a, hyperlinks.to_a, worksheet_drawing.drawing].flatten.compact || []
573
- objects.index(object)
574
- end
575
-
576
606
  # Returns the cell or cells defined using excel style A1:B3 references.
577
607
  # @param [String|Integer] cell_def the string defining the cell or range of cells, or the rownumber
578
608
  # @return [Cell, Array]
@@ -629,6 +659,11 @@ module Axlsx
629
659
  end
630
660
 
631
661
  private
662
+
663
+ def xml_space
664
+ workbook.xml_space
665
+ end
666
+
632
667
  def outline(collection, range, level = 1, collapsed = true)
633
668
  range.each do |index|
634
669
  unless (item = collection[index]).nil?
@@ -653,7 +688,7 @@ module Axlsx
653
688
  sheet_data, sheet_calc_pr, @sheet_protection, protected_ranges,
654
689
  auto_filter, merged_cells, conditional_formattings,
655
690
  data_validations, hyperlinks, print_options, page_margins,
656
- page_setup, header_footer, worksheet_drawing, worksheet_comments,
691
+ page_setup, header_footer, row_breaks, col_breaks, worksheet_drawing, worksheet_comments,
657
692
  tables]
658
693
  end
659
694
 
@@ -699,7 +734,7 @@ module Axlsx
699
734
  # Helper method for parsingout the root node for worksheet
700
735
  # @return [String]
701
736
  def worksheet_node
702
- "<worksheet xmlns=\"%s\" xmlns:r=\"%s\">" % [XML_NS, XML_NS_R]
737
+ "<worksheet xmlns=\"%s\" xmlns:r=\"%s\" xml:space=\"#{xml_space}\">" % [XML_NS, XML_NS_R]
703
738
  end
704
739
 
705
740
  def sheet_data