axlsx 1.1.7 → 1.1.8

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 (130) hide show
  1. data/README.md +41 -5
  2. data/Rakefile +3 -2
  3. data/examples/chart_colors.rb +18 -3
  4. data/examples/example.rb +100 -46
  5. data/examples/extractive.pdf +0 -0
  6. data/lib/axlsx.rb +7 -6
  7. data/lib/axlsx/content_type/content_type.rb +2 -0
  8. data/lib/axlsx/content_type/default.rb +21 -12
  9. data/lib/axlsx/content_type/override.rb +22 -11
  10. data/lib/axlsx/doc_props/app.rb +36 -32
  11. data/lib/axlsx/doc_props/core.rb +9 -5
  12. data/lib/axlsx/drawing/ax_data_source.rb +7 -6
  13. data/lib/axlsx/drawing/axis.rb +48 -27
  14. data/lib/axlsx/drawing/bar_3D_chart.rb +47 -37
  15. data/lib/axlsx/drawing/bar_series.rb +1 -0
  16. data/lib/axlsx/drawing/cat_axis.rb +42 -38
  17. data/lib/axlsx/drawing/chart.rb +34 -27
  18. data/lib/axlsx/drawing/drawing.rb +5 -4
  19. data/lib/axlsx/drawing/line_3D_chart.rb +1 -1
  20. data/lib/axlsx/drawing/num_data_source.rb +1 -1
  21. data/lib/axlsx/drawing/pie_3D_chart.rb +7 -7
  22. data/lib/axlsx/drawing/two_cell_anchor.rb +3 -8
  23. data/lib/axlsx/drawing/view_3D.rb +41 -31
  24. data/lib/axlsx/drawing/vml_drawing.rb +1 -1
  25. data/lib/axlsx/package.rb +11 -11
  26. data/lib/axlsx/rels/relationship.rb +3 -3
  27. data/lib/axlsx/stylesheet/styles.rb +1 -1
  28. data/lib/axlsx/util/constants.rb +4 -0
  29. data/lib/axlsx/util/simple_typed_list.rb +2 -2
  30. data/lib/axlsx/util/validators.rb +2 -2
  31. data/lib/axlsx/version.rb +1 -1
  32. data/lib/axlsx/workbook/workbook.rb +1 -2
  33. data/lib/axlsx/workbook/worksheet/cell.rb +1 -1
  34. data/lib/axlsx/workbook/worksheet/data_bar.rb +1 -1
  35. data/lib/axlsx/workbook/worksheet/page_setup.rb +9 -0
  36. data/lib/axlsx/workbook/worksheet/protected_range.rb +46 -0
  37. data/lib/axlsx/workbook/worksheet/worksheet.rb +180 -56
  38. data/test/content_type/tc_content_type.rb +1 -6
  39. data/test/doc_props/tc_core.rb +1 -1
  40. data/test/drawing/tc_axis.rb +8 -0
  41. data/test/drawing/tc_bar_3D_chart.rb +12 -12
  42. data/test/drawing/tc_bar_series.rb +0 -1
  43. data/test/drawing/tc_chart.rb +1 -5
  44. data/test/drawing/tc_pie_3D_chart.rb +3 -7
  45. data/test/drawing/tc_view_3D.rb +18 -18
  46. data/test/tc_package.rb +2 -0
  47. data/test/workbook/worksheet/tc_page_setup.rb +20 -3
  48. data/test/workbook/worksheet/tc_protected_range.rb +18 -0
  49. data/test/workbook/worksheet/tc_selection.rb +1 -1
  50. data/test/workbook/worksheet/tc_worksheet.rb +39 -18
  51. metadata +54 -103
  52. data/examples/axis-titles.xlsx +0 -0
  53. data/examples/basic_charts.xlsx +0 -0
  54. data/examples/chart_colors.xlsx +0 -0
  55. data/examples/charts.xlsx +0 -0
  56. data/examples/conditional_formatting/getting_barred.xlsx +0 -0
  57. data/examples/doc/_index.html +0 -84
  58. data/examples/doc/class_list.html +0 -47
  59. data/examples/doc/css/common.css +0 -1
  60. data/examples/doc/css/full_list.css +0 -55
  61. data/examples/doc/css/style.css +0 -322
  62. data/examples/doc/file_list.html +0 -46
  63. data/examples/doc/frames.html +0 -13
  64. data/examples/doc/index.html +0 -84
  65. data/examples/doc/js/app.js +0 -205
  66. data/examples/doc/js/full_list.js +0 -173
  67. data/examples/doc/js/jquery.js +0 -16
  68. data/examples/doc/method_list.html +0 -46
  69. data/examples/doc/top-level-namespace.html +0 -95
  70. data/examples/example.xlsx +0 -0
  71. data/examples/example_streamed.xlsx +0 -0
  72. data/examples/examples_saved.xlsx +0 -0
  73. data/examples/extractive.xlsx +0 -0
  74. data/examples/fish.xlsx +0 -0
  75. data/examples/no-use_autowidth.xlsx +0 -0
  76. data/examples/pareto.rb +0 -28
  77. data/examples/pareto.xlsx +0 -0
  78. data/examples/pie_chart_excel.xlsx +0 -0
  79. data/examples/pie_chart_saved.xlsx +0 -0
  80. data/examples/shared_strings_example.xlsx +0 -0
  81. data/examples/sheet_protection.xlsx +0 -0
  82. data/examples/sheet_view.xlsx +0 -0
  83. data/examples/two_cell_anchor_image.xlsx +0 -0
  84. data/examples/~$example.xlsx +0 -0
  85. data/lib/axlsx/drawing/ax_data_source.rb~ +0 -55
  86. data/lib/axlsx/drawing/data_source.rb~ +0 -51
  87. data/lib/axlsx/drawing/hlink_click.rb~ +0 -0
  88. data/lib/axlsx/drawing/hyperlink.rb~ +0 -64
  89. data/lib/axlsx/drawing/num_data.rb~ +0 -51
  90. data/lib/axlsx/drawing/num_data_source.rb~ +0 -54
  91. data/lib/axlsx/drawing/num_val.rb~ +0 -40
  92. data/lib/axlsx/drawing/picture_locking.rb~ +0 -36
  93. data/lib/axlsx/drawing/ref.rb~ +0 -41
  94. data/lib/axlsx/drawing/str_data.rb~ +0 -58
  95. data/lib/axlsx/drawing/str_val.rb~ +0 -35
  96. data/lib/axlsx/drawing/vml_drawing.rb~ +0 -6
  97. data/lib/axlsx/drawing/vml_shape.rb~ +0 -61
  98. data/lib/axlsx/util/cbf.rb +0 -333
  99. data/lib/axlsx/util/cfb.rb~ +0 -201
  100. data/lib/axlsx/util/font_tables.rb~ +0 -0
  101. data/lib/axlsx/util/ms_off_crypto.rb +0 -189
  102. data/lib/axlsx/util/ms_off_crypto.rb~ +0 -3
  103. data/lib/axlsx/util/ms_offcrypto.rb~ +0 -0
  104. data/lib/axlsx/util/parser.rb~ +0 -6
  105. data/lib/axlsx/util/storage.rb~ +0 -0
  106. data/lib/axlsx/workbook/shared_strings_table.rb~ +0 -69
  107. data/lib/axlsx/workbook/worksheet/cfvo.rb~ +0 -0
  108. data/lib/axlsx/workbook/worksheet/col.rb~ +0 -0
  109. data/lib/axlsx/workbook/worksheet/color_scale.rb~ +0 -46
  110. data/lib/axlsx/workbook/worksheet/comment.rb~ +0 -91
  111. data/lib/axlsx/workbook/worksheet/comments.rb~ +0 -86
  112. data/lib/axlsx/workbook/worksheet/data_bar.rb~ +0 -0
  113. data/lib/axlsx/workbook/worksheet/icon_set.rb~ +0 -95
  114. data/lib/axlsx/workbook/worksheet/shared_strings_table.rb~ +0 -0
  115. data/lib/axlsx/workbook/worksheet/table.rb~ +0 -97
  116. data/lib/schema/dc.xsd~ +0 -118
  117. data/lib/schema/dcterms.xsd~ +0 -331
  118. data/lib/schema/opc-coreProperties.xsd~ +0 -50
  119. data/test/drawing/tc_data_source.rb~ +0 -30
  120. data/test/drawing/tc_num_data.rb~ +0 -35
  121. data/test/drawing/tc_num_val.rb~ +0 -29
  122. data/test/drawing/tc_str_data.rb~ +0 -30
  123. data/test/drawing/tc_str_val.rb~ +0 -26
  124. data/test/drawing/tc_vml_drawing.rb~ +0 -0
  125. data/test/workbook/worksheet/table/tc_table.rb~ +0 -72
  126. data/test/workbook/worksheet/tc_cfvo.rb~ +0 -20
  127. data/test/workbook/worksheet/tc_col.rb~ +0 -10
  128. data/test/workbook/worksheet/tc_color_scale.rb~ +0 -0
  129. data/test/workbook/worksheet/tc_data_bar.rb~ +0 -0
  130. data/test/workbook/worksheet/tc_icon_set.rb~ +0 -0
