axlsx 2.0.1 → 2.1.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 (155) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -3
  3. data/Rakefile +9 -10
  4. data/examples/IMAGE1UP.JPEG +0 -0
  5. data/examples/auto_filter.rb +10 -1
  6. data/examples/conditional_formatting/example_conditional_formatting.rb +3 -3
  7. data/examples/example.rb +72 -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.rb +30 -16
  13. data/lib/axlsx/content_type/abstract_content_type.rb +1 -1
  14. data/lib/axlsx/content_type/content_type.rb +1 -1
  15. data/lib/axlsx/doc_props/app.rb +1 -1
  16. data/lib/axlsx/doc_props/core.rb +5 -5
  17. data/lib/axlsx/drawing/axes.rb +1 -1
  18. data/lib/axlsx/drawing/axis.rb +12 -9
  19. data/lib/axlsx/drawing/bar_3D_chart.rb +13 -13
  20. data/lib/axlsx/drawing/bar_series.rb +9 -9
  21. data/lib/axlsx/drawing/bubble_chart.rb +59 -0
  22. data/lib/axlsx/drawing/bubble_series.rb +63 -0
  23. data/lib/axlsx/drawing/cat_axis.rb +5 -5
  24. data/lib/axlsx/drawing/chart.rb +44 -7
  25. data/lib/axlsx/drawing/drawing.rb +3 -1
  26. data/lib/axlsx/drawing/graphic_frame.rb +3 -3
  27. data/lib/axlsx/drawing/hyperlink.rb +1 -3
  28. data/lib/axlsx/drawing/line_3D_chart.rb +2 -2
  29. data/lib/axlsx/drawing/line_chart.rb +10 -10
  30. data/lib/axlsx/drawing/line_series.rb +14 -2
  31. data/lib/axlsx/drawing/marker.rb +1 -1
  32. data/lib/axlsx/drawing/num_data.rb +4 -4
  33. data/lib/axlsx/drawing/num_data_source.rb +6 -6
  34. data/lib/axlsx/drawing/num_val.rb +1 -1
  35. data/lib/axlsx/drawing/one_cell_anchor.rb +1 -1
  36. data/lib/axlsx/drawing/pic.rb +2 -3
  37. data/lib/axlsx/drawing/picture_locking.rb +1 -3
  38. data/lib/axlsx/drawing/pie_3D_chart.rb +5 -6
  39. data/lib/axlsx/drawing/pie_series.rb +6 -6
  40. data/lib/axlsx/drawing/scaling.rb +4 -4
  41. data/lib/axlsx/drawing/scatter_chart.rb +10 -10
  42. data/lib/axlsx/drawing/scatter_series.rb +26 -7
  43. data/lib/axlsx/drawing/ser_axis.rb +2 -2
  44. data/lib/axlsx/drawing/series.rb +3 -3
  45. data/lib/axlsx/drawing/series_title.rb +2 -2
  46. data/lib/axlsx/drawing/str_data.rb +3 -3
  47. data/lib/axlsx/drawing/str_val.rb +1 -1
  48. data/lib/axlsx/drawing/title.rb +3 -3
  49. data/lib/axlsx/drawing/val_axis.rb +1 -1
  50. data/lib/axlsx/drawing/vml_drawing.rb +1 -1
  51. data/lib/axlsx/package.rb +39 -28
  52. data/lib/axlsx/rels/relationship.rb +1 -1
  53. data/lib/axlsx/rels/relationships.rb +2 -2
  54. data/lib/axlsx/stylesheet/border_pr.rb +2 -2
  55. data/lib/axlsx/stylesheet/cell_alignment.rb +1 -3
  56. data/lib/axlsx/stylesheet/cell_protection.rb +1 -3
  57. data/lib/axlsx/stylesheet/cell_style.rb +1 -3
  58. data/lib/axlsx/stylesheet/color.rb +1 -3
  59. data/lib/axlsx/stylesheet/font.rb +1 -1
  60. data/lib/axlsx/stylesheet/gradient_stop.rb +1 -1
  61. data/lib/axlsx/stylesheet/num_fmt.rb +1 -3
  62. data/lib/axlsx/stylesheet/pattern_fill.rb +1 -1
  63. data/lib/axlsx/stylesheet/styles.rb +6 -6
  64. data/lib/axlsx/stylesheet/table_style_element.rb +1 -3
  65. data/lib/axlsx/util/accessors.rb +6 -6
  66. data/lib/axlsx/util/constants.rb +106 -101
  67. data/lib/axlsx/util/options_parser.rb +2 -1
  68. data/lib/axlsx/util/parser.rb +4 -4
  69. data/lib/axlsx/util/serialized_attributes.rb +16 -6
  70. data/lib/axlsx/util/simple_typed_list.rb +28 -52
  71. data/lib/axlsx/util/storage.rb +4 -4
  72. data/lib/axlsx/util/string.rb +7 -0
  73. data/lib/axlsx/util/validators.rb +20 -13
  74. data/lib/axlsx/version.rb +1 -1
  75. data/lib/axlsx/workbook/defined_name.rb +11 -12
  76. data/lib/axlsx/workbook/defined_names.rb +2 -2
  77. data/lib/axlsx/workbook/shared_strings_table.rb +5 -5
  78. data/lib/axlsx/workbook/workbook.rb +19 -12
  79. data/lib/axlsx/workbook/workbook_view.rb +78 -0
  80. data/lib/axlsx/workbook/workbook_views.rb +22 -0
  81. data/lib/axlsx/workbook/worksheet/auto_filter/auto_filter.rb +2 -2
  82. data/lib/axlsx/workbook/worksheet/auto_filter/filters.rb +1 -3
  83. data/lib/axlsx/workbook/worksheet/break.rb +1 -3
  84. data/lib/axlsx/workbook/worksheet/cell.rb +128 -73
  85. data/lib/axlsx/workbook/worksheet/cell_serializer.rb +50 -40
  86. data/lib/axlsx/workbook/worksheet/cfvo.rb +1 -3
  87. data/lib/axlsx/workbook/worksheet/cfvos.rb +1 -1
  88. data/lib/axlsx/workbook/worksheet/col.rb +7 -10
  89. data/lib/axlsx/workbook/worksheet/col_breaks.rb +2 -2
  90. data/lib/axlsx/workbook/worksheet/comment.rb +5 -6
  91. data/lib/axlsx/workbook/worksheet/comments.rb +9 -12
  92. data/lib/axlsx/workbook/worksheet/conditional_formatting.rb +1 -1
  93. data/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb +1 -1
  94. data/lib/axlsx/workbook/worksheet/data_bar.rb +4 -6
  95. data/lib/axlsx/workbook/worksheet/data_validation.rb +6 -4
  96. data/lib/axlsx/workbook/worksheet/dimension.rb +2 -2
  97. data/lib/axlsx/workbook/worksheet/header_footer.rb +6 -8
  98. data/lib/axlsx/workbook/worksheet/icon_set.rb +3 -5
  99. data/lib/axlsx/workbook/worksheet/merged_cells.rb +2 -2
  100. data/lib/axlsx/workbook/worksheet/page_margins.rb +1 -3
  101. data/lib/axlsx/workbook/worksheet/page_set_up_pr.rb +1 -1
  102. data/lib/axlsx/workbook/worksheet/page_setup.rb +21 -23
  103. data/lib/axlsx/workbook/worksheet/pane.rb +1 -3
  104. data/lib/axlsx/workbook/worksheet/pivot_table.rb +17 -24
  105. data/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb +4 -4
  106. data/lib/axlsx/workbook/worksheet/print_options.rb +1 -3
  107. data/lib/axlsx/workbook/worksheet/protected_range.rb +1 -3
  108. data/lib/axlsx/workbook/worksheet/protected_ranges.rb +1 -1
  109. data/lib/axlsx/workbook/worksheet/rich_text.rb +35 -0
  110. data/lib/axlsx/workbook/worksheet/rich_text_run.rb +254 -0
  111. data/lib/axlsx/workbook/worksheet/row.rb +33 -51
  112. data/lib/axlsx/workbook/worksheet/row_breaks.rb +2 -2
  113. data/lib/axlsx/workbook/worksheet/selection.rb +1 -3
  114. data/lib/axlsx/workbook/worksheet/sheet_data.rb +3 -1
  115. data/lib/axlsx/workbook/worksheet/sheet_protection.rb +1 -3
  116. data/lib/axlsx/workbook/worksheet/table.rb +6 -6
  117. data/lib/axlsx/workbook/worksheet/table_style_info.rb +1 -3
  118. data/lib/axlsx/workbook/worksheet/tables.rb +1 -1
  119. data/lib/axlsx/workbook/worksheet/worksheet.rb +59 -30
  120. data/lib/axlsx/workbook/worksheet/worksheet_hyperlinks.rb +3 -3
  121. data/test/drawing/tc_axis.rb +27 -0
  122. data/test/drawing/tc_bubble_chart.rb +44 -0
  123. data/test/drawing/tc_bubble_series.rb +21 -0
  124. data/test/drawing/tc_data_source.rb +6 -0
  125. data/test/drawing/tc_line_chart.rb +5 -5
  126. data/test/drawing/tc_line_series.rb +10 -2
  127. data/test/drawing/tc_pic.rb +4 -0
  128. data/test/drawing/tc_scatter_series.rb +25 -1
  129. data/test/tc_helper.rb +1 -1
  130. data/test/tc_package.rb +7 -1
  131. data/test/util/tc_simple_typed_list.rb +1 -2
  132. data/test/workbook/tc_defined_name.rb +12 -4
  133. data/test/workbook/tc_workbook.rb +16 -2
  134. data/test/workbook/tc_workbook_view.rb +50 -0
  135. data/test/workbook/worksheet/auto_filter/tc_filters.rb +1 -1
  136. data/test/workbook/worksheet/tc_break.rb +1 -1
  137. data/test/workbook/worksheet/tc_cell.rb +30 -4
  138. data/test/workbook/worksheet/tc_col.rb +2 -2
  139. data/test/workbook/worksheet/tc_conditional_formatting.rb +2 -2
  140. data/test/workbook/worksheet/tc_data_bar.rb +1 -1
  141. data/test/workbook/worksheet/tc_data_validation.rb +11 -11
  142. data/test/workbook/worksheet/tc_header_footer.rb +2 -2
  143. data/test/workbook/worksheet/tc_icon_set.rb +1 -1
  144. data/test/workbook/worksheet/tc_page_setup.rb +3 -3
  145. data/test/workbook/worksheet/tc_print_options.rb +1 -1
  146. data/test/workbook/worksheet/tc_rich_text.rb +44 -0
  147. data/test/workbook/worksheet/tc_rich_text_run.rb +172 -0
  148. data/test/workbook/worksheet/tc_row.rb +2 -2
  149. data/test/workbook/worksheet/tc_sheet_calc_pr.rb +1 -1
  150. data/test/workbook/worksheet/tc_sheet_format_pr.rb +4 -4
  151. data/test/workbook/worksheet/tc_sheet_protection.rb +5 -5
  152. data/test/workbook/worksheet/tc_sheet_view.rb +4 -4
  153. data/test/workbook/worksheet/tc_worksheet.rb +49 -10
  154. metadata +81 -55
  155. data/test/axlsx.qcachegrind +0 -2226
