axlsx 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +3 -2
  3. data/.yardopts_guide +19 -0
  4. data/CHANGELOG.md +64 -1
  5. data/README.md +105 -87
  6. data/Rakefile +0 -5
  7. data/examples/2010_comments.rb +17 -0
  8. data/examples/anchor_swapping.rb +28 -0
  9. data/examples/auto_filter.rb +16 -0
  10. data/examples/basic_charts.rb +4 -0
  11. data/examples/colored_links.rb +59 -0
  12. data/examples/conditional_formatting/example_conditional_formatting.rb +4 -2
  13. data/examples/example.rb +569 -258
  14. data/examples/ios_preview.rb +14 -0
  15. data/examples/page_setup.rb +11 -0
  16. data/examples/pivot_table.rb +39 -0
  17. data/examples/skydrive/real_example.rb +8 -8
  18. data/examples/styles.rb +66 -0
  19. data/examples/underline.rb +13 -0
  20. data/examples/wrap_text.rb +21 -0
  21. data/lib/axlsx/content_type/abstract_content_type.rb +32 -0
  22. data/lib/axlsx/content_type/content_type.rb +1 -1
  23. data/lib/axlsx/content_type/default.rb +6 -37
  24. data/lib/axlsx/content_type/override.rb +6 -38
  25. data/lib/axlsx/doc_props/app.rb +10 -7
  26. data/lib/axlsx/doc_props/core.rb +6 -1
  27. data/lib/axlsx/drawing/axes.rb +61 -0
  28. data/lib/axlsx/drawing/axis.rb +20 -17
  29. data/lib/axlsx/drawing/bar_3D_chart.rb +31 -22
  30. data/lib/axlsx/drawing/cat_axis.rb +2 -10
  31. data/lib/axlsx/drawing/chart.rb +33 -6
  32. data/lib/axlsx/drawing/d_lbls.rb +21 -31
  33. data/lib/axlsx/drawing/drawing.rb +11 -17
  34. data/lib/axlsx/drawing/graphic_frame.rb +3 -3
  35. data/lib/axlsx/drawing/hyperlink.rb +42 -36
  36. data/lib/axlsx/drawing/line_3D_chart.rb +28 -70
  37. data/lib/axlsx/drawing/line_chart.rb +99 -0
  38. data/lib/axlsx/drawing/line_series.rb +20 -2
  39. data/lib/axlsx/drawing/marker.rb +38 -18
  40. data/lib/axlsx/drawing/num_data.rb +6 -6
  41. data/lib/axlsx/drawing/num_data_source.rb +17 -16
  42. data/lib/axlsx/drawing/one_cell_anchor.rb +28 -21
  43. data/lib/axlsx/drawing/pic.rb +43 -45
  44. data/lib/axlsx/drawing/picture_locking.rb +12 -44
  45. data/lib/axlsx/drawing/pie_3D_chart.rb +2 -1
  46. data/lib/axlsx/drawing/scaling.rb +13 -13
  47. data/lib/axlsx/drawing/scatter_chart.rb +32 -21
  48. data/lib/axlsx/drawing/ser_axis.rb +15 -16
  49. data/lib/axlsx/drawing/series.rb +3 -6
  50. data/lib/axlsx/drawing/str_data.rb +3 -3
  51. data/lib/axlsx/drawing/str_val.rb +7 -8
  52. data/lib/axlsx/drawing/two_cell_anchor.rb +7 -27
  53. data/lib/axlsx/drawing/val_axis.rb +14 -13
  54. data/lib/axlsx/drawing/view_3D.rb +52 -38
  55. data/lib/axlsx/drawing/vml_shape.rb +20 -79
  56. data/lib/axlsx/package.rb +58 -28
  57. data/lib/axlsx/rels/relationship.rb +75 -9
  58. data/lib/axlsx/rels/relationships.rb +8 -1
  59. data/lib/axlsx/stylesheet/border.rb +29 -20
  60. data/lib/axlsx/stylesheet/border_pr.rb +5 -4
  61. data/lib/axlsx/stylesheet/cell_alignment.rb +55 -29
  62. data/lib/axlsx/stylesheet/cell_protection.rb +7 -4
  63. data/lib/axlsx/stylesheet/cell_style.rb +19 -14
  64. data/lib/axlsx/stylesheet/color.rb +20 -17
  65. data/lib/axlsx/stylesheet/dxf.rb +4 -4
  66. data/lib/axlsx/stylesheet/font.rb +22 -22
  67. data/lib/axlsx/stylesheet/gradient_fill.rb +45 -21
  68. data/lib/axlsx/stylesheet/num_fmt.rb +23 -14
  69. data/lib/axlsx/stylesheet/pattern_fill.rb +12 -21
  70. data/lib/axlsx/stylesheet/styles.rb +37 -22
  71. data/lib/axlsx/stylesheet/table_style.rb +17 -16
  72. data/lib/axlsx/stylesheet/table_style_element.rb +15 -11
  73. data/lib/axlsx/stylesheet/table_styles.rb +14 -11
  74. data/lib/axlsx/stylesheet/xf.rb +28 -26
  75. data/lib/axlsx/util/accessors.rb +64 -0
  76. data/lib/axlsx/util/constants.rb +127 -6
  77. data/lib/axlsx/util/options_parser.rb +15 -0
  78. data/lib/axlsx/util/serialized_attributes.rb +79 -0
  79. data/lib/axlsx/util/simple_typed_list.rb +50 -15
  80. data/lib/axlsx/util/validators.rb +51 -16
  81. data/lib/axlsx/version.rb +2 -2
  82. data/lib/axlsx/workbook/defined_name.rb +14 -59
  83. data/lib/axlsx/workbook/shared_strings_table.rb +13 -4
  84. data/lib/axlsx/workbook/workbook.rb +85 -6
  85. data/lib/axlsx/workbook/worksheet/auto_filter/auto_filter.rb +77 -0
  86. data/lib/axlsx/workbook/worksheet/auto_filter/filter_column.rb +94 -0
  87. data/lib/axlsx/workbook/worksheet/auto_filter/filters.rb +246 -0
  88. data/lib/axlsx/workbook/worksheet/break.rb +37 -0
  89. data/lib/axlsx/workbook/worksheet/cell.rb +109 -107
  90. data/lib/axlsx/workbook/worksheet/cell_serializer.rb +144 -0
  91. data/lib/axlsx/workbook/worksheet/cfvo.rb +15 -15
  92. data/lib/axlsx/workbook/worksheet/cfvos.rb +15 -0
  93. data/lib/axlsx/workbook/worksheet/col.rb +43 -29
  94. data/lib/axlsx/workbook/worksheet/col_breaks.rb +35 -0
  95. data/lib/axlsx/workbook/worksheet/color_scale.rb +62 -28
  96. data/lib/axlsx/workbook/worksheet/comment.rb +28 -37
  97. data/lib/axlsx/workbook/worksheet/comments.rb +3 -3
  98. data/lib/axlsx/workbook/worksheet/conditional_formatting.rb +11 -11
  99. data/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb +31 -27
  100. data/lib/axlsx/workbook/worksheet/data_bar.rb +75 -41
  101. data/lib/axlsx/workbook/worksheet/data_validation.rb +61 -62
  102. data/lib/axlsx/workbook/worksheet/date_time_converter.rb +6 -5
  103. data/lib/axlsx/workbook/worksheet/dimension.rb +0 -1
  104. data/lib/axlsx/workbook/worksheet/header_footer.rb +54 -0
  105. data/lib/axlsx/workbook/worksheet/icon_set.rb +20 -20
  106. data/lib/axlsx/workbook/worksheet/page_margins.rb +21 -19
  107. data/lib/axlsx/workbook/worksheet/page_set_up_pr.rb +44 -0
  108. data/lib/axlsx/workbook/worksheet/page_setup.rb +152 -23
  109. data/lib/axlsx/workbook/worksheet/pane.rb +48 -51
  110. data/lib/axlsx/workbook/worksheet/pivot_table.rb +273 -0
  111. data/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb +66 -0
  112. data/lib/axlsx/workbook/worksheet/pivot_tables.rb +24 -0
  113. data/lib/axlsx/workbook/worksheet/print_options.rb +8 -30
  114. data/lib/axlsx/workbook/worksheet/protected_range.rb +16 -13
  115. data/lib/axlsx/workbook/worksheet/row.rb +59 -83
  116. data/lib/axlsx/workbook/worksheet/row_breaks.rb +33 -0
  117. data/lib/axlsx/workbook/worksheet/selection.rb +30 -38
  118. data/lib/axlsx/workbook/worksheet/sheet_calc_pr.rb +29 -0
  119. data/lib/axlsx/workbook/worksheet/sheet_format_pr.rb +60 -0
  120. data/lib/axlsx/workbook/worksheet/sheet_pr.rb +49 -4
  121. data/lib/axlsx/workbook/worksheet/sheet_protection.rb +51 -155
  122. data/lib/axlsx/workbook/worksheet/sheet_view.rb +68 -234
  123. data/lib/axlsx/workbook/worksheet/table.rb +25 -19
  124. data/lib/axlsx/workbook/worksheet/table_style_info.rb +51 -0
  125. data/lib/axlsx/workbook/worksheet/tables.rb +1 -1
  126. data/lib/axlsx/workbook/worksheet/worksheet.rb +174 -35
  127. data/lib/axlsx/workbook/worksheet/worksheet_comments.rb +6 -5
  128. data/lib/axlsx/workbook/worksheet/worksheet_drawing.rb +4 -10
  129. data/lib/axlsx/workbook/worksheet/worksheet_hyperlink.rb +74 -0
  130. data/lib/axlsx/workbook/worksheet/worksheet_hyperlinks.rb +38 -0
  131. data/lib/axlsx.rb +55 -5
  132. data/lib/schema/sml.xsd +4 -0
  133. data/test/axlsx.qcachegrind +2226 -0
  134. data/test/benchmark.rb +2 -3
  135. data/test/content_type/tc_default.rb +0 -11
  136. data/test/content_type/tc_override.rb +0 -13
  137. data/test/doc_props/tc_core.rb +7 -0
  138. data/test/drawing/tc_axes.rb +8 -0
  139. data/test/drawing/tc_axis.rb +15 -17
  140. data/test/drawing/tc_bar_3D_chart.rb +6 -0
  141. data/test/drawing/tc_cat_axis.rb +9 -9
  142. data/test/drawing/tc_chart.rb +20 -0
  143. data/test/drawing/tc_d_lbls.rb +14 -4
  144. data/test/drawing/tc_drawing.rb +0 -5
  145. data/test/drawing/tc_graphic_frame.rb +5 -3
  146. data/test/drawing/tc_hyperlink.rb +0 -4
  147. data/test/drawing/tc_line_chart.rb +39 -0
  148. data/test/drawing/tc_line_series.rb +8 -2
  149. data/test/drawing/tc_pic.rb +14 -3
  150. data/test/drawing/tc_ser_axis.rb +13 -12
  151. data/test/drawing/tc_two_cell_anchor.rb +3 -3
  152. data/test/drawing/tc_val_axis.rb +6 -6
  153. data/test/drawing/tc_vml_shape.rb +9 -3
  154. data/test/profile.rb +11 -21
  155. data/test/rels/tc_relationship.rb +32 -9
  156. data/test/rels/tc_relationships.rb +12 -1
  157. data/test/stylesheet/tc_color.rb +6 -0
  158. data/test/stylesheet/tc_styles.rb +15 -2
  159. data/test/tc_axlsx.rb +35 -2
  160. data/test/tc_helper.rb +7 -1
  161. data/test/tc_package.rb +44 -5
  162. data/test/util/tc_serialized_attributes.rb +19 -0
  163. data/test/util/tc_validators.rb +26 -1
  164. data/test/workbook/tc_defined_name.rb +2 -2
  165. data/test/workbook/tc_shared_strings_table.rb +6 -0
  166. data/test/workbook/tc_workbook.rb +38 -1
  167. data/test/workbook/worksheet/auto_filter/tc_auto_filter.rb +38 -0
  168. data/test/workbook/worksheet/auto_filter/tc_filter_column.rb +76 -0
  169. data/test/workbook/worksheet/auto_filter/tc_filters.rb +50 -0
  170. data/test/workbook/worksheet/tc_break.rb +49 -0
  171. data/test/workbook/worksheet/tc_cell.rb +40 -6
  172. data/test/workbook/worksheet/tc_col.rb +11 -1
  173. data/test/workbook/worksheet/tc_color_scale.rb +31 -2
  174. data/test/workbook/worksheet/tc_comment.rb +21 -6
  175. data/test/workbook/worksheet/tc_comments.rb +2 -2
  176. data/test/workbook/worksheet/tc_conditional_formatting.rb +7 -0
  177. data/test/workbook/worksheet/tc_data_bar.rb +7 -0
  178. data/test/workbook/worksheet/tc_date_time_converter.rb +3 -11
  179. data/test/workbook/worksheet/tc_header_footer.rb +151 -0
  180. data/test/workbook/worksheet/tc_page_set_up_pr.rb +15 -0
  181. data/test/workbook/worksheet/tc_page_setup.rb +6 -1
  182. data/test/workbook/worksheet/tc_pane.rb +5 -45
  183. data/test/workbook/worksheet/tc_pivot_table.rb +120 -0
  184. data/test/workbook/worksheet/tc_pivot_table_cache_definition.rb +54 -0
  185. data/test/workbook/worksheet/tc_row.rb +4 -4
  186. data/test/workbook/worksheet/tc_selection.rb +9 -48
  187. data/test/workbook/worksheet/tc_sheet_calc_pr.rb +18 -0
  188. data/test/workbook/worksheet/tc_sheet_format_pr.rb +88 -0
  189. data/test/workbook/worksheet/tc_sheet_pr.rb +27 -0
  190. data/test/workbook/worksheet/tc_sheet_view.rb +39 -39
  191. data/test/workbook/worksheet/{table/tc_table.rb → tc_table.rb} +8 -3
  192. data/test/workbook/worksheet/tc_table_style_info.rb +53 -0
  193. data/test/workbook/worksheet/tc_worksheet.rb +131 -16
  194. data/test/workbook/worksheet/tc_worksheet_hyperlink.rb +55 -0
  195. metadata +97 -64
  196. data/examples/example.xlsx +0 -0
  197. data/examples/example_streamed.xlsx +0 -0
  198. data/examples/extractive.pdf +0 -0
  199. data/examples/image1.gif +0 -0
  200. data/examples/image1.jpg +0 -0
  201. data/examples/image1.png +0 -0
  202. data/examples/no-use_autowidth.xlsx +0 -0
  203. data/examples/sample.png +0 -0
  204. data/examples/scraping_html.rb +0 -91
  205. data/examples/shared_strings_example.xlsx +0 -0
  206. data/examples/sheet_view.rb +0 -34
  207. data/examples/two_cell_anchor_image.rb +0 -11
  208. data/lib/axlsx/workbook/worksheet/auto_filter.rb +0 -34