@@ -156,18 +156,18 @@ module Axlsx
156
156
  # @return [Zip::ZipOutputStream]
157
157
  def write_parts(zip)
158
158
  p = parts
159
- p.each do |part|
160
- unless part[:doc].nil?
161
- zip.put_next_entry(part[:entry]);
162
- entry = ['1.9.2', '1.9.3'].include?(RUBY_VERSION) ? part[:doc].force_encoding('BINARY') : part[:doc]
163
- zip.puts(entry)
164
- end
165
- unless part[:path].nil?
166
- zip.put_next_entry(part[:entry]);
167
- # binread for 1.9.3
168
- zip.write IO.respond_to?(:binread) ? IO.binread(part[:path]) : IO.read(part[:path])
169
- end
159
+ p.each do |part|
160
+ unless part[:doc].nil?
161
+ zip.put_next_entry(part[:entry])
162
+ entry = ['1.9.2', '1.9.3'].include?(RUBY_VERSION) ? part[:doc].force_encoding('BINARY') : part[:doc]
163
+ zip.puts(entry)
170
164
  end
165
+ unless part[:path].nil?
166
+ zip.put_next_entry(part[:entry]);
167
+ # binread for 1.9.3
168
+ zip.write IO.respond_to?(:binread) ? IO.binread(part[:path]) : IO.read(part[:path])
169
+ end
170
+ end
171
171
  zip
