axlsx 2.0.1 → 3.0.0.pre

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 (180) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/README.md +23 -23
  4. data/Rakefile +9 -11
  5. data/examples/auto_filter.rb +10 -1
  6. data/examples/conditional_formatting/example_conditional_formatting.rb +18 -3
  7. data/examples/example.rb +102 -4
  8. data/examples/merge_cells.rb +17 -0
  9. data/examples/no_grid_with_borders.rb +18 -0
  10. data/examples/pivot_test.rb +63 -0
  11. data/examples/split.rb +16 -0
  12. data/lib/axlsx/content_type/abstract_content_type.rb +1 -1
  13. data/lib/axlsx/content_type/content_type.rb +1 -1
  14. data/lib/axlsx/doc_props/app.rb +1 -1
  15. data/lib/axlsx/doc_props/core.rb +5 -5
  16. data/lib/axlsx/drawing/area_chart.rb +99 -0
  17. data/lib/axlsx/drawing/area_series.rb +110 -0
  18. data/lib/axlsx/drawing/axes.rb +1 -1
  19. data/lib/axlsx/drawing/axis.rb +12 -9
  20. data/lib/axlsx/drawing/bar_3D_chart.rb +13 -13
  21. data/lib/axlsx/drawing/bar_chart.rb +143 -0
  22. data/lib/axlsx/drawing/bar_series.rb +9 -9
  23. data/lib/axlsx/drawing/bubble_chart.rb +59 -0
  24. data/lib/axlsx/drawing/bubble_series.rb +63 -0
  25. data/lib/axlsx/drawing/cat_axis.rb +5 -5
  26. data/lib/axlsx/drawing/chart.rb +52 -8
  27. data/lib/axlsx/drawing/d_lbls.rb +3 -3
  28. data/lib/axlsx/drawing/drawing.rb +6 -1
  29. data/lib/axlsx/drawing/graphic_frame.rb +3 -3
  30. data/lib/axlsx/drawing/hyperlink.rb +1 -3
  31. data/lib/axlsx/drawing/line_3D_chart.rb +2 -2
  32. data/lib/axlsx/drawing/line_chart.rb +10 -10
  33. data/lib/axlsx/drawing/line_series.rb +32 -3
  34. data/lib/axlsx/drawing/marker.rb +1 -1
  35. data/lib/axlsx/drawing/num_data.rb +4 -4
  36. data/lib/axlsx/drawing/num_data_source.rb +6 -6
  37. data/lib/axlsx/drawing/num_val.rb +3 -1
  38. data/lib/axlsx/drawing/one_cell_anchor.rb +3 -2
  39. data/lib/axlsx/drawing/pic.rb +25 -19
  40. data/lib/axlsx/drawing/picture_locking.rb +1 -3
  41. data/lib/axlsx/drawing/pie_3D_chart.rb +5 -6
  42. data/lib/axlsx/drawing/pie_series.rb +6 -6
  43. data/lib/axlsx/drawing/scaling.rb +6 -6
  44. data/lib/axlsx/drawing/scatter_chart.rb +10 -10
  45. data/lib/axlsx/drawing/scatter_series.rb +40 -7
  46. data/lib/axlsx/drawing/ser_axis.rb +2 -2
  47. data/lib/axlsx/drawing/series.rb +3 -3
  48. data/lib/axlsx/drawing/series_title.rb +2 -2
  49. data/lib/axlsx/drawing/str_data.rb +3 -3
  50. data/lib/axlsx/drawing/str_val.rb +3 -1
  51. data/lib/axlsx/drawing/title.rb +22 -4
  52. data/lib/axlsx/drawing/two_cell_anchor.rb +6 -1
  53. data/lib/axlsx/drawing/val_axis.rb +1 -1
  54. data/lib/axlsx/drawing/view_3D.rb +2 -2
  55. data/lib/axlsx/drawing/vml_drawing.rb +1 -1
  56. data/lib/axlsx/package.rb +34 -32
  57. data/lib/axlsx/rels/relationship.rb +1 -1
  58. data/lib/axlsx/rels/relationships.rb +7 -4
  59. data/lib/axlsx/stylesheet/border_pr.rb +2 -2
  60. data/lib/axlsx/stylesheet/cell_alignment.rb +1 -3
  61. data/lib/axlsx/stylesheet/cell_protection.rb +1 -3
  62. data/lib/axlsx/stylesheet/cell_style.rb +1 -3
  63. data/lib/axlsx/stylesheet/color.rb +1 -3
  64. data/lib/axlsx/stylesheet/font.rb +1 -1
  65. data/lib/axlsx/stylesheet/gradient_stop.rb +1 -1
  66. data/lib/axlsx/stylesheet/num_fmt.rb +10 -3
  67. data/lib/axlsx/stylesheet/pattern_fill.rb +1 -1
  68. data/lib/axlsx/stylesheet/styles.rb +7 -7
  69. data/lib/axlsx/stylesheet/table_style_element.rb +1 -3
  70. data/lib/axlsx/util/accessors.rb +6 -6
  71. data/lib/axlsx/util/constants.rb +107 -99
  72. data/lib/axlsx/util/mime_type_utils.rb +11 -0
  73. data/lib/axlsx/util/options_parser.rb +2 -1
  74. data/lib/axlsx/util/parser.rb +4 -4
  75. data/lib/axlsx/util/serialized_attributes.rb +16 -6
  76. data/lib/axlsx/util/simple_typed_list.rb +28 -52
  77. data/lib/axlsx/util/storage.rb +4 -4
  78. data/lib/axlsx/util/validators.rb +29 -17
  79. data/lib/axlsx/version.rb +1 -1
  80. data/lib/axlsx/workbook/defined_name.rb +11 -12
  81. data/lib/axlsx/workbook/defined_names.rb +2 -2
  82. data/lib/axlsx/workbook/shared_strings_table.rb +5 -5
  83. data/lib/axlsx/workbook/workbook.rb +36 -11
  84. data/lib/axlsx/workbook/workbook_view.rb +80 -0
  85. data/lib/axlsx/workbook/workbook_views.rb +22 -0
  86. data/lib/axlsx/workbook/worksheet/auto_filter/auto_filter.rb +2 -2
  87. data/lib/axlsx/workbook/worksheet/auto_filter/filters.rb +1 -3
  88. data/lib/axlsx/workbook/worksheet/break.rb +1 -3
  89. data/lib/axlsx/workbook/worksheet/cell.rb +136 -74
  90. data/lib/axlsx/workbook/worksheet/cell_serializer.rb +63 -43
  91. data/lib/axlsx/workbook/worksheet/cfvo.rb +1 -3
  92. data/lib/axlsx/workbook/worksheet/cfvos.rb +4 -1
  93. data/lib/axlsx/workbook/worksheet/col.rb +7 -10
  94. data/lib/axlsx/workbook/worksheet/col_breaks.rb +2 -2
  95. data/lib/axlsx/workbook/worksheet/cols.rb +5 -2
  96. data/lib/axlsx/workbook/worksheet/comment.rb +5 -6
  97. data/lib/axlsx/workbook/worksheet/comments.rb +9 -12
  98. data/lib/axlsx/workbook/worksheet/conditional_formatting.rb +1 -1
  99. data/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb +1 -1
  100. data/lib/axlsx/workbook/worksheet/data_bar.rb +4 -6
  101. data/lib/axlsx/workbook/worksheet/data_validation.rb +6 -4
  102. data/lib/axlsx/workbook/worksheet/dimension.rb +2 -2
  103. data/lib/axlsx/workbook/worksheet/header_footer.rb +6 -8
  104. data/lib/axlsx/workbook/worksheet/icon_set.rb +3 -5
  105. data/lib/axlsx/workbook/worksheet/merged_cells.rb +4 -2
  106. data/lib/axlsx/workbook/worksheet/outline_pr.rb +33 -0
  107. data/lib/axlsx/workbook/worksheet/page_margins.rb +1 -3
  108. data/lib/axlsx/workbook/worksheet/page_set_up_pr.rb +1 -1
  109. data/lib/axlsx/workbook/worksheet/page_setup.rb +21 -23
  110. data/lib/axlsx/workbook/worksheet/pane.rb +1 -3
  111. data/lib/axlsx/workbook/worksheet/pivot_table.rb +44 -28
  112. data/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb +4 -4
  113. data/lib/axlsx/workbook/worksheet/print_options.rb +1 -3
  114. data/lib/axlsx/workbook/worksheet/protected_range.rb +1 -3
  115. data/lib/axlsx/workbook/worksheet/protected_ranges.rb +5 -2
  116. data/lib/axlsx/workbook/worksheet/rich_text.rb +55 -0
  117. data/lib/axlsx/workbook/worksheet/rich_text_run.rb +250 -0
  118. data/lib/axlsx/workbook/worksheet/row.rb +40 -51
  119. data/lib/axlsx/workbook/worksheet/row_breaks.rb +2 -2
  120. data/lib/axlsx/workbook/worksheet/selection.rb +1 -3
  121. data/lib/axlsx/workbook/worksheet/sheet_data.rb +3 -1
  122. data/lib/axlsx/workbook/worksheet/sheet_pr.rb +21 -3
  123. data/lib/axlsx/workbook/worksheet/sheet_protection.rb +1 -3
  124. data/lib/axlsx/workbook/worksheet/table.rb +6 -6
  125. data/lib/axlsx/workbook/worksheet/table_style_info.rb +1 -3
  126. data/lib/axlsx/workbook/worksheet/tables.rb +4 -1
  127. data/lib/axlsx/workbook/worksheet/worksheet.rb +64 -78
  128. data/lib/axlsx/workbook/worksheet/worksheet_drawing.rb +10 -10
  129. data/lib/axlsx/workbook/worksheet/worksheet_hyperlinks.rb +3 -3
  130. data/lib/axlsx.rb +34 -15
  131. data/test/drawing/tc_area_chart.rb +39 -0
  132. data/test/drawing/tc_area_series.rb +71 -0
  133. data/test/drawing/tc_axis.rb +27 -0
  134. data/test/drawing/tc_bar_chart.rb +71 -0
  135. data/test/drawing/tc_bubble_chart.rb +44 -0
  136. data/test/drawing/tc_bubble_series.rb +21 -0
  137. data/test/drawing/tc_chart.rb +23 -10
  138. data/test/drawing/tc_data_source.rb +6 -0
  139. data/test/drawing/tc_drawing.rb +2 -2
  140. data/test/drawing/tc_line_chart.rb +5 -5
  141. data/test/drawing/tc_line_series.rb +47 -6
  142. data/test/drawing/tc_pic.rb +11 -15
  143. data/test/drawing/tc_scatter_series.rb +36 -1
  144. data/test/drawing/tc_str_val.rb +9 -0
  145. data/test/drawing/tc_title.rb +5 -0
  146. data/test/stylesheet/tc_styles.rb +2 -2
  147. data/test/tc_axlsx.rb +31 -0
  148. data/test/tc_helper.rb +2 -0
  149. data/test/tc_package.rb +19 -1
  150. data/test/util/tc_mime_type_utils.rb +13 -0
  151. data/test/util/tc_simple_typed_list.rb +2 -3
  152. data/test/util/tc_validators.rb +34 -10
  153. data/test/workbook/tc_defined_name.rb +12 -4
  154. data/test/workbook/tc_shared_strings_table.rb +16 -1
  155. data/test/workbook/tc_workbook.rb +38 -3
  156. data/test/workbook/tc_workbook_view.rb +50 -0
  157. data/test/workbook/worksheet/auto_filter/tc_filters.rb +1 -1
  158. data/test/workbook/worksheet/tc_break.rb +1 -1
  159. data/test/workbook/worksheet/tc_cell.rb +76 -8
  160. data/test/workbook/worksheet/tc_col.rb +2 -2
  161. data/test/workbook/worksheet/tc_conditional_formatting.rb +2 -2
  162. data/test/workbook/worksheet/tc_data_bar.rb +1 -1
  163. data/test/workbook/worksheet/tc_data_validation.rb +11 -11
  164. data/test/workbook/worksheet/tc_header_footer.rb +2 -2
  165. data/test/workbook/worksheet/tc_icon_set.rb +1 -1
  166. data/test/workbook/worksheet/tc_outline_pr.rb +19 -0
  167. data/test/workbook/worksheet/tc_page_setup.rb +3 -3
  168. data/test/workbook/worksheet/tc_pivot_table.rb +21 -6
  169. data/test/workbook/worksheet/tc_print_options.rb +1 -1
  170. data/test/workbook/worksheet/tc_rich_text.rb +44 -0
  171. data/test/workbook/worksheet/tc_rich_text_run.rb +172 -0
  172. data/test/workbook/worksheet/tc_row.rb +7 -2
  173. data/test/workbook/worksheet/tc_sheet_calc_pr.rb +1 -1
  174. data/test/workbook/worksheet/tc_sheet_format_pr.rb +4 -4
  175. data/test/workbook/worksheet/tc_sheet_pr.rb +26 -4
  176. data/test/workbook/worksheet/tc_sheet_protection.rb +5 -5
  177. data/test/workbook/worksheet/tc_sheet_view.rb +4 -4
  178. data/test/workbook/worksheet/tc_table.rb +2 -3
  179. data/test/workbook/worksheet/tc_worksheet.rb +99 -45
  180. metadata +142 -64