@@ -11,8 +11,8 @@ module Axlsx
11
11
  # @param [String] str
12
12
  # @return [String]
13
13
  def to_xml_string(str = '')
14
- return if @list.empty?
15
- str << "<definedNames>"
14
+ return if empty?
15
+ str << '<definedNames>'
16
16
  each { |defined_name| defined_name.to_xml_string(str) }
17
17
  str << '</definedNames>'
18
18
  end
@@ -38,7 +38,7 @@ module Axlsx
38
38
  @xml_space = xml_space
39
39
  @unique_cells = {}
40
40
  @shared_xml_string = ""
41
- shareable_cells = cells.flatten.select{ |cell| cell.plain_string? }
41
+ shareable_cells = cells.flatten.select{ |cell| cell.plain_string? || cell.contains_rich_text? }
42
42
  @count = shareable_cells.size
43
43
  resolve(shareable_cells)
44
44
  end
@@ -47,10 +47,10 @@ module Axlsx
47
47
  # @param [String] str
48
48
  # @return [String]
49
49
  def to_xml_string(str='')
50
- str << '<?xml version="1.0" encoding="UTF-8"?><sst xmlns="' << XML_NS << '"'
51
- str << ' count="' << @count.to_s << '" uniqueCount="' << unique_count.to_s << '"'
52
- str << ' xml:space="' << xml_space.to_s << '">' << @shared_xml_string << '</sst>'
53
- str = Axlsx::sanitize(str)
50
+ Axlsx::sanitize(@shared_xml_string)
51
+ str << ('<?xml version="1.0" encoding="UTF-8"?><sst xmlns="' << XML_NS << '"')
52
+ str << (' count="' << @count.to_s << '" uniqueCount="' << unique_count.to_s << '"')
53
+ str << (' xml:space="' << xml_space.to_s << '">' << @shared_xml_string << '</sst>')
54
54
  end