172
172
  end
173
173
 
@@ -30,9 +30,9 @@ module Axlsx
30
30
  attr_reader :TargetMode
31
31
 
32
32
  # creates a new relationship
33
- # @param [String] Type The type of the relationship
34
- # @param [String] Target The target for the relationship
35
- # @option [Symbol] target_mode only accepts :external.
33
+ # @param [String] type The type of the relationship
34
+ # @param [String] target The target for the relationship
35
+ # @option [Symbol] :target_mode only accepts :external.
36
36
  def initialize(type, target, options={})
37
37
  self.Target=target
38
38
  self.Type=type
@@ -133,7 +133,7 @@ module Axlsx
133
133
  # @option options [Integer] family The font family to use.
134
134
  # @option options [String] font_name The name of the font to use
135
135
  # @option options [Integer] num_fmt The number format to apply
136
- # @option options [String] format_code The formatting to apply. If this is specified, num_fmt is ignored.
136
+ # @option options [String] format_code The formatting to apply.
137
137
  # @option options [Integer|Hash] border The border style to use.
138
138
  # @option options [String] bg_color The background color to apply to the cell
139
139
  # @option options [Boolean] hidden Indicates if the cell should be hidden
@@ -249,6 +249,10 @@ module Axlsx
249
249
 
250
250
  # error message for sheets that use a name which is longer than 31 bytes
251
251
  ERR_SHEET_NAME_TOO_LONG = "Your worksheet name '%s' is too long. Worksheet names must be 31 characters (bytes) or less"
252
+
253
+ # error message for sheets that use a name which includes a colon
254
+
255
+ ERR_SHEET_NAME_COLON_FORBIDDEN = "Your worksheet name '%s' contains a colon, which is not allowed by MS Excel and will cause repair warnings. Please change the name of your sheet."
252
256
 
253
257
  # error message for duplicate sheet names
254
258
  ERR_DUPLICATE_SHEET_NAME = "There is already a worksheet in this workbook named '%s'. Please use a unique name"
@@ -18,7 +18,7 @@ module Axlsx
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
- # @param [String] serialize The tag name to use in serialization
21
+ # @param [String] serialize_as The tag name to use in serialization
22
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
@@ -149,7 +149,7 @@ module Axlsx
149
149
 
150
150
  def to_xml_string(str = '')
151
151
  classname = @allowed_types[0].name.split('::').last
152
- el_name = serialize_as || (classname[0,1].downcase + classname[1..-1])
152
+ el_name = serialize_as.to_s || (classname[0,1].downcase + classname[1..-1])
153
153
  str << '<' << el_name << ' count="' << @list.size.to_s << '">'
154
154
  @list.each { |item| item.to_xml_string(str) }
155
155
  str << '</' << el_name << '>'
@@ -181,7 +181,7 @@ module Axlsx
181
181
  # Requires that the value is a valid scatterStyle
182
182
  # must be one of :none | :line | :lineMarker | :marker | :smooth | :smoothMarker
183
183
  # must be one of "none" | "line" | "lineMarker" | "marker" | "smooth" | "smoothMarker"
184
- # @param [Symbol|String] the value to validate
184
+ # @param [Symbol|String] v the value to validate
185
185
  def self.validate_scatter_style(v)