@@ -28,9 +28,7 @@ module Axlsx
28
28
 
29
29
  # serializes the break to xml
30
30
  def to_xml_string(str='')
31
- str << '<brk '
32
- serialized_attributes str
33
- str << '></brk>'
31
+ serialized_tag('brk', str)
34
32
  end
35
33
  end
36
34
  end
@@ -30,17 +30,24 @@ module Axlsx
30
30
  # @option options [String] color an 8 letter rgb specification
31
31
  # @option options [Number] formula_value The value to cache for a formula cell.
32
32
  # @option options [Symbol] scheme must be one of :none, major, :minor
33
- def initialize(row, value="", options={})
34
- self.row=row
35
- @value = nil
36
- #@value = @font_name = @charset = @family = @b = @i = @strike = @outline = @shadow = nil
37
- #@formula_value = @condense = @u = @vertAlign = @sz = @color = @scheme = @extend = @ssti = nil
38
- @styles = row.worksheet.workbook.styles
39
- @row.cells << self
40
- parse_options options
41
- @style ||= 0
42
- @type ||= cell_type_from_value(value)
43
- @value = cast_value(value)
33
+ def initialize(row, value = nil, options = {})
34
+ @row = row
35
+ # Do not use instance vars if not needed to use less RAM
36
+ # And do not call parse_options on frequently used options
37
+ # to get less GC cycles
38
+ type = options.delete(:type) || cell_type_from_value(value)
39
+ self.type = type unless type == :string
40
+
41
+
42
+ val = options.delete(:style)
43
+ self.style = val unless val.nil? || val == 0
44
+ val = options.delete(:formula_value)
45
+ self.formula_value = val unless val.nil?
46
+
47
+ parse_options(options)
48
+
49
+ self.value = value
50
+ value.cell = self if contains_rich_text?
44
51
  end