55
55
 
56
56
  private
@@ -5,6 +5,8 @@ require 'axlsx/workbook/worksheet/auto_filter/auto_filter.rb'
5
5
  require 'axlsx/workbook/worksheet/date_time_converter.rb'
6
6
  require 'axlsx/workbook/worksheet/protected_range.rb'
7
7
  require 'axlsx/workbook/worksheet/protected_ranges.rb'
8
+ require 'axlsx/workbook/worksheet/rich_text_run'
9
+ require 'axlsx/workbook/worksheet/rich_text'
8
10
  require 'axlsx/workbook/worksheet/cell_serializer.rb'
9
11
  require 'axlsx/workbook/worksheet/cell.rb'
10
12
  require 'axlsx/workbook/worksheet/page_margins.rb'
@@ -37,7 +39,8 @@ require 'axlsx/workbook/worksheet/worksheet_hyperlinks'
37
39
  require 'axlsx/workbook/worksheet/break'
38
40
  require 'axlsx/workbook/worksheet/row_breaks'
39
41
  require 'axlsx/workbook/worksheet/col_breaks'
40
-
42
+ require 'axlsx/workbook/workbook_view'
43
+ require 'axlsx/workbook/workbook_views'
41
44
 
42
45
 
43
46
  require 'axlsx/workbook/worksheet/worksheet.rb'