186
186
  Axlsx::RestrictionValidator.validate "ScatterChart.scatterStyle", [:none, :line, :lineMarker, :marker, :smooth, :smoothMarker], v.to_sym
187
187
  end
@@ -262,4 +262,4 @@ module Axlsx
262
262
  def self.validate_split_state_type(v)
263
263
  RestrictionValidator.validate :split_state_type, [:frozen, :frozen_split, :split], v
264
264
  end
265
- end
265
+ end
@@ -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.1.7" unless defined? Axlsx::VERSION
8
+ VERSION="1.1.8" unless defined? Axlsx::VERSION
9
9
 
10
10
  end
@@ -2,6 +2,7 @@
2
2
  module Axlsx
3
3
 
4
4
  require 'axlsx/workbook/worksheet/date_time_converter.rb'
5
+ require 'axlsx/workbook/worksheet/protected_range.rb'
5
6
  require 'axlsx/workbook/worksheet/cell.rb'
6
7
  require 'axlsx/workbook/worksheet/page_margins.rb'
7
8
  require 'axlsx/workbook/worksheet/page_setup.rb'
@@ -100,7 +101,6 @@ require 'axlsx/workbook/worksheet/selection.rb'
100
101
  # @return [SimpleTypedList]
101
102
  attr_reader :tables
102
103
 
103
-
104
104
  # A colllection of comments associated with this workbook
105
105
  # @note The recommended way to manage comments is Worksheet#add_comment
106
106
  # @see Worksheet#add_comment
@@ -143,7 +143,6 @@ require 'axlsx/workbook/worksheet/selection.rb'
143
143
  @drawings = SimpleTypedList.new Drawing
144
144
  @charts = SimpleTypedList.new Chart
145
145
  @images = SimpleTypedList.new Pic
146
-
147
146
  # Are these even used????? Check package serialization parts
148
147
  @tables = SimpleTypedList.new Table
149
148
  @comments = SimpleTypedList.new Comments
@@ -142,7 +142,7 @@ module Axlsx
142
142
  # The inline color property for the cell
143
143
  # @return [Color]
144
144
  attr_reader :color
145
- # @param [String] The 8 character representation for an rgb color #FFFFFFFF"
145
+ # @param [String] v The 8 character representation for an rgb color #FFFFFFFF"
146
146
  def color=(v)
147
147
  @color = v.is_a?(Color) ? v : Color.new(:rgb=>v)
148
148
  @is_text_run = true
@@ -65,7 +65,7 @@ module Axlsx
65
65
  def showValue=(v); Axlsx.validate_boolean(v); @showValue = v end
66
66
 
67
67
  # Sets the color for the data bars.
68
- # @param [Color|String] The color object, or rgb string value to apply
68
+ # @param [Color|String] v The color object, or rgb string value to apply
69
69
  def color=(v)
70
70
  @color = v if v.is_a? Color
71
71
  self.color.rgb = v if v.is_a? String
@@ -92,6 +92,15 @@ module Axlsx
92
92
  [@fit_to_width, @fit_to_height]
93
93
  end
94
94
 
95
+
96
+ # helper method for worksheet to determine if the page setup is configured for fit to page printing
97
+ # We treat any page set up that has a value set for fit_to_width or fit_to_height value as fit_to_page.
98
+ # @return [Boolean]
99
+ def fit_to_page?
100
+ # is there some better what to express this?
101
+ (fit_to_width != nil || fit_to_height != nil)
102
+ end
103
+
95
104
  # Serializes the page settings element.
96
105
  # @param [String] str
97
106
  # @return [String]
@@ -0,0 +1,46 @@
1
+ module Axlsx
2
+ # The Protected Range class represents a set of cells in the worksheet
3
+ # @note the recommended way to manage protected ranges with via Worksheet#protect_range
4
+ # @see Worksheet#protect_range
5
+ class ProtectedRange
6
+
7
+ # The reference for the protected range
8
+ # @return [String]
9
+ attr_reader :sqref
10
+
11
+ # The name of the protected range
12
+ # @return [String]
13
+ attr_reader :name
14
+
15
+ # Initializes a new protected range object
16
+ # @option [String] sqref The cell range reference to protect. This can be an absolute or a relateve range however, it only applies to the current sheet.
17
+ # @option [String] name An optional name for the protected name.
18
+ def initialize(options={})
19
+ options.each do |o|
20
+ self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
21
+ end
22
+ yield self if block_given?
23
+ end
24
+
25
+ # @see sqref
26
+ def sqref=(v)
27
+ Axlsx.validate_string(v)
28
+ @sqref = v
29
+ end
30
+
31
+ # @see name
32
+ def name=(v)
33
+ Axlsx.validate_string(v)
34
+ @name = v
35
+ end
36
+
37
+ # serializes the proteted range
38
+ # @param [String] str if this string object is provided we append
39
+ # our output to that object. Use this - it helps limit the number of
40
+ # objects created during serialization
41
+ def to_xml_string(str="")
42
+ attrs = self.instance_values.reject{ |key, value| value == nil }
43
+ str << '<protectedRange ' << attrs.map { |key, value| '' << key << '="' << value.to_s << '"' }.join(' ') << '/>'
44
+ end
45
+ end
46
+ end
@@ -16,7 +16,13 @@ module Axlsx
16
16
  yield @sheet_protection if block_given?