45
52
 
46
53
  # this is the cached value for formula cells. If you want the values to render in iOS/Mac OSX preview
@@ -53,21 +60,27 @@ module Axlsx
53
60
  # needs to define bla=(v) and bla methods on the class that hook into a
54
61
  # set_attr method that kicks the suplied validator and updates the instance_variable
55
62
  # for the key
56
- INLINE_STYLES = ['value', 'type', 'font_name', 'charset',
57
- 'family', 'b', 'i', 'strike','outline',
58
- 'shadow', 'condense', 'extend', 'u',
59
- 'vertAlign', 'sz', 'color', 'scheme']
63
+ INLINE_STYLES = [:value, :type, :font_name, :charset,
64
+ :family, :b, :i, :strike, :outline,
65
+ :shadow, :condense, :extend, :u,
66
+ :vertAlign, :sz, :color, :scheme].freeze
67
+
68
+ # An array of valid cell types
69
+ CELL_TYPES = [:date, :time, :float, :integer, :richtext,
70
+ :string, :boolean, :iso_8601, :text].freeze
60
71
 
61
72
  # The index of the cellXfs item to be applied to this cell.
62
73
  # @return [Integer]
63
74
  # @see Axlsx::Styles
64
- attr_reader :style
75
+ def style
76
+ defined?(@style) ? @style : 0
77
+ end
65
78
 