@@ -139,6 +142,10 @@ require 'axlsx/workbook/worksheet/selection.rb'
139
142
  # @return [SimpleTypedList]
140
143
  attr_reader :pivot_tables
141
144
 
145
+ # A collection of views for this workbook
146
+ def views
147
+ @views ||= WorkbookViews.new
148
+ end
142
149
 
143
150
  # A collection of defined names for this workbook
144
151
  # @note The recommended way to manage defined names is Workbook#add_defined_name
@@ -263,6 +270,10 @@ require 'axlsx/workbook/worksheet/selection.rb'
263
270
  worksheet
264
271
  end
265
272
 
273
+ def add_view(options={})
274
+ views << WorkbookView.new(options)
275
+ end
276
+
266
277
  # Adds a defined name to this workbook
267
278
  # @return [DefinedName]
268
279
  # @param [String] formula @see DefinedName
@@ -283,7 +294,7 @@ require 'axlsx/workbook/worksheet/selection.rb'
283
294
  end
284
295
  r << Relationship.new(self, STYLES_R, STYLES_PN)
285
296
  if use_shared_strings
286
- r << Relationship.new(self, SHARED_STRINGS_R, SHARED_STRINGS_PN)
297
+ r << Relationship.new(self, SHARED_STRINGS_R, SHARED_STRINGS_PN)
287
298
  end
288
299
  r
289
300
  end
@@ -327,23 +338,19 @@ require 'axlsx/workbook/worksheet/selection.rb'
327
338
  # @param [String] str
328
339
  # @return [String]
329
340
  def to_xml_string(str='')
330
- add_worksheet unless worksheets.size > 0
341
+ add_worksheet(name: 'Sheet1') unless worksheets.size > 0
331
342
  str << '<?xml version="1.0" encoding="UTF-8"?>'
332
- str << '<workbook xmlns="' << XML_NS << '" xmlns:r="' << XML_NS_R << '">'
333
- str << '<workbookPr date1904="' << @@date1904.to_s << '"/>'
343
+ str << ('<workbook xmlns="' << XML_NS << '" xmlns:r="' << XML_NS_R << '">')
344
+ str << ('<workbookPr date1904="' << @@date1904.to_s << '"/>')
345
+ views.to_xml_string(str)
334
346
  str << '<sheets>'
335
- @worksheets.each_with_index do |sheet, index|
336
- str << '<sheet name="' << sheet.name << '" sheetId="' << (index+1).to_s << '" r:id="' << sheet.rId << '"/>'
337
- if defined_name = sheet.auto_filter.defined_name
338
- add_defined_name defined_name, :name => '_xlnm._FilterDatabase', :local_sheet_id => index, :hidden => 1
339
- end
340
- end
347
+ worksheets.each { |sheet| sheet.to_sheet_node_xml_string(str) }
341
348
  str << '</sheets>'
342
349
  defined_names.to_xml_string(str)
343
350
  unless pivot_tables.empty?
344
351
  str << '<pivotCaches>'
345
352
  pivot_tables.each do |pivot_table|