17
17
  @sheet_protection
18
18
  end
19
-
19
+
20
+ # A collection of protected ranges in the worksheet
21
+ # @note The recommended way to manage protected ranges is with Worksheet#protect_range
22
+ # @see Worksheet#protect_range
23
+ # @return [SimpleTypedList] The protected ranges for this worksheet
24
+ attr_reader :protected_ranges
25
+
20
26
  # The sheet view object for this worksheet
21
27
  # @return [SheetView]
22
28
  # @see [SheetView]
@@ -34,6 +40,8 @@ module Axlsx
34
40
  # @return [Array] of Table
35
41
  attr_reader :tables
36
42
 
43
+ # The comments associated with this worksheet
44
+ # @return [SimpleTypedList]
37
45
  attr_reader :comments
38
46
 
39
47
  # The rows in this worksheet
@@ -81,8 +89,9 @@ module Axlsx
81
89
  # If you want the worksheet to fit on more pages (e.g. 2x2), set {PageSetup#fit_to_width} and {PageSetup#fit_to_height} accordingly.
82
90
  # @return Boolean
83
91
  # @see #page_setup
84
- def fit_to_page
85
- (@page_setup != nil && (@page_setup.fit_to_width != nil || @page_setup.fit_to_height != nil))
92
+ def fit_to_page?
93
+ return false unless @page_setup
94
+ @page_setup.fit_to_page?
86
95
  end
87
96
 
88
97
 
@@ -110,7 +119,6 @@ module Axlsx
110
119
  @page_margins ||= PageMargins.new
111
120
  yield @page_margins if block_given?
112
121
  @page_margins
113
-
114
122
  end
115
123
 
116
124
  # Page setup settings for printing the worksheet.
@@ -188,7 +196,7 @@ module Axlsx
188
196
  @print_options = PrintOptions.new options[:print_options] if options[:print_options]
189
197
  @rows = SimpleTypedList.new Row
190
198
  @column_info = SimpleTypedList.new Col
191
- # @cols = SimpleTypedList.new Cell
199
+ @protected_ranges = SimpleTypedList.new ProtectedRange
192
200
  @tables = SimpleTypedList.new Table
193
201
 
194
202
  options.each do |o|
@@ -217,7 +225,7 @@ module Axlsx
217
225
  cf.add_rules rules
218
226
  @conditional_formattings << cf
219
227
  end
220
-
228
+
221
229
  # Add data validation to this worksheet.
222
230
  #
223
231
  # @param [String] cells The cells the validation will apply to.
@@ -237,23 +245,37 @@ module Axlsx
237
245
  # worksheet.merge_cells worksheet.rows.first.cells[(2..4)]
238
246
  # #alternatively you can do it from a single cell
239
247
  # worksheet["C1"].merge worksheet["E1"]
240
- # @param [Array, string]
248
+ # @param [Array, string] cells
241
249
  def merge_cells(cells)
242
250
  @merged_cells << if cells.is_a?(String)
243
251
  cells
244
- elsif cells.is_a?(Array)
245
- cells = cells.sort { |x, y| [x.index, x.row.index] <=> [y.index, y.row.index] }
246
- "#{cells.first.r}:#{cells.last.r}"
247
- end
252
+ elsif cells.is_a?(Array)
253
+ cells = cells.sort { |x, y| [x.index, x.row.index] <=> [y.index, y.row.index] }
254
+ "#{cells.first.r}:#{cells.last.r}"
255
+ end
248
256
  end
249
257
 