66
79
  # The row this cell belongs to.
67
80
  # @return [Row]
68
81
  attr_reader :row
69
82
 
70
- # The cell's data type. Currently only six types are supported, :date, :time, :float, :integer, :string and :boolean.
83
+ # The cell's data type.
71
84
  # Changing the type for a cell will recast the value into that type. If no type option is specified in the constructor, the type is
72
85
  # automatically determed.
73
86
  # @see Cell#cell_type_from_value
@@ -78,18 +91,21 @@ module Axlsx
78
91
  # :string to :integer or :float, type conversions always return 0 or 0.0
79
92
  # :string, :integer, or :float to :time conversions always return the original value as a string and set the cells type to :string.
80
93
  # No support is currently implemented for parsing time strings.
81
- attr_reader :type
94
+ def type
95
+ defined?(@type) ? @type : :string
96
+ end
97
+
82
98
  # @see type
83
99
  def type=(v)
84
- RestrictionValidator.validate "Cell.type", [:date, :time, :float, :integer, :string, :boolean, :iso_8601], v
85
- @type=v
86
- self.value = @value unless @value.nil?
100
+ RestrictionValidator.validate :cell_type, CELL_TYPES, v
101
+ @type = v
102
+ self.value = @value unless !defined?(@value) || @value.nil?
87
103
  end
88
104
 
89
-
90
105
  # The value of this cell.
91
106
  # @return [String, Integer, Float, Time, Boolean] casted value based on cell's type attribute.
92
107
  attr_reader :value
108
+
93
109
  # @see value
94
110
  def value=(v)
95
111
  #TODO: consider doing value based type determination first?
@@ -99,16 +115,20 @@ module Axlsx
99
115
  # Indicates that the cell has one or more of the custom cell styles applied.
100
116
  # @return [Boolean]
101
117
  def is_text_run?
102
- @is_text_run ||= false
118
+ defined?(@is_text_run) && @is_text_run && !contains_rich_text?
119
+ end
120
+
121
+ def contains_rich_text?
122
+ type == :richtext
103
123
  end
104
124
 
105
125
  # Indicates if the cell is good for shared string table
106
126
  def plain_string?
107
- @type == :string && # String typed
127
+ (type == :string || type == :text) && # String typed
108
128
  !is_text_run? && # No inline styles
109
129
  !@value.nil? && # Not nil
110
130
  !@value.empty? && # Not empty
111
- !@value.start_with?('=') # Not a formula
131
+ !@value.start_with?(?=) # Not a formula
112
132
  end
113
133
 
114
134
  # The inline font_name property for the cell
@@ -231,7 +251,7 @@ module Axlsx
231
251
  attr_reader :vertAlign
232
252
  # @see vertAlign
233
253
  def vertAlign=(v)
234
- RestrictionValidator.validate "Cell.vertAlign", [:baseline, :subscript, :superscript], v
254
+ RestrictionValidator.validate :cell_vertAlign, [:baseline, :subscript, :superscript], v
235
255
  set_run_style nil, :vertAlign, v
236
256
  end
237
257
 
@@ -241,7 +261,7 @@ module Axlsx
241
261
  attr_reader :scheme
242
262
  # @see scheme
243
263
  def scheme=(v)
244
- RestrictionValidator.validate "Cell.schema", [:none, :major, :minor], v
264
+ RestrictionValidator.validate :cell_scheme, [:none, :major, :minor], v
245
265
  set_run_style nil, :scheme, v
246
266
  end
247
267
 
@@ -251,14 +271,14 @@ module Axlsx
251
271
 
252
272
  # @return [Integer] The index of the cell in the containing row.
253
273
  def index
254
- @row.cells.index(self)
274
+ @row.index(self)
255
275
  end
256
276
 
257
277
  # @return [String] The alpha(column)numeric(row) reference for this sell.
258
278
  # @example Relative Cell Reference
259
279
  # ws.rows.first.cells.first.r #=> "A1"
260
280
  def r
261
- Axlsx::cell_r index, @row.index
281
+ Axlsx::cell_r index, @row.row_index
262
282
  end
263
283
 
264
284
  # @return [String] The absolute alpha(column)numeric(row) reference for this sell.