@@ -3,12 +3,14 @@ module Axlsx
3
3
 
4
4
  # The Worksheet class represents a worksheet in the workbook.
5
5
  class Worksheet
6
+ include Axlsx::OptionsParser
6
7
 
7
8
  # definition of characters which are less than the maximum width of 0-9 in the default font for use in String#count.
8
9
  # This is used for autowidth calculations
9
10
  # @return [String]
10
11
  def self.thin_chars
11
- @thin_chars ||= "^.acefijklrstxyzFIJL()-"
12
+ # removed 'e' and 'y' from this list - as a GUESS
13
+ @thin_chars ||= "^.acfijklrstxzFIJL()-"
12
14
  end
13
15
 
14
16
  # Creates a new worksheet.
@@ -17,16 +19,15 @@ module Axlsx
17
19
  # @option options [String] name The name of this worksheet.
18
20
  # @option options [Hash] page_margins A hash containing page margins for this worksheet. @see PageMargins
19
21
  # @option options [Hash] print_options A hash containing print options for this worksheet. @see PrintOptions
22
+ # @option options [Hash] header_footer A hash containing header/footer options for this worksheet. @see HeaderFooter
20
23
  # @option options [Boolean] show_gridlines indicates if gridlines should be shown for this sheet.
21
24
  def initialize(wb, options={})