258
+ # Adds a new protected cell range to the worksheet. Note that protected ranges are only in effect when sheet protection is enabled.
259
+ # @param [String|Array] cells The string reference for the cells to protect or an array of cells.
260
+ # @return [ProtectedRange]
261
+ # @note When using an array of cells, a contiguous range is created from the minimum top left to the maximum top bottom of the cells provided.
262
+ def protect_range(cells)
263
+ sqref = if cells.is_a?(String)
264
+ cells
265
+ elsif cells.is_a?(SimpleTypedList)
266
+ cells = cells.sort { |x, y| [x.index, x.row.index] <=> [y.index, y.row.index] }
267
+ "#{cells.first.r}:#{cells.last.r}"
268
+ end
269
+ @protected_ranges << ProtectedRange.new(:sqref => sqref, :name => 'Range#{@protected_ranges.size}')
270
+ @protected_ranges.last
271
+ end
250
272
 
251
273
  # The demensions of a worksheet. This is not actually a required element by the spec,
252
274
  # but at least a few other document readers expect this for conversion
253
275
  # @return [String] the A1:B2 style reference for the first and last row column intersection in the workbook
254
276
  def dimension
255
277
  dim_start = rows.first.cells.first == nil ? 'A1' : rows.first.cells.first.r
256
- dim_end = rows.last.cells.last == nil ? 'AA:200' : rows.last.cells.last.r
278
+ dim_end = rows.last.cells.last == nil ? 'AA200' : rows.last.cells.last.r
257
279
  "#{dim_start}:#{dim_end}"
258
280
  end
259
281
 
@@ -281,7 +303,7 @@ module Axlsx
281
303
  # @return [Boolean]
282
304
  def fit_to_page=(v)
283
305
  warn('axlsx::DEPRECIATED: Worksheet#fit_to_page has been depreciated. This value will automatically be set for you when you use PageSetup#fit_to.')
284
- fit_to_page
306
+ fit_to_page?
285
307
  end
286
308
 
287
309
 
@@ -300,6 +322,8 @@ module Axlsx
300
322
  def name=(v)
301
323
  DataTypeValidator.validate "Worksheet.name", String, v
302
324
  raise ArgumentError, (ERR_SHEET_NAME_TOO_LONG % v) if v.size > 31
325
+ raise ArgumentError, (ERR_SHEET_NAME_COLON_FORBIDDEN % v) if v.include? ':'
326
+ v = Axlsx::coder.encode(v)
303
327
  sheet_names = @workbook.worksheets.map { |s| s.name }
304
328
  raise ArgumentError, (ERR_DUPLICATE_SHEET_NAME % v) if sheet_names.include?(v)
305
329
  @name=v
@@ -402,8 +426,9 @@ module Axlsx
402
426
 
403
427
  # Set the style for cells in a specific row
404
428
  # @param [Integer] index or range of indexes in the table
405
- # @param [Integer] the cellXfs index
406
- # @option options [Integer] col_offset only cells after this column will be updated.
429
+ # @param [Integer] style the cellXfs index
430
+ # @param [Hash] options the options used when applying the style
431
+ # @option [Integer] :col_offset only cells after this column will be updated.
407
432
  # @note You can also specify the style in the add_row call
408
433
  # @see Worksheet#add_row
409
434
  # @see README.md for an example
@@ -425,8 +450,9 @@ module Axlsx
425
450
 
426
451
  # Set the style for cells in a specific column
427
452
  # @param [Integer] index the index of the column
428
- # @param [Integer] the cellXfs index
429
- # @option options [Integer] row_offset only cells after this column will be updated.
453
+ # @param [Integer] style the cellXfs index
454
+ # @param [Hash] options
455
+ # @option [Integer] :row_offset only cells after this column will be updated.
430
456
  # @note You can also specify the style for specific columns in the call to add_row by using an array for the :styles option
431
457
  # @see Worksheet#add_row
432
458
  # @see README.md for an example
@@ -449,9 +475,9 @@ module Axlsx
449
475
  # @example This would set the first and third column widhts but leave the second column in autofit state.
450
476
  # ws.column_widths 7.2, nil, 3
451
477
  # @note For updating only a single column it is probably easier to just set the width of the ws.column_info[col_index].width directly
452
- # @param [Integer|Float|Fixnum|nil] values
453
- def column_widths(*args)
454
- args.each_with_index do |value, index|
478
+ # @param [Integer|Float|Fixnum|nil] widths
479
+ def column_widths(*widths)
480
+ widths.each_with_index do |value, index|
455
481
  next if value == nil
456
482
  Axlsx::validate_unsigned_numeric(value) unless value == nil
457
483
  @column_info[index] ||= Col.new index+1, index+1
@@ -493,55 +519,49 @@ module Axlsx
493
519
  end
494
520
 
495
521
  # Adds a media item to the worksheets drawing
496
- # @param [Class] media_type
497
- # @option options [] unknown
522
+ # @option [Hash] options options passed to drawing.add_image
498
523
  def add_image(options={})
499
524
  image = drawing.add_image(options)
500
525
  yield image if block_given?