346
- str << '<pivotCache cacheId="' << pivot_table.cache_definition.cache_id.to_s << '" r:id="' << pivot_table.cache_definition.rId << '"/>'
353
+ str << ('<pivotCache cacheId="' << pivot_table.cache_definition.cache_id.to_s << '" r:id="' << pivot_table.cache_definition.rId << '"/>')
347
354
  end
348
355
  str << '</pivotCaches>'
349
356
  end
@@ -0,0 +1,78 @@
1
+ # <xsd:complexType name="CT_BookView">
2
+ # <xsd:sequence>
3
+ # <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
4
+ # </xsd:sequence>
5
+ # <xsd:attribute name="visibility" type="ST_Visibility" use="optional" default="visible"/>
6
+ # <xsd:attribute name="minimized" type="xsd:boolean" use="optional" default="false"/>
7
+ # <xsd:attribute name="showHorizontalScroll" type="xsd:boolean" use="optional" default="true"/>
8
+ # <xsd:attribute name="showVerticalScroll" type="xsd:boolean" use="optional" default="true"/>
9
+ # <xsd:attribute name="showSheetTabs" type="xsd:boolean" use="optional" default="true"/>
10
+ # <xsd:attribute name="xWindow" type="xsd:int" use="optional"/>
11
+ # <xsd:attribute name="yWindow" type="xsd:int" use="optional"/>
12
+ # <xsd:attribute name="windowWidth" type="xsd:unsignedInt" use="optional"/>
13
+ # <xsd:attribute name="windowHeight" type="xsd:unsignedInt" use="optional"/>
14
+ # <xsd:attribute name="tabRatio" type="xsd:unsignedInt" use="optional" default="600"/>
15
+ # <xsd:attribute name="firstSheet" type="xsd:unsignedInt" use="optional" default="0"/>
16
+ # <xsd:attribute name="activeTab" type="xsd:unsignedInt" use="optional" default="0"/>
17
+ # <xsd:attribute name="autoFilterDateGrouping" type="xsd:boolean" use="optional"
18
+ # default="true"/>
19
+ # </xsd:complexType>
20
+
21
+ module Axlsx
22
+
23
+ # A BookView defines the display properties for a workbook.
24
+ # Units for window widths and other dimensions are expressed in twips.
25
+ # Twip measurements are portable between different display resolutions.
26
+ # The formula is (screen pixels) * (20 * 72) / (logical device dpi),
27
+ # where the logical device dpi can be different for x and y coordinates.
28
+ class WorkbookView
29
+
30
+ include Axlsx::SerializedAttributes
31
+ include Axlsx::OptionsParser
32
+ include Axlsx::Accessors
33
+
34
+
35
+ # Creates a new BookView object
36
+ # @params [Hash] options A hash of key/value pairs that will be mapped to this instances attributes.
37
+ # @option [Symbol] visibility Specifies visible state of the workbook window. The default value for this attribute is :visible.
38
+ # @option [Boolean] minimized Specifies a boolean value that indicates whether the workbook window is minimized.
39
+ # @option [Boolean] show_horizontal_scroll Specifies a boolean value that indicates whether to display the horizontal scroll bar in the user interface.
40
+ # @option [Boolean] show_vertical_scroll Specifies a boolean value that indicates whether to display the vertical scroll bar.
41
+ # @option [Boolean] show_sheet_tabs Specifies a boolean value that indicates whether to display the sheet tabs in the user interface.
42
+ # @option [Integer] tab_ratio Specifies ratio between the workbook tabs bar and the horizontal scroll bar.
43
+ # @option [Integer] first_sheet Specifies the index to the first sheet in this book view.
44
+ # @option [Integer] active_tab Specifies an unsignedInt that contains the index to the active sheet in this book view.
45
+ # @option [Integer] x_window Specifies the X coordinate for the upper left corner of the workbook window. The unit of measurement for this value is twips.
46
+ # @option [Integer] y_window Specifies the Y coordinate for the upper left corner of the workbook window. The unit of measurement for this value is twips.
47
+ # @option [Integer] window_width Specifies the width of the workbook window. The unit of measurement for this value is twips.
48
+ # @option [Integer] window_height Specifies the height of the workbook window. The unit of measurement for this value is twips.
49
+ # @option [Boolean] auto_filter_date_grouping Specifies a boolean value that indicates whether to group dates when presenting the user with filtering options in the user interface.
50
+ def initialize(options={})
51
+ parse_options options
52
+ yield self if block_given?
53
+ end
54
+
55
+
56
+ unsigned_int_attr_accessor :x_window, :y_window, :window_width, :window_height,
57
+ :tab_ratio, :first_sheet, :active_tab
58
+
59
+ validated_attr_accessor [:visibility], :validate_view_visibility
60
+
61
+ serializable_attributes :visibility, :minimized,
62
+ :show_horizontal_scroll, :show_vertical_scroll,
63
+ :show_sheet_tabs, :tab_ratio, :first_sheet, :active_tab,
64
+ :x_window, :y_window, :window_width, :window_height,
65
+ :auto_filter_date_grouping
66
+
67
+ boolean_attr_accessor :minimized, :show_horizontal_scroll, :show_vertical_scroll,
68
+ :show_sheet_tabs, :auto_filter_date_grouping
69
+
70
+
71
+
72
+ def to_xml_string(str = '')
73
+ str << '<workbookView '
74
+ serialized_attributes str
75
+ str << '></workbookView>'
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,22 @@
1
+ module Axlsx
2
+ # a simple types list of BookView objects
3
+ class WorkbookViews < SimpleTypedList
4
+
5
+ # creates the book views object
6
+ def initialize
7
+ super WorkbookView
8
+ end
9
+
10
+ # Serialize to xml
11
+ # @param [String] str
12
+ # @return [String]
13
+ def to_xml_string(str = '')
14
+ return if empty?
15
+ str << "<bookViews>"
16
+ each { |view| view.to_xml_string(str) }
17
+ str << '</bookViews>'
18
+ end
19
+ end
20
+ end
21
+
22
+
@@ -47,8 +47,8 @@ module Axlsx
47
47
  columns.last