22
25
  self.workbook = wb
23
- @workbook.worksheets << self
24
26
  @sheet_protection = nil
25
27
 
26
28
  initialize_page_options(options)
27
- options.each do |o|
28
- self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
29
- end
29
+ parse_options options
30
+ @workbook.worksheets << self
30
31
  end
31
32
 
32
33
  # Initalizes page margin, setup and print options
@@ -35,6 +36,9 @@ module Axlsx
35
36
  @page_margins = PageMargins.new options[:page_margins] if options[:page_margins]
36
37
  @page_setup = PageSetup.new options[:page_setup] if options[:page_setup]
37
38
  @print_options = PrintOptions.new options[:print_options] if options[:print_options]
39
+ @header_footer = HeaderFooter.new options[:header_footer] if options[:header_footer]
40
+ @row_breaks = RowBreaks.new
41
+ @col_breaks = ColBreaks.new
38
42
  end
39
43
 
40
44
  # The name of the worksheet
@@ -43,6 +47,12 @@ module Axlsx
43
47
  @name ||= "Sheet" + (index+1).to_s
44
48
  end
45
49
 
50
+ # The sheet calculation properties
51
+ # @return [SheetCalcPr]
52
+ def sheet_calc_pr
53
+ @sheet_calc_pr ||= SheetCalcPr.new
54
+ end
55
+
46
56
  # The sheet protection object for this workbook