501
526
  image
502
527
  end
503
528
 
504
- # Serializes the object
505
- # @param [String] str
529
+ # Serializes the worksheet object to an xml string
530
+ # This intentionally does not use nokogiri for performance reasons
506
531
  # @return [String]
507
532
  def to_xml_string
508
- rels = relationships
509
533
  str = '<?xml version="1.0" encoding="UTF-8"?>'
510
- str.concat "<worksheet xmlns=\"%s\" xmlns:r=\"%s\">" % [XML_NS, XML_NS_R]
511
- str.concat "<sheetPr><pageSetUpPr fitToPage=\"%s\"></pageSetUpPr></sheetPr>" % fit_to_page if fit_to_page
512
- str.concat "<dimension ref=\"%s\"></dimension>" % dimension unless rows.size == 0
534
+ str << worksheet_node
535
+ str << sheet_pr_node
536
+ str << dimension_node
513
537
  @sheet_view.to_xml_string(str) if @sheet_view
514
- if @column_info.size > 0
515
- str << "<cols>"
516
- @column_info.each { |col| col.to_xml_string(str) }
517
- str.concat '</cols>'
518
- end
519
- str.concat '<sheetData>'
520
- @rows.each_with_index { |row, index| row.to_xml_string(index, str) }
521
- str.concat '</sheetData>'
522
- str.concat "<autoFilter ref='%s'></autoFilter>" % @auto_filter if @auto_filter
538
+ str << cols_node
539
+ str << sheet_data_node
540
+
541
+ str << auto_filter_node
523
542
  @sheet_protection.to_xml_string(str) if @sheet_protection
524
- str.concat "<mergeCells count='%s'>%s</mergeCells>" % [@merged_cells.size, @merged_cells.reduce('') { |memo, obj| memo += "<mergeCell ref='%s'></mergeCell>" % obj } ] unless @merged_cells.empty?
525
- print_options.to_xml_string(str) if @print_options
543
+ str << protected_ranges_node
544
+ str << merged_cells_node
545
+ @print_options.to_xml_string(str) if @print_options
526
546
  page_margins.to_xml_string(str) if @page_margins
527
547
  page_setup.to_xml_string(str) if @page_setup
528
- str << "<drawing r:id='rId" << (rels.index{ |r| r.Type == DRAWING_R } + 1).to_s << "'/>" if @drawing
529
- str << "<legacyDrawing r:id='rId" << (rels.index{ |r| r.Type == VML_DRAWING_R } + 1).to_s << "'/>" if @comments.size > 0
530
- unless @tables.empty?
531
- str.concat "<tableParts count='%s'>%s</tableParts>" % [@tables.size, @tables.reduce('') { |memo, obj| memo += "<tablePart r:id='%s'/>" % obj.rId }]
532
- end
533
- @conditional_formattings.each do |cf|
534
- str.concat cf.to_xml_string
535
- end
536
-
537
- unless @data_validations.empty?
538
- str.concat "<dataValidations count=\"#{@data_validations.size}\">"
539
- @data_validations.each do |df|
540
- str.concat df.to_xml_string
541
- end
542
- str.concat '</dataValidations>'
543
- end
544
- str + '</worksheet>'
548
+ str << drawing_node
549
+ str << legacy_drawing_node
550
+ str << table_parts_node
551
+ str << conditional_formattings_node
552
+ str << data_validations_node
553
+ str << '</worksheet>'
554
+ # User reported that when parsing some old data that had control characters excel chokes.
555
+ # All of the following are defined as illegal xml characters in the xml spec, but for now I am only dealing with control
556
+ # characters. Thanks to asakusarb and @hsbt's flash of code on the screen!
557
+ # [#x1-#x8], [#xB-#xC], [#xE-#x1F], [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF],
558
+ # [#x1FFFE-#x1FFFF], [#x2FFFE-#x2FFFF], [#x3FFFE-#x3FFFF],
559
+ # [#x4FFFE-#x4FFFF], [#x5FFFE-#x5FFFF], [#x6FFFE-#x6FFFF],
560
+ # [#x7FFFE-#x7FFFF], [#x8FFFE-#x8FFFF], [#x9FFFE-#x9FFFF],
561
+ # [#xAFFFE-#xAFFFF], [#xBFFFE-#xBFFFF], [#xCFFFE-#xCFFFF],
562
+ # [#xDFFFE-#xDFFFF], [#xEFFFE-#xEFFFF], [#xFFFFE-#xFFFFF],
563
+ # [#x10FFFE-#x10FFFF].
564
+ str.gsub(/[[:cntrl:]]/,'')
545
565
  end
546
566
 