@@ -272,26 +292,26 @@ module Axlsx
272
292
  # @raise [ArgumentError] Invalid cellXfs id if the value provided is not within cellXfs items range.
273
293
  def style=(v)
274
294
  Axlsx::validate_unsigned_int(v)
275
- count = @styles.cellXfs.size
295
+ count = styles.cellXfs.size
276
296
  raise ArgumentError, "Invalid cellXfs id" unless v < count
277
297
  @style = v
278
298
  end
279
299
 
280
- # @return [Array] of x/y coordinates in the cheet for this cell.
300
+ # @return [Array] of x/y coordinates in the sheet for this cell.
281
301
  def pos
282
- [index, row.index]
302
+ [index, row.row_index]
283
303
  end
284
304
 
285
305
  # Merges all the cells in a range created between this cell and the cell or string name for a cell provided
286
306
  # @see worksheet.merge_cells
287
307
  # @param [Cell, String] target The last cell, or str ref for the cell in the merge range
288
308
  def merge(target)
289
- range_end = if target.is_a?(String)
290
- target
291
- elsif(target.is_a?(Cell))
292
- target.r
293
- end
294
- self.row.worksheet.merge_cells "#{self.r}:#{range_end}" unless range_end.nil?
309
+ start, stop = if target.is_a?(String)
310
+ [self.r, target]
311
+ elsif(target.is_a?(Cell))
312
+ Axlsx.sort_cells([self, target]).map { |c| c.r }
313
+ end
314
+ self.row.worksheet.merge_cells "#{start}:#{stop}" unless stop.nil?
295
315
  end
296
316
 
297
317
  # Serializes the cell
@@ -304,17 +324,11 @@ module Axlsx
304
324
  end
305
325
 
306
326
  def is_formula?
307
- @type == :string && @value.to_s.start_with?('=')
327
+ type == :string && @value.to_s.start_with?(?=)
308
328
  end
309
329
 
310
- # This is still not perfect...
311
- # - scaling is not linear as font sizes increst
312
- # - different fonts have different mdw and char widths
313
- def autowidth
314
- return if is_formula? || value == nil
315
- mdw = 1.78 #This is the widest width of 0..9 in arial@10px)
316
- font_scale = (font_size/10.0).to_f
317
- ((value.to_s.count(Worksheet.thin_chars) * mdw + 5) / mdw * 256) / 256.0 * font_scale
330
+ def is_array_formula?
331
+ type == :string && @value.to_s.start_with?('{=') && @value.to_s.end_with?('}')
318
332
  end
319
333
 
320
334
  # returns the absolute or relative string style reference for
@@ -326,21 +340,71 @@ module Axlsx
326
340
  absolute ? r_abs : r
327
341
  end
328
342
 
343
+ # Creates a defined name in the workbook for this cell.
344
+ def name=(label)
345
+ row.worksheet.workbook.add_defined_name "#{row.worksheet.name}!#{r_abs}", name: label
346
+ @name = label
347
+ end
348
+
349
+ # returns the name of the cell
350
+ attr_reader :name
351
+
352
+ # Attempts to determine the correct width for this cell's content
353
+ # @return [Float]
354
+ def autowidth
355
+ return if is_formula? || value.nil?
356
+ if contains_rich_text?
357
+ string_width('', font_size) + value.autowidth
358
+ elsif styles.cellXfs[style].alignment && styles.cellXfs[style].alignment.wrap_text
359
+ max_width = 0
360
+ value.to_s.split(/\r?\n/).each do |line|
361
+ width = string_width(line, font_size)
362
+ max_width = width if width > max_width
363
+ end
364
+ max_width
365
+ else
366
+ string_width(value, font_size)
367
+ end
368
+ end
369
+
370
+ # Returns the sanatized value
371
+ # TODO find a better way to do this as it accounts for 30% of
372
+ # processing time in benchmarking...
373
+ def clean_value
374
+ if (type == :string || type == :text) && !Axlsx::trust_input
375
+ Axlsx::sanitize(::CGI.escapeHTML(@value.to_s))
376
+ else
377
+ @value.to_s
378
+ end
379
+ end
380
+
329
381
  private
330
382
 
383
+ def styles
384
+ row.worksheet.styles
385
+ end
386
+
387
+ # Returns the width of a string according to the current style
388
+ # This is still not perfect...
389
+ # - scaling is not linear as font sizes increase
390
+ def string_width(string, font_size)
391
+ font_scale = font_size / 10.0
392
+ (string.to_s.count(Worksheet::THIN_CHARS) + 3.0) * font_scale
393
+ end
394
+
331
395
  # we scale the font size if bold style is applied to either the style font or
332
396
  # the cell itself. Yes, it is a bit of a hack, but it is much better than using
333
397
  # imagemagick and loading metrics for every character.
334
398
  def font_size
335
- font = @styles.fonts[@styles.cellXfs[style].fontId] || @styles.fonts[0]
336
- size_from_styles = (font.b || b) ? font.sz * 1.5 : font.sz
337
- sz || size_from_styles
399
+ return sz if sz
400
+ font = styles.fonts[styles.cellXfs[style].fontId] || styles.fonts[0]
401
+ (font.b || (defined?(@b) && @b)) ? (font.sz * 1.5) : font.sz
338
402
  end