48
48
  end
49
49
 
50
- # actually performs the filtering of rows who's cells do not
51
- # match the filter.
50
+ # actually performs the filtering of rows who's cells do not
51
+ # match the filter.
52
52
  def apply
53
53
  first_cell, last_cell = range.split(':')
54
54
  start_point = Axlsx::name_to_indices(first_cell)
@@ -237,9 +237,7 @@ include Axlsx::SerializedAttributes
237
237
  # Serialize the object to xml
238
238
  # @param [String] str The string object this serialization will be concatenated to.
239
239
  def to_xml_string(str = '')
240
- str << '<dateGroupItem '
241
- serialized_attributes str
242
- str << '/>'
240
+ serialized_tag('dateGroupItem', str)
243
241
  end
244
242
  end
245
243
  end
@@ -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,15 +60,20 @@ 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
+ CELL_TYPES = [:date, :time, :float, :integer, :richtext,
69
+ :string, :boolean, :iso_8601].freeze
60
70
 
61
71
  # The index of the cellXfs item to be applied to this cell.
62
72
  # @return [Integer]
63
73
  # @see Axlsx::Styles
64
- attr_reader :style
74
+ def style
75
+ defined?(@style) ? @style : 0
76
+ end
65
77
 
66
78
  # The row this cell belongs to.
67
79
  # @return [Row]
@@ -78,18 +90,21 @@ module Axlsx
78
90
  # :string to :integer or :float, type conversions always return 0 or 0.0
79
91
  # :string, :integer, or :float to :time conversions always return the original value as a string and set the cells type to :string.
80
92
  # No support is currently implemented for parsing time strings.
81
- attr_reader :type
93
+ def type
94
+ defined?(@type) ? @type : :string
95
+ end
96
+
82
97
  # @see type
83
98
  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?
99
+ RestrictionValidator.validate :cell_type, CELL_TYPES, v
100
+ @type = v
101
+ self.value = @value unless !defined?(@value) || @value.nil?
87
102
  end
88
103
 
89
-
90
104
  # The value of this cell.
91
105
  # @return [String, Integer, Float, Time, Boolean] casted value based on cell's type attribute.
92
106
  attr_reader :value
107
+
93
108
  # @see value
94
109
  def value=(v)
95
110
  #TODO: consider doing value based type determination first?
@@ -99,16 +114,20 @@ module Axlsx
99
114
  # Indicates that the cell has one or more of the custom cell styles applied.
100
115
  # @return [Boolean]
101
116
  def is_text_run?
102
- @is_text_run ||= false
117
+ defined?(@is_text_run) && @is_text_run && !contains_rich_text?
118
+ end
119
+
120
+ def contains_rich_text?
121
+ type == :richtext
103
122
  end
104
-
123
+
105
124
  # Indicates if the cell is good for shared string table
106
125
  def plain_string?
107
- @type == :string && # String typed
126
+ type == :string && # String typed
108
127
  !is_text_run? && # No inline styles
