axlsx 2.0.1 → 2.1.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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