caxlsx 3.2.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (301) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +9 -9
  3. data/.yardopts_guide +18 -18
  4. data/CHANGELOG.md +394 -354
  5. data/LICENSE +21 -21
  6. data/README.md +184 -168
  7. data/Rakefile +28 -29
  8. data/examples/generate.rb +15 -15
  9. data/lib/axlsx/content_type/abstract_content_type.rb +29 -32
  10. data/lib/axlsx/content_type/content_type.rb +22 -26
  11. data/lib/axlsx/content_type/default.rb +21 -25
  12. data/lib/axlsx/content_type/override.rb +21 -25
  13. data/lib/axlsx/doc_props/app.rb +230 -235
  14. data/lib/axlsx/doc_props/core.rb +34 -39
  15. data/lib/axlsx/drawing/area_chart.rb +96 -99
  16. data/lib/axlsx/drawing/area_series.rb +107 -110
  17. data/lib/axlsx/drawing/ax_data_source.rb +21 -26
  18. data/lib/axlsx/drawing/axes.rb +60 -61
  19. data/lib/axlsx/drawing/axis.rb +185 -190
  20. data/lib/axlsx/drawing/bar_3D_chart.rb +145 -148
  21. data/lib/axlsx/drawing/bar_chart.rb +135 -138
  22. data/lib/axlsx/drawing/bar_series.rb +91 -97
  23. data/lib/axlsx/drawing/bubble_chart.rb +56 -59
  24. data/lib/axlsx/drawing/bubble_series.rb +60 -63
  25. data/lib/axlsx/drawing/cat_axis.rb +80 -85
  26. data/lib/axlsx/drawing/chart.rb +294 -276
  27. data/lib/axlsx/drawing/d_lbls.rb +92 -90
  28. data/lib/axlsx/drawing/drawing.rb +163 -167
  29. data/lib/axlsx/drawing/graphic_frame.rb +51 -54
  30. data/lib/axlsx/drawing/hyperlink.rb +97 -100
  31. data/lib/axlsx/drawing/line_3D_chart.rb +64 -68
  32. data/lib/axlsx/drawing/line_chart.rb +96 -99
  33. data/lib/axlsx/drawing/line_series.rb +107 -110
  34. data/lib/axlsx/drawing/marker.rb +80 -84
  35. data/lib/axlsx/drawing/num_data.rb +47 -52
  36. data/lib/axlsx/drawing/num_data_source.rb +58 -62
  37. data/lib/axlsx/drawing/num_val.rb +31 -34
  38. data/lib/axlsx/drawing/one_cell_anchor.rb +97 -99
  39. data/lib/axlsx/drawing/pic.rb +244 -211
  40. data/lib/axlsx/drawing/picture_locking.rb +39 -42
  41. data/lib/axlsx/drawing/pie_3D_chart.rb +42 -47
  42. data/lib/axlsx/drawing/pie_series.rb +69 -74
  43. data/lib/axlsx/drawing/scaling.rb +57 -60
  44. data/lib/axlsx/drawing/scatter_chart.rb +71 -74
  45. data/lib/axlsx/drawing/scatter_series.rb +126 -129
  46. data/lib/axlsx/drawing/ser_axis.rb +41 -45
  47. data/lib/axlsx/drawing/series.rb +67 -69
  48. data/lib/axlsx/drawing/series_title.rb +23 -25
  49. data/lib/axlsx/drawing/str_data.rb +37 -42
  50. data/lib/axlsx/drawing/str_val.rb +31 -34
  51. data/lib/axlsx/drawing/title.rb +104 -97
  52. data/lib/axlsx/drawing/two_cell_anchor.rb +95 -97
  53. data/lib/axlsx/drawing/val_axis.rb +34 -37
  54. data/lib/axlsx/drawing/view_3D.rb +115 -115
  55. data/lib/axlsx/drawing/vml_drawing.rb +39 -42
  56. data/lib/axlsx/drawing/vml_shape.rb +63 -66
  57. data/lib/axlsx/package.rb +397 -388
  58. data/lib/axlsx/rels/relationship.rb +127 -130
  59. data/lib/axlsx/rels/relationships.rb +29 -32
  60. data/lib/axlsx/stylesheet/border.rb +70 -73
  61. data/lib/axlsx/stylesheet/border_pr.rb +69 -71
  62. data/lib/axlsx/stylesheet/cell_alignment.rb +124 -132
  63. data/lib/axlsx/stylesheet/cell_protection.rb +38 -41
  64. data/lib/axlsx/stylesheet/cell_style.rb +68 -72
  65. data/lib/axlsx/stylesheet/color.rb +77 -76
  66. data/lib/axlsx/stylesheet/dxf.rb +75 -79
  67. data/lib/axlsx/stylesheet/fill.rb +31 -35
  68. data/lib/axlsx/stylesheet/font.rb +157 -156
  69. data/lib/axlsx/stylesheet/gradient_fill.rb +101 -103
  70. data/lib/axlsx/stylesheet/gradient_stop.rb +36 -37
  71. data/lib/axlsx/stylesheet/num_fmt.rb +83 -86
  72. data/lib/axlsx/stylesheet/pattern_fill.rb +71 -73
  73. data/lib/axlsx/stylesheet/styles.rb +543 -494
  74. data/lib/axlsx/stylesheet/table_style.rb +51 -54
  75. data/lib/axlsx/stylesheet/table_style_element.rb +74 -77
  76. data/lib/axlsx/stylesheet/table_styles.rb +42 -46
  77. data/lib/axlsx/stylesheet/xf.rb +144 -147
  78. data/lib/axlsx/util/accessors.rb +62 -64
  79. data/lib/axlsx/util/constants.rb +414 -410
  80. data/lib/axlsx/util/mime_type_utils.rb +24 -11
  81. data/lib/axlsx/util/options_parser.rb +15 -16
  82. data/lib/axlsx/util/serialized_attributes.rb +88 -89
  83. data/lib/axlsx/util/simple_typed_list.rb +180 -179
  84. data/lib/axlsx/util/storage.rb +142 -146
  85. data/lib/axlsx/util/validators.rb +315 -312
  86. data/lib/axlsx/util/zip_command.rb +71 -73
  87. data/lib/axlsx/version.rb +4 -5
  88. data/lib/axlsx/workbook/defined_name.rb +129 -128
  89. data/lib/axlsx/workbook/defined_names.rb +20 -21
  90. data/lib/axlsx/workbook/shared_strings_table.rb +74 -77
  91. data/lib/axlsx/workbook/workbook.rb +430 -395
  92. data/lib/axlsx/workbook/workbook_view.rb +75 -80
  93. data/lib/axlsx/workbook/workbook_views.rb +20 -22
  94. data/lib/axlsx/workbook/worksheet/auto_filter/auto_filter.rb +78 -77
  95. data/lib/axlsx/workbook/worksheet/auto_filter/filter_column.rb +94 -94
  96. data/lib/axlsx/workbook/worksheet/auto_filter/filters.rb +243 -244
  97. data/lib/axlsx/workbook/worksheet/border_creator.rb +79 -0
  98. data/lib/axlsx/workbook/worksheet/break.rb +32 -35
  99. data/lib/axlsx/workbook/worksheet/cell.rb +552 -506
  100. data/lib/axlsx/workbook/worksheet/cell_serializer.rb +164 -164
  101. data/lib/axlsx/workbook/worksheet/cfvo.rb +60 -60
  102. data/lib/axlsx/workbook/worksheet/cfvos.rb +16 -18
  103. data/lib/axlsx/workbook/worksheet/col.rb +142 -145
  104. data/lib/axlsx/workbook/worksheet/col_breaks.rb +34 -35
  105. data/lib/axlsx/workbook/worksheet/color_scale.rb +108 -110
  106. data/lib/axlsx/workbook/worksheet/cols.rb +23 -23
  107. data/lib/axlsx/workbook/worksheet/comment.rb +90 -91
  108. data/lib/axlsx/workbook/worksheet/comments.rb +78 -82
  109. data/lib/axlsx/workbook/worksheet/conditional_formatting.rb +81 -82
  110. data/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb +216 -220
  111. data/lib/axlsx/workbook/worksheet/conditional_formattings.rb +23 -25
  112. data/lib/axlsx/workbook/worksheet/data_bar.rb +127 -129
  113. data/lib/axlsx/workbook/worksheet/data_validation.rb +266 -246
  114. data/lib/axlsx/workbook/worksheet/data_validations.rb +25 -28
  115. data/lib/axlsx/workbook/worksheet/date_time_converter.rb +28 -30
  116. data/lib/axlsx/workbook/worksheet/dimension.rb +65 -64
  117. data/lib/axlsx/workbook/worksheet/header_footer.rb +51 -52
  118. data/lib/axlsx/workbook/worksheet/icon_set.rb +80 -81
  119. data/lib/axlsx/workbook/worksheet/merged_cells.rb +37 -37
  120. data/lib/axlsx/workbook/worksheet/outline_pr.rb +32 -33
  121. data/lib/axlsx/workbook/worksheet/page_margins.rb +97 -97
  122. data/lib/axlsx/workbook/worksheet/page_set_up_pr.rb +42 -44
  123. data/lib/axlsx/workbook/worksheet/page_setup.rb +237 -240
  124. data/lib/axlsx/workbook/worksheet/pane.rb +138 -139
  125. data/lib/axlsx/workbook/worksheet/pivot_table.rb +332 -296
  126. data/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb +63 -66
  127. data/lib/axlsx/workbook/worksheet/pivot_tables.rb +23 -24
  128. data/lib/axlsx/workbook/worksheet/print_options.rb +38 -39
  129. data/lib/axlsx/workbook/worksheet/protected_range.rb +46 -47
  130. data/lib/axlsx/workbook/worksheet/protected_ranges.rb +37 -37
  131. data/lib/axlsx/workbook/worksheet/rich_text.rb +53 -55
  132. data/lib/axlsx/workbook/worksheet/rich_text_run.rb +266 -250
  133. data/lib/axlsx/workbook/worksheet/row.rb +173 -164
  134. data/lib/axlsx/workbook/worksheet/row_breaks.rb +32 -33
  135. data/lib/axlsx/workbook/worksheet/selection.rb +99 -101
  136. data/lib/axlsx/workbook/worksheet/sheet_calc_pr.rb +28 -29
  137. data/lib/axlsx/workbook/worksheet/sheet_data.rb +25 -27
  138. data/lib/axlsx/workbook/worksheet/sheet_format_pr.rb +18 -18
  139. data/lib/axlsx/workbook/worksheet/sheet_pr.rb +87 -87
  140. data/lib/axlsx/workbook/worksheet/sheet_protection.rb +117 -118
  141. data/lib/axlsx/workbook/worksheet/sheet_view.rb +206 -213
  142. data/lib/axlsx/workbook/worksheet/table.rb +100 -102
  143. data/lib/axlsx/workbook/worksheet/table_style_info.rb +48 -49
  144. data/lib/axlsx/workbook/worksheet/tables.rb +34 -34
  145. data/lib/axlsx/workbook/worksheet/worksheet.rb +857 -786
  146. data/lib/axlsx/workbook/worksheet/worksheet_comments.rb +58 -58
  147. data/lib/axlsx/workbook/worksheet/worksheet_drawing.rb +59 -58
  148. data/lib/axlsx/workbook/worksheet/worksheet_hyperlink.rb +73 -74
  149. data/lib/axlsx/workbook/worksheet/worksheet_hyperlinks.rb +38 -38
  150. data/lib/axlsx.rb +218 -185
  151. data/lib/caxlsx.rb +1 -2
  152. data/lib/schema/dc.xsd +118 -118
  153. data/lib/schema/dcmitype.xsd +51 -51
  154. data/lib/schema/dcterms.xsd +331 -331
  155. data/lib/schema/dml-chartDrawing.xsd +146 -146
  156. data/lib/schema/dml-compatibility.xsd +14 -14
  157. data/lib/schema/dml-lockedCanvas.xsd +11 -11
  158. data/lib/schema/dml-main.xsd +3048 -3048
  159. data/lib/schema/dml-picture.xsd +23 -23
  160. data/lib/schema/dml-spreadsheetDrawing.xsd +185 -185
  161. data/lib/schema/dml-wordprocessingDrawing.xsd +185 -185
  162. data/lib/schema/shared-additionalCharacteristics.xsd +28 -28
  163. data/lib/schema/shared-bibliography.xsd +144 -144
  164. data/lib/schema/shared-commonSimpleTypes.xsd +166 -166
  165. data/lib/schema/shared-customXmlDataProperties.xsd +25 -25
  166. data/lib/schema/shared-customXmlSchemaProperties.xsd +18 -18
  167. data/lib/schema/shared-documentPropertiesCustom.xsd +59 -59
  168. data/lib/schema/shared-documentPropertiesExtended.xsd +56 -56
  169. data/lib/schema/shared-documentPropertiesVariantTypes.xsd +195 -195
  170. data/lib/schema/shared-relationshipReference.xsd +25 -25
  171. data/lib/schema/vml-main.xsd +569 -569
  172. data/lib/schema/vml-officeDrawing.xsd +509 -509
  173. data/lib/schema/vml-presentationDrawing.xsd +12 -12
  174. data/lib/schema/vml-spreadsheetDrawing.xsd +108 -108
  175. data/lib/schema/vml-wordprocessingDrawing.xsd +96 -96
  176. data/lib/schema/xml.xsd +116 -116
  177. metadata +5 -252
  178. data/test/benchmark.rb +0 -72
  179. data/test/content_type/tc_content_type.rb +0 -76
  180. data/test/content_type/tc_default.rb +0 -16
  181. data/test/content_type/tc_override.rb +0 -14
  182. data/test/doc_props/tc_app.rb +0 -43
  183. data/test/doc_props/tc_core.rb +0 -42
  184. data/test/drawing/tc_area_chart.rb +0 -39
  185. data/test/drawing/tc_area_series.rb +0 -71
  186. data/test/drawing/tc_axes.rb +0 -8
  187. data/test/drawing/tc_axis.rb +0 -112
  188. data/test/drawing/tc_bar_3D_chart.rb +0 -86
  189. data/test/drawing/tc_bar_chart.rb +0 -86
  190. data/test/drawing/tc_bar_series.rb +0 -46
  191. data/test/drawing/tc_bubble_chart.rb +0 -44
  192. data/test/drawing/tc_bubble_series.rb +0 -21
  193. data/test/drawing/tc_cat_axis.rb +0 -31
  194. data/test/drawing/tc_cat_axis_data.rb +0 -27
  195. data/test/drawing/tc_chart.rb +0 -123
  196. data/test/drawing/tc_d_lbls.rb +0 -57
  197. data/test/drawing/tc_data_source.rb +0 -23
  198. data/test/drawing/tc_drawing.rb +0 -80
  199. data/test/drawing/tc_graphic_frame.rb +0 -27
  200. data/test/drawing/tc_hyperlink.rb +0 -64
  201. data/test/drawing/tc_line_3d_chart.rb +0 -47
  202. data/test/drawing/tc_line_chart.rb +0 -39
  203. data/test/drawing/tc_line_series.rb +0 -71
  204. data/test/drawing/tc_marker.rb +0 -44
  205. data/test/drawing/tc_named_axis_data.rb +0 -27
  206. data/test/drawing/tc_num_data.rb +0 -31
  207. data/test/drawing/tc_num_val.rb +0 -29
  208. data/test/drawing/tc_one_cell_anchor.rb +0 -66
  209. data/test/drawing/tc_pic.rb +0 -103
  210. data/test/drawing/tc_picture_locking.rb +0 -72
  211. data/test/drawing/tc_pie_3D_chart.rb +0 -28
  212. data/test/drawing/tc_pie_series.rb +0 -33
  213. data/test/drawing/tc_scaling.rb +0 -36
  214. data/test/drawing/tc_scatter_chart.rb +0 -48
  215. data/test/drawing/tc_scatter_series.rb +0 -74
  216. data/test/drawing/tc_ser_axis.rb +0 -31
  217. data/test/drawing/tc_series.rb +0 -23
  218. data/test/drawing/tc_series_title.rb +0 -54
  219. data/test/drawing/tc_str_data.rb +0 -18
  220. data/test/drawing/tc_str_val.rb +0 -30
  221. data/test/drawing/tc_title.rb +0 -70
  222. data/test/drawing/tc_two_cell_anchor.rb +0 -36
  223. data/test/drawing/tc_val_axis.rb +0 -24
  224. data/test/drawing/tc_view_3D.rb +0 -54
  225. data/test/drawing/tc_vml_drawing.rb +0 -25
  226. data/test/drawing/tc_vml_shape.rb +0 -106
  227. data/test/fixtures/image1.gif +0 -0
  228. data/test/fixtures/image1.jpeg +0 -0
  229. data/test/fixtures/image1.jpg +0 -0
  230. data/test/fixtures/image1.png +0 -0
  231. data/test/fixtures/image1_fake.jpg +0 -0
  232. data/test/profile.rb +0 -24
  233. data/test/rels/tc_relationship.rb +0 -52
  234. data/test/rels/tc_relationships.rb +0 -37
  235. data/test/stylesheet/tc_border.rb +0 -37
  236. data/test/stylesheet/tc_border_pr.rb +0 -32
  237. data/test/stylesheet/tc_cell_alignment.rb +0 -81
  238. data/test/stylesheet/tc_cell_protection.rb +0 -29
  239. data/test/stylesheet/tc_cell_style.rb +0 -57
  240. data/test/stylesheet/tc_color.rb +0 -43
  241. data/test/stylesheet/tc_dxf.rb +0 -81
  242. data/test/stylesheet/tc_fill.rb +0 -18
  243. data/test/stylesheet/tc_font.rb +0 -133
  244. data/test/stylesheet/tc_gradient_fill.rb +0 -72
  245. data/test/stylesheet/tc_gradient_stop.rb +0 -31
  246. data/test/stylesheet/tc_num_fmt.rb +0 -30
  247. data/test/stylesheet/tc_pattern_fill.rb +0 -43
  248. data/test/stylesheet/tc_styles.rb +0 -309
  249. data/test/stylesheet/tc_table_style.rb +0 -44
  250. data/test/stylesheet/tc_table_style_element.rb +0 -45
  251. data/test/stylesheet/tc_table_styles.rb +0 -29
  252. data/test/stylesheet/tc_xf.rb +0 -120
  253. data/test/tc_axlsx.rb +0 -109
  254. data/test/tc_helper.rb +0 -10
  255. data/test/tc_package.rb +0 -317
  256. data/test/util/tc_mime_type_utils.rb +0 -13
  257. data/test/util/tc_serialized_attributes.rb +0 -19
  258. data/test/util/tc_simple_typed_list.rb +0 -77
  259. data/test/util/tc_validators.rb +0 -210
  260. data/test/workbook/tc_defined_name.rb +0 -49
  261. data/test/workbook/tc_shared_strings_table.rb +0 -59
  262. data/test/workbook/tc_workbook.rb +0 -165
  263. data/test/workbook/tc_workbook_view.rb +0 -50
  264. data/test/workbook/worksheet/auto_filter/tc_auto_filter.rb +0 -38
  265. data/test/workbook/worksheet/auto_filter/tc_filter_column.rb +0 -76
  266. data/test/workbook/worksheet/auto_filter/tc_filters.rb +0 -50
  267. data/test/workbook/worksheet/tc_break.rb +0 -49
  268. data/test/workbook/worksheet/tc_cell.rb +0 -465
  269. data/test/workbook/worksheet/tc_cfvo.rb +0 -31
  270. data/test/workbook/worksheet/tc_col.rb +0 -93
  271. data/test/workbook/worksheet/tc_color_scale.rb +0 -58
  272. data/test/workbook/worksheet/tc_comment.rb +0 -72
  273. data/test/workbook/worksheet/tc_comments.rb +0 -57
  274. data/test/workbook/worksheet/tc_conditional_formatting.rb +0 -224
  275. data/test/workbook/worksheet/tc_data_bar.rb +0 -46
  276. data/test/workbook/worksheet/tc_data_validation.rb +0 -265
  277. data/test/workbook/worksheet/tc_date_time_converter.rb +0 -124
  278. data/test/workbook/worksheet/tc_header_footer.rb +0 -151
  279. data/test/workbook/worksheet/tc_icon_set.rb +0 -45
  280. data/test/workbook/worksheet/tc_outline_pr.rb +0 -19
  281. data/test/workbook/worksheet/tc_page_margins.rb +0 -97
  282. data/test/workbook/worksheet/tc_page_set_up_pr.rb +0 -15
  283. data/test/workbook/worksheet/tc_page_setup.rb +0 -143
  284. data/test/workbook/worksheet/tc_pane.rb +0 -54
  285. data/test/workbook/worksheet/tc_pivot_table.rb +0 -180
  286. data/test/workbook/worksheet/tc_pivot_table_cache_definition.rb +0 -62
  287. data/test/workbook/worksheet/tc_print_options.rb +0 -72
  288. data/test/workbook/worksheet/tc_protected_range.rb +0 -17
  289. data/test/workbook/worksheet/tc_rich_text.rb +0 -44
  290. data/test/workbook/worksheet/tc_rich_text_run.rb +0 -173
  291. data/test/workbook/worksheet/tc_row.rb +0 -160
  292. data/test/workbook/worksheet/tc_selection.rb +0 -55
  293. data/test/workbook/worksheet/tc_sheet_calc_pr.rb +0 -18
  294. data/test/workbook/worksheet/tc_sheet_format_pr.rb +0 -88
  295. data/test/workbook/worksheet/tc_sheet_pr.rb +0 -49
  296. data/test/workbook/worksheet/tc_sheet_protection.rb +0 -117
  297. data/test/workbook/worksheet/tc_sheet_view.rb +0 -214
  298. data/test/workbook/worksheet/tc_table.rb +0 -77
  299. data/test/workbook/worksheet/tc_table_style_info.rb +0 -53
  300. data/test/workbook/worksheet/tc_worksheet.rb +0 -632
  301. data/test/workbook/worksheet/tc_worksheet_hyperlink.rb +0 -55