109
128
  !@value.nil? && # Not nil
110
129
  !@value.empty? && # Not empty
111
- !@value.start_with?('=') # Not a formula
130
+ !@value.start_with?(?=) # Not a formula
112
131
  end
113
132
 
114
133
  # The inline font_name property for the cell
@@ -231,7 +250,7 @@ module Axlsx
231
250
  attr_reader :vertAlign
232
251
  # @see vertAlign
233
252
  def vertAlign=(v)
234
- RestrictionValidator.validate "Cell.vertAlign", [:baseline, :subscript, :superscript], v
253
+ RestrictionValidator.validate :cell_vertAlign, [:baseline, :subscript, :superscript], v
235
254
  set_run_style nil, :vertAlign, v
236
255
  end
237
256
 
@@ -241,7 +260,7 @@ module Axlsx
241
260
  attr_reader :scheme
242
261
  # @see scheme
243
262
  def scheme=(v)
244
- RestrictionValidator.validate "Cell.schema", [:none, :major, :minor], v
263
+ RestrictionValidator.validate :cell_scheme, [:none, :major, :minor], v
245
264
  set_run_style nil, :scheme, v
246
265
  end
247
266
 
@@ -251,14 +270,14 @@ module Axlsx
251
270
 
252
271
  # @return [Integer] The index of the cell in the containing row.
253
272
  def index
254
- @row.cells.index(self)
273
+ @row.index(self)
255
274
  end
256
275
 
257
276
  # @return [String] The alpha(column)numeric(row) reference for this sell.
258
277
  # @example Relative Cell Reference
259
278
  # ws.rows.first.cells.first.r #=> "A1"
260
279
  def r
261
- Axlsx::cell_r index, @row.index
280
+ Axlsx::cell_r index, @row.row_index
262
281
  end
263
282
 
264
283
  # @return [String] The absolute alpha(column)numeric(row) reference for this sell.
@@ -272,26 +291,26 @@ module Axlsx
272
291
  # @raise [ArgumentError] Invalid cellXfs id if the value provided is not within cellXfs items range.
273
292
  def style=(v)
274
293
  Axlsx::validate_unsigned_int(v)
275
- count = @styles.cellXfs.size
294
+ count = styles.cellXfs.size
276
295
  raise ArgumentError, "Invalid cellXfs id" unless v < count
277
296
  @style = v
278
297
  end
279
298
 
280
- # @return [Array] of x/y coordinates in the cheet for this cell.
299
+ # @return [Array] of x/y coordinates in the sheet for this cell.
281
300
  def pos
282
- [index, row.index]
301
+ [index, row.row_index]
283
302
  end
284
303
 
285
304
  # Merges all the cells in a range created between this cell and the cell or string name for a cell provided
286
305
  # @see worksheet.merge_cells
287
306
  # @param [Cell, String] target The last cell, or str ref for the cell in the merge range
288
307
  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?
308
+ start, stop = if target.is_a?(String)
309
+ [self.r, target]
310
+ elsif(target.is_a?(Cell))
311
+ Axlsx.sort_cells([self, target]).map { |c| c.r }
312
+ end
313
+ self.row.worksheet.merge_cells "#{start}:#{stop}" unless stop.nil?
295
314
  end
296
315
 
297
316
  # Serializes the cell
@@ -304,17 +323,11 @@ module Axlsx
304
323
  end
305
324
 
306
325
  def is_formula?
307
- @type == :string && @value.to_s.start_with?('=')
326
+ type == :string && @value.to_s.start_with?(?=)
308
327
  end
309
328
 
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
329
+ def is_array_formula?
330
+ type == :string && @value.to_s.start_with?('{=') && @value.to_s.end_with?('}')
318
331
  end
319
332
 
320
333
  # returns the absolute or relative string style reference for
@@ -326,21 +339,69 @@ module Axlsx
326
339
  absolute ? r_abs : r
327
340
  end
328
341
 