339
403
 
340
404
  # 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
405
+ def set_run_style(validator, attr, value)
406
+ return unless INLINE_STYLES.include?(attr.to_sym)
407
+ Axlsx.send(validator, value) unless validator.nil?
344
408
  self.instance_variable_set :"@#{attr.to_s}", value
345
409
  @is_text_run = true
346
410
  end
@@ -351,9 +415,6 @@ module Axlsx
351
415
  @ssti = v
352
416
  end
353
417
 
354
- # assigns the owning row for this cell.
355
- def row=(v) @row=v end
356
-
357
418
  # Determines the cell type based on the cell value.
358
419
  # @note This is only used when a cell is created but no :type option is specified, the following rules apply:
359
420
  # 1. If the value is an instance of Date, the type is set to :date
@@ -369,15 +430,14 @@ module Axlsx
369
430
  :time
370
431
  elsif v.is_a?(TrueClass) || v.is_a?(FalseClass)
371
432
  :boolean
372
- elsif v.to_s =~ /\A[+-]?\d+?\Z/ #numeric
433
+ elsif v.to_s =~ Axlsx::NUMERIC_REGEX
373
434
  :integer
374
- elsif v.to_s =~ /\A[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\Z/ #float
435
+ elsif v.to_s =~ Axlsx::FLOAT_REGEX
375
436
  :float
376
- # \A(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[0-1]|0[1-9]|[1-2][0-9])
377
- # T(2[0-3]|[0-1][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?
378
- # (Z|[+-](?:2[0-3]|[0-1][0-9]):[0-5][0-9])?\Z
379
- elsif v.to_s =~/\A(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[0-1]|0[1-9]|[1-2][0-9])T(2[0-3]|[0-1][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[0-1][0-9]):[0-5][0-9])?\Z/
437
+ elsif v.to_s =~ Axlsx::ISO_8601_REGEX
380
438
  :iso_8601
439
+ elsif v.is_a? RichText
440
+ :richtext
381
441
  else
382
442
  :string
383
443
  end
@@ -388,27 +448,29 @@ module Axlsx
388
448
  # 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.
389
449
  # @see Axlsx#date1904
390
450
  def cast_value(v)
391
- return nil if v.nil?
392
- if @type == :date
451
+ return v if v.is_a?(RichText) || v.nil?
452
+ case type
453
+ when :date
393
454
  self.style = STYLE_DATE if self.style == 0
394
455
  v
395
- elsif (@type == :time && v.is_a?(Time)) || (@type == :time && v.respond_to?(:to_time))
456
+ when :time
396
457
  self.style = STYLE_DATE if self.style == 0
397
- v.respond_to?(:to_time) ? v.to_time : v
398
- elsif @type == :float
458
+ if !v.is_a?(Time) && v.respond_to?(:to_time)
459
+ v.to_time
460
+ else
461
+ v
462
+ end
463
+ when :float
399
464
  v.to_f
400
- elsif @type == :integer
465
+ when :integer
401
466
  v.to_i
402
- elsif @type == :boolean
467
+ when :boolean
403
468
  v ? 1 : 0
404
- elsif @type == :iso_8601
469
+ when :iso_8601
405
470
  #consumer is responsible for ensuring the iso_8601 format when specifying this type
406
471
  v
407
472
  else
408
- @type = :string
409
- # TODO find a better way to do this as it accounts for 30% of
410
- # processing time in benchmarking...
411
- Axlsx::trust_input ? v.to_s : ::CGI.escapeHTML(v.to_s)
473
+ v.to_s
412
474
  end
413
475
  end
414
476
 
@@ -3,43 +3,32 @@ module Axlsx
3
3
  # The Cell Serializer class contains the logic for serializing cells based on their type.
4
4
  class CellSerializer
5
5
  class << self
6
-
7
-
8
6
  # Calls the proper serialization method based on type.
9
7
  # @param [Integer] row_index The index of the cell's row
10
8
  # @param [Integer] column_index The index of the cell's column
11
9
  # @param [String] str The string to apend serialization to.
12
10
  # @return [String]
13
11
  def to_xml_string(row_index, column_index, cell, str='')
14
- str << '<c r="' << Axlsx::cell_r(column_index, row_index) << '" s="' << cell.style.to_s << '" '
12
+ str << ('<c r="' << Axlsx::cell_r(column_index, row_index) << '" s="' << cell.style.to_s << '" ')
15
13
  return str << '/>' if cell.value.nil?
16
- method = (cell.type.to_s << '_type_serialization').to_sym
14
+ method = cell.type
17
15
  self.send(method, cell, str)
18
16
  str << '</c>'
19
- end
20
-
17
+ end
21
18
 
22
19
  # builds an xml text run based on this cells attributes.
23
20
  # @param [String] str The string instance this run will be concated to.
24
21
  # @return [String]
25
22
  def run_xml_string(cell, str = '')
26
23
  if cell.is_text_run?
27
- data = cell.instance_values.reject{|key, value| value == nil || key == 'value' || key == 'type' }
28
- keys = data.keys & Cell::INLINE_STYLES
29
- str << "<r><rPr>"
30
- keys.each do |key|
31
- case key
32
- when 'font_name'
33
- str << "<rFont val='"<< cell.font_name << "'/>"
34
- when 'color'
35
- str << data[key].to_xml_string
36
- else
37
- str << "<" << key.to_s << " val='" << data[key].to_s << "'/>"
38
- end
39
- end
40
- str << "</rPr>" << "<t>" << cell.value.to_s << "</t></r>"
24
+ valid = RichTextRun::INLINE_STYLES - [:value, :type]
25
+ data = Hash[cell.instance_values.map{ |k, v| [k.to_sym, v] }]
26
+ data = data.select { |key, value| valid.include?(key) && !value.nil? }
27
+ RichText.new(cell.value.to_s, data).to_xml_string(str)
28
+ elsif cell.contains_rich_text?
29
+ cell.value.to_xml_string(str)
41
30
  else
42
- str << "<t>" << cell.value.to_s << "</t>"
31
+ str << ('<t>' << cell.clean_value << '</t>')
43
32
  end
44
33
  str
45
34
  end
@@ -48,16 +37,15 @@ module Axlsx
48
37
  # @param [Cell] cell The cell that is being serialized
49
38
  # @param [String] str The string the serialized content will be appended to.
50
39
  # @return [String]
51
- def iso_8601_type_serialization(cell, str='')
40
+ def iso_8601(cell, str='')
52
41
  value_serialization 'd', cell.value, str
53
42
  end
54
43
 
55
-
56
44
  # serializes cells that are type date
57
45
  # @param [Cell] cell The cell that is being serialized
58
46
  # @param [String] str The string the serialized content will be appended to.
59
47
  # @return [String]
60
- def date_type_serialization(cell, str='')
48
+ def date(cell, str='')
61
49
  value_serialization false, DateTimeConverter::date_to_serial(cell.value).to_s, str
62
50
  end
63
51
 
@@ -65,7 +53,7 @@ module Axlsx
65
53
  # @param [Cell] cell The cell that is being serialized
66
54
  # @param [String] str The string the serialized content will be appended to.
67
55
  # @return [String]
68
- def time_type_serialization(cell, str='')
56
+ def time(cell, str='')
69
57
  value_serialization false, DateTimeConverter::time_to_serial(cell.value).to_s, str
70
58
  end
71
59
 
@@ -73,7 +61,7 @@ module Axlsx
73
61
  # @param [Cell] cell The cell that is being serialized
74
62
  # @param [String] str The string the serialized content will be appended to.
75
63
  # @return [String]
76
- def boolean_type_serialization(cell, str='')
64
+ def boolean(cell, str='')
77
65
  value_serialization 'b', cell.value.to_s, str
78
66
  end
79
67
 
@@ -81,26 +69,34 @@ module Axlsx
81
69
  # @param [Cell] cell The cell that is being serialized
82
70
  # @param [String] str The string the serialized content will be appended to.
83
71
  # @return [String]
84
- def float_type_serialization(cell, str='')
85
- numeric_type_serialization cell, str
72
+ def float(cell, str='')
73
+ numeric cell, str
86
74
  end
87
75
 
88
76
  # Serializes cells that are type integer
89
77
  # @param [Cell] cell The cell that is being serialized
90
78
  # @param [String] str The string the serialized content will be appended to.
91
79
  # @return [String]
92
- def integer_type_serialization(cell, str = '')
93
- numeric_type_serialization cell, str
80
+ def integer(cell, str = '')
81
+ numeric cell, str
94
82
  end
95
83
 
96
-
97
84
  # Serializes cells that are type formula
98
85
  # @param [Cell] cell The cell that is being serialized
99
86
  # @param [String] str The string the serialized content will be appended to.
100
87
  # @return [String]
101
88
  def formula_serialization(cell, str='')
102
- str << 't="str">' << '<f>' << cell.value.to_s.sub('=', '') << '</f>'
103
- str << '<v>' << cell.formula_value.to_s << '</v>' unless cell.formula_value.nil?
89
+ str << ('t="str"><f>' << cell.clean_value.to_s.sub('=', '') << '</f>')
90
+ str << ('<v>' << cell.formula_value.to_s << '</v>') unless cell.formula_value.nil?
91
+ end
92
+
93
+ # Serializes cells that are type array formula
94
+ # @param [Cell] cell The cell that is being serialized
95
+ # @param [String] str The string the serialized content will be appended to.
96
+ # @return [String]
97
+ def array_formula_serialization(cell, str='')
98
+ str << ('t="str">' << '<f t="array" ref="' << cell.r << '">' << cell.clean_value.to_s.sub('{=', '').sub(/}$/, '') << '</f>')
99
+ str << ('<v>' << cell.formula_value.to_s << '</v>') unless cell.formula_value.nil?
104
100
  end