47
57
  # @return [SheetProtection]
48
58
  # @see SheetProtection
@@ -61,6 +71,15 @@ module Axlsx
61
71
  @sheet_view
62
72
  end
63
73
 
74
+ # The sheet format pr for this worksheet
75
+ # @return [SheetFormatPr]
76
+ # @see [SheetFormatPr]
77
+ def sheet_format_pr
78
+ @sheet_format_pr ||= SheetFormatPr.new
79
+ yeild @sheet_format_pr if block_given?
80
+ @sheet_format_pr
81
+ end
82
+
64
83
  # The workbook that owns this worksheet
65
84
  # @return [Workbook]
66
85
  attr_reader :workbook
@@ -70,7 +89,35 @@ module Axlsx
70
89
  def tables
71
90
  @tables ||= Tables.new self
72
91
  end
73
-
92
+
93
+ # The pivot tables in this worksheet
94
+ # @return [Array] of Table
95
+ def pivot_tables
96
+ @pivot_tables ||= PivotTables.new self
97
+ end
98
+
99
+ # A collection of column breaks added to this worksheet
100
+ # @note Please do not use this directly. Instead use
101
+ # add_page_break
102
+ # @see Worksheet#add_page_break
103
+ def col_breaks
104
+ @col_breaks ||= ColBreaks.new
105
+ end
106
+
107
+ # A collection of row breaks added to this worksheet
108
+ # @note Please do not use this directly. Instead use
109
+ # add_page_break
110
+ # @see Worksheet#add_page_break
111
+ def row_breaks
112
+ @row_breaks ||= RowBreaks.new
113
+ end
114
+
115
+ # A typed collection of hyperlinks associated with this worksheet
116
+ # @return [WorksheetHyperlinks]
117
+ def hyperlinks
118
+ @hyperlinks ||= WorksheetHyperlinks.new self
119
+ end
120
+
74
121
  # The a shortcut to the worksheet_comments list of comments
75
122
  # @return [Array|SimpleTypedList]
76
123
  def comments
@@ -85,15 +132,20 @@ module Axlsx
85
132
  @rows ||= SimpleTypedList.new Row
86
133
  end
87
134
 
88
- # returns the sheet data as columnw
89
- def cols
90
- @rows.transpose
135
+ # returns the sheet data as columns
136
+ # If you pass a block, it will be evaluated whenever a row does not have a
137
+ # cell at a specific index. The block will be called with the row and column
138
+ # index in the missing cell was found.
139
+ # @example
140
+ # cols { |row_index, column_index| p "warn - row #{row_index} is does not have a cell at #{column_index}
141
+ def cols(&block)
142
+ @rows.transpose(&block)
91
143
  end
92
144
 
93
- # An range that excel will apply an autfilter to "A1:B3"
145
+ # An range that excel will apply an auto-filter to "A1:B3"
94
146
  # This will turn filtering on for the cells in the range.
95
- # The first row is considered the header, while subsequent rows are considerd to be data.
96
- # @return String
147
+ # The first row is considered the header, while subsequent rows are considered to be data.
148
+ # @return String
97
149
  def auto_filter
98
150
  @auto_filter ||= AutoFilter.new self
99
151
  end
@@ -182,6 +234,21 @@ module Axlsx
182
234
  @print_options
183
235
  end
184
236
 
237
+ # Options for headers and footers.
238
+ # @example
239
+ # wb = Axlsx::Package.new.workbook
240
+ # # would generate something like: "file.xlsx : sheet_name 2 of 7 date with timestamp"
241
+ # header = {:different_odd_ => false, :odd_header => "&L&F : &A&C&Pof%N%R%D %T"}
242
+ # ws = wb.add_worksheet :header_footer => header
243
+ #
244
+ # @see HeaderFooter#initialize
245
+ # @return [HeaderFooter]
246
+ def header_footer
247
+ @header_footer ||= HeaderFooter.new
248
+ yield @header_footer if block_given?
249
+ @header_footer
250
+ end
251
+
185
252
  # convinience method to access all cells in this worksheet
186
253
  # @return [Array] cells
187
254
  def cells
@@ -226,7 +293,7 @@ module Axlsx
226
293
  # Indicates if gridlines should be shown in the sheet.
227
294
  # This is true by default.
228
295
  # @return [Boolean]
229
- # @deprecated Use {SheetView#show_grid_lines=} instead.
296
+ # @deprecated Use SheetView#show_grid_lines= instead.
230
297
  def show_gridlines=(v)
231
298
  warn('axlsx::DEPRECIATED: Worksheet#show_gridlines= has been depreciated. This value can be set over SheetView#show_grid_lines=.')
232
299
  Axlsx::validate_boolean v
@@ -235,7 +302,7 @@ module Axlsx
235
302
 
236
303
  # @see selected
237
304
  # @return [Boolean]
238
- # @deprecated Use {SheetView#tab_selected=} instead.
305
+ # @deprecated Use SheetView#tab_selected= instead.
239
306
  def selected=(v)
240
307
  warn('axlsx::DEPRECIATED: Worksheet#selected= has been depreciated. This value can be set over SheetView#tab_selected=.')
241
308
  Axlsx::validate_boolean v
@@ -244,7 +311,7 @@ module Axlsx
244
311
 
245
312
  # Indicates if the worksheet should show gridlines or not
246
313
  # @return Boolean
247
- # @deprecated Use {SheetView#show_grid_lines} instead.
314
+ # @deprecated Use SheetView#show_grid_lines instead.
248
315
  def show_gridlines
249
316
  warn('axlsx::DEPRECIATED: Worksheet#show_gridlines has been depreciated. This value can get over SheetView#show_grid_lines.')
250
317
  sheet_view.show_grid_lines
@@ -254,7 +321,7 @@ module Axlsx
254
321
  # It is possible to have more than one worksheet selected, however it might cause issues
255
322
  # in some older versions of excel when using copy and paste.
256
323
  # @return Boolean
257
- # @deprecated Use {SheetView#tab_selected} instead.
324
+ # @deprecated Use SheetView#tab_selected instead.
258
325
  def selected
259
326
  warn('axlsx::DEPRECIATED: Worksheet#selected has been depreciated. This value can get over SheetView#tab_selected.')
260
327
  sheet_view.tab_selected
@@ -269,7 +336,7 @@ module Axlsx
269
336
 
270
337
  # The name of the worksheet
271
338
  # The name of a worksheet must be unique in the workbook, and must not exceed 31 characters
272
- # @param [String] name
339
+ # @param [String] name
273
340
  def name=(name)
274
341
  validate_sheet_name name
275
342
  @name=Axlsx::coder.encode(name)
@@ -283,6 +350,10 @@ module Axlsx
283
350
  auto_filter.range = v
284
351
  end
285
352
 
353
+ # Accessor for controlling whether leading and trailing spaces in cells are
354
+ # preserved or ignored. The default is to preserve spaces.
355
+ attr_accessor :preserve_spaces
356
+
286
357
  # The part name of this worksheet
287
358
  # @return [String]
288
359
  def pn
@@ -295,10 +366,11 @@ module Axlsx
295
366
  "#{WORKSHEET_RELS_PN % (index+1)}"
296
367
  end
297
368
 
298
- # The relationship Id of thiw worksheet
369
+ # The relationship id of this worksheet.
299
370
  # @return [String]
371
+ # @see Relationship#Id
300
372
  def rId
301
- "rId#{index+1}"
373
+ @workbook.relationships.for(self).Id
302
374
  end
303
375
 
304
376
  # The index of this worksheet in the owning Workbook's worksheets list.
@@ -377,7 +449,7 @@ module Axlsx
377
449
  cf = ConditionalFormatting.new( :sqref => cells )
378
450
  cf.add_rules rules
379
451
  conditional_formattings << cf
380
- conditional_formattings
452
+ conditional_formattings
381
453
  end
382
454
 
383
455
  # Add data validation to this worksheet.
@@ -391,6 +463,14 @@ module Axlsx
391
463
  data_validations << dv
392
464
  end
393
465
 
466
+ # Adds a new hyperlink to the worksheet
467
+ # @param [Hash] options for the hyperlink
468
+ # @see WorksheetHyperlink for a list of options
469
+ # @return [WorksheetHyperlink]
470
+ def add_hyperlink(options={})
471
+ hyperlinks.add(options)
472
+ end
473
+
394
474
  # 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.
395
475
  # @param [Class] chart_type
396
476
  # @option options [Array] start_at
@@ -417,6 +497,12 @@ module Axlsx
417
497
  tables.last
418
498
  end
419
499
 
500
+ def add_pivot_table(ref, range, options={})
501
+ pivot_tables << PivotTable.new(ref, range, self, options)
502
+ yield pivot_tables.last if block_given?
503
+ pivot_tables.last
504
+ end
505
+
420
506
  # Shortcut to worsksheet_comments#add_comment
421
507
  def add_comment(options={})
422
508
  worksheet_comments.add_comment(options)
@@ -430,6 +516,24 @@ module Axlsx
430
516
  image
431
517
  end
432
518
 
519
+ # Adds a page break (row break) to the worksheet
520
+ # @param cell A Cell object or excel style string reference indicating where the break
521
+ # should be added to the sheet.
522
+ # @example
523
+ # ws.add_page_break("A4")
524
+ def add_page_break(cell)
525
+ DataTypeValidator.validate "Worksheet#add_page_break cell", [String, Cell], cell
526
+ column_index, row_index = if cell.is_a?(String)
527
+ Axlsx.name_to_indices(cell)
528
+ else
529
+ cell.pos
530
+ end
531
+ if column_index > 0
532
+ col_breaks.add_break(:id => column_index)
533
+ end
534
+ row_breaks.add_break(:id => row_index)
535
+ end
536
+
433
537
  # This is a helper method that Lets you specify a fixed width for multiple columns in a worksheet in one go.
434
538
  # Axlsx is sparse, so if you have not set data for a column, you cannot set the width.
435
539
  # 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.
@@ -477,22 +581,25 @@ module Axlsx
477
581
  # This intentionally does not use nokogiri for performance reasons
478
582
  # @return [String]
479
583
  def to_xml_string
584
+ auto_filter.apply if auto_filter.range
480
585
  str = '<?xml version="1.0" encoding="UTF-8"?>'
481
586
  str << worksheet_node
482
587
  serializable_parts.each do |item|
483
588
  item.to_xml_string(str) if item
484
589
  end
485
590
  str << '</worksheet>'
486
- str.gsub(/[[:cntrl:]]/,'')
591
+ Axlsx::sanitize(str)
487
592
  end
488
593
 
489
594
  # The worksheet relationships. This is managed automatically by the worksheet
490
595
  # @return [Relationships]
491
596
  def relationships
492
597
  r = Relationships.new
493
- r + [tables.relationships,
494
- worksheet_comments.relationships,
495
- worksheet_drawing.relationship].flatten.compact || []
598
+ r + [tables.relationships,
599
+ worksheet_comments.relationships,
600
+ hyperlinks.relationships,
601
+ worksheet_drawing.relationship,
602
+ pivot_tables.relationships].flatten.compact || []
496
603
  r
497
604
  end
498
605
 
@@ -531,25 +638,57 @@ module Axlsx
531
638
  @styles ||= self.workbook.styles
532
639
  end
533
640
 
641
+ # shortcut level to specify the outline level for a series of rows
642
+ # Oulining is what lets you add collapse and expand to a data set.
643
+ # @param [Integer] start_index The zero based index of the first row of outlining.
644
+ # @param [Integer] end_index The zero based index of the last row to be outlined
645
+ # @param [integer] level The level of outline to apply
646
+ # @param [Integer] collapsed The initial collapsed state of the outline group
647
+ def outline_level_rows(start_index, end_index, level = 1, collapsed = true)
648
+ outline rows, (start_index..end_index), level, collapsed
649
+ end
650
+
651
+ # shortcut level to specify the outline level for a series of columns
652
+ # Oulining is what lets you add collapse and expand to a data set.
653
+ # @param [Integer] start_index The zero based index of the first column of outlining.
654
+ # @param [Integer] end_index The zero based index of the last column to be outlined
655
+ # @param [integer] level The level of outline to apply
656
+ # @param [Integer] collapsed The initial collapsed state of the outline group
657
+ def outline_level_columns(start_index, end_index, level = 1, collapsed = true)
658
+ outline column_info, (start_index..end_index), level, collapsed
659
+ end
534
660
 
535
661
  private
536
662
 
663
+ def xml_space
664
+ workbook.xml_space
665
+ end
666
+
667
+ def outline(collection, range, level = 1, collapsed = true)
668
+ range.each do |index|
669
+ unless (item = collection[index]).nil?
670
+ item.outline_level = level
671
+ item.hidden = collapsed
672
+ end
673
+ sheet_view.show_outline_symbols = true
674
+ end
675
+ end
676
+
537
677
  def validate_sheet_name(name)
538
678
  DataTypeValidator.validate "Worksheet.name", String, name
539
679
  raise ArgumentError, (ERR_SHEET_NAME_TOO_LONG % name) if name.size > 31
540
- raise ArgumentError, (ERR_SHEET_NAME_COLON_FORBIDDEN % name) if name.include? ':'
541
- name = Axlsx::coder.encode(name)
542
- sheet_names = @workbook.worksheets.map { |s| s.name }
680
+ raise ArgumentError, (ERR_SHEET_NAME_CHARACTER_FORBIDDEN % name) if '[]*/\?:'.chars.any? { |char| name.include? char }
681
+ name = Axlsx::coder.encode(name)
682
+ sheet_names = @workbook.worksheets.reject { |s| s == self }.map { |s| s.name }
543
683
  raise ArgumentError, (ERR_DUPLICATE_SHEET_NAME % name) if sheet_names.include?(name)
544
684
  end
545
685
 
546
-
547
686
  def serializable_parts
548
- [sheet_pr, dimension, sheet_view, column_info,
549
- sheet_data, @sheet_protection, protected_ranges,
687
+ [sheet_pr, dimension, sheet_view, sheet_format_pr, column_info,
688
+ sheet_data, sheet_calc_pr, @sheet_protection, protected_ranges,
550
689
  auto_filter, merged_cells, conditional_formattings,
551
- data_validations, print_options, page_margins,
552
- page_setup, worksheet_drawing, worksheet_comments,
690
+ data_validations, hyperlinks, print_options, page_margins,
691
+ page_setup, header_footer, row_breaks, col_breaks, worksheet_drawing, worksheet_comments,
553
692
  tables]
554
693
  end
555
694
 
@@ -569,7 +708,7 @@ module Axlsx
569
708
  # @see Worksheet#protect_range
570
709
  # @return [SimpleTypedList] The protected ranges for this worksheet
571
710
  def protected_ranges
572
- @protected_ranges ||= ProtectedRanges.new self
711
+ @protected_ranges ||= ProtectedRanges.new self
573
712
  # SimpleTypedList.new ProtectedRange
574
713
  end
575
714
 
@@ -582,7 +721,7 @@ module Axlsx
582
721
  # data validations array
583
722
  # @return [Array]
584
723
  def data_validations
585
- @data_validations ||= DataValidations.new self
724
+ @data_validations ||= DataValidations.new self
586
725
  end
587
726
 
588
727
  # merged cells array
@@ -595,7 +734,7 @@ module Axlsx
595
734
  # Helper method for parsingout the root node for worksheet
596
735
  # @return [String]
597
736
  def worksheet_node
598
- "<worksheet xmlns=\"%s\" xmlns:r=\"%s\">" % [XML_NS, XML_NS_R]
737
+ "<worksheet xmlns=\"%s\" xmlns:r=\"%s\" xml:space=\"#{xml_space}\">" % [XML_NS, XML_NS_R]
599
738
  end
600
739
 
601
740
  def sheet_data
@@ -40,10 +40,11 @@ module Axlsx
40
40
  !comments.empty?
41
41
  end
42
42
 
43
- # The index in the worksheet's relationships for the VML drawing that will render the comments
44
- # @return [Integer]
45
- def index
46
- worksheet.relationships.index { |r| r.Type == VML_DRAWING_R } + 1
43
+ # The relationship id of the VML drawing that will render the comments.
44
+ # @see Relationship#Id
45
+ # @return [String]
46
+ def drawing_rId
47
+ comments.relationships.find{ |r| r.Type == VML_DRAWING_R }.Id
47
48
  end
48
49
 
49
50
  # Seraalize the object
@@ -51,7 +52,7 @@ module Axlsx
51
52
  # @return [String]
52
53
  def to_xml_string(str = '')
53
54
  return unless has_comments?
54
- str << "<legacyDrawing r:id='rId#{index}' />"
55
+ str << "<legacyDrawing r:id='#{drawing_rId}' />"
55
56
  end
56
57
  end
57
58
  end
@@ -10,7 +10,7 @@ module Axlsx
10
10
  # @param [Worksheet] worksheet
11
11
  def initialize(worksheet)
12
12
  raise ArgumentError, 'you must provide a worksheet' unless worksheet.is_a?(Worksheet)
13
- @worksheet = worksheet
13
+ @worksheet = worksheet
14
14
  @drawing = nil
15
15
  end
16
16
 
@@ -41,24 +41,18 @@ module Axlsx
41
41
  @drawing.is_a? Drawing
42
42
  end
43
43
 
44
- # The relationship required by this object
44
+ # The relationship instance for this drawing.
45
45
  # @return [Relationship]
46
46
  def relationship
47
47
  return unless has_drawing?
48
- Relationship.new(DRAWING_R, "../#{drawing.pn}")
49
- end
50
-
51
- # returns the index of the worksheet releationship that defines this drawing.
52
- # @return [Integer]
53
- def index
54
- worksheet.relationships.index{ |r| r.Type == DRAWING_R } +1
48
+ Relationship.new(self, DRAWING_R, "../#{drawing.pn}")
55
49
  end
56
50
 
57
51
  # Serialize the drawing for the worksheet
58
52
  # @param [String] str
59
53
  def to_xml_string(str = '')
60
54
  return unless has_drawing?
61
- str << "<drawing r:id='rId#{index}'/>"
55
+ str << "<drawing r:id='#{relationship.Id}'/>"
62
56
  end
63
57
  end
64
58
  end
@@ -0,0 +1,74 @@
1
+ module Axlsx
2
+
3
+ # A worksheet hyperlink object. Note that this is not the same as a drawing hyperlink object.
4
+ class WorksheetHyperlink
5
+
6
+ include Axlsx::OptionsParser
7
+ include Axlsx::Accessors
8
+ include Axlsx::SerializedAttributes
9
+ # Creates a new hyperlink object.
10
+ # @note the preferred way to add hyperlinks to your worksheet is the Worksheet#add_hyperlink method
11
+ # @param [Worksheet] worksheet the Worksheet that owns this hyperlink
12
+ # @param [Hash] options options to use when creating this hyperlink
13
+ # @option [String] display Display string, if different from string in string table. This is a property on the hyperlink object, but does not need to appear in the spreadsheet application UI.
14
+ # @option [String] location Location within target. If target is a workbook (or this workbook) this shall refer to a sheet and cell or a defined name. Can also be an HTML anchor if target is HTML file.
15
+ # @option [String] tooltip The tip to display when the user positions the mouse cursor over this hyperlink
16
+ # @option [Symbol] target This is :external by default. If you set it to anything else, the location is interpreted to be the current workbook.
17
+ # @option [String|Cell] ref The location of this hyperlink in the worksheet
18
+ def initialize(worksheet, options={})
19
+ DataTypeValidator.validate "Hyperlink.worksheet", [Worksheet], worksheet
20
+ @worksheet = worksheet
21
+ @target = :external
22
+ parse_options options
23
+ yield self if block_given?
24
+ end
25
+
26
+ string_attr_accessor :display, :location, :tooltip
27
+
28
+ serializable_attributes :display, :tooltip, :ref
29
+
30
+ #Cell location of hyperlink on worksheet.
31
+ # @return [String]
32
+ attr_reader :ref
33
+
34
+ # Sets the target for this hyperlink. Anything other than :external instructs the library to treat the location as an in-workbook reference.
35
+ # @param [Symbol] target
36
+ def target=(target)
37
+ @target = target
38
+ end
39
+
40
+ # Sets the cell location of this hyperlink in the worksheet
41
+ # @param [String|Cell] cell_reference The string reference or cell that defines where this hyperlink shows in the worksheet.
42
+ def ref=(cell_reference)
43
+ cell_reference = cell_reference.r if cell_reference.is_a?(Cell)
44
+ Axlsx::validate_string cell_reference
45
+ @ref = cell_reference
46
+ end
47
+
48
+ # The relationship instance for this hyperlink.
49
+ # A relationship is only required if `@target` is `:external`. If not, this method will simply return `nil`.
50
+ # @see #target=
51
+ # @return [Relationship]
52
+ def relationship
53
+ return unless @target == :external
54
+ Relationship.new(self, HYPERLINK_R, location, :target_mode => :External)
55
+ end
56
+
57
+ # Seralize the object
58
+ # @param [String] str
59
+ # @return [String]
60
+ def to_xml_string(str='')
61
+ str << '<hyperlink '
62
+ serialized_attributes str, location_or_id
63
+ str << '/>'
64
+ end
65
+
66
+ # The values to be used in serialization based on the target.
67
+ # location should only be specified for non-external targets.
68
+ # r:id should only be specified for external targets.
69
+ # @return [Hash]
70
+ def location_or_id
71
+ @target == :external ? { :"r:id" => relationship.Id } : { :location => Axlsx::coder.encode(location) }
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,38 @@
1
+ module Axlsx
2
+
3
+ #A collection of hyperlink objects for a worksheet
4
+ class WorksheetHyperlinks < SimpleTypedList
5
+
6
+ # Creates a new Hyperlinks collection
7
+ # @param [Worksheet] worksheet the worksheet that owns these hyperlinks
8
+ def initialize(worksheet)
9
+ DataTypeValidator.validate "Hyperlinks.worksheet", [Worksheet], worksheet
10
+ @worksheet = worksheet
11
+ super WorksheetHyperlink
12
+ end
13
+
14
+ # Creates and adds a new hyperlink based on the options provided
15
+ # @see WorksheetHyperlink#initialize
16
+ # @return [WorksheetHyperlink]
17
+ def add(options)
18
+ @list << WorksheetHyperlink.new(@worksheet, options)
19
+ @list.last
20
+ end
21
+
22
+ # The relationships required by this collection's hyperlinks
23
+ # @return Array
24
+ def relationships
25
+ return [] if empty?
26
+ map { |hyperlink| hyperlink.relationship }
27
+ end
28
+
29
+ # seralize the collection of hyperlinks
30
+ # @return [String]
31
+ def to_xml_string(str='')
32
+ return if empty?
33
+ str << '<hyperlinks>'
34
+ @list.each { |hyperlink| hyperlink.to_xml_string(str) }
35
+ str << '</hyperlinks>'
36
+ end
37
+ end
38
+ end
data/lib/axlsx.rb CHANGED
@@ -5,7 +5,9 @@ require 'axlsx/version.rb'
5
5
  require 'axlsx/util/simple_typed_list.rb'
6
6
  require 'axlsx/util/constants.rb'
7
7
  require 'axlsx/util/validators.rb'
8
-
8
+ require 'axlsx/util/accessors.rb'
9
+ require 'axlsx/util/serialized_attributes'
10
+ require 'axlsx/util/options_parser'
9
11
  # to be included with parsable intitites.
10
12
  #require 'axlsx/util/parser.rb'
11
13
 
@@ -37,15 +39,25 @@ if !Object.respond_to?(:instance_values)
37
39
  end
38
40
  end
39
41
 
40
- # xlsx generation with charts, images, automated column width, customizable styles and full schema validation. Axlsx excels at helping you generate beautiful Office Open XML Spreadsheet documents without having to understand the entire ECMA specification. Check out the README for some examples of how easy it is. Best of all, you can validate your xlsx file before serialization so you know for sure that anything generated is going to load on your client's machine.
42
+ # xlsx generation with charts, images, automated column width, customizable styles
43
+ # and full schema validation. Axlsx excels at helping you generate beautiful
44
+ # Office Open XML Spreadsheet documents without having to understand the entire
45
+ # ECMA specification. Check out the README for some examples of how easy it is.
46
+ # Best of all, you can validate your xlsx file before serialization so you know
47
+ # for sure that anything generated is going to load on your client's machine.
41
48
  module Axlsx
42
49
 
43
50
  # determines the cell range for the items provided
44
51
  def self.cell_range(cells, absolute=true)
45
52
  return "" unless cells.first.is_a? Cell
46
- sort_cells(cells)
53
+ cells = sort_cells(cells)
47
54
  reference = "#{cells.first.reference(absolute)}:#{cells.last.reference(absolute)}"
48
- absolute ? "'#{cells.first.row.worksheet.name}'!#{reference}" : reference
55
+ if absolute
56
+ escaped_name = cells.first.row.worksheet.name.gsub "&apos;", "''"
57
+ "'#{escaped_name}'!#{reference}"
58
+ else
59
+ reference
60
+ end
49
61
  end
50
62
 
51
63
  # sorts the array of cells provided to start from the minimum x,y to
@@ -61,10 +73,11 @@ module Axlsx
61
73
  def self.coder
62
74
  @@coder ||= ::HTMLEntities.new
63
75
  end
64
-
76
+
65
77
  # returns the x, y position of a cell
66
78
  def self.name_to_indices(name)
67
79
  raise ArgumentError, 'invalid cell name' unless name.size > 1
80
+ # capitalization?!?
68
81
  v = name[/[A-Z]+/].reverse.chars.reduce({:base=>1, :i=>0}) do |val, c|
69
82
  val[:i] += ((c.bytes.first - 64) * val[:base]); val[:base] *= 26; val
70
83
  end
@@ -91,12 +104,49 @@ module Axlsx
91
104
  Axlsx::col_ref(c_index).to_s << (r_index+1).to_s
92
105
  end
93
106
 
107
+ # Creates an array of individual cell references based on an excel reference range.
108
+ # @param [String] range A cell range, for example A1:D5
109
+ # @return [Array]
110
+ def self.range_to_a(range)
111
+ range.match(/^(\w+?\d+)\:(\w+?\d+)$/)
112
+ start_col, start_row = name_to_indices($1)
113
+ end_col, end_row = name_to_indices($2)
114
+ (start_row..end_row).to_a.map do |row_num|
115
+ (start_col..end_col).to_a.map do |col_num|
116
+ "#{col_ref(col_num)}#{row_num+1}"
117
+ end
118
+ end
119
+ end
120
+
94
121
  # performs the increadible feat of changing snake_case to CamelCase
95
122
  # @param [String] s The snake case string to camelize
96
123
  # @return [String]
97
124
  def self.camel(s="", all_caps = true)
125
+ s = s.to_s
98
126
  s = s.capitalize if all_caps
99
127
  s.gsub(/_(.)/){ $1.upcase }
100
128
  end
101
129
 
130
+ # returns the provided string with all invalid control charaters
131
+ # removed.
132
+ # @param [String] str The sting to process
133
+ # @return [String]
134
+ def self.sanitize(str)
135
+ str.gsub(CONTROL_CHAR_REGEX, '')
136
+ end
137
+
138
+
139
+ # Instructs the serializer to not try to escape cell value input.
140
+ # This will give you a huge speed bonus, but if you content has <, > or other xml character data
141
+ # the workbook will be invalid and excel will complain.
142
+ def self.trust_input
143
+ @trust_input ||= false
144
+ end
145
+
146
+ # @param[Boolean] trust_me A boolean value indicating if the cell value content is to be trusted
147
+ # @return [Boolean]
148
+ # @see Axlsx::trust_input
149
+ def self.trust_input=(trust_me)
150
+ @trust_input = trust_me
151
+ end
102
152
  end