547
567
  # The worksheet relationships. This is managed automatically by the worksheet
@@ -584,6 +604,110 @@ module Axlsx
584
604
 
585
605
  private
586
606
 
607
+ # Helper method for parsingout the root node for worksheet
608
+ # @return [String]
609
+ def worksheet_node
610
+ "<worksheet xmlns=\"%s\" xmlns:r=\"%s\">" % [XML_NS, XML_NS_R]
611
+ end
612
+
613
+ # Helper method fo parsing out the sheetPr node
614
+ # @return [String]
615
+ def sheet_pr_node
616
+ return '' unless fit_to_page?
617
+ "<sheetPr><pageSetUpPr fitToPage=\"%s\"></pageSetUpPr></sheetPr>" % fit_to_page?
618
+ end
619
+
620
+ # Helper method for parsing out the demension node
621
+ # @return [String]
622
+ def dimension_node
623
+ return '' if rows.size == 0
624
+ "<dimension ref=\"%s\"></dimension>" % dimension
625
+ end
626
+
627
+ # Helper method for parsing out the sheetData node
628
+ # @return [String]
629
+ def sheet_data_node
630
+ str = '<sheetData>'
631
+ @rows.each_with_index { |row, index| row.to_xml_string(index, str) }
632
+ str << '</sheetData>'
633
+ end
634
+
635
+ # Helper method for parsing out the autoFilter node
636
+ # @return [String]
637
+ def auto_filter_node
638
+ return '' unless @auto_filter
639
+ "<autoFilter ref='%s'></autoFilter>" % @auto_filter
640
+ end
641
+
642
+ # Helper method for parsing out the cols node
643
+ # @return [String]
644
+ def cols_node
645
+ return '' if @column_info.empty?
646
+ str = "<cols>"
647
+ @column_info.each { |col| col.to_xml_string(str) }
648
+ str << '</cols>'
649
+ end
650
+
651
+ # Helper method for parsing out the protectedRanges node
652
+ # @return [String]
653
+ def protected_ranges_node
654
+ return '' if @protected_ranges.empty?
655
+ str = '<protectedRanges>'
656
+ @protected_ranges.each { |pr| pr.to_xml_string(str) }
657
+ str << '</protectedRanges>'
658
+ end
659
+
660
+ # Helper method for parsing out the mergedCells node
661
+ # @return [String]
662
+ def merged_cells_node
663
+ return '' if @merged_cells.size == 0
664
+ str = "<mergeCells count='#{@merged_cells.size}'>"
665
+ @merged_cells.each { |merged_cell| str << "<mergeCell ref='#{merged_cell}'></mergeCell>" }
666
+ str << '</mergeCells>'
667
+ end
668
+
669
+ # Helper method for parsing out the drawing node
670
+ # @return [String]
671
+ def drawing_node
672
+ return '' unless @drawing
673
+ "<drawing r:id='rId" << (relationships.index{ |r| r.Type == DRAWING_R } + 1).to_s << "'/>"
674
+ end
675
+
676
+ # Helper method for parsing out the legacyDrawing node required for comments
677
+ # @return [String]
678
+ def legacy_drawing_node
679
+ return '' if @comments.empty?
680
+ "<legacyDrawing r:id='rId" << (relationships.index{ |r| r.Type == VML_DRAWING_R } + 1).to_s << "'/>"
681
+ end
682
+
683
+ # Helper method for parsing out the tableParts node
684
+ # @return [String]
685
+ def table_parts_node
686
+ return '' if @tables.empty?
687
+ str = "<tableParts count='#{@tables.size}'>"
688
+ @tables.each { |table| str << "<tablePart r:id='#{table.rId}'/>" }
689
+ str << '</tableParts>'
690
+ end
691
+
692
+ # Helper method for parsing out the conditional formattings
693
+ # @return [String]
694
+ def conditional_formattings_node
695
+ return '' if @conditional_formattings.size == 0
696
+ str = ''
697
+ @conditional_formattings.each { |conditional_formatting| str << conditional_formatting.to_xml_string }
698
+ str
699
+ end
700
+
701
+ # Helper method for parsing out the dataValidations node
702
+ # @return [String]
703
+ def data_validations_node
704
+ return '' if @data_validations.size == 0
705
+ str = "<dataValidations count='#{@data_validations.size}'>"
706
+ @data_validations.each { |data_validation| str << data_validation.to_xml_string }
707
+ str << '</dataValidations>'
708
+ end
709
+
710
+
587
711
  # assigns the owner workbook for this worksheet
588
712
  def workbook=(v) DataTypeValidator.validate "Worksheet.workbook", Workbook, v; @workbook = v; end
589
713