105
101
 
106
102
  # Serializes cells that are type inline_string
@@ -108,7 +104,7 @@ module Axlsx
108
104
  # @param [String] str The string the serialized content will be appended to.
109
105
  # @return [String]
110
106
  def inline_string_serialization(cell, str = '')
111
- str << 't="inlineStr">' << '<is>'
107
+ str << 't="inlineStr"><is>'
112
108
  run_xml_string cell, str
113
109
  str << '</is>'
114
110
  end
@@ -117,28 +113,52 @@ module Axlsx
117
113
  # @param [Cell] cell The cell that is being serialized
118
114
  # @param [String] str The string the serialized content will be appended to.
119
115
  # @return [String]
120
- def string_type_serialization(cell, str='')
121
- if cell.is_formula?
116
+ def string(cell, str='')
117
+ if cell.is_array_formula?
118
+ array_formula_serialization cell, str
119
+ elsif cell.is_formula?
122
120
  formula_serialization cell, str
123
121
  elsif !cell.ssti.nil?
124
- value_serialization 's', cell.ssti.to_s, str
122
+ value_serialization 's', cell.ssti, str
125
123
  else
126
124
  inline_string_serialization cell, str
127
125
  end
128
126
  end
129
127
 
128
+ # Serializes cells that are of the type richtext
129
+ # @param [Cell] cell The cell that is being serialized
130
+ # @param [String] str The string the serialized content will be appended to.
131
+ # @return [String]
132
+ def richtext(cell, str)
133
+ if cell.ssti.nil?
134
+ inline_string_serialization cell, str
135
+ else
136
+ value_serialization 's', cell.ssti, str
137
+ end
138
+ end
139
+
140
+ # Serializes cells that are of the type text
141
+ # @param [Cell] cell The cell that is being serialized
142
+ # @param [String] str The string the serialized content will be appended to.
143
+ # @return [String]
144
+ def text(cell, str)
145
+ if cell.ssti.nil?
146
+ inline_string_serialization cell, str
147
+ else
148
+ value_serialization 's', cell.ssti, str
149
+ end
150
+ end
151
+
130
152
  private
131
153
 
132
- def numeric_type_serialization(cell, str = '')
133
- value_serialization 'n', cell.value.to_s, str
154
+ def numeric(cell, str = '')
155
+ value_serialization 'n', cell.value, str
134
156
  end
135
157
 
136
158
  def value_serialization(serialization_type, serialization_value, str = '')
137
- str << 't="' << serialization_type << '"' if serialization_type
138
- str << '><v>' << serialization_value << '</v>'
159
+ str << ('t="' << serialization_type.to_s << '"') if serialization_type
160
+ str << ('><v>' << serialization_value.to_s << '</v>')
139
161
  end
140
-
141
-
142
162
  end
143
163
  end
144
164
  end
@@ -54,9 +54,7 @@ module Axlsx
54
54
  # @param [String] str
55
55
  # @return [String]
56
56
  def to_xml_string(str = '')
57
- str << '<cfvo '
58
- serialized_attributes str
59
- str << ' />'
57
+ serialized_tag('cfvo', str)
60
58
  end
61
59
  end
62
60
  end
@@ -8,8 +8,11 @@ module Axlsx
8
8
  super(Cfvo)
9
9
  end
10
10
 
11
+ # Serialize the Cfvo object
12
+ # @param [String] str
13
+ # @return [String]
11
14
  def to_xml_string(str='')
12
- @list.each { |cfvo| cfvo.to_xml_string(str) }
15
+ each { |cfvo| cfvo.to_xml_string(str) }
13
16
  end
14
17
  end
15
18
  end
@@ -122,22 +122,19 @@ module Axlsx
122
122
  # @param [Boolean] use_autowidth If this is false, the cell's
123
123
  # autowidth value will be ignored.
124
124
  def update_width(cell, fixed_width=nil, use_autowidth=true)
125
- if fixed_width.is_a? Numeric
126
- self.width = fixed_width
127
- elsif use_autowidth
128
- cell_width = cell.autowidth
129
- self.width = cell_width unless (width || 0) > (cell_width || 0)
130
- #self.width = [width || 0, cell.autowidth || 0].max
131
- end
125
+ if fixed_width.is_a? Numeric
126
+ self.width = fixed_width
127
+ elsif use_autowidth
128
+ cell_width = cell.autowidth
129
+ self.width = cell_width unless (width || 0) > (cell_width || 0)
130
+ end
132
131
  end
133
132
 
134
133
  # Serialize this columns data to an xml string
135
134
  # @param [String] str
136
135
  # @return [String]
137
136
  def to_xml_string(str = '')
138
- str << '<col '
139
- serialized_attributes str
140
- str << '/>'
137
+ serialized_tag('col', str)
141
138
  end
142
139
 
143
140
  end
@@ -16,7 +16,7 @@ module Axlsx
16
16
  # Break will be passed to the created break object.
17
17
  # @see Break
18
18
  def add_break(options)
19
- @list << Break.new(options.merge(:max => 1048575, :man => true))
19
+ self << Break.new(options.merge(:max => 1048575, :man => true))
20
20
  last
21
21
  end
22
22
 
@@ -27,7 +27,7 @@ module Axlsx
27
27
  # </colBreaks>
28
28
  def to_xml_string(str='')
29
29
  return if empty?
30
- str << '<colBreaks count="' << @list.size.to_s << '" manualBreakCount="' << @list.size.to_s << '">'
30
+ str << ('<colBreaks count="' << size.to_s << '" manualBreakCount="' << size.to_s << '">')
31
31
  each { |brk| brk.to_xml_string(str) }
32
32
  str << '</colBreaks>'
33
33
  end