@@ -1,786 +1,857 @@
1
- # encoding: UTF-8
2
- module Axlsx
3
-
4
- # The Worksheet class represents a worksheet in the workbook.
5
- class Worksheet
6
- include Axlsx::OptionsParser
7
- include Axlsx::SerializedAttributes
8
-
9
- # Creates a new worksheet.
10
- # @note the recommended way to manage worksheets is Workbook#add_worksheet
11
- # @see Workbook#add_worksheet
12
- # @option options [String] name The name of this worksheet.
13
- # @option options [Hash] page_margins A hash containing page margins for this worksheet. @see PageMargins
14
- # @option options [Hash] print_options A hash containing print options for this worksheet. @see PrintOptions
15
- # @option options [Hash] header_footer A hash containing header/footer options for this worksheet. @see HeaderFooter
16
- # @option options [Boolean] show_gridlines indicates if gridlines should be shown for this sheet.
17
- def initialize(wb, options={})
18
- self.workbook = wb
19
- @sheet_protection = nil
20
- initialize_page_options(options)
21
- parse_options options
22
- @workbook.worksheets << self
23
- @sheet_id = index + 1
24
- yield self if block_given?
25
- end
26
-
27
- serializable_attributes :sheet_id, :state
28
-
29
- # Initalizes page margin, setup and print options
30
- # @param [Hash] options Options passed in from the initializer
31
- def initialize_page_options(options)
32
- @page_margins = PageMargins.new options[:page_margins] if options[:page_margins]
33
- @page_setup = PageSetup.new options[:page_setup] if options[:page_setup]
34
- @print_options = PrintOptions.new options[:print_options] if options[:print_options]
35
- @header_footer = HeaderFooter.new options[:header_footer] if options[:header_footer]
36
- @row_breaks = RowBreaks.new
37
- @col_breaks = ColBreaks.new
38
- end
39
-
40
- # The name of the worksheet
41
- # @return [String]
42
- def name
43
- @name ||= "Sheet" + (index+1).to_s
44
- end
45
-
46
- # Specifies the visible state of this sheet. Allowed states are
47
- # :visible, :hidden or :very_hidden. The default value is :visible.
48
- #
49
- # Worksheets in the :hidden state can be shown using the sheet formatting properties in excel.
50
- # :very_hidden sheets should be inaccessible to end users.
51
- # @param [Symbol] sheet_state The visible state for this sheet.
52
- def state=(sheet_state)
53
- RestrictionValidator.validate :worksheet_state, [:visible, :hidden, :very_hidden], sheet_state
54
- @state = sheet_state
55
- end
56
-
57
- # The visibility of this sheet
58
- def state
59
- @state ||= :visible
60
- end
61
-
62
- # The sheet calculation properties
63
- # @return [SheetCalcPr]
64
- def sheet_calc_pr
65
- @sheet_calc_pr ||= SheetCalcPr.new
66
- end
67
-
68
- # The sheet protection object for this workbook
69
- # @return [SheetProtection]
70
- # @see SheetProtection
71
- def sheet_protection
72
- @sheet_protection ||= SheetProtection.new
73
- yield @sheet_protection if block_given?
74
- @sheet_protection
75
- end
76
-
77
- # The sheet view object for this worksheet
78
- # @return [SheetView]
79
- # @see [SheetView]
80
- def sheet_view
81
- @sheet_view ||= SheetView.new
82
- yield @sheet_view if block_given?
83
- @sheet_view
84
- end
85
-
86
- # The sheet format pr for this worksheet
87
- # @return [SheetFormatPr]
88
- # @see [SheetFormatPr]
89
- def sheet_format_pr
90
- @sheet_format_pr ||= SheetFormatPr.new
91
- yield @sheet_format_pr if block_given?
92
- @sheet_format_pr
93
- end
94
-
95
- # The workbook that owns this worksheet
96
- # @return [Workbook]
97
- attr_reader :workbook
98
-
99
- # The tables in this worksheet
100
- # @return [Array] of Table
101
- def tables
102
- @tables ||= Tables.new self
103
- end
104
-
105
- # The pivot tables in this worksheet
106
- # @return [Array] of Table
107
- def pivot_tables
108
- @pivot_tables ||= PivotTables.new self
109
- end
110
-
111
- # A collection of column breaks added to this worksheet
112
- # @note Please do not use this directly. Instead use
113
- # add_page_break
114
- # @see Worksheet#add_page_break
115
- def col_breaks
116
- @col_breaks ||= ColBreaks.new
117
- end
118
-
119
- # A collection of row breaks added to this worksheet
120
- # @note Please do not use this directly. Instead use
121
- # add_page_break
122
- # @see Worksheet#add_page_break
123
- def row_breaks
124
- @row_breaks ||= RowBreaks.new
125
- end
126
-
127
- # A typed collection of hyperlinks associated with this worksheet
128
- # @return [WorksheetHyperlinks]
129
- def hyperlinks
130
- @hyperlinks ||= WorksheetHyperlinks.new self
131
- end
132
-
133
- # The a shortcut to the worksheet_comments list of comments
134
- # @return [Array|SimpleTypedList]
135
- def comments
136
- worksheet_comments.comments if worksheet_comments.has_comments?
137
- end
138
-
139
- # The rows in this worksheet
140
- # @note The recommended way to manage rows is Worksheet#add_row
141
- # @return [SimpleTypedList]
142
- # @see Worksheet#add_row
143
- def rows
144
- @rows ||= SimpleTypedList.new Row
145
- end
146
-
147
- # returns the sheet data as columns
148
- # If you pass a block, it will be evaluated whenever a row does not have a
149
- # cell at a specific index. The block will be called with the row and column
150
- # index in the missing cell was found.
151
- # @example
152
- # cols { |row_index, column_index| puts "warn - row #{row_index} does not have a cell at #{column_index}" }
153
- def cols(&block)
154
- @rows.transpose(&block)
155
- end
156
-
157
- # A range that excel will apply an auto-filter to "A1:B3"
158
- # This will turn filtering on for the cells in the range.
159
- # The first row is considered the header, while subsequent rows are considered to be data.
160
- # @return String
161
- def auto_filter
162
- @auto_filter ||= AutoFilter.new self
163
- end
164
-
165
- # Indicates if the worksheet will be fit by witdh or height to a specific number of pages.
166
- # To alter the width or height for page fitting, please use page_setup.fit_to_widht or page_setup.fit_to_height.
167
- # If you want the worksheet to fit on more pages (e.g. 2x2), set {PageSetup#fit_to_width} and {PageSetup#fit_to_height} accordingly.
168
- # @return Boolean
169
- # @see #page_setup
170
- def fit_to_page?
171
- return false unless self.instance_values.keys.include?('page_setup')
172
- page_setup.fit_to_page?
173
- end
174
-
175
-
176
- # Column info for the sheet
177
- # @return [SimpleTypedList]
178
- def column_info
179
- @column_info ||= Cols.new self
180
- end
181
-
182
- # Page margins for printing the worksheet.
183
- # @example
184
- # wb = Axlsx::Package.new.workbook
185
- # # using options when creating the worksheet.
186
- # ws = wb.add_worksheet :page_margins => {:left => 1.9, :header => 0.1}
187
- #
188
- # # use the set method of the page_margins object
189
- # ws.page_margins.set(:bottom => 3, :footer => 0.7)
190
- #
191
- # # set page margins in a block
192
- # ws.page_margins do |margins|
193
- # margins.right = 6
194
- # margins.top = 0.2
195
- # end
196
- # @see PageMargins#initialize
197
- # @return [PageMargins]
198
- def page_margins
199
- @page_margins ||= PageMargins.new
200
- yield @page_margins if block_given?
201
- @page_margins
202
- end
203
-
204
- # Page setup settings for printing the worksheet.
205
- # @example
206
- # wb = Axlsx::Package.new.workbook
207
- #
208
- # # using options when creating the worksheet.
209
- # ws = wb.add_worksheet :page_setup => {:fit_to_width => 2, :orientation => :landscape}
210
- #
211
- # # use the set method of the page_setup object
212
- # ws.page_setup.set(:paper_width => "297mm", :paper_height => "210mm")
213
- #
214
- # # setup page in a block
215
- # ws.page_setup do |page|
216
- # page.scale = 80
217
- # page.orientation = :portrait
218
- # end
219
- # @see PageSetup#initialize
220
- # @return [PageSetup]
221
- def page_setup
222
- @page_setup ||= PageSetup.new
223
- yield @page_setup if block_given?
224
- @page_setup
225
- end
226
-
227
- # Options for printing the worksheet.
228
- # @example
229
- # wb = Axlsx::Package.new.workbook
230
- # # using options when creating the worksheet.
231
- # ws = wb.add_worksheet :print_options => {:grid_lines => true, :horizontal_centered => true}
232
- #
233
- # # use the set method of the page_margins object
234
- # ws.print_options.set(:headings => true)
235
- #
236
- # # set page margins in a block
237
- # ws.print_options do |options|
238
- # options.horizontal_centered = true
239
- # options.vertical_centered = true
240
- # end
241
- # @see PrintOptions#initialize
242
- # @return [PrintOptions]
243
- def print_options
244
- @print_options ||= PrintOptions.new
245
- yield @print_options if block_given?
246
- @print_options
247
- end
248
-
249
- # Options for headers and footers.
250
- # @example
251
- # wb = Axlsx::Package.new.workbook
252
- # # would generate something like: "file.xlsx : sheet_name 2 of 7 date with timestamp"
253
- # header = {:different_odd_ => false, :odd_header => "&L&F : &A&C&Pof%N%R%D %T"}
254
- # ws = wb.add_worksheet :header_footer => header
255
- #
256
- # @see HeaderFooter#initialize
257
- # @return [HeaderFooter]
258
- def header_footer
259
- @header_footer ||= HeaderFooter.new
260
- yield @header_footer if block_given?
261
- @header_footer
262
- end
263
-
264
- # convenience method to access all cells in this worksheet
265
- # @return [Array] cells
266
- def cells
267
- rows.flatten
268
- end
269
-
270
- # Creates merge information for this worksheet.
271
- # Cells can be merged by calling the merge_cells method on a worksheet.
272
- # @example This would merge the three cells C1..E1 #
273
- # worksheet.merge_cells "C1:E1"
274
- # # you can also provide an array of cells to be merged
275
- # worksheet.merge_cells worksheet.rows.first.cells[(2..4)]
276
- # #alternatively you can do it from a single cell
277
- # worksheet["C1"].merge worksheet["E1"]
278
- # @param [Array, string] cells
279
- def merge_cells(cells)
280
- merged_cells.add cells
281
- end
282
-
283
- # Adds a new protected cell range to the worksheet. Note that protected ranges are only in effect when sheet protection is enabled.
284
- # @param [String|Array] cells The string reference for the cells to protect or an array of cells.
285
- # @return [ProtectedRange]
286
- # @note When using an array of cells, a contiguous range is created from the minimum top left to the maximum top bottom of the cells provided.
287
- def protect_range(cells)
288
- protected_ranges.add_range(cells)
289
- end
290
-
291
- # The dimensions of a worksheet. This is not actually a required element by the spec,
292
- # but at least a few other document readers expect this for conversion
293
- # @return [Dimension]
294
- def dimension
295
- @dimension ||= Dimension.new self
296
- end
297
-
298
- # The sheet properties for this workbook.
299
- # Currently only pageSetUpPr -> fitToPage is implemented
300
- # @return [SheetPr]
301
- def sheet_pr
302
- @sheet_pr ||= SheetPr.new self
303
- end
304
-
305
- # The name of the worksheet
306
- # The name of a worksheet must be unique in the workbook, and must not exceed 31 characters
307
- # @param [String] name
308
- def name=(name)
309
- validate_sheet_name name
310
- @name=Axlsx::coder.encode(name)
311
- end
312
-
313
- # The auto filter range for the worksheet
314
- # @param [String] v
315
- # @see auto_filter
316
- def auto_filter=(v)
317
- DataTypeValidator.validate :worksheet_auto_filter, String, v
318
- auto_filter.range = v
319
- end
320
-
321
- # Accessor for controlling whether leading and trailing spaces in cells are
322
- # preserved or ignored. The default is to preserve spaces.
323
- attr_accessor :preserve_spaces
324
-
325
- # The part name of this worksheet
326
- # @return [String]
327
- def pn
328
- "#{WORKSHEET_PN % (index+1)}"
329
- end
330
-
331
- # The relationship part name of this worksheet
332
- # @return [String]
333
- def rels_pn
334
- "#{WORKSHEET_RELS_PN % (index+1)}"
335
- end
336
-
337
- # The relationship id of this worksheet.
338
- # @return [String]
339
- # @see Relationship#Id
340
- def rId
341
- @workbook.relationships.for(self).Id
342
- end
343
-
344
- # The index of this worksheet in the owning Workbook's worksheets list.
345
- # @return [Integer]
346
- def index
347
- @workbook.worksheets.index(self)
348
- end
349
-
350
- # The drawing associated with this worksheet.
351
- # @note the recommended way to work with drawings and charts is Worksheet#add_chart
352
- # @return [Drawing]
353
- # @see Worksheet#add_chart
354
- def drawing
355
- worksheet_drawing.drawing
356
- end
357
-
358
- # Adds a row to the worksheet and updates auto fit data.
359
- # @example - put a vanilla row in your spreadsheet
360
- # ws.add_row [1, 'fish on my pl', '8']
361
- #
362
- # @example - specify a fixed width for a column in your spreadsheet
363
- # # The first column will ignore the content of this cell when calculating column autowidth.
364
- # # The second column will include this text in calculating the columns autowidth
365
- # # The third cell will set a fixed with of 80 for the column.
366
- # # If you need to un-fix a column width, use :auto. That will recalculate the column width based on all content in the column
367
- #
368
- # ws.add_row ['I wish', 'for a fish', 'on my fish wish dish'], :widths=>[:ignore, :auto, 80]
369
- #
370
- # @example - specify a fixed height for a row
371
- # ws.add_row ['I wish', 'for a fish', 'on my fish wish dish'], :height => 40
372
- #
373
- # @example - create and use a style for all cells in the row
374
- # blue = ws.styles.add_style :color => "#00FF00"
375
- # ws.add_row [1, 2, 3], :style=>blue
376
- #
377
- # @example - only style some cells
378
- # blue = ws.styles.add_style :color => "#00FF00"
379
- # red = ws.styles.add_style :color => "#FF0000"
380
- # big = ws.styles.add_style :sz => 40
381
- # ws.add_row ["red fish", "blue fish", "one fish", "two fish"], :style=>[red, blue, nil, big] # the last nil is optional
382
- #
383
- #
384
- # @example - force the second cell to be a float value
385
- # ws.add_row [3, 4, 5], :types => [nil, :float]
386
- #
387
- # @example - use << alias
388
- # ws << [3, 4, 5], :types => [nil, :float]
389
- #
390
- # @example - specify whether a row should escape formulas or not
391
- # ws.add_row ['=IF(2+2=4,4,5)', 2, 3], :escape_formulas=>true
392
- #
393
- # @example - specify whether a certain cells in a row should escape formulas or not
394
- # ws.add_row ['=IF(2+2=4,4,5)', '=IF(13+13=4,4,5)'], :escape_formulas=>[true, false]
395
- #
396
- # @example - add a column offset when adding a row (inserts 'n' blank, unstyled columns before data)
397
- # ws.add_row ['I wish', 'for a fish', 'on my fish wish dish'], offset: 3
398
- #
399
- # @see Worksheet#column_widths
400
- # @return [Row]
401
- # @option options [Array] values
402
- # @option options [Array, Symbol] types
403
- # @option options [Array, Integer] style
404
- # @option options [Array] widths each member of the widths array will affect how auto_fit behavies.
405
- # @option options [Float] height the row's height (in points)
406
- # @option options [Integer] offset - add empty columns before values
407
- # @option options [Array, Boolean] escape_formulas - Whether to treat a value starting with an equal
408
- # sign as formula (default) or as simple string.
409
- # Allowing user generated data to be interpreted as formulas can be dangerous
410
- # (see https://www.owasp.org/index.php/CSV_Injection for details).
411
- def add_row(values=[], options={})
412
- row = Row.new(self, values, options)
413
- update_column_info row, options.delete(:widths)
414
- yield row if block_given?
415
- row
416
- end
417
-
418
- alias :<< :add_row
419
-
420
- # Add conditional formatting to this worksheet.
421
- #
422
- # @param [String] cells The range to apply the formatting to
423
- # @param [Array|Hash] rules An array of hashes (or just one) to create Conditional formatting rules from.
424
- # @example This would format column A whenever it is FALSE.
425
- # # for a longer example, see examples/example_conditional_formatting.rb (link below)
426
- # worksheet.add_conditional_formatting( "A1:A1048576", { :type => :cellIs, :operator => :equal, :formula => "FALSE", :dxfId => 1, :priority => 1 }
427
- #
428
- # @see ConditionalFormattingRule#initialize
429
- # @see file:examples/example_conditional_formatting.rb
430
- def add_conditional_formatting(cells, rules)
431
- cf = ConditionalFormatting.new( :sqref => cells )
432
- cf.add_rules rules
433
- conditional_formattings << cf
434
- conditional_formattings
435
- end
436
-
437
- # Add data validation to this worksheet.
438
- #
439
- # @param [String] cells The cells the validation will apply to.
440
- # @param [hash] data_validation options defining the validation to apply.
441
- # @see examples/data_validation.rb for an example
442
- def add_data_validation(cells, data_validation)
443
- dv = DataValidation.new(data_validation)
444
- dv.sqref = cells
445
- data_validations << dv
446
- end
447
-
448
- # Adds a new hyperlink to the worksheet
449
- # @param [Hash] options for the hyperlink
450
- # @see WorksheetHyperlink for a list of options
451
- # @return [WorksheetHyperlink]
452
- def add_hyperlink(options={})
453
- hyperlinks.add(options)
454
- end
455
-
456
- # Adds a chart to this worksheets drawing. This is the recommended way to create charts for your worksheet. This method wraps the complexity of dealing with ooxml drawing, anchors, markers graphic frames chart objects and all the other dirty details.
457
- # @param [Class] chart_type
458
- # @option options [Array] start_at
459
- # @option options [Array] end_at
460
- # @option options [Cell, String] title
461
- # @option options [Boolean] show_legend
462
- # @option options [Integer] style
463
- # @note each chart type also specifies additional options
464
- # @see Chart
465
- # @see Pie3DChart
466
- # @see Bar3DChart
467
- # @see Line3DChart
468
- # @see README for examples
469
- def add_chart(chart_type, options={})
470
- chart = worksheet_drawing.add_chart(chart_type, options)
471
- yield chart if block_given?
472
- chart
473
- end
474
-
475
- # needs documentation
476
- def add_table(ref, options={})
477
- tables << Table.new(ref, self, options)
478
- yield tables.last if block_given?
479
- tables.last
480
- end
481
-
482
- def add_pivot_table(ref, range, options={})
483
- pivot_tables << PivotTable.new(ref, range, self, options)
484
- yield pivot_tables.last if block_given?
485
- pivot_tables.last
486
- end
487
-
488
- # Shortcut to worsksheet_comments#add_comment
489
- def add_comment(options={})
490
- worksheet_comments.add_comment(options)
491
- end
492
-
493
- # Adds a media item to the worksheets drawing
494
- # @option [Hash] options options passed to drawing.add_image
495
- def add_image(options={})
496
- image = worksheet_drawing.add_image(options)
497
- yield image if block_given?
498
- image
499
- end
500
-
501
- # Adds a page break (row break) to the worksheet
502
- # @param cell A Cell object or excel style string reference indicating where the break
503
- # should be added to the sheet.
504
- # @example
505
- # ws.add_page_break("A4")
506
- def add_page_break(cell)
507
- DataTypeValidator.validate :worksheet_page_break, [String, Cell], cell
508
- column_index, row_index = if cell.is_a?(String)
509
- Axlsx.name_to_indices(cell)
510
- else
511
- cell.pos
512
- end
513
- if column_index > 0
514
- col_breaks.add_break(:id => column_index)
515
- end
516
- row_breaks.add_break(:id => row_index)
517
- end
518
-
519
- # This is a helper method that Lets you specify a fixed width for multiple columns in a worksheet in one go.
520
- # Note that you must call column_widths AFTER adding data, otherwise the width will not be set successfully.
521
- # Setting a fixed column width to nil will revert the behaviour back to calculating the width for you on the next call to add_row.
522
- # @example This would set the first and third column widhts but leave the second column in autofit state.
523
- # ws.column_widths 7.2, nil, 3
524
- # @note For updating only a single column it is probably easier to just set the width of the ws.column_info[col_index].width directly
525
- # @param [Integer|Float|nil] widths
526
- def column_widths(*widths)
527
- widths.each_with_index do |value, index|
528
- next if value == nil
529
- Axlsx::validate_unsigned_numeric(value) unless value == nil
530
- find_or_create_column_info(index).width = value
531
- end
532
- end
533
-
534
- # Set the style for cells in a specific column
535
- # @param [Integer] index the index of the column
536
- # @param [Integer] style the cellXfs index
537
- # @param [Hash] options
538
- # @option [Integer] :row_offset only cells after this column will be updated.
539
- # @note You can also specify the style for specific columns in the call to add_row by using an array for the :styles option
540
- # @see Worksheet#add_row
541
- # @see README.md for an example
542
- def col_style(index, style, options={})
543
- offset = options.delete(:row_offset) || 0
544
- cells = @rows[(offset..-1)].map { |row| row[index] }.flatten.compact
545
- cells.each { |cell| cell.style = style }
546
- end
547
-
548
- # Set the style for cells in a specific row
549
- # @param [Integer] index or range of indexes in the table
550
- # @param [Integer] style the cellXfs index
551
- # @param [Hash] options the options used when applying the style
552
- # @option [Integer] :col_offset only cells after this column will be updated.
553
- # @note You can also specify the style in the add_row call
554
- # @see Worksheet#add_row
555
- # @see README.md for an example
556
- def row_style(index, style, options={})
557
- offset = options.delete(:col_offset) || 0
558
- cells = cols[(offset..-1)].map { |column| column[index] }.flatten.compact
559
- cells.each { |cell| cell.style = style }
560
- end
561
-
562
- # Returns a sheet node serialization for this sheet in the workbook.
563
- def to_sheet_node_xml_string(str='')
564
- add_autofilter_defined_name_to_workbook
565
- str << '<sheet '
566
- serialized_attributes str
567
- str << ('name="' << name << '" ')
568
- str << ('r:id="' << rId << '"></sheet>')
569
- end
570
-
571
- # Serializes the worksheet object to an xml string
572
- # This intentionally does not use nokogiri for performance reasons
573
- # @return [String]
574
- def to_xml_string str=''
575
- add_autofilter_defined_name_to_workbook
576
- auto_filter.apply if auto_filter.range
577
- str << '<?xml version="1.0" encoding="UTF-8"?>'
578
- str << worksheet_node
579
- serializable_parts.each do |item|
580
- item.to_xml_string(str) if item
581
- end
582
- str << '</worksheet>'
583
- end
584
-
585
- # The worksheet relationships. This is managed automatically by the worksheet
586
- # @return [Relationships]
587
- def relationships
588
- r = Relationships.new
589
- r + [tables.relationships,
590
- worksheet_comments.relationships,
591
- hyperlinks.relationships,
592
- worksheet_drawing.relationship,
593
- pivot_tables.relationships].flatten.compact || []
594
- r
595
- end
596
-
597
- # Returns the cell or cells defined using excel style A1:B3 references.
598
- # @param [String|Integer] cell_def the string defining the cell or range of cells, or the rownumber
599
- # @return [Cell, Array]
600
- def [](cell_def)
601
- return rows[cell_def] if cell_def.is_a?(Integer)
602
-
603
- parts = cell_def.split(':').map{ |part| name_to_cell part }
604
-
605
- if parts.size == 1
606
- parts.first
607
- else
608
- if parts.size > 2
609
- raise ArgumentError, (ERR_CELL_REFERENCE_INVALID % cell_def)
610
- elsif parts.first.nil?
611
- raise ArgumentError, (ERR_CELL_REFERENCE_MISSING_CELL % [cell_def.split(":").first, cell_def])
612
- elsif parts.last.nil?
613
- raise ArgumentError, (ERR_CELL_REFERENCE_MISSING_CELL % [cell_def.split(":").last, cell_def])
614
- end
615
-
616
- range(*parts)
617
- end
618
- end
619
-
620
- # returns the column and row index for a named based cell
621
- # @param [String] name The cell or cell range to return. "A1" will return the first cell of the first row.
622
- # @return [Cell]
623
- def name_to_cell(name)
624
- col_index, row_index = *Axlsx::name_to_indices(name)
625
-
626
- r = rows[row_index]
627
-
628
- if r
629
- return r[col_index]
630
- end
631
- end
632
-
633
- # shortcut method to access styles direclty from the worksheet
634
- # This lets us do stuff like:
635
- # @example
636
- # p = Axlsx::Package.new
637
- # p.workbook.add_worksheet(:name => 'foo') do |sheet|
638
- # my_style = sheet.styles.add_style { :bg_color => "FF0000" }
639
- # sheet.add_row ['Oh No!'], :styles => my_style
640
- # end
641
- # p.serialize 'foo.xlsx'
642
- def styles
643
- @styles ||= self.workbook.styles
644
- end
645
-
646
- # shortcut level to specify the outline level for a series of rows
647
- # Oulining is what lets you add collapse and expand to a data set.
648
- # @param [Integer] start_index The zero based index of the first row of outlining.
649
- # @param [Integer] end_index The zero based index of the last row to be outlined
650
- # @param [integer] level The level of outline to apply
651
- # @param [Integer] collapsed The initial collapsed state of the outline group
652
- def outline_level_rows(start_index, end_index, level = 1, collapsed = true)
653
- outline rows, (start_index..end_index), level, collapsed
654
- end
655
-
656
- # shortcut level to specify the outline level for a series of columns
657
- # Oulining is what lets you add collapse and expand to a data set.
658
- # @param [Integer] start_index The zero based index of the first column of outlining.
659
- # @param [Integer] end_index The zero based index of the last column to be outlined
660
- # @param [integer] level The level of outline to apply
661
- # @param [Integer] collapsed The initial collapsed state of the outline group
662
- def outline_level_columns(start_index, end_index, level = 1, collapsed = true)
663
- outline column_info, (start_index..end_index), level, collapsed
664
- end
665
-
666
- private
667
-
668
- def xml_space
669
- workbook.xml_space
670
- end
671
-
672
- def outline(collection, range, level = 1, collapsed = true)
673
- range.each do |index|
674
- unless (item = collection[index]).nil?
675
- item.outline_level = level
676
- item.hidden = collapsed
677
- end
678
- sheet_view.show_outline_symbols = true
679
- end
680
- end
681
-
682
- def validate_sheet_name(name)
683
- DataTypeValidator.validate :worksheet_name, String, name
684
- # ignore first character (BOM) after encoding to utf16 because Excel does so, too.
685
- raise ArgumentError, (ERR_SHEET_NAME_EMPTY) if name.empty?
686
- character_length = name.encode("utf-16")[1..-1].encode("utf-16").bytesize / 2
687
- raise ArgumentError, (ERR_SHEET_NAME_TOO_LONG % name) if character_length > 31
688
- raise ArgumentError, (ERR_SHEET_NAME_CHARACTER_FORBIDDEN % name) if '[]*/\?:'.chars.any? { |char| name.include? char }
689
- name = Axlsx::coder.encode(name)
690
- sheet_names = @workbook.worksheets.reject { |s| s == self }.map { |s| s.name }
691
- raise ArgumentError, (ERR_DUPLICATE_SHEET_NAME % name) if sheet_names.include?(name)
692
- end
693
-
694
- def serializable_parts
695
- [sheet_pr, dimension, sheet_view, sheet_format_pr, column_info,
696
- sheet_data, sheet_calc_pr, @sheet_protection, protected_ranges,
697
- auto_filter, merged_cells, conditional_formattings,
698
- data_validations, hyperlinks, print_options, page_margins,
699
- page_setup, header_footer, row_breaks, col_breaks, worksheet_drawing, worksheet_comments,
700
- tables]
701
- end
702
-
703
- def range(*cell_def)
704
- first, last = cell_def
705
- cells = []
706
-
707
- rows[(first.row.row_index..last.row.row_index)].each do |r|
708
- r[(first.index..last.index)].each do |c|
709
- cells << c
710
- end
711
- end
712
-
713
- cells
714
- end
715
-
716
- # A collection of protected ranges in the worksheet
717
- # @note The recommended way to manage protected ranges is with Worksheet#protect_range
718
- # @see Worksheet#protect_range
719
- # @return [SimpleTypedList] The protected ranges for this worksheet
720
- def protected_ranges
721
- @protected_ranges ||= ProtectedRanges.new self
722
- # SimpleTypedList.new ProtectedRange
723
- end
724
-
725
- # conditional formattings
726
- # @return [Array]
727
- def conditional_formattings
728
- @conditional_formattings ||= ConditionalFormattings.new self
729
- end
730
-
731
- # data validations array
732
- # @return [Array]
733
- def data_validations
734
- @data_validations ||= DataValidations.new self
735
- end
736
-
737
- # merged cells array
738
- # @return [Array]
739
- def merged_cells
740
- @merged_cells ||= MergedCells.new self
741
- end
742
-
743
-
744
- # Helper method for parsingout the root node for worksheet
745
- # @return [String]
746
- def worksheet_node
747
- "<worksheet xmlns=\"#{XML_NS}\" xmlns:r=\"#{XML_NS_R}\" xml:space=\"#{xml_space}\">"
748
- end
749
-
750
- def sheet_data
751
- @sheet_data ||= SheetData.new self
752
- end
753
-
754
- def worksheet_drawing
755
- @worksheet_drawing ||= WorksheetDrawing.new self
756
- end
757
-
758
- # The comments associated with this worksheet
759
- # @return [SimpleTypedList]
760
- def worksheet_comments
761
- @worksheet_comments ||= WorksheetComments.new self
762
- end
763
-
764
- def workbook=(v) DataTypeValidator.validate "Worksheet.workbook", Workbook, v; @workbook = v; end
765
-
766
- def update_column_info(cells, widths = nil)
767
- cells.each_with_index do |cell, index|
768
- width = widths ? widths[index] : nil
769
- col = find_or_create_column_info(index)
770
- next if width == :ignore
771
-
772
- col.update_width(cell, width, workbook.use_autowidth)
773
- end
774
- end
775
-
776
- def find_or_create_column_info(index)
777
- column_info[index] ||= Col.new(index + 1, index + 1)
778
- end
779
-
780
- def add_autofilter_defined_name_to_workbook
781
- return if !auto_filter.range
782
- workbook.add_defined_name auto_filter.defined_name, name: '_xlnm._FilterDatabase', local_sheet_id: index, hidden: 1
783
- end
784
-
785
- end
786
- end
1
+ require_relative "border_creator"
2
+
3
+ module Axlsx
4
+ # The Worksheet class represents a worksheet in the workbook.
5
+ class Worksheet
6
+ include Axlsx::OptionsParser
7
+ include Axlsx::SerializedAttributes
8
+
9
+ # Creates a new worksheet.
10
+ # @note the recommended way to manage worksheets is Workbook#add_worksheet
11
+ # @see Workbook#add_worksheet
12
+ # @option options [String] name The name of this worksheet.
13
+ # @option options [Hash] page_margins A hash containing page margins for this worksheet. @see PageMargins
14
+ # @option options [Hash] print_options A hash containing print options for this worksheet. @see PrintOptions
15
+ # @option options [Hash] header_footer A hash containing header/footer options for this worksheet. @see HeaderFooter
16
+ # @option options [Boolean] show_gridlines Whether gridlines should be shown for this sheet.
17
+ # @option options [Boolean] escape_formulas Whether formulas should be escaped by default. Can be overridden at a
18
+ # row/cell level.
19
+ def initialize(wb, options = {})
20
+ self.workbook = wb
21
+ @sheet_protection = nil
22
+ initialize_page_options(options)
23
+ parse_options options
24
+ self.escape_formulas = wb.escape_formulas unless defined? @escape_formulas
25
+ @workbook.worksheets << self
26
+ @sheet_id = index + 1
27
+ yield self if block_given?
28
+ end
29
+
30
+ serializable_attributes :sheet_id, :state
31
+
32
+ # Initalizes page margin, setup and print options
33
+ # @param [Hash] options Options passed in from the initializer
34
+ def initialize_page_options(options)
35
+ @page_margins = PageMargins.new options[:page_margins] if options[:page_margins]
36
+ @page_setup = PageSetup.new options[:page_setup] if options[:page_setup]
37
+ @print_options = PrintOptions.new options[:print_options] if options[:print_options]
38
+ @header_footer = HeaderFooter.new options[:header_footer] if options[:header_footer]
39
+ @row_breaks = RowBreaks.new
40
+ @col_breaks = ColBreaks.new
41
+ end
42
+
43
+ # The name of the worksheet
44
+ # @return [String]
45
+ def name
46
+ @name ||= "Sheet" + (index + 1).to_s
47
+ end
48
+
49
+ # Whether to treat values starting with an equals sign as formulas or as literal strings.
50
+ # Allowing user-generated data to be interpreted as formulas is a security risk.
51
+ # See https://www.owasp.org/index.php/CSV_Injection for details.
52
+ # @return [Boolean]
53
+ attr_reader :escape_formulas
54
+
55
+ # Sets whether to treat values starting with an equals sign as formulas or as literal strings.
56
+ # @param [Boolean] value The value to set.
57
+ # @return [Boolean]
58
+ def escape_formulas=(value)
59
+ Axlsx.validate_boolean(value)
60
+ @escape_formulas = value
61
+ end
62
+
63
+ # Specifies the visible state of this sheet. Allowed states are
64
+ # :visible, :hidden or :very_hidden. The default value is :visible.
65
+ #
66
+ # Worksheets in the :hidden state can be shown using the sheet formatting properties in excel.
67
+ # :very_hidden sheets should be inaccessible to end users.
68
+ # @param [Symbol] sheet_state The visible state for this sheet.
69
+ def state=(sheet_state)
70
+ RestrictionValidator.validate :worksheet_state, [:visible, :hidden, :very_hidden], sheet_state
71
+ @state = sheet_state
72
+ end
73
+
74
+ # The visibility of this sheet
75
+ def state
76
+ @state ||= :visible
77
+ end
78
+
79
+ # The sheet calculation properties
80
+ # @return [SheetCalcPr]
81
+ def sheet_calc_pr
82
+ @sheet_calc_pr ||= SheetCalcPr.new
83
+ end
84
+
85
+ # The sheet protection object for this workbook
86
+ # @return [SheetProtection]
87
+ # @see SheetProtection
88
+ def sheet_protection
89
+ @sheet_protection ||= SheetProtection.new
90
+ yield @sheet_protection if block_given?
91
+ @sheet_protection
92
+ end
93
+
94
+ # The sheet view object for this worksheet
95
+ # @return [SheetView]
96
+ # @see [SheetView]
97
+ def sheet_view
98
+ @sheet_view ||= SheetView.new
99
+ yield @sheet_view if block_given?
100
+ @sheet_view
101
+ end
102
+
103
+ # The sheet format pr for this worksheet
104
+ # @return [SheetFormatPr]
105
+ # @see [SheetFormatPr]
106
+ def sheet_format_pr
107
+ @sheet_format_pr ||= SheetFormatPr.new
108
+ yield @sheet_format_pr if block_given?
109
+ @sheet_format_pr
110
+ end
111
+
112
+ # The workbook that owns this worksheet
113
+ # @return [Workbook]
114
+ attr_reader :workbook
115
+
116
+ # The tables in this worksheet
117
+ # @return [Array] of Table
118
+ def tables
119
+ @tables ||= Tables.new self
120
+ end
121
+
122
+ # The pivot tables in this worksheet
123
+ # @return [Array] of Table
124
+ def pivot_tables
125
+ @pivot_tables ||= PivotTables.new self
126
+ end
127
+
128
+ # A collection of column breaks added to this worksheet
129
+ # @note Please do not use this directly. Instead use
130
+ # add_page_break
131
+ # @see Worksheet#add_page_break
132
+ def col_breaks
133
+ @col_breaks ||= ColBreaks.new
134
+ end
135
+
136
+ # A collection of row breaks added to this worksheet
137
+ # @note Please do not use this directly. Instead use
138
+ # add_page_break
139
+ # @see Worksheet#add_page_break
140
+ def row_breaks
141
+ @row_breaks ||= RowBreaks.new
142
+ end
143
+
144
+ # A typed collection of hyperlinks associated with this worksheet
145
+ # @return [WorksheetHyperlinks]
146
+ def hyperlinks
147
+ @hyperlinks ||= WorksheetHyperlinks.new self
148
+ end
149
+
150
+ # The a shortcut to the worksheet_comments list of comments
151
+ # @return [Array|SimpleTypedList]
152
+ def comments
153
+ worksheet_comments.comments if worksheet_comments.has_comments?
154
+ end
155
+
156
+ # The rows in this worksheet
157
+ # @note The recommended way to manage rows is Worksheet#add_row
158
+ # @return [SimpleTypedList]
159
+ # @see Worksheet#add_row
160
+ def rows
161
+ @rows ||= SimpleTypedList.new Row
162
+ end
163
+
164
+ # returns the sheet data as columns
165
+ # If you pass a block, it will be evaluated whenever a row does not have a
166
+ # cell at a specific index. The block will be called with the row and column
167
+ # index in the missing cell was found.
168
+ # @example
169
+ # cols { |row_index, column_index| puts "warn - row #{row_index} does not have a cell at #{column_index}" }
170
+ def cols(&block)
171
+ @rows.transpose(&block)
172
+ end
173
+
174
+ # A range that excel will apply an auto-filter to "A1:B3"
175
+ # This will turn filtering on for the cells in the range.
176
+ # The first row is considered the header, while subsequent rows are considered to be data.
177
+ # @return String
178
+ def auto_filter
179
+ @auto_filter ||= AutoFilter.new self
180
+ end
181
+
182
+ # Indicates if the worksheet will be fit by witdh or height to a specific number of pages.
183
+ # To alter the width or height for page fitting, please use page_setup.fit_to_widht or page_setup.fit_to_height.
184
+ # If you want the worksheet to fit on more pages (e.g. 2x2), set {PageSetup#fit_to_width} and {PageSetup#fit_to_height} accordingly.
185
+ # @return Boolean
186
+ # @see #page_setup
187
+ def fit_to_page?
188
+ return false unless Axlsx.instance_values_for(self).keys.include?('page_setup')
189
+
190
+ page_setup.fit_to_page?
191
+ end
192
+
193
+ # Column info for the sheet
194
+ # @return [SimpleTypedList]
195
+ def column_info
196
+ @column_info ||= Cols.new self
197
+ end
198
+
199
+ # Page margins for printing the worksheet.
200
+ # @example
201
+ # wb = Axlsx::Package.new.workbook
202
+ # # using options when creating the worksheet.
203
+ # ws = wb.add_worksheet :page_margins => {:left => 1.9, :header => 0.1}
204
+ #
205
+ # # use the set method of the page_margins object
206
+ # ws.page_margins.set(:bottom => 3, :footer => 0.7)
207
+ #
208
+ # # set page margins in a block
209
+ # ws.page_margins do |margins|
210
+ # margins.right = 6
211
+ # margins.top = 0.2
212
+ # end
213
+ # @see PageMargins#initialize
214
+ # @return [PageMargins]
215
+ def page_margins
216
+ @page_margins ||= PageMargins.new
217
+ yield @page_margins if block_given?
218
+ @page_margins
219
+ end
220
+
221
+ # Page setup settings for printing the worksheet.
222
+ # @example
223
+ # wb = Axlsx::Package.new.workbook
224
+ #
225
+ # # using options when creating the worksheet.
226
+ # ws = wb.add_worksheet :page_setup => {:fit_to_width => 2, :orientation => :landscape}
227
+ #
228
+ # # use the set method of the page_setup object
229
+ # ws.page_setup.set(:paper_width => "297mm", :paper_height => "210mm")
230
+ #
231
+ # # setup page in a block
232
+ # ws.page_setup do |page|
233
+ # page.scale = 80
234
+ # page.orientation = :portrait
235
+ # end
236
+ # @see PageSetup#initialize
237
+ # @return [PageSetup]
238
+ def page_setup
239
+ @page_setup ||= PageSetup.new
240
+ yield @page_setup if block_given?
241
+ @page_setup
242
+ end
243
+
244
+ # Options for printing the worksheet.
245
+ # @example
246
+ # wb = Axlsx::Package.new.workbook
247
+ # # using options when creating the worksheet.
248
+ # ws = wb.add_worksheet :print_options => {:grid_lines => true, :horizontal_centered => true}
249
+ #
250
+ # # use the set method of the page_margins object
251
+ # ws.print_options.set(:headings => true)
252
+ #
253
+ # # set page margins in a block
254
+ # ws.print_options do |options|
255
+ # options.horizontal_centered = true
256
+ # options.vertical_centered = true
257
+ # end
258
+ # @see PrintOptions#initialize
259
+ # @return [PrintOptions]
260
+ def print_options
261
+ @print_options ||= PrintOptions.new
262
+ yield @print_options if block_given?
263
+ @print_options
264
+ end
265
+
266
+ # Options for headers and footers.
267
+ # @example
268
+ # wb = Axlsx::Package.new.workbook
269
+ # # would generate something like: "file.xlsx : sheet_name 2 of 7 date with timestamp"
270
+ # header = {:different_odd_ => false, :odd_header => "&L&F : &A&C&Pof%N%R%D %T"}
271
+ # ws = wb.add_worksheet :header_footer => header
272
+ #
273
+ # @see HeaderFooter#initialize
274
+ # @return [HeaderFooter]
275
+ def header_footer
276
+ @header_footer ||= HeaderFooter.new
277
+ yield @header_footer if block_given?
278
+ @header_footer
279
+ end
280
+
281
+ # convenience method to access all cells in this worksheet
282
+ # @return [Array] cells
283
+ def cells
284
+ rows.flatten
285
+ end
286
+
287
+ # Creates merge information for this worksheet.
288
+ # Cells can be merged by calling the merge_cells method on a worksheet.
289
+ # @example This would merge the three cells C1..E1 #
290
+ # worksheet.merge_cells "C1:E1"
291
+ # # you can also provide an array of cells to be merged
292
+ # worksheet.merge_cells worksheet.rows.first.cells[(2..4)]
293
+ # #alternatively you can do it from a single cell
294
+ # worksheet["C1"].merge worksheet["E1"]
295
+ # @param [Array, string] cells
296
+ def merge_cells(cells)
297
+ merged_cells.add cells
298
+ end
299
+
300
+ # Adds a new protected cell range to the worksheet. Note that protected ranges are only in effect when sheet protection is enabled.
301
+ # @param [String|Array] cells The string reference for the cells to protect or an array of cells.
302
+ # @return [ProtectedRange]
303
+ # @note When using an array of cells, a contiguous range is created from the minimum top left to the maximum top bottom of the cells provided.
304
+ def protect_range(cells)
305
+ protected_ranges.add_range(cells)
306
+ end
307
+
308
+ # The dimensions of a worksheet. This is not actually a required element by the spec,
309
+ # but at least a few other document readers expect this for conversion
310
+ # @return [Dimension]
311
+ def dimension
312
+ @dimension ||= Dimension.new self
313
+ end
314
+
315
+ # The sheet properties for this workbook.
316
+ # Currently only pageSetUpPr -> fitToPage is implemented
317
+ # @return [SheetPr]
318
+ def sheet_pr
319
+ @sheet_pr ||= SheetPr.new self
320
+ end
321
+
322
+ # The name of the worksheet
323
+ # The name of a worksheet must be unique in the workbook, and must not exceed the number
324
+ # of characters defined in Axlsx::WORKSHEET_MAX_NAME_LENGTH
325
+ # @param [String] name
326
+ def name=(name)
327
+ validate_sheet_name name
328
+ @name = Axlsx::coder.encode(name)
329
+ end
330
+
331
+ # The auto filter range for the worksheet
332
+ # @param [String] v
333
+ # @see auto_filter
334
+ def auto_filter=(v)
335
+ DataTypeValidator.validate :worksheet_auto_filter, String, v
336
+ auto_filter.range = v
337
+ end
338
+
339
+ # Accessor for controlling whether leading and trailing spaces in cells are
340
+ # preserved or ignored. The default is to preserve spaces.
341
+ attr_accessor :preserve_spaces
342
+
343
+ # The part name of this worksheet
344
+ # @return [String]
345
+ def pn
346
+ "#{WORKSHEET_PN % (index + 1)}"
347
+ end
348
+
349
+ # The relationship part name of this worksheet
350
+ # @return [String]
351
+ def rels_pn
352
+ "#{WORKSHEET_RELS_PN % (index + 1)}"
353
+ end
354
+
355
+ # The relationship id of this worksheet.
356
+ # @return [String]
357
+ # @see Relationship#Id
358
+ def rId
359
+ @workbook.relationships.for(self).Id
360
+ end
361
+
362
+ # The index of this worksheet in the owning Workbook's worksheets list.
363
+ # @return [Integer]
364
+ def index
365
+ @workbook.worksheets.index(self)
366
+ end
367
+
368
+ # The drawing associated with this worksheet.
369
+ # @note the recommended way to work with drawings and charts is Worksheet#add_chart
370
+ # @return [Drawing]
371
+ # @see Worksheet#add_chart
372
+ def drawing
373
+ worksheet_drawing.drawing
374
+ end
375
+
376
+ # Adds a row to the worksheet and updates auto fit data.
377
+ # @example - put a vanilla row in your spreadsheet
378
+ # ws.add_row [1, 'fish on my pl', '8']
379
+ #
380
+ # @example - specify a fixed width for a column in your spreadsheet
381
+ # # The first column will ignore the content of this cell when calculating column autowidth.
382
+ # # The second column will include this text in calculating the columns autowidth
383
+ # # The third cell will set a fixed with of 80 for the column.
384
+ # # If you need to un-fix a column width, use :auto. That will recalculate the column width based on all content in the column
385
+ #
386
+ # ws.add_row ['I wish', 'for a fish', 'on my fish wish dish'], :widths=>[:ignore, :auto, 80]
387
+ #
388
+ # @example - specify a fixed height for a row
389
+ # ws.add_row ['I wish', 'for a fish', 'on my fish wish dish'], :height => 40
390
+ #
391
+ # @example - create and use a style for all cells in the row
392
+ # blue = ws.styles.add_style :color => "#00FF00"
393
+ # ws.add_row [1, 2, 3], :style=>blue
394
+ #
395
+ # @example - only style some cells
396
+ # blue = ws.styles.add_style :color => "#00FF00"
397
+ # red = ws.styles.add_style :color => "#FF0000"
398
+ # big = ws.styles.add_style :sz => 40
399
+ # ws.add_row ["red fish", "blue fish", "one fish", "two fish"], :style=>[red, blue, nil, big] # the last nil is optional
400
+ #
401
+ #
402
+ # @example - force the second cell to be a float value
403
+ # ws.add_row [3, 4, 5], :types => [nil, :float]
404
+ #
405
+ # @example - use << alias
406
+ # ws << [3, 4, 5], :types => [nil, :float]
407
+ #
408
+ # @example - specify whether a row should escape formulas or not
409
+ # ws.add_row ['=IF(2+2=4,4,5)', 2, 3], :escape_formulas=>true
410
+ #
411
+ # @example - specify whether a certain cells in a row should escape formulas or not
412
+ # ws.add_row ['=IF(2+2=4,4,5)', '=IF(13+13=4,4,5)'], :escape_formulas=>[true, false]
413
+ #
414
+ # @example - add a column offset when adding a row (inserts 'n' blank, unstyled columns before data)
415
+ # ws.add_row ['I wish', 'for a fish', 'on my fish wish dish'], offset: 3
416
+ #
417
+ # @see Worksheet#column_widths
418
+ # @return [Row]
419
+ # @option options [Array] values
420
+ # @option options [Array, Symbol] types
421
+ # @option options [Array, Integer] style
422
+ # @option options [Array] widths each member of the widths array will affect how auto_fit behavies.
423
+ # @option options [Float] height the row's height (in points)
424
+ # @option options [Integer] offset - add empty columns before values
425
+ # @option options [Array, Boolean] escape_formulas - Whether to treat a value starting with an equal
426
+ # sign as formula (default) or as simple string.
427
+ # Allowing user generated data to be interpreted as formulas can be dangerous
428
+ # (see https://www.owasp.org/index.php/CSV_Injection for details).
429
+ def add_row(values = [], options = {})
430
+ options[:escape_formulas] = escape_formulas if options[:escape_formulas].nil?
431
+ row = Row.new(self, values, options)
432
+ update_column_info row, options.delete(:widths)
433
+ yield row if block_given?
434
+ row
435
+ end
436
+
437
+ alias :<< :add_row
438
+
439
+ # Add conditional formatting to this worksheet.
440
+ #
441
+ # @param [String] cells The range to apply the formatting to
442
+ # @param [Array|Hash] rules An array of hashes (or just one) to create Conditional formatting rules from.
443
+ # @example This would format column A whenever it is FALSE.
444
+ # # for a longer example, see examples/example_conditional_formatting.rb (link below)
445
+ # worksheet.add_conditional_formatting( "A1:A1048576", { :type => :cellIs, :operator => :equal, :formula => "FALSE", :dxfId => 1, :priority => 1 }
446
+ #
447
+ # @see ConditionalFormattingRule#initialize
448
+ # @see file:examples/example_conditional_formatting.rb
449
+ def add_conditional_formatting(cells, rules)
450
+ cf = ConditionalFormatting.new(:sqref => cells)
451
+ cf.add_rules rules
452
+ conditional_formattings << cf
453
+ conditional_formattings
454
+ end
455
+
456
+ # Add data validation to this worksheet.
457
+ #
458
+ # @param [String] cells The cells the validation will apply to.
459
+ # @param [hash] data_validation options defining the validation to apply.
460
+ # @see examples/data_validation.rb for an example
461
+ def add_data_validation(cells, data_validation)
462
+ dv = DataValidation.new(data_validation)
463
+ dv.sqref = cells
464
+ data_validations << dv
465
+ end
466
+
467
+ # Adds a new hyperlink to the worksheet
468
+ # @param [Hash] options for the hyperlink
469
+ # @see WorksheetHyperlink for a list of options
470
+ # @return [WorksheetHyperlink]
471
+ def add_hyperlink(options = {})
472
+ hyperlinks.add(options)
473
+ end
474
+
475
+ # Adds a chart to this worksheets drawing. This is the recommended way to create charts for your worksheet. This method wraps the complexity of dealing with ooxml drawing, anchors, markers graphic frames chart objects and all the other dirty details.
476
+ # @param [Class] chart_type
477
+ # @option options [Array] start_at
478
+ # @option options [Array] end_at
479
+ # @option options [Cell, String] title
480
+ # @option options [Boolean] show_legend
481
+ # @option options [Integer] style
482
+ # @note each chart type also specifies additional options
483
+ # @see Chart
484
+ # @see Pie3DChart
485
+ # @see Bar3DChart
486
+ # @see Line3DChart
487
+ # @see README for examples
488
+ def add_chart(chart_type, options = {})
489
+ chart = worksheet_drawing.add_chart(chart_type, options)
490
+ yield chart if block_given?
491
+ chart
492
+ end
493
+
494
+ # needs documentation
495
+ def add_table(ref, options = {})
496
+ tables << Table.new(ref, self, options)
497
+ yield tables.last if block_given?
498
+ tables.last
499
+ end
500
+
501
+ def add_pivot_table(ref, range, options = {})
502
+ pivot_tables << PivotTable.new(ref, range, self, options)
503
+ yield pivot_tables.last if block_given?
504
+ pivot_tables.last
505
+ end
506
+
507
+ # Shortcut to worsksheet_comments#add_comment
508
+ def add_comment(options = {})
509
+ worksheet_comments.add_comment(options)
510
+ end
511
+
512
+ # Adds a media item to the worksheets drawing
513
+ # @option [Hash] options options passed to drawing.add_image
514
+ def add_image(options = {})
515
+ image = worksheet_drawing.add_image(options)
516
+ yield image if block_given?
517
+ image
518
+ end
519
+
520
+ # Adds a page break (row break) to the worksheet
521
+ # @param cell A Cell object or excel style string reference indicating where the break
522
+ # should be added to the sheet.
523
+ # @example
524
+ # ws.add_page_break("A4")
525
+ def add_page_break(cell)
526
+ DataTypeValidator.validate :worksheet_page_break, [String, Cell], cell
527
+ column_index, row_index = if cell.is_a?(String)
528
+ Axlsx.name_to_indices(cell)
529
+ else
530
+ cell.pos
531
+ end
532
+ if column_index > 0
533
+ col_breaks.add_break(:id => column_index)
534
+ end
535
+ row_breaks.add_break(:id => row_index)
536
+ end
537
+
538
+ # This is a helper method that Lets you specify a fixed width for multiple columns in a worksheet in one go.
539
+ # Note that you must call column_widths AFTER adding data, otherwise the width will not be set successfully.
540
+ # Setting a fixed column width to nil will revert the behaviour back to calculating the width for you on the next call to add_row.
541
+ # @example This would set the first and third column widhts but leave the second column in autofit state.
542
+ # ws.column_widths 7.2, nil, 3
543
+ # @note For updating only a single column it is probably easier to just set the width of the ws.column_info[col_index].width directly
544
+ # @param [Integer|Float|nil] widths
545
+ def column_widths(*widths)
546
+ widths.each_with_index do |value, index|
547
+ next if value == nil
548
+
549
+ Axlsx::validate_unsigned_numeric(value) unless value == nil
550
+ find_or_create_column_info(index).width = value
551
+ end
552
+ end
553
+
554
+ # Set the style for cells in a specific column
555
+ # @param [Integer] index the index of the column
556
+ # @param [Integer] style the cellXfs index
557
+ # @param [Hash] options
558
+ # @option [Integer] :row_offset only cells after this column will be updated.
559
+ # @note You can also specify the style for specific columns in the call to add_row by using an array for the :styles option
560
+ # @see Worksheet#add_row
561
+ # @see README.md for an example
562
+ def col_style(index, style, options = {})
563
+ offset = options.delete(:row_offset) || 0
564
+ cells = @rows[(offset..-1)].map { |row| row[index] }.flatten.compact
565
+ cells.each { |cell| cell.style = style }
566
+ end
567
+
568
+ # Set the style for cells in a specific row
569
+ # @param [Integer] index or range of indexes in the table
570
+ # @param [Integer] style the cellXfs index
571
+ # @param [Hash] options the options used when applying the style
572
+ # @option [Integer] :col_offset only cells after this column will be updated.
573
+ # @note You can also specify the style in the add_row call
574
+ # @see Worksheet#add_row
575
+ # @see README.md for an example
576
+ def row_style(index, style, options = {})
577
+ offset = options.delete(:col_offset) || 0
578
+ cells = cols[(offset..-1)].map { |column| column[index] }.flatten.compact
579
+ cells.each { |cell| cell.style = style }
580
+ end
581
+
582
+ # Set the style for cells in a specific column
583
+ # @param [String|Array] cell references
584
+ # @param [Hash] styles
585
+ def add_style(cell_refs, *styles)
586
+ if !cell_refs.is_a?(Array)
587
+ cell_refs = [cell_refs]
588
+ end
589
+
590
+ cell_refs.each do |cell_ref|
591
+ item = self[cell_ref]
592
+
593
+ cells = item.is_a?(Array) ? item : [item]
594
+
595
+ cells.each do |cell|
596
+ styles.each do |style|
597
+ cell.add_style(style)
598
+ end
599
+ end
600
+ end
601
+ end
602
+
603
+ # Set the style for cells in a specific column
604
+ # @param [String|Array] cell references
605
+ # @param [Hash|Array|Symbol] border options
606
+ def add_border(cell_refs, options = nil)
607
+ if options.is_a?(Hash)
608
+ border_edges = options[:edges]
609
+ border_style = options[:style]
610
+ border_color = options[:color]
611
+ else
612
+ border_edges = options
613
+ end
614
+
615
+ if !cell_refs.is_a?(Array)
616
+ cell_refs = [cell_refs]
617
+ end
618
+
619
+ cell_refs.each do |cell_ref|
620
+ item = self[cell_ref]
621
+
622
+ cells = item.is_a?(Array) ? item : [item]
623
+
624
+ Axlsx::BorderCreator.new(worksheet: self, cells: cells, edges: border_edges, style: border_style, color: border_color).draw
625
+ end
626
+ end
627
+
628
+ # Returns a sheet node serialization for this sheet in the workbook.
629
+ def to_sheet_node_xml_string(str = '')
630
+ add_autofilter_defined_name_to_workbook
631
+ str << '<sheet '
632
+ serialized_attributes str
633
+ str << ('name="' << name << '" ')
634
+ str << ('r:id="' << rId << '"></sheet>')
635
+ end
636
+
637
+ # Serializes the worksheet object to an xml string
638
+ # This intentionally does not use nokogiri for performance reasons
639
+ # @return [String]
640
+ def to_xml_string str = ''
641
+ add_autofilter_defined_name_to_workbook
642
+ auto_filter.apply if auto_filter.range
643
+ str << '<?xml version="1.0" encoding="UTF-8"?>'
644
+ str << worksheet_node
645
+ serializable_parts.each do |item|
646
+ item.to_xml_string(str) if item
647
+ end
648
+ str << '</worksheet>'
649
+ end
650
+
651
+ # The worksheet relationships. This is managed automatically by the worksheet
652
+ # @return [Relationships]
653
+ def relationships
654
+ r = Relationships.new
655
+ r + [tables.relationships,
656
+ worksheet_comments.relationships,
657
+ hyperlinks.relationships,
658
+ worksheet_drawing.relationship,
659
+ pivot_tables.relationships].flatten.compact || []
660
+ r
661
+ end
662
+
663
+ # Returns the cell or cells defined using excel style A1:B3 references.
664
+ # @param [String|Integer] cell_def the string defining the cell or range of cells, or the rownumber
665
+ # @return [Cell, Array]
666
+ def [](cell_def)
667
+ return rows[cell_def] if cell_def.is_a?(Integer)
668
+
669
+ parts = cell_def.split(':').map { |part| name_to_cell part }
670
+
671
+ if parts.size == 1
672
+ parts.first
673
+ else
674
+ if parts.size > 2
675
+ raise ArgumentError, (ERR_CELL_REFERENCE_INVALID % cell_def)
676
+ elsif parts.first.nil?
677
+ raise ArgumentError, (ERR_CELL_REFERENCE_MISSING_CELL % [cell_def.split(":").first, cell_def])
678
+ elsif parts.last.nil?
679
+ raise ArgumentError, (ERR_CELL_REFERENCE_MISSING_CELL % [cell_def.split(":").last, cell_def])
680
+ end
681
+
682
+ range(*parts)
683
+ end
684
+ end
685
+
686
+ # returns the column and row index for a named based cell
687
+ # @param [String] name The cell or cell range to return. "A1" will return the first cell of the first row.
688
+ # @return [Cell]
689
+ def name_to_cell(name)
690
+ col_index, row_index = *Axlsx::name_to_indices(name)
691
+
692
+ r = rows[row_index]
693
+
694
+ if r
695
+ return r[col_index]
696
+ end
697
+ end
698
+
699
+ # Shortcut method to access workbook styles
700
+ #
701
+ # This lets us do stuff like:
702
+ # @example
703
+ # p = Axlsx::Package.new
704
+ # p.workbook.add_worksheet(:name => 'foo') do |sheet|
705
+ # my_style = sheet.styles.add_style { :bg_color => "FF0000" }
706
+ # sheet.add_row ['Oh No!'], :styles => my_style
707
+ # end
708
+ # p.serialize 'foo.xlsx'
709
+ #
710
+ # @note The XLSX format does not support worksheet-specific styles. Even when using this method
711
+ # you're still working with the single global {Axlsx::Styles} object in the workbook.
712
+ def styles
713
+ @styles ||= self.workbook.styles
714
+ end
715
+
716
+ # shortcut level to specify the outline level for a series of rows
717
+ # Oulining is what lets you add collapse and expand to a data set.
718
+ # @param [Integer] start_index The zero based index of the first row of outlining.
719
+ # @param [Integer] end_index The zero based index of the last row to be outlined
720
+ # @param [integer] level The level of outline to apply
721
+ # @param [Integer] collapsed The initial collapsed state of the outline group
722
+ def outline_level_rows(start_index, end_index, level = 1, collapsed = true)
723
+ outline rows, (start_index..end_index), level, collapsed
724
+ end
725
+
726
+ # shortcut level to specify the outline level for a series of columns
727
+ # Oulining is what lets you add collapse and expand to a data set.
728
+ # @param [Integer] start_index The zero based index of the first column of outlining.
729
+ # @param [Integer] end_index The zero based index of the last column to be outlined
730
+ # @param [integer] level The level of outline to apply
731
+ # @param [Integer] collapsed The initial collapsed state of the outline group
732
+ def outline_level_columns(start_index, end_index, level = 1, collapsed = true)
733
+ outline column_info, (start_index..end_index), level, collapsed
734
+ end
735
+
736
+ private
737
+
738
+ def xml_space
739
+ workbook.xml_space
740
+ end
741
+
742
+ def outline(collection, range, level = 1, collapsed = true)
743
+ range.each do |index|
744
+ unless (item = collection[index]).nil?
745
+ item.outline_level = level
746
+ item.hidden = collapsed
747
+ end
748
+ sheet_view.show_outline_symbols = true
749
+ end
750
+ end
751
+
752
+ def validate_sheet_name(name)
753
+ DataTypeValidator.validate :worksheet_name, String, name
754
+ # ignore first character (BOM) after encoding to utf16 because Excel does so, too.
755
+ raise ArgumentError, (ERR_SHEET_NAME_EMPTY) if name.empty?
756
+
757
+ character_length = name.encode("utf-16")[1..-1].encode("utf-16").bytesize / 2
758
+ raise ArgumentError, (ERR_SHEET_NAME_TOO_LONG % name) if character_length > WORKSHEET_MAX_NAME_LENGTH
759
+ raise ArgumentError, (ERR_SHEET_NAME_CHARACTER_FORBIDDEN % name) if WORKSHEET_NAME_FORBIDDEN_CHARS.any? { |char| name.include? char }
760
+
761
+ name = Axlsx::coder.encode(name)
762
+ sheet_names = @workbook.worksheets.reject { |s| s == self }.map { |s| s.name }
763
+ raise ArgumentError, (ERR_DUPLICATE_SHEET_NAME % name) if sheet_names.include?(name)
764
+ end
765
+
766
+ def serializable_parts
767
+ [sheet_pr, dimension, sheet_view, sheet_format_pr, column_info,
768
+ sheet_data, sheet_calc_pr, @sheet_protection, protected_ranges,
769
+ auto_filter, merged_cells, conditional_formattings,
770
+ data_validations, hyperlinks, print_options, page_margins,
771
+ page_setup, header_footer, row_breaks, col_breaks, worksheet_drawing, worksheet_comments,
772
+ tables]
773
+ end
774
+
775
+ def range(*cell_def)
776
+ first, last = cell_def
777
+ cells = []
778
+
779
+ rows[(first.row.row_index..last.row.row_index)].each do |r|
780
+ r[(first.index..last.index)].each do |c|
781
+ cells << c
782
+ end
783
+ end
784
+
785
+ cells
786
+ end
787
+
788
+ # A collection of protected ranges in the worksheet
789
+ # @note The recommended way to manage protected ranges is with Worksheet#protect_range
790
+ # @see Worksheet#protect_range
791
+ # @return [SimpleTypedList] The protected ranges for this worksheet
792
+ def protected_ranges
793
+ @protected_ranges ||= ProtectedRanges.new self
794
+ # SimpleTypedList.new ProtectedRange
795
+ end
796
+
797
+ # conditional formattings
798
+ # @return [Array]
799
+ def conditional_formattings
800
+ @conditional_formattings ||= ConditionalFormattings.new self
801
+ end
802
+
803
+ # data validations array
804
+ # @return [Array]
805
+ def data_validations
806
+ @data_validations ||= DataValidations.new self
807
+ end
808
+
809
+ # merged cells array
810
+ # @return [Array]
811
+ def merged_cells
812
+ @merged_cells ||= MergedCells.new self
813
+ end
814
+
815
+ # Helper method for parsingout the root node for worksheet
816
+ # @return [String]
817
+ def worksheet_node
818
+ "<worksheet xmlns=\"#{XML_NS}\" xmlns:r=\"#{XML_NS_R}\" xml:space=\"#{xml_space}\">"
819
+ end
820
+
821
+ def sheet_data
822
+ @sheet_data ||= SheetData.new self
823
+ end
824
+
825
+ def worksheet_drawing
826
+ @worksheet_drawing ||= WorksheetDrawing.new self
827
+ end
828
+
829
+ # The comments associated with this worksheet
830
+ # @return [SimpleTypedList]
831
+ def worksheet_comments
832
+ @worksheet_comments ||= WorksheetComments.new self
833
+ end
834
+
835
+ def workbook=(v) DataTypeValidator.validate "Worksheet.workbook", Workbook, v; @workbook = v; end
836
+
837
+ def update_column_info(cells, widths = nil)
838
+ cells.each_with_index do |cell, index|
839
+ width = widths ? widths[index] : nil
840
+ col = find_or_create_column_info(index)
841
+ next if width == :ignore
842
+
843
+ col.update_width(cell, width, workbook.use_autowidth)
844
+ end
845
+ end
846
+
847
+ def find_or_create_column_info(index)
848
+ column_info[index] ||= Col.new(index + 1, index + 1)
849
+ end
850
+
851
+ def add_autofilter_defined_name_to_workbook
852
+ return if !auto_filter.range
853
+
854
+ workbook.add_defined_name auto_filter.defined_name, name: '_xlnm._FilterDatabase', local_sheet_id: index, hidden: 1
855
+ end
856
+ end
857
+ end