axlsx 1.0.17 → 1.1.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 (167) hide show
  1. data/CHANGELOG.md +11 -3
  2. data/README.md +159 -51
  3. data/examples/example.csv +1000 -0
  4. data/examples/example.rb +255 -150
  5. data/examples/example.xlsx +0 -0
  6. data/examples/example_streamed.xlsx +0 -0
  7. data/examples/no-use_autowidth.xlsx +0 -0
  8. data/examples/shared_strings_example.xlsx +0 -0
  9. data/lib/axlsx/content_type/content_type.rb +12 -12
  10. data/lib/axlsx/content_type/default.rb +9 -6
  11. data/lib/axlsx/content_type/override.rb +12 -8
  12. data/lib/axlsx/doc_props/app.rb +37 -40
  13. data/lib/axlsx/doc_props/core.rb +12 -17
  14. data/lib/axlsx/drawing/axis.rb +38 -19
  15. data/lib/axlsx/drawing/bar_3D_chart.rb +33 -32
  16. data/lib/axlsx/drawing/bar_series.rb +13 -14
  17. data/lib/axlsx/drawing/cat_axis.rb +15 -14
  18. data/lib/axlsx/drawing/cat_axis_data.rb +16 -18
  19. data/lib/axlsx/drawing/chart.rb +39 -40
  20. data/lib/axlsx/drawing/drawing.rb +15 -12
  21. data/lib/axlsx/drawing/graphic_frame.rb +21 -21
  22. data/lib/axlsx/drawing/hyperlink.rb +12 -11
  23. data/lib/axlsx/drawing/line_3D_chart.rb +30 -28
  24. data/lib/axlsx/drawing/line_series.rb +11 -11
  25. data/lib/axlsx/drawing/marker.rb +10 -8
  26. data/lib/axlsx/drawing/named_axis_data.rb +36 -0
  27. data/lib/axlsx/drawing/one_cell_anchor.rb +17 -16
  28. data/lib/axlsx/drawing/pic.rb +25 -37
  29. data/lib/axlsx/drawing/picture_locking.rb +21 -18
  30. data/lib/axlsx/drawing/pie_3D_chart.rb +10 -8
  31. data/lib/axlsx/drawing/pie_series.rb +15 -12
  32. data/lib/axlsx/drawing/scaling.rb +10 -10
  33. data/lib/axlsx/drawing/scatter_chart.rb +69 -0
  34. data/lib/axlsx/drawing/scatter_series.rb +39 -0
  35. data/lib/axlsx/drawing/ser_axis.rb +10 -10
  36. data/lib/axlsx/drawing/series.rb +15 -15
  37. data/lib/axlsx/drawing/series_title.rb +14 -14
  38. data/lib/axlsx/drawing/title.rb +26 -26
  39. data/lib/axlsx/drawing/two_cell_anchor.rb +18 -20
  40. data/lib/axlsx/drawing/val_axis.rb +8 -7
  41. data/lib/axlsx/drawing/val_axis_data.rb +17 -17
  42. data/lib/axlsx/drawing/view_3D.rb +22 -20
  43. data/lib/axlsx/package.rb +60 -43
  44. data/lib/axlsx/rels/relationship.rb +11 -8
  45. data/lib/axlsx/rels/relationships.rb +7 -1
  46. data/lib/axlsx/stylesheet/#num_fmt.rb# +69 -0
  47. data/lib/axlsx/stylesheet/border.rb +27 -23
  48. data/lib/axlsx/stylesheet/border_pr.rb +16 -15
  49. data/lib/axlsx/stylesheet/cell_alignment.rb +23 -21
  50. data/lib/axlsx/stylesheet/cell_protection.rb +10 -7
  51. data/lib/axlsx/stylesheet/cell_style.rb +8 -5
  52. data/lib/axlsx/stylesheet/color.rb +20 -14
  53. data/lib/axlsx/stylesheet/fill.rb +7 -5
  54. data/lib/axlsx/stylesheet/font.rb +21 -14
  55. data/lib/axlsx/stylesheet/gradient_fill.rb +19 -16
  56. data/lib/axlsx/stylesheet/gradient_stop.rb +9 -5
  57. data/lib/axlsx/stylesheet/num_fmt.rb +12 -6
  58. data/lib/axlsx/stylesheet/pattern_fill.rb +25 -10
  59. data/lib/axlsx/stylesheet/styles.rb +47 -35
  60. data/lib/axlsx/stylesheet/table_style.rb +9 -4
  61. data/lib/axlsx/stylesheet/table_style_element.rb +10 -7
  62. data/lib/axlsx/stylesheet/table_styles.rb +11 -8
  63. data/lib/axlsx/stylesheet/xf.rb +29 -25
  64. data/lib/axlsx/util/constants.rb +20 -14
  65. data/lib/axlsx/util/simple_typed_list.rb +18 -9
  66. data/lib/axlsx/util/validators.rb +13 -6
  67. data/lib/axlsx/version.rb +1 -1
  68. data/lib/axlsx/workbook/shared_strings_table.rb +19 -21
  69. data/lib/axlsx/workbook/workbook.rb +58 -34
  70. data/lib/axlsx/workbook/worksheet/cell.rb +149 -132
  71. data/lib/axlsx/workbook/worksheet/col.rb +114 -0
  72. data/lib/axlsx/workbook/worksheet/col.rb~ +0 -0
  73. data/lib/axlsx/workbook/worksheet/date_time_converter.rb +29 -0
  74. data/lib/axlsx/workbook/worksheet/page_margins.rb +97 -0
  75. data/lib/axlsx/workbook/worksheet/row.rb +40 -23
  76. data/lib/axlsx/workbook/worksheet/table.rb +96 -0
  77. data/lib/axlsx/workbook/worksheet/table.rb~ +97 -0
  78. data/lib/axlsx/workbook/worksheet/worksheet.rb +243 -127
  79. data/lib/axlsx.rb +30 -9
  80. data/lib/schema/dc.xsd +5 -5
  81. data/lib/schema/dcmitype.xsd +5 -3
  82. data/lib/schema/dcterms.xsd +15 -15
  83. data/lib/schema/opc-coreProperties.xsd +6 -2
  84. data/lib/schema/xml.xsd +7 -8
  85. data/test/#benchmark.txt# +7 -0
  86. data/test/#tc_helper.rb# +3 -0
  87. data/test/benchmark.rb +81 -0
  88. data/test/benchmark.rb~ +0 -0
  89. data/test/benchmark.txt +6 -0
  90. data/test/benchmark.txt~ +6 -0
  91. data/test/content_type/tc_content_type.rb +30 -32
  92. data/test/content_type/tc_default.rb +8 -23
  93. data/test/content_type/tc_override.rb +7 -21
  94. data/test/doc_props/tc_app.rb +2 -8
  95. data/test/doc_props/tc_core.rb +6 -7
  96. data/test/drawing/tc_axis.rb +7 -3
  97. data/test/drawing/tc_bar_3D_chart.rb +6 -7
  98. data/test/drawing/tc_bar_series.rb +4 -5
  99. data/test/drawing/tc_cat_axis.rb +2 -3
  100. data/test/drawing/tc_cat_axis_data.rb +2 -3
  101. data/test/drawing/tc_chart.rb +13 -14
  102. data/test/drawing/tc_drawing.rb +7 -8
  103. data/test/drawing/tc_graphic_frame.rb +3 -4
  104. data/test/drawing/tc_hyperlink.rb +2 -3
  105. data/test/drawing/tc_line_3d_chart.rb +5 -6
  106. data/test/drawing/tc_line_series.rb +3 -4
  107. data/test/drawing/tc_marker.rb +3 -4
  108. data/test/drawing/tc_one_cell_anchor.rb +6 -7
  109. data/test/drawing/tc_pic.rb +8 -9
  110. data/test/drawing/tc_picture_locking.rb +2 -3
  111. data/test/drawing/tc_pie_3D_chart.rb +5 -6
  112. data/test/drawing/tc_pie_series.rb +4 -5
  113. data/test/drawing/tc_scaling.rb +3 -4
  114. data/test/drawing/tc_scatter_chart.rb +43 -0
  115. data/test/drawing/tc_scatter_series.rb +20 -0
  116. data/test/drawing/tc_ser_axis.rb +2 -3
  117. data/test/drawing/tc_series.rb +4 -5
  118. data/test/drawing/tc_series_title.rb +4 -5
  119. data/test/drawing/tc_title.rb +4 -5
  120. data/test/drawing/tc_two_cell_anchor.rb +4 -5
  121. data/test/drawing/tc_val_axis.rb +2 -3
  122. data/test/drawing/tc_val_axis_data.rb +2 -3
  123. data/test/drawing/tc_view_3D.rb +6 -7
  124. data/test/example.csv +1000 -0
  125. data/test/example.xlsx +0 -0
  126. data/test/example_streamed.xlsx +0 -0
  127. data/test/profile.rb +33 -0
  128. data/test/rels/tc_relationship.rb +5 -6
  129. data/test/rels/tc_relationships.rb +4 -5
  130. data/test/stylesheet/tc_border.rb +3 -4
  131. data/test/stylesheet/tc_border_pr.rb +3 -4
  132. data/test/stylesheet/tc_cell_alignment.rb +10 -6
  133. data/test/stylesheet/tc_cell_protection.rb +2 -3
  134. data/test/stylesheet/tc_cell_style.rb +2 -3
  135. data/test/stylesheet/tc_color.rb +2 -3
  136. data/test/stylesheet/tc_fill.rb +1 -2
  137. data/test/stylesheet/tc_font.rb +12 -5
  138. data/test/stylesheet/tc_gradient_fill.rb +1 -2
  139. data/test/stylesheet/tc_gradient_stop.rb +1 -2
  140. data/test/stylesheet/tc_num_fmt.rb +1 -2
  141. data/test/stylesheet/tc_pattern_fill.rb +3 -4
  142. data/test/stylesheet/tc_styles.rb +16 -6
  143. data/test/stylesheet/tc_table_style.rb +2 -3
  144. data/test/stylesheet/tc_table_style_element.rb +2 -3
  145. data/test/stylesheet/tc_table_styles.rb +3 -4
  146. data/test/stylesheet/tc_xf.rb +16 -17
  147. data/test/tc_axlsx.rb +39 -0
  148. data/test/tc_axlsx.rb~ +0 -0
  149. data/test/tc_helper.rb +3 -0
  150. data/test/tc_helper.rb~ +3 -0
  151. data/test/tc_package.rb +13 -10
  152. data/test/util/tc_simple_typed_list.rb +8 -9
  153. data/test/util/tc_validators.rb +7 -8
  154. data/test/workbook/tc_shared_strings_table.rb +5 -6
  155. data/test/workbook/tc_workbook.rb +24 -6
  156. data/test/workbook/worksheet/table/tc_table.rb +71 -0
  157. data/test/workbook/worksheet/table/tc_table.rb~ +72 -0
  158. data/test/workbook/worksheet/tc_cell.rb +62 -18
  159. data/test/workbook/worksheet/tc_col.rb +59 -0
  160. data/test/workbook/worksheet/tc_col.rb~ +10 -0
  161. data/test/workbook/worksheet/tc_date_time_converter.rb +126 -0
  162. data/test/workbook/worksheet/tc_page_margins.rb +97 -0
  163. data/test/workbook/worksheet/tc_row.rb +54 -4
  164. data/test/workbook/worksheet/tc_worksheet.rb +177 -34
  165. metadata +69 -41
  166. data/test/drawing/tc_hyperlink.rb~ +0 -71
  167. data/test/workbook/tc_shared_strings_table.rb~ +0 -8