342
+ # Creates a defined name in the workbook for this cell.
343
+ def name=(label)
344
+ row.worksheet.workbook.add_defined_name "#{row.worksheet.name}!#{r_abs}", name: label
345
+ @name = label
346
+ end
347
+
348
+ # returns the name of the cell
349
+ attr_reader :name
350
+
351
+ def autowidth
352
+ return if is_formula? || value.nil?
353
+ if contains_rich_text?
354
+ string_width('', font_size) + value.autowidth
355
+ elsif styles.cellXfs[style].alignment && styles.cellXfs[style].alignment.wrap_text
356
+ max_width = 0
357
+ value.to_s.split(/\r?\n/).each do |line|
358
+ width = string_width(line, font_size)
359
+ max_width = width if width > max_width
360
+ end
361
+ max_width
362
+ else
363
+ string_width(value, font_size)
364
+ end
365
+ end
366
+
367
+ # Returns the sanatized value
368
+ # TODO find a better way to do this as it accounts for 30% of
369
+ # processing time in benchmarking...
370
+ def clean_value
371
+ if type == :string && !Axlsx::trust_input
372
+ Axlsx::sanitize(::CGI.escapeHTML(@value.to_s))
373
+ else
374
+ @value.to_s
375
+ end
376
+ end
377
+
329
378
  private
379
+
380
+ def styles
381
+ row.worksheet.styles
382
+ end
383
+
384
+ # Returns the width of a string according to the current style
385
+ # This is still not perfect...
386
+ # - scaling is not linear as font sizes increase
387
+ def string_width(string, font_size)
388
+ font_scale = font_size / 10.0
389
+ (string.to_s.count(Worksheet::THIN_CHARS) + 3.0) * (font_size/10.0)
390
+ end
330
391
 
331
392
  # we scale the font size if bold style is applied to either the style font or
332
393
  # the cell itself. Yes, it is a bit of a hack, but it is much better than using
333
394
  # imagemagick and loading metrics for every character.
334
395
  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
396
+ return sz if sz
397
+ font = styles.fonts[styles.cellXfs[style].fontId] || styles.fonts[0]
398
+ (font.b || (defined?(@b) && @b)) ? (font.sz * 1.5) : font.sz
338
399
  end
339
400
 
340
401
  # 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
402
+ def set_run_style(validator, attr, value)
403
+ return unless INLINE_STYLES.include?(attr.to_sym)
404
+ Axlsx.send(validator, value) unless validator.nil?
344
405
  self.instance_variable_set :"@#{attr.to_s}", value
345
406
  @is_text_run = true
346
407
  end
@@ -351,9 +412,6 @@ module Axlsx
351
412
  @ssti = v
352
413
  end
353
414
 
354
- # assigns the owning row for this cell.
355
- def row=(v) @row=v end
356
-
357
415
  # Determines the cell type based on the cell value.
358
416
  # @note This is only used when a cell is created but no :type option is specified, the following rules apply:
359
417
  # 1. If the value is an instance of Date, the type is set to :date
@@ -369,15 +427,14 @@ module Axlsx
369
427
  :time
370
428
  elsif v.is_a?(TrueClass) || v.is_a?(FalseClass)
371
429
  :boolean
372
- elsif v.to_s =~ /\A[+-]?\d+?\Z/ #numeric
430
+ elsif v.to_s =~ Axlsx::NUMERIC_REGEX
373
431
  :integer
374
- elsif v.to_s =~ /\A[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\Z/ #float
432
+ elsif v.to_s =~ Axlsx::FLOAT_REGEX
375
433
  :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/
434
+ elsif v.to_s =~ Axlsx::ISO_8601_REGEX
380
435
  :iso_8601
436
+ elsif v.is_a? RichText
437
+ :richtext
381
438
  else
382
439
  :string
383
440
  end
@@ -388,27 +445,25 @@ module Axlsx
388
445
  # 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
446
  # @see Axlsx#date1904
390
447
  def cast_value(v)
391
- return nil if v.nil?
392
- if @type == :date
448
+ return v if v.is_a?(RichText) || v.nil?
449
+ case type
450
+ when :date
393
451
  self.style = STYLE_DATE if self.style == 0
394
452
  v
395
- elsif (@type == :time && v.is_a?(Time)) || (@type == :time && v.respond_to?(:to_time))
453
+ when :time
396
454
  self.style = STYLE_DATE if self.style == 0
397
455
  v.respond_to?(:to_time) ? v.to_time : v
398
- elsif @type == :float
456
+ when :float
399
457
  v.to_f
400
- elsif @type == :integer
458
+ when :integer
401
459
  v.to_i
402
- elsif @type == :boolean
460
+ when :boolean
403
461
  v ? 1 : 0
404
- elsif @type == :iso_8601
462
+ when :iso_8601
405
463
  #consumer is responsible for ensuring the iso_8601 format when specifying this type
406
464
  v
407
465
  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)
466
+ v.to_s
412
467
  end
413
468
  end
414
469