@@ -1,9 +1,9 @@
1
1
  # encoding: UTF-8
2
2
  module Axlsx
3
-
4
- # The Worksheet class represents a worksheet in the workbook.
3
+
4
+ # The Worksheet class represents a worksheet in the workbook.
5
5
  class Worksheet
6
-
6
+
7
7
  # The name of the worksheet
8
8
  # @return [String]
9
9
  attr_reader :name
@@ -12,6 +12,9 @@ module Axlsx
12
12
  # @return [Workbook]
13
13
  attr_reader :workbook
14
14
 
15
+ # The tables in this worksheet
16
+ # @return [Array] of Table
17
+ attr_reader :tables
15
18
 
16
19
  # The rows in this worksheet
17
20
  # @note The recommended way to manage rows is Worksheet#add_row
@@ -21,7 +24,7 @@ module Axlsx
21
24
 
22
25
  # An array of content based calculated column widths.
23
26
  # @note a single auto fit data item is a hash with :longest => [String] and :sz=> [Integer] members.
24
- # @return [Array] of Hash
27
+ # @return [Array] of Hash
25
28
  attr_reader :auto_fit_data
26
29
 
27
30
  # An array of merged cell ranges e.d "A1:B3"
@@ -34,21 +37,85 @@ module Axlsx
34
37
  # The first row is considered the header, while subsequent rows are considerd to be data.
35
38
  # @return Array
36
39
  attr_reader :auto_filter
37
-
40
+
41
+ # Indicates if the worksheet should show gridlines or not
42
+ # @return Boolean
43
+ attr_reader :show_gridlines
44
+
45
+
46
+ # Indicates if the worksheet is selected in the workbook
47
+ # It is possible to have more than one worksheet selected, however it might cause issues
48
+ # in some older versions of excel when using copy and paste.
49
+ # @return Boolean
50
+ attr_reader :selected
51
+
52
+ # Indicates if the worksheet should print in a single page
53
+ # @return Boolean
54
+ attr_reader :fit_to_page
55
+
56
+
57
+ # Column info for the sheet
58
+ # @return [SimpleTypedList]
59
+ attr_reader :column_info
60
+
61
+ # Page margins for printing the worksheet.
62
+ # @example
63
+ # wb = Axlsx::Package.new.workbook
64
+ # # using options when creating the worksheet.
65
+ # ws = wb.add_worksheet :page_margins => {:left => 1.9, :header => 0.1}
66
+ #
67
+ # # use the set method of the page_margins object
68
+ # ws.page_margins.set(:bottom => 3, :footer => 0.7)
69
+ #
70
+ # # set page margins in a block
71
+ # ws.page_margins do |margins|
72
+ # margins.right = 6
73
+ # margins.top = 0.2
74
+ # end
75
+ # @see PageMargins#initialize
76
+ # @return [PageMargins]
77
+ def page_margins
78
+ @page_margins ||= PageMargins.new
79
+ yield @page_margins if block_given?
80
+ @page_margins
81
+
82
+ end
83
+
38
84
  # Creates a new worksheet.
39
85
  # @note the recommended way to manage worksheets is Workbook#add_worksheet
40
86
  # @see Workbook#add_worksheet
41
- # @option options [String] name The name of this sheet.
87
+ # @option options [String] name The name of this worksheet.
88
+ # @option options [Hash] page_margins A hash containing page margins for this worksheet. @see PageMargins
89
+ # @option options [Boolean] show_gridlines indicates if gridlines should be shown for this sheet.
42
90
  def initialize(wb, options={})
43
- @drawing = nil
44
- @rows = SimpleTypedList.new Row
45
91
  self.workbook = wb
46
92
  @workbook.worksheets << self
47
- @auto_fit_data = []
48
- self.name = options[:name] || "Sheet" + (index+1).to_s
49
- @magick_draw = Magick::Draw.new
50
- @cols = SimpleTypedList.new Cell
93
+
94
+ @drawing = @page_margins = @auto_filter = nil
51
95
  @merged_cells = []
96
+ @auto_fit_data = []
97
+
98
+ @selected = false
99
+ @show_gridlines = true
100
+ self.name = "Sheet" + (index+1).to_s
101
+ @page_margins = PageMargins.new options[:page_margins] if options[:page_margins]
102
+
103
+ @rows = SimpleTypedList.new Row
104
+ @column_info = SimpleTypedList.new Col
105
+ # @cols = SimpleTypedList.new Cell
106
+ @tables = SimpleTypedList.new Table
107
+
108
+ if self.workbook.use_autowidth
109
+ require 'RMagick' unless defined?(Magick)
110
+ @magick_draw = Magick::Draw.new
111
+ else
112
+ @magick_draw = nil
113
+ end
114
+
115
+ options.each do |o|
116
+ self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
117
+ end
118
+
52
119
  end
53
120
 
54
121
  # convinience method to access all cells in this worksheet
@@ -57,54 +124,57 @@ module Axlsx
57
124
  rows.flatten
58
125
  end
59
126
 
60
- # Creates merge information for this worksheet.
127
+ # Creates merge information for this worksheet.
61
128
  # Cells can be merged by calling the merge_cells method on a worksheet.
62
- # @example This would merge the three cells C1..E1 #
129
+ # @example This would merge the three cells C1..E1 #
63
130
  # worksheet.merge_cells "C1:E1"
64
131
  # # you can also provide an array of cells to be merged
65
132
  # worksheet.merge_cells worksheet.rows.first.cells[(2..4)]
66
133
  # #alternatively you can do it from a single cell
67
134
  # worksheet["C1"].merge worksheet["E1"]
68
- # @param [Array, string]
135
+ # @param [Array, string]
69
136
  def merge_cells(cells)
70
137
  @merged_cells << if cells.is_a?(String)
71
138
  cells
72
139
  elsif cells.is_a?(Array)
73
140
  cells = cells.sort { |x, y| x.r <=> y.r }
74
141
  "#{cells.first.r}:#{cells.last.r}"
75
- end
142
+ end
76
143
  end
77
144
 
78
145
 
79
- # The demensions of a worksheet. This is not actually a required element by the spec,
146
+ # The demensions of a worksheet. This is not actually a required element by the spec,
80
147
  # but at least a few other document readers expect this for conversion
81
148
  # @return [String] the A1:B2 style reference for the first and last row column intersection in the workbook
82
149
  def dimension
83
150
  "#{rows.first.cells.first.r}:#{rows.last.cells.last.r}"
84
151
  end
85
152
 
153
+ # Indicates if gridlines should be shown in the sheet.
154
+ # This is true by default.
155
+ # @return [Boolean]
156
+ def show_gridlines=(v)
157
+ Axlsx::validate_boolean v
158
+ @show_gridlines = v
159
+ end
86
160
 
87
- # Returns the cell or cells defined using excel style A1:B3 references.
88
- # @param [String] cell_def the string defining the cell or range of cells
89
- # @return [Cell, Array]
90
- def [](cell_def)
91
- parts = cell_def.split(':')
92
- first = name_to_cell parts[0]
161
+ # @see selected
162
+ # @return [Boolean]
163
+ def selected=(v)
164
+ Axlsx::validate_boolean v
165
+ @selected = v
166
+ end
93
167
 
94
- if parts.size == 1
95
- first
96
- else
97
- cells = []
98
- last = name_to_cell(parts[1])
99
- rows[(first.row.index..last.row.index)].each do |r|
100
- r.cells[(first.index..last.index)].each do |c|
101
- cells << c
102
- end
103
- end
104
- cells
105
- end
168
+
169
+ # Indicates if the worksheet should print in a single page.
170
+ # This is true by default.
171
+ # @return [Boolean]
172
+ def fit_to_page=(v)
173
+ Axlsx::validate_boolean v
174
+ @fit_to_page = v
106
175
  end
107
176
 
177
+
108
178
  # returns the column and row index for a named based cell
109
179
  # @param [String] name The cell or cell range to return. "A1" will return the first cell of the first row.
110
180
  # @return [Cell]
@@ -115,18 +185,26 @@ module Axlsx
115
185
  end
116
186
 
117
187
  # The name of the worksheet
188
+ # The name of a worksheet must be unique in the workbook, and must not exceed 31 characters
118
189
  # @param [String] v
119
- def name=(v)
190
+ def name=(v)
120
191
  DataTypeValidator.validate "Worksheet.name", String, v
192
+ raise ArgumentError, (ERR_SHEET_NAME_TOO_LONG % v) if v.size > 31
121
193
  sheet_names = @workbook.worksheets.map { |s| s.name }
122
- raise ArgumentError, (ERR_DUPLICATE_SHEET_NAME % v) if sheet_names.include?(v)
123
- @name=v
194
+ raise ArgumentError, (ERR_DUPLICATE_SHEET_NAME % v) if sheet_names.include?(v)
195
+ @name=v
196
+ end
197
+
198
+ # The absolute auto filter range
199
+ # @see auto_filter
200
+ def abs_auto_filter
201
+ Axlsx.cell_range(@auto_filter.split(':').collect { |name| name_to_cell(name)}) if @auto_filter
124
202
  end
125
203
 
126
204
  # The auto filter range for the worksheet
127
205
  # @param [String] v
128
206
  # @see auto_filter
129
- def auto_filter=(v)
207
+ def auto_filter=(v)
130
208
  DataTypeValidator.validate "Worksheet.auto_filter", String, v
131
209
  @auto_filter = v
132
210
  end
@@ -163,18 +241,55 @@ module Axlsx
163
241
  @drawing || @drawing = Axlsx::Drawing.new(self)
164
242
  end
165
243
 
166
- # Adds a row to the worksheet and updates auto fit data
244
+ # Adds a row to the worksheet and updates auto fit data.
245
+ # @example - put a vanilla row in your spreadsheet
246
+ # ws.add_row [1, 'fish on my pl', '8']
247
+ #
248
+ # @example - specify a fixed width for a column in your spreadsheet
249
+ # # The first column will ignore the content of this cell when calculating column autowidth.
250
+ # # The second column will include this text in calculating the columns autowidth
251
+ # # The third cell will set a fixed with of 80 for the column.
252
+ # # If you need to un-fix a column width, use :auto. That will recalculate the column width based on all content in the column
253
+ #
254
+ # ws.add_row ['I wish', 'for a fish', 'on my fish wish dish'], :widths=>[:ignore, :auto, 80]
255
+ #
256
+ # @example - specify a fixed height for a row
257
+ # ws.add_row ['I wish', 'for a fish', 'on my fish wish dish'], :height => 40
258
+ #
259
+ # @example - create and use a style for all cells in the row
260
+ # blue = ws.styles.add_style :color => "#00FF00"
261
+ # ws.add_row [1, 2, 3], :style=>blue
262
+ #
263
+ # @example - only style some cells
264
+ # blue = ws.styles.add_style :color => "#00FF00"
265
+ # red = ws.styles.add_style :color => "#FF0000"
266
+ # big = ws.styles.add_style :sz => 40
267
+ # ws.add_row ["red fish", "blue fish", "one fish", "two fish"], :style=>[red, blue, nil, big] # the last nil is optional
268
+ #
269
+ #
270
+ # @example - force the second cell to be a float value
271
+ # ws.add_row [3, 4, 5], :types => [nil, :float]
272
+ #
273
+ # @example - use << alias
274
+ # ws << [3, 4, 5], :types => [nil, :float]
275
+ #
276
+ # @see Worksheet#column_widths
167
277
  # @return [Row]
168
278
  # @option options [Array] values
169
- # @option options [Array, Symbol] types
170
- # @option options [Array, Integer] style
279
+ # @option options [Array, Symbol] types
280
+ # @option options [Array, Integer] style
281
+ # @option options [Array] widths each member of the widths array will affect how auto_fit behavies.
282
+ # @option options [Float] height the row's height (in points)
171
283
  def add_row(values=[], options={})
172
284
  Row.new(self, values, options)
173
- update_auto_fit_data @rows.last.cells
285
+ update_column_info @rows.last.cells, options.delete(:widths) ||[], options.delete(:style) || []
286
+ # update_auto_fit_data @rows.last.cells, options.delete(:widths) || []
174
287
  yield @rows.last if block_given?
175
288
  @rows.last
176
289
  end
177
290
 
291
+ alias :<< :add_row
292
+
178
293
  # Set the style for cells in a specific row
179
294
  # @param [Integer] index or range of indexes in the table
180
295
  # @param [Integer] the cellXfs index
@@ -207,7 +322,7 @@ module Axlsx
207
322
  # @see README.md for an example
208
323
  def col_style(index, style, options={})
209
324
  offset = options.delete(:row_offset) || 0
210
- @rows[(offset..-1)].each do |r|
325
+ @rows[(offset..-1)].each do |r|
211
326
  cells = r.cells[index]
212
327
  next unless cells
213
328
  if cells.is_a?(Array)
@@ -218,29 +333,29 @@ module Axlsx
218
333
  end
219
334
  end
220
335
 
221
- # This is a helper method that Lets you specify a fixed width for multiple columns in a worksheet in one go.
336
+ # This is a helper method that Lets you specify a fixed width for multiple columns in a worksheet in one go.
222
337
  # Axlsx is sparse, so if you have not set data for a column, you cannot set the width.
223
338
  # Setting a fixed column width to nil will revert the behaviour back to calculating the width for you.
224
339
  # @example This would set the first and third column widhts but leave the second column in autofit state.
225
340
  # ws.column_widths 7.2, nil, 3
226
341
  # @note For updating only a single column it is probably easier to just set ws.auto_fit_data[col_index][:fixed] directly
227
- # @param [Integer|Float|Fixnum|nil] values
342
+ # @param [Integer|Float|Fixnum|nil] values
228
343
  def column_widths(*args)
229
344
  args.each_with_index do |value, index|
230
- raise ArgumentError, "Invalid column specification" unless index < @auto_fit_data.size
345
+ raise ArgumentError, "Invalid column specification" unless index < @column_info.size
231
346
  Axlsx::validate_unsigned_numeric(value) unless value == nil
232
- @auto_fit_data[index][:fixed] = value
347
+ @column_info[index].width = value
233
348
  end
234
349
  end
235
350
 
236
- # 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.
351
+ # 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.
237
352
  # @param [Class] chart_type
238
353
  # @option options [Array] start_at
239
354
  # @option options [Array] end_at
240
355
  # @option options [Cell, String] title
241
356
  # @option options [Boolean] show_legend
242
- # @option options [Integer] style
243
- # @note each chart type also specifies additional options
357
+ # @option options [Integer] style
358
+ # @note each chart type also specifies additional options
244
359
  # @see Chart
245
360
  # @see Pie3DChart
246
361
  # @see Bar3DChart
@@ -252,6 +367,14 @@ module Axlsx
252
367
  chart
253
368
  end
254
369
 
370
+ # needs documentation
371
+ def add_table(ref, options={})
372
+ table = Table.new(ref, self, options)
373
+ @tables << table
374
+ yield table if block_given?
375
+ table
376
+ end
377
+
255
378
  # Adds a media item to the worksheets drawing
256
379
  # @param [Class] media_type
257
380
  # @option options [] unknown
@@ -261,108 +384,101 @@ module Axlsx
261
384
  image
262
385
  end
263
386
 
264
- # Serializes the worksheet document
387
+ # Serializes the object
388
+ # @param [String] str
265
389
  # @return [String]
266
- def to_xml
267
- builder = Nokogiri::XML::Builder.new(:encoding => ENCODING) do |xml|
268
- xml.worksheet(:xmlns => XML_NS,
269
- :'xmlns:r' => XML_NS_R) {
270
- xml.dimension :ref=>dimension unless rows.size == 0
271
- if @auto_fit_data.size > 0
272
- xml.cols {
273
- @auto_fit_data.each_with_index do |col, index|
274
- min_max = index+1
275
- xml.col(:min=>min_max, :max=>min_max, :width => auto_width(col), :customWidth=>1)
276
- end
277
- }
278
- end
279
- xml.sheetData {
280
- @rows.each do |row|
281
- row.to_xml(xml)
282
- end
283
- }
284
- xml.autoFilter :ref=>@auto_filter if @auto_filter
285
- xml.mergeCells(:count=>@merged_cells.size) { @merged_cells.each { | mc | xml.mergeCell(:ref=>mc) } } unless @merged_cells.empty?
286
- xml.drawing :"r:id"=>"rId1" if @drawing
287
- }
390
+ def to_xml_string
391
+ str = '<?xml version="1.0" encoding="UTF-8"?>'
392
+ str.concat "<worksheet xmlns=\"%s\" xmlns:r=\"%s\">" % [XML_NS, XML_NS_R]
393
+ str.concat "<sheetPr><pageSetUpPr fitToPage=\"%s\"></pageSetUpPr></sheetPr>" % fit_to_page if fit_to_page
394
+ str.concat "<dimension ref=\"%s\"></dimension>" % dimension unless rows.size == 0
395
+ str.concat "<sheetViews><sheetView tabSelected='%s' workbookViewId='0' showGridLines='%s'><selection activeCell=\"A1\" sqref=\"A1\"/></sheetView></sheetViews>" % [@selected, show_gridlines]
396
+
397
+ if @column_info.size > 0
398
+ str << "<cols>"
399
+ @column_info.each { |col| col.to_xml_string(str) }
400
+ str.concat '</cols>'
288
401
  end
289
- builder.to_xml(:save_with => 0)
402
+ str.concat '<sheetData>'
403
+ @rows.each_with_index { |row, index| row.to_xml_string(index, str) }
404
+ str.concat '</sheetData>'
405
+ page_margins.to_xml_string(str) if @page_margins
406
+ str.concat "<autoFilter ref='%s'></autoFilter>" % @auto_filter if @auto_filter
407
+ str.concat "<mergeCells count='%s'>%s</mergeCells>" % [@merged_cells.size, @merged_cells.reduce('') { |memo, obj| "<mergeCell ref='%s'></mergeCell>" % obj } ] unless @merged_cells.empty?
408
+ str.concat "<drawing r:id='rId1'></drawing>" if @drawing
409
+ unless @tables.empty?
410
+ str.concat "<tableParts count='%s'>%s</tableParts>" % [@tables.size, @tables.reduce('') { |memo, obj| memo += "<tablePart r:id='%s'/>" % obj.rId }]
411
+ end
412
+ str + '</worksheet>'
290
413
  end
291
414
 
292
415
  # The worksheet relationships. This is managed automatically by the worksheet
293
416
  # @return [Relationships]
294
417
  def relationships
295
418
  r = Relationships.new
419
+ @tables.each do |table|
420
+ r << Relationship.new(TABLE_R, "../#{table.pn}")
421
+ end
296
422
  r << Relationship.new(DRAWING_R, "../#{@drawing.pn}") if @drawing
297
423
  r
298
424
  end
299
425
 
300
- private
426
+ # Returns the cell or cells defined using excel style A1:B3 references.
427
+ # @param [String|Integer] cell_def the string defining the cell or range of cells, or the rownumber
428
+ # @return [Cell, Array]
429
+
430
+ def [] (cell_def)
431
+ return rows[cell_def] if cell_def.is_a?(Integer)
432
+
433
+ parts = cell_def.split(':')
434
+ first = name_to_cell parts[0]
435
+ if parts.size == 1
436
+ first
437
+ else
438
+ cells = []
439
+ last = name_to_cell(parts[1])
440
+ rows[(first.row.index..last.row.index)].each do |r|
441
+ r.cells[(first.index..last.index)].each do |c|
442
+ cells << c
443
+ end
444
+ end
445
+ cells
446
+ end
447
+ end
448
+
449
+ private
301
450
 
302
451
  # assigns the owner workbook for this worksheet
303
452
  def workbook=(v) DataTypeValidator.validate "Worksheet.workbook", Workbook, v; @workbook = v; end
304
453
 
305
- # Updates auto fit data.
306
- # We store an auto_fit_data item for each column. when a row is added we multiple the font size by the length of the text to
307
- # attempt to identify the longest cell in the column. This is not 100% accurate as it needs to take into account
308
- # any formatting that will be applied to the data, as well as the actual rendering size when the length and size is equal
309
- # for two cells.
310
- # @return [Array] of Cell objects
311
- # @param [Array] cells an array of cells
312
- def update_auto_fit_data(cells)
313
- # TODO delay this until rendering. too much work when we dont know what they are going to do to the sheet.
454
+
455
+ def update_column_info(cells, widths=[], style=[])
314
456
  styles = self.workbook.styles
315
457
  cellXfs, fonts = styles.cellXfs, styles.fonts
316
458
  sz = 11
317
- cells.each_with_index do |item, index|
318
- # ignore formula - there is no way for us to know the result
319
- next if item.value.is_a?(String) && item.value.start_with?('=')
320
-
321
- col = @auto_fit_data[index] || {:longest=>"", :sz=>sz, :fixed=>nil}
322
- cell_xf = cellXfs[item.style]
323
- font = fonts[cell_xf.fontId || 0]
324
- sz = item.sz || font.sz || fonts[0].sz
325
- if (col[:longest].scan(/./mu).size * col[:sz]) < (item.value.to_s.scan(/./mu).size * sz)
326
- col[:sz] = sz
327
- col[:longest] = item.value.to_s
459
+ cells.each_with_index do |cell, index|
460
+ @column_info[index] ||= Col.new index+1, index+1
461
+ col = @column_info[index]
462
+ width = widths[index]
463
+ col.width = width if [Integer, Float, Fixnum].include?(width.class)
464
+ c_style = style[index] if [Integer, Fixnum].include?(style[index].class)
465
+ next if width == :ignore || col.width || (cell.value.is_a?(String) && cell.value.start_with?('='))
466
+ if self.workbook.use_autowidth
467
+ cell_xf = cellXfs[(c_style || 0)]
468
+ font = fonts[(cell_xf.fontId || 0)]
469
+ sz = cell.sz || font.sz || sz
470
+ col.width = [(col.width || 0), calculate_width(cell.value.to_s, sz)].max
328
471
  end
329
- @auto_fit_data[index] = col
330
472
  end
331
- cells
332
473
  end
333
-
334
- # Determines the proper width for a column based on content.
335
- # @note
336
- # width = Truncate([!{Number of Characters} * !{Maximum Digit Width} + !{5 pixel padding}]/!{Maximum Digit Width}*256)/256
337
- # @return [Float]
338
- # @param [Hash] A hash of auto_fit_data
339
- def auto_width(col)
340
- return col[:fixed] unless col[:fixed] == nil
341
-
342
- mdw_count, font_scale, mdw = 0, col[:sz]/11.0, 6.0
343
- mdw_count = col[:longest].scan(/./mu).reduce(0) do | count, char |
474
+
475
+ def calculate_width(text, sz)
476
+ mdw_count, font_scale, mdw = 0, sz/11.0, 6.0
477
+ mdw_count = text.scan(/./mu).reduce(0) do | count, char |
344
478
  count +=1 if @magick_draw.get_type_metrics(char).max_advance >= mdw
345
479
  count
346
480
  end
347
481
  ((mdw_count * mdw + 5) / mdw * 256) / 256.0 * font_scale
348
482
  end
349
-
350
- # Something to look into:
351
- # width calculation actually needs to be done agains the formatted value for items that apply a
352
- # format
353
- # def excel_format(cell)
354
- # # The most common case.
355
- # return time.value.to_s if cell.style == 0
356
- #
357
- # # The second most common case
358
- # num_fmt = workbook.styles.cellXfs[items.style].numFmtId
359
- # return value.to_s if num_fmt == 0
360
- #
361
- # format_code = workbook.styles.numFmts[num_fmt]
362
- # # need to find some exceptionally fast way of parsing value according to
363
- # # an excel format_code
364
- # item.value.to_s
365
- # end
366
-
367
483
  end
368
484
  end
data/lib/axlsx.rb CHANGED
@@ -5,6 +5,9 @@ require 'axlsx/util/simple_typed_list.rb'
5
5
  require 'axlsx/util/constants.rb'
6
6
  require 'axlsx/util/validators.rb'
7
7
  require 'axlsx/util/storage.rb'
8
+
9
+ #not even close to being ready but it does not break anything so it stays for now.
10
+ # needs a full re-write to use agile-encryption properly
8
11
  require 'axlsx/util/cbf.rb'
9
12
  require 'axlsx/util/ms_off_crypto.rb'
10
13
 
@@ -22,10 +25,8 @@ require 'axlsx/rels/relationships.rb'
22
25
  require 'axlsx/drawing/drawing.rb'
23
26
  require 'axlsx/workbook/workbook.rb'
24
27
  require 'axlsx/package.rb'
25
-
26
28
  #required gems
27
29
  require 'nokogiri'
28
- require 'RMagick'
29
30
  require 'zip/zip'
30
31
 
31
32
  #core dependencies
@@ -33,8 +34,8 @@ require 'bigdecimal'
33
34
  require 'time'
34
35
 
35
36
  #if object does not have this already, I am borrowing it from active_support.
36
- # I am a very big fan of activesupports instance_values method, but do not want to require nor include the entire
37
- # library just for this one method.
37
+ # I am a very big fan of activesupports instance_values method, but do not want to require nor include the entire
38
+ # library just for this one method.
38
39
  if !Object.respond_to?(:instance_values)
39
40
  Object.send :public # patch for 1.8.7 as it uses private scope
40
41
  Object.send :define_method, :instance_values do
@@ -42,12 +43,11 @@ if !Object.respond_to?(:instance_values)
42
43
  end
43
44
  end
44
45
 
45
-
46
46
  # 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.
47
47
  module Axlsx
48
48
  # determines the cell range for the items provided
49
49
  def self.cell_range(items)
50
- return "" unless items.first.is_a? Cell
50
+ return "" unless items.first.is_a? Cell
51
51
  ref = "'#{items.first.row.worksheet.name}'!" +
52
52
  "#{items.first.r_abs}"
53
53
  ref += ":#{items.last.r_abs}" if items.size > 1
@@ -56,11 +56,32 @@ module Axlsx
56
56
 
57
57
  def self.name_to_indices(name)
58
58
  raise ArgumentError, 'invalid cell name' unless name.size > 1
59
- v = name[/[A-Z]+/].reverse.chars.reduce({:base=>1, :i=>0}) do |v, c|
60
- v[:i] += ((c.bytes.first - 65) + v[:base]); v[:base] *= 26; v
59
+ v = name[/[A-Z]+/].reverse.chars.reduce({:base=>1, :i=>0}) do |val, c|
60
+ val[:i] += ((c.bytes.first - 64) * val[:base]); val[:base] *= 26; val
61
61
  end
62
62
 
63
- [v[:i]-1, ((name[/[1-9]+/]).to_i)-1]
63
+ [v[:i]-1, ((name[/[1-9][0-9]*/]).to_i)-1]
64
64
 
65
65
  end
66
+
67
+ # converts the column index into alphabetical values.
68
+ # @note This follows the standard spreadsheet convention of naming columns A to Z, followed by AA to AZ etc.
69
+ # @return [String]
70
+ def self.col_ref(index)
71
+ chars = []
72
+ while index >= 26 do
73
+ chars << ((index % 26) + 65).chr
74
+ index = index / 26 - 1
75
+ end
76
+ chars << (index + 65).chr
77
+ chars.reverse.join
78
+ end
79
+
80
+ # @return [String] The alpha(column)numeric(row) reference for this sell.
81
+ # @example Relative Cell Reference
82
+ # ws.rows.first.cells.first.r #=> "A1"
83
+ def self.cell_r(c_index, r_index)
84
+ Axlsx::col_ref(c_index).to_s << (r_index+1).to_s
85
+ end
86
+
66
87
  end
data/lib/schema/dc.xsd CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  Created 2003-04-02
14
14
 
15
- Created by
15
+ Created by
16
16
 
17
17
  Tim Cole (t-cole3@uiuc.edu)
18
18
  Tom Habing (thabing@uiuc.edu)
@@ -23,14 +23,14 @@
23
23
  This schema declares XML elements for the 15 DC elements from the
24
24
  http://purl.org/dc/elements/1.1/ namespace.
25
25
 
26
- It defines a complexType SimpleLiteral which permits mixed content
26
+ It defines a complexType SimpleLiteral which permits mixed content
27
27
  and makes the xml:lang attribute available. It disallows child elements by
28
28
  use of minOcccurs/maxOccurs.
29
29
 
30
30
  However, this complexType does permit the derivation of other complexTypes
31
31
  which would permit child elements.
32
32
 
33
- All elements are declared as substitutable for the abstract element any,
33
+ All elements are declared as substitutable for the abstract element any,
34
34
  which means that the default type for all elements is dc:SimpleLiteral.
35
35
 
36
36
  </xs:documentation>
@@ -49,7 +49,7 @@
49
49
  It permits text content only with optional
50
50
  xml:lang attribute.
51
51
  Text is allowed because mixed="true", but sub-elements
52
- are disallowed because minOccurs="0" and maxOccurs="0"
52
+ are disallowed because minOccurs="0" and maxOccurs="0"
53
53
  are on the xs:any tag.
54
54
 
55
55
  This complexType allows for restriction or extension permitting
@@ -89,7 +89,7 @@
89
89
  <xs:annotation>
90
90
  <xs:documentation xml:lang="en">
91
91
  This group is included as a convenience for schema authors
92
- who need to refer to all the elements in the
92
+ who need to refer to all the elements in the
93
93
  http://purl.org/dc/elements/1.1/ namespace.
94
94
  </xs:documentation>
95
95
  </xs:annotation>