axlsx 1.0.16 → 1.0.18

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 (91) hide show
  1. data/.yardopts +1 -0
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +105 -46
  4. data/examples/example.rb +172 -142
  5. data/lib/axlsx/content_type/content_type.rb +4 -3
  6. data/lib/axlsx/content_type/default.rb +1 -0
  7. data/lib/axlsx/content_type/override.rb +1 -0
  8. data/lib/axlsx/doc_props/app.rb +1 -1
  9. data/lib/axlsx/doc_props/core.rb +1 -0
  10. data/lib/axlsx/drawing/axis.rb +1 -0
  11. data/lib/axlsx/drawing/bar_3D_chart.rb +1 -0
  12. data/lib/axlsx/drawing/bar_series.rb +1 -0
  13. data/lib/axlsx/drawing/cat_axis.rb +1 -0
  14. data/lib/axlsx/drawing/cat_axis_data.rb +1 -0
  15. data/lib/axlsx/drawing/chart.rb +4 -4
  16. data/lib/axlsx/drawing/drawing.rb +1 -0
  17. data/lib/axlsx/drawing/graphic_frame.rb +1 -0
  18. data/lib/axlsx/drawing/hyperlink.rb +1 -1
  19. data/lib/axlsx/drawing/line_3D_chart.rb +1 -0
  20. data/lib/axlsx/drawing/line_series.rb +1 -0
  21. data/lib/axlsx/drawing/marker.rb +1 -0
  22. data/lib/axlsx/drawing/one_cell_anchor.rb +1 -0
  23. data/lib/axlsx/drawing/pic.rb +2 -1
  24. data/lib/axlsx/drawing/picture_locking.rb +1 -0
  25. data/lib/axlsx/drawing/pie_3D_chart.rb +1 -0
  26. data/lib/axlsx/drawing/pie_series.rb +1 -0
  27. data/lib/axlsx/drawing/scaling.rb +1 -0
  28. data/lib/axlsx/drawing/ser_axis.rb +1 -0
  29. data/lib/axlsx/drawing/series.rb +1 -0
  30. data/lib/axlsx/drawing/series_title.rb +1 -0
  31. data/lib/axlsx/drawing/title.rb +1 -0
  32. data/lib/axlsx/drawing/two_cell_anchor.rb +1 -0
  33. data/lib/axlsx/drawing/val_axis.rb +1 -0
  34. data/lib/axlsx/drawing/val_axis_data.rb +1 -0
  35. data/lib/axlsx/drawing/view_3D.rb +1 -0
  36. data/lib/axlsx/package.rb +86 -38
  37. data/lib/axlsx/rels/relationship.rb +4 -3
  38. data/lib/axlsx/rels/relationships.rb +1 -0
  39. data/lib/axlsx/stylesheet/border.rb +1 -0
  40. data/lib/axlsx/stylesheet/border_pr.rb +1 -0
  41. data/lib/axlsx/stylesheet/cell_alignment.rb +1 -0
  42. data/lib/axlsx/stylesheet/cell_protection.rb +1 -0
  43. data/lib/axlsx/stylesheet/cell_style.rb +1 -0
  44. data/lib/axlsx/stylesheet/color.rb +1 -0
  45. data/lib/axlsx/stylesheet/fill.rb +1 -0
  46. data/lib/axlsx/stylesheet/font.rb +8 -0
  47. data/lib/axlsx/stylesheet/gradient_fill.rb +1 -1
  48. data/lib/axlsx/stylesheet/gradient_stop.rb +1 -1
  49. data/lib/axlsx/stylesheet/num_fmt.rb +1 -0
  50. data/lib/axlsx/stylesheet/pattern_fill.rb +1 -0
  51. data/lib/axlsx/stylesheet/styles.rb +12 -9
  52. data/lib/axlsx/stylesheet/table_style.rb +1 -0
  53. data/lib/axlsx/stylesheet/table_style_element.rb +1 -0
  54. data/lib/axlsx/stylesheet/table_styles.rb +1 -0
  55. data/lib/axlsx/stylesheet/xf.rb +1 -0
  56. data/lib/axlsx/util/cbf.rb +1 -0
  57. data/lib/axlsx/util/constants.rb +23 -14
  58. data/lib/axlsx/util/ms_off_crypto.rb +1 -1
  59. data/lib/axlsx/util/parser.rb +1 -0
  60. data/lib/axlsx/util/simple_typed_list.rb +2 -1
  61. data/lib/axlsx/util/storage.rb +1 -0
  62. data/lib/axlsx/util/validators.rb +11 -2
  63. data/lib/axlsx/version.rb +2 -1
  64. data/lib/axlsx/workbook/shared_strings_table.rb +71 -0
  65. data/lib/axlsx/workbook/shared_strings_table.rb~ +69 -0
  66. data/lib/axlsx/workbook/workbook.rb +47 -25
  67. data/lib/axlsx/workbook/worksheet/cell.rb +131 -78
  68. data/lib/axlsx/workbook/worksheet/date_time_converter.rb +29 -0
  69. data/lib/axlsx/workbook/worksheet/page_margins.rb +94 -0
  70. data/lib/axlsx/workbook/worksheet/row.rb +44 -12
  71. data/lib/axlsx/workbook/worksheet/shared_strings_table.rb~ +0 -0
  72. data/lib/axlsx/workbook/worksheet/worksheet.rb +158 -44
  73. data/lib/axlsx.rb +16 -10
  74. data/test/content_type/tc_content_type.rb +2 -0
  75. data/test/content_type/tc_default.rb +2 -0
  76. data/test/content_type/tc_override.rb +1 -0
  77. data/test/drawing/tc_chart.rb +2 -2
  78. data/test/stylesheet/tc_cell_alignment.rb +8 -3
  79. data/test/stylesheet/tc_font.rb +8 -0
  80. data/test/stylesheet/tc_styles.rb +6 -2
  81. data/test/tc_package.rb +31 -0
  82. data/test/workbook/tc_shared_strings_table.rb +39 -0
  83. data/test/workbook/tc_shared_strings_table.rb~ +8 -0
  84. data/test/workbook/tc_workbook.rb +8 -0
  85. data/test/workbook/worksheet/tc_cell.rb +52 -9
  86. data/test/workbook/worksheet/tc_date_time_converter.rb +127 -0
  87. data/test/workbook/worksheet/tc_date_time_converter.rb~ +69 -0
  88. data/test/workbook/worksheet/tc_page_margins.rb +100 -0
  89. data/test/workbook/worksheet/tc_row.rb +36 -0
  90. data/test/workbook/worksheet/tc_worksheet.rb +97 -3
  91. metadata +33 -40
@@ -1,9 +1,9 @@
1
- # -*- coding: utf-8 -*-
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
@@ -21,7 +21,7 @@ module Axlsx
21
21
 
22
22
  # An array of content based calculated column widths.
23
23
  # @note a single auto fit data item is a hash with :longest => [String] and :sz=> [Integer] members.
24
- # @return [Array] of Hash
24
+ # @return [Array] of Hash
25
25
  attr_reader :auto_fit_data
26
26
 
27
27
  # An array of merged cell ranges e.d "A1:B3"
@@ -34,46 +34,87 @@ module Axlsx
34
34
  # The first row is considered the header, while subsequent rows are considerd to be data.
35
35
  # @return Array
36
36
  attr_reader :auto_filter
37
-
37
+
38
+ # Page margins for printing the worksheet.
39
+ # @example
40
+ # wb = Axlsx::Package.new.workbook
41
+ # # using options when creating the worksheet.
42
+ # ws = wb.add_worksheet :page_margins => {:left => 1.9, :header => 0.1}
43
+ #
44
+ # # use the set method of the page_margins object
45
+ # ws.page_margins.set(:bottom => 3, :footer => 0.7)
46
+ #
47
+ # # set page margins in a block
48
+ # ws.page_margins do |margins|
49
+ # margins.right = 6
50
+ # margins.top = 0.2
51
+ # end
52
+ # @see PageMargins#initialize
53
+ # @return [PageMargins]
54
+ def page_margins
55
+ @page_margins ||= PageMargins.new
56
+ yield @page_margins if block_given?
57
+ @page_margins
58
+ end
59
+
38
60
  # Creates a new worksheet.
39
61
  # @note the recommended way to manage worksheets is Workbook#add_worksheet
40
62
  # @see Workbook#add_worksheet
41
- # @option options [String] name The name of this sheet.
63
+ # @option options [String] name The name of this worksheet.
64
+ # @option options [Hash] page_margins A hash containing page margins for this worksheet. @see PageMargins
42
65
  def initialize(wb, options={})
43
- @drawing = nil
66
+ @drawing = @page_margins = @auto_filter = nil
44
67
  @rows = SimpleTypedList.new Row
45
68
  self.workbook = wb
46
69
  @workbook.worksheets << self
47
70
  @auto_fit_data = []
48
71
  self.name = options[:name] || "Sheet" + (index+1).to_s
72
+
49
73
  @magick_draw = Magick::Draw.new
50
74
  @cols = SimpleTypedList.new Cell
51
75
  @merged_cells = []
76
+
77
+ @page_margins = PageMargins.new options[:page_margins] if options[:page_margins]
52
78
  end
53
79
 
54
- # Creates merge information for this worksheet.
80
+ # convinience method to access all cells in this worksheet
81
+ # @return [Array] cells
82
+ def cells
83
+ rows.flatten
84
+ end
85
+
86
+ # Creates merge information for this worksheet.
55
87
  # Cells can be merged by calling the merge_cells method on a worksheet.
56
- # @example This would merge the three cells C1..E1 #
88
+ # @example This would merge the three cells C1..E1 #
57
89
  # worksheet.merge_cells "C1:E1"
58
90
  # # you can also provide an array of cells to be merged
59
91
  # worksheet.merge_cells worksheet.rows.first.cells[(2..4)]
60
92
  # #alternatively you can do it from a single cell
61
93
  # worksheet["C1"].merge worksheet["E1"]
62
- # @param [Array, string]
94
+ # @param [Array, string]
63
95
  def merge_cells(cells)
64
96
  @merged_cells << if cells.is_a?(String)
65
97
  cells
66
98
  elsif cells.is_a?(Array)
67
99
  cells = cells.sort { |x, y| x.r <=> y.r }
68
100
  "#{cells.first.r}:#{cells.last.r}"
69
- end
101
+ end
102
+ end
103
+
104
+
105
+ # The demensions of a worksheet. This is not actually a required element by the spec,
106
+ # but at least a few other document readers expect this for conversion
107
+ # @return [String] the A1:B2 style reference for the first and last row column intersection in the workbook
108
+ def dimension
109
+ "#{rows.first.cells.first.r}:#{rows.last.cells.last.r}"
70
110
  end
71
111
 
72
112
 
73
113
  # Returns the cell or cells defined using excel style A1:B3 references.
74
- # @param [String] cell_def the string defining the cell or range of cells
114
+ # @param [String|Integer] cell_def the string defining the cell or range of cells, or the rownumber
75
115
  # @return [Cell, Array]
76
116
  def [](cell_def)
117
+ return rows[cell_def - 1] if cell_def.is_a? Integer
77
118
  parts = cell_def.split(':')
78
119
  first = name_to_cell parts[0]
79
120
 
@@ -101,18 +142,20 @@ module Axlsx
101
142
  end
102
143
 
103
144
  # The name of the worksheet
145
+ # The name of a worksheet must be unique in the workbook, and must not exceed 31 characters
104
146
  # @param [String] v
105
- def name=(v)
147
+ def name=(v)
106
148
  DataTypeValidator.validate "Worksheet.name", String, v
149
+ raise ArgumentError, (ERR_SHEET_NAME_TOO_LONG % v) if v.size > 31
107
150
  sheet_names = @workbook.worksheets.map { |s| s.name }
108
- raise ArgumentError, (ERR_DUPLICATE_SHEET_NAME % v) if sheet_names.include?(v)
109
- @name=v
151
+ raise ArgumentError, (ERR_DUPLICATE_SHEET_NAME % v) if sheet_names.include?(v)
152
+ @name=v
110
153
  end
111
154
 
112
155
  # The auto filter range for the worksheet
113
156
  # @param [String] v
114
157
  # @see auto_filter
115
- def auto_filter=(v)
158
+ def auto_filter=(v)
116
159
  DataTypeValidator.validate "Worksheet.auto_filter", String, v
117
160
  @auto_filter = v
118
161
  end
@@ -149,18 +192,54 @@ module Axlsx
149
192
  @drawing || @drawing = Axlsx::Drawing.new(self)
150
193
  end
151
194
 
152
- # Adds a row to the worksheet and updates auto fit data
195
+ # Adds a row to the worksheet and updates auto fit data.
196
+ # @example - put a vanilla row in your spreadsheet
197
+ # ws.add_row [1, 'fish on my pl', '8']
198
+ #
199
+ # @example - specify a fixed width for a column in your spreadsheet
200
+ # # The first column will ignore the content of this cell when calculating column autowidth.
201
+ # # The second column will include this text in calculating the columns autowidth
202
+ # # The third cell will set a fixed with of 80 for the column.
203
+ # # If you need to un-fix a column width, use :auto. That will recalculate the column width based on all content in the column
204
+ #
205
+ # ws.add_row ['I wish', 'for a fish', 'on my fish wish dish'], :widths=>[:ignore, :auto, 80]
206
+ #
207
+ # @example - specify a fixed height for a row
208
+ # ws.add_row ['I wish', 'for a fish', 'on my fish wish dish'], :height => 40
209
+ #
210
+ # @example - create and use a style for all cells in the row
211
+ # blue = ws.styles.add_style :color => "#00FF00"
212
+ # ws.add_row [1, 2, 3], :style=>blue
213
+ #
214
+ # @example - only style some cells
215
+ # blue = ws.styles.add_style :color => "#00FF00"
216
+ # red = ws.styles.add_style :color => "#FF0000"
217
+ # big = ws.styles.add_style :sz => 40
218
+ # ws.add_row ["red fish", "blue fish", "one fish", "two fish"], :style=>[red, blue, nil, big] # the last nil is optional
219
+ #
220
+ #
221
+ # @example - force the second cell to be a float value
222
+ # ws.add_row [3, 4, 5], :types => [nil, :float]
223
+ #
224
+ # @example - use << alias
225
+ # ws << [3, 4, 5], :types => [nil, :float]
226
+ #
227
+ # @see Worksheet#column_widths
153
228
  # @return [Row]
154
229
  # @option options [Array] values
155
- # @option options [Array, Symbol] types
156
- # @option options [Array, Integer] style
230
+ # @option options [Array, Symbol] types
231
+ # @option options [Array, Integer] style
232
+ # @option options [Array] widths each member of the widths array will affect how auto_fit behavies.
233
+ # @option options [Float] height the row's height (in points)
157
234
  def add_row(values=[], options={})
158
235
  Row.new(self, values, options)
159
- update_auto_fit_data @rows.last.cells
236
+ update_auto_fit_data @rows.last.cells, options.delete(:widths) || []
160
237
  yield @rows.last if block_given?
161
238
  @rows.last
162
239
  end
163
240
 
241
+ alias :<< :add_row
242
+
164
243
  # Set the style for cells in a specific row
165
244
  # @param [Integer] index or range of indexes in the table
166
245
  # @param [Integer] the cellXfs index
@@ -193,7 +272,7 @@ module Axlsx
193
272
  # @see README.md for an example
194
273
  def col_style(index, style, options={})
195
274
  offset = options.delete(:row_offset) || 0
196
- @rows[(offset..-1)].each do |r|
275
+ @rows[(offset..-1)].each do |r|
197
276
  cells = r.cells[index]
198
277
  next unless cells
199
278
  if cells.is_a?(Array)
@@ -203,15 +282,30 @@ module Axlsx
203
282
  end
204
283
  end
205
284
  end
206
-
207
- # 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.
285
+
286
+ # This is a helper method that Lets you specify a fixed width for multiple columns in a worksheet in one go.
287
+ # Axlsx is sparse, so if you have not set data for a column, you cannot set the width.
288
+ # Setting a fixed column width to nil will revert the behaviour back to calculating the width for you.
289
+ # @example This would set the first and third column widhts but leave the second column in autofit state.
290
+ # ws.column_widths 7.2, nil, 3
291
+ # @note For updating only a single column it is probably easier to just set ws.auto_fit_data[col_index][:fixed] directly
292
+ # @param [Integer|Float|Fixnum|nil] values
293
+ def column_widths(*args)
294
+ args.each_with_index do |value, index|
295
+ raise ArgumentError, "Invalid column specification" unless index < @auto_fit_data.size
296
+ Axlsx::validate_unsigned_numeric(value) unless value == nil
297
+ @auto_fit_data[index][:fixed] = value
298
+ end
299
+ end
300
+
301
+ # 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.
208
302
  # @param [Class] chart_type
209
303
  # @option options [Array] start_at
210
304
  # @option options [Array] end_at
211
305
  # @option options [Cell, String] title
212
306
  # @option options [Boolean] show_legend
213
- # @option options [Integer] style
214
- # @note each chart type also specifies additional options
307
+ # @option options [Integer] style
308
+ # @note each chart type also specifies additional options
215
309
  # @see Chart
216
310
  # @see Pie3DChart
217
311
  # @see Bar3DChart
@@ -236,8 +330,19 @@ module Axlsx
236
330
  # @return [String]
237
331
  def to_xml
238
332
  builder = Nokogiri::XML::Builder.new(:encoding => ENCODING) do |xml|
239
- xml.worksheet(:xmlns => XML_NS,
333
+ xml.worksheet(:xmlns => XML_NS,
240
334
  :'xmlns:r' => XML_NS_R) {
335
+ # another patch for the folks at rubyXL as thier parser depends on this optional element.
336
+ xml.dimension :ref=>dimension unless rows.size == 0
337
+ # this is required by rubyXL, spec says who cares - but it seems they didnt notice
338
+ # however, it also seems to be causing some odd [Grouped] stuff in excel 2011 - so
339
+ # removing until I understand it better.
340
+ # xml.sheetViews {
341
+ # xml.sheetView(:tabSelected => 1, :workbookViewId => 0) {
342
+ # xml.selection :activeCell=>"A1", :sqref => "A1"
343
+ # }
344
+ # }
345
+
241
346
  if @auto_fit_data.size > 0
242
347
  xml.cols {
243
348
  @auto_fit_data.each_with_index do |col, index|
@@ -253,7 +358,8 @@ module Axlsx
253
358
  }
254
359
  xml.autoFilter :ref=>@auto_filter if @auto_filter
255
360
  xml.mergeCells(:count=>@merged_cells.size) { @merged_cells.each { | mc | xml.mergeCell(:ref=>mc) } } unless @merged_cells.empty?
256
- xml.drawing :"r:id"=>"rId1" if @drawing
361
+ page_margins.to_xml(xml) if @page_margins
362
+ xml.drawing :"r:id"=>"rId1" if @drawing
257
363
  }
258
364
  end
259
365
  builder.to_xml(:save_with => 0)
@@ -267,28 +373,35 @@ module Axlsx
267
373
  r
268
374
  end
269
375
 
270
- private
376
+ private
271
377
 
272
378
  # assigns the owner workbook for this worksheet
273
379
  def workbook=(v) DataTypeValidator.validate "Worksheet.workbook", Workbook, v; @workbook = v; end
274
380
 
275
- # Updates auto fit data.
276
- # 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
381
+ # Updates auto fit data.
382
+ # 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
277
383
  # attempt to identify the longest cell in the column. This is not 100% accurate as it needs to take into account
278
- # any formatting that will be applied to the data, as well as the actual rendering size when the length and size is equal
384
+ # any formatting that will be applied to the data, as well as the actual rendering size when the length and size is equal
279
385
  # for two cells.
386
+
280
387
  # @return [Array] of Cell objects
281
388
  # @param [Array] cells an array of cells
282
- def update_auto_fit_data(cells)
389
+ # @param [Array] widths an array of cell widths @see Worksheet#add_row
390
+ def update_auto_fit_data(cells, widths=[])
283
391
  # TODO delay this until rendering. too much work when we dont know what they are going to do to the sheet.
284
392
  styles = self.workbook.styles
285
393
  cellXfs, fonts = styles.cellXfs, styles.fonts
286
394
  sz = 11
287
395
  cells.each_with_index do |item, index|
288
- # ignore formula - there is no way for us to know the result
289
- next if item.value.is_a?(String) && item.value.start_with?('=')
396
+ col = @auto_fit_data[index] ||= {:longest=>"", :sz=>sz, :fixed=>nil}
397
+ width = widths[index]
398
+ # set fixed width and skip if numeric width is given
399
+ col[:fixed] = width if [Integer, Float, Fixnum].include?(width.class)
400
+ # ignore default column widths and formula
401
+ next if width == :ignore || (item.value.is_a?(String) && item.value.start_with?('='))
402
+ # make sure we can turn that fixed with off!
403
+ col[:fixed] = nil if width == :auto
290
404
 
291
- col = @auto_fit_data[index] || {:longest=>"", :sz=>sz}
292
405
  cell_xf = cellXfs[item.style]
293
406
  font = fonts[cell_xf.fontId || 0]
294
407
  sz = item.sz || font.sz || fonts[0].sz
@@ -296,17 +409,18 @@ module Axlsx
296
409
  col[:sz] = sz
297
410
  col[:longest] = item.value.to_s
298
411
  end
299
- @auto_fit_data[index] = col
300
412
  end
301
413
  cells
302
414
  end
303
-
415
+
304
416
  # Determines the proper width for a column based on content.
305
- # @note
417
+ # @note
306
418
  # width = Truncate([!{Number of Characters} * !{Maximum Digit Width} + !{5 pixel padding}]/!{Maximum Digit Width}*256)/256
307
419
  # @return [Float]
308
- # @param [Hash] A hash of auto_fit_data
420
+ # @param [Hash] A hash of auto_fit_data
309
421
  def auto_width(col)
422
+ return col[:fixed] unless col[:fixed] == nil
423
+
310
424
  mdw_count, font_scale, mdw = 0, col[:sz]/11.0, 6.0
311
425
  mdw_count = col[:longest].scan(/./mu).reduce(0) do | count, char |
312
426
  count +=1 if @magick_draw.get_type_metrics(char).max_advance >= mdw
@@ -316,21 +430,21 @@ module Axlsx
316
430
  end
317
431
 
318
432
  # Something to look into:
319
- # width calculation actually needs to be done agains the formatted value for items that apply a
433
+ # width calculation actually needs to be done agains the formatted value for items that apply a
320
434
  # format
321
435
  # def excel_format(cell)
322
436
  # # The most common case.
323
437
  # return time.value.to_s if cell.style == 0
324
438
  #
325
- # # The second most common case
439
+ # # The second most common case
326
440
  # num_fmt = workbook.styles.cellXfs[items.style].numFmtId
327
441
  # return value.to_s if num_fmt == 0
328
- #
442
+ #
329
443
  # format_code = workbook.styles.numFmts[num_fmt]
330
444
  # # need to find some exceptionally fast way of parsing value according to
331
445
  # # an excel format_code
332
446
  # item.value.to_s
333
- # end
447
+ # end
334
448
 
335
449
  end
336
450
  end
data/lib/axlsx.rb CHANGED
@@ -1,14 +1,12 @@
1
- Encoding::default_internal = 'UTF-8' unless RUBY_VERSION < '1.9'
2
- Encoding::default_external = 'UTF-8' unless RUBY_VERSION < '1.9'
3
-
1
+ # encoding: UTF-8
4
2
  require 'axlsx/version.rb'
5
3
 
6
4
  require 'axlsx/util/simple_typed_list.rb'
7
5
  require 'axlsx/util/constants.rb'
8
6
  require 'axlsx/util/validators.rb'
9
- # require 'axlsx/util/storage.rb'
10
- # require 'axlsx/util/cbf.rb'
11
- # require 'axlsx/util/ms_off_crypto.rb'
7
+ require 'axlsx/util/storage.rb'
8
+ require 'axlsx/util/cbf.rb'
9
+ require 'axlsx/util/ms_off_crypto.rb'
12
10
 
13
11
 
14
12
  # to be included with parsable intitites.
@@ -27,8 +25,6 @@ require 'axlsx/package.rb'
27
25
 
28
26
  #required gems
29
27
  require 'nokogiri'
30
- require 'active_support/core_ext/object/instance_variables'
31
- require 'active_support/inflector'
32
28
  require 'RMagick'
33
29
  require 'zip/zip'
34
30
 
@@ -36,6 +32,16 @@ require 'zip/zip'
36
32
  require 'bigdecimal'
37
33
  require 'time'
38
34
 
35
+ #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.
38
+ if !Object.respond_to?(:instance_values)
39
+ Object.send :public # patch for 1.8.7 as it uses private scope
40
+ Object.send :define_method, :instance_values do
41
+ Hash[instance_variables.map { |name| [name.to_s[1..-1], instance_variable_get(name)] }]
42
+ end
43
+ end
44
+
39
45
 
40
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.
41
47
  module Axlsx
@@ -50,8 +56,8 @@ module Axlsx
50
56
 
51
57
  def self.name_to_indices(name)
52
58
  raise ArgumentError, 'invalid cell name' unless name.size > 1
53
- v = name[/[A-Z]+/].reverse.chars.reduce({:base=>1, :i=>0}) do |v, c|
54
- 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 - 65) + val[:base]); val[:base] *= 26; val
55
61
  end
56
62
 
57
63
  [v[:i]-1, ((name[/[1-9]+/]).to_i)-1]
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'test/unit'
2
4
  require 'axlsx.rb'
3
5
 
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'test/unit'
2
4
  require 'axlsx.rb'
3
5
 
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  require 'test/unit'
2
3
  require 'axlsx.rb'
3
4
 
@@ -41,9 +41,9 @@ class TestChart < Test::Unit::TestCase
41
41
 
42
42
  end
43
43
 
44
- def end_at
44
+ def test_end_at
45
45
  @chart.end_at 25, 90
46
- assert_equal(@chart.graphic_frame.anchor.from.col, 25)
46
+ assert_equal(@chart.graphic_frame.anchor.to.col, 25)
47
47
  assert_equal(@chart.graphic_frame.anchor.to.row, 90)
48
48
  end
49
49
 
@@ -5,8 +5,7 @@ class TestCellAlignment < Test::Unit::TestCase
5
5
  def setup
6
6
  @item = Axlsx::CellAlignment.new
7
7
  end
8
- def teardown
9
- end
8
+
10
9
  def test_initialiation
11
10
  assert_equal(@item.horizontal, nil)
12
11
  assert_equal(@item.vertical, nil)
@@ -17,7 +16,13 @@ class TestCellAlignment < Test::Unit::TestCase
17
16
  assert_equal(@item.justifyLastLine, nil)
18
17
  assert_equal(@item.shrinkToFit, nil)
19
18
  assert_equal(@item.readingOrder, nil)
20
-
19
+ options = { :horizontal => :left, :vertical => :top, :textRotation => 3,
20
+ :wrapText => true, :indent => 2, :relativeIndent => 5,
21
+ :justifyLastLine => true, :shrinkToFit => true, :readingOrder => 2 }
22
+ ca = Axlsx::CellAlignment.new options
23
+ options.each do |key, value|
24
+ assert_equal(ca.send(key.to_sym),value)
25
+ end
21
26
  end
22
27
 
23
28
  def test_horizontal
@@ -17,6 +17,7 @@ class TestFont < Test::Unit::TestCase
17
17
  assert_equal(@item.family, nil)
18
18
  assert_equal(@item.b, nil)
19
19
  assert_equal(@item.i, nil)
20
+ assert_equal(@item.u, nil)
20
21
  assert_equal(@item.strike, nil)
21
22
  assert_equal(@item.outline, nil)
22
23
  assert_equal(@item.shadow, nil)
@@ -61,6 +62,13 @@ class TestFont < Test::Unit::TestCase
61
62
  assert_nothing_raised { @item.i = true }
62
63
  assert_equal(@item.i, true)
63
64
  end
65
+
66
+ # def u=(v) Axlsx::validate_boolean v; @u = v end
67
+ def test_u
68
+ assert_raise(ArgumentError) { @item.u = -7 }
69
+ assert_nothing_raised { @item.u = true }
70
+ assert_equal(@item.u, true)
71
+ end
64
72
 
65
73
  # def strike=(v) Axlsx::validate_boolean v; @strike = v end
66
74
  def test_strike
@@ -42,10 +42,14 @@ class TestStyles < Test::Unit::TestCase
42
42
 
43
43
  assert(xf.alignment.is_a?(Axlsx::CellAlignment), "alignment was created")
44
44
  assert_equal(xf.alignment.horizontal, :left, "horizontal alignment applied")
45
- assert_equal(xf.applyProtection, 1, "protection applied")
46
45
  assert_equal(xf.protection.hidden, true, "hidden protection set")
47
46
  assert_equal(xf.protection.locked, true, "cell locking set")
48
- assert_raise(ArgumentError, "should reject invalid borderId") { @styles.add_style :border => 2 }
47
+ assert_raise(ArgumentError, "should reject invalid borderId") { @styles.add_style :border => 2 }
48
+
49
+
50
+ assert_equal(xf.applyProtection, 1, "protection applied")
51
+ assert_equal(xf.applyBorder, true, "border applied")
52
+ assert_equal(xf.applyNumberFormat, true, "border applied")
49
53
 
50
54
  end
51
55
 
data/test/tc_package.rb CHANGED
@@ -21,6 +21,13 @@ class TestPackage < Test::Unit::TestCase
21
21
  assert_raise(NoMethodError) {@package.app = nil }
22
22
  end
23
23
 
24
+ def test_use_shared_strings
25
+ assert_equal(@package.use_shared_strings, nil)
26
+ assert_raise(ArgumentError) {@package.use_shared_strings 9}
27
+ assert_nothing_raised {@package.use_shared_strings = true}
28
+ assert_equal(@package.use_shared_strings, @package.workbook.use_shared_strings)
29
+ end
30
+
24
31
  def test_default_objects_are_created
25
32
  assert(@package.instance_values["app"].is_a?(Axlsx::App), 'App object not created')
26
33
  assert(@package.instance_values["core"].is_a?(Axlsx::Core), 'Core object not created')
@@ -69,10 +76,34 @@ class TestPackage < Test::Unit::TestCase
69
76
 
70
77
  #no mystery parts
71
78
  assert_equal(p.size, 12)
79
+
80
+ end
72
81
 
82
+ def test_shared_strings_requires_part
83
+ @package.use_shared_strings = true
84
+ p = @package.send(:parts)
85
+ assert_equal(p.select{ |part| part[:entry] =~/xl\/sharedStrings.xml/}.size, 1, "shared strings table missing")
73
86
  end
74
87
 
75
88
  def test_workbook_is_a_workbook
76
89
  assert @package.workbook.is_a? Axlsx::Workbook
77
90
  end
91
+
92
+ def test_base_content_types
93
+ ct = @package.send(:base_content_types)
94
+ assert(ct.select { |c| c.ContentType == Axlsx::RELS_CT }.size == 1, "rels content type missing")
95
+ assert(ct.select { |c| c.ContentType == Axlsx::XML_CT }.size == 1, "xml content type missing")
96
+ assert(ct.select { |c| c.ContentType == Axlsx::APP_CT }.size == 1, "app content type missing")
97
+ assert(ct.select { |c| c.ContentType == Axlsx::CORE_CT }.size == 1, "core content type missing")
98
+ assert(ct.select { |c| c.ContentType == Axlsx::STYLES_CT }.size == 1, "styles content type missing")
99
+ assert(ct.select { |c| c.ContentType == Axlsx::WORKBOOK_CT }.size == 1, "workbook content type missing")
100
+ assert(ct.size == 6)
101
+ end
102
+
103
+ def test_content_type_added_with_shared_strings
104
+ @package.use_shared_strings = true
105
+ ct = @package.send(:content_types)
106
+ assert(ct.select { |ct| ct.ContentType == Axlsx::SHARED_STRINGS_CT }.size == 1)
107
+ end
108
+
78
109
  end
@@ -0,0 +1,39 @@
1
+ require 'test/unit'
2
+ require 'axlsx.rb'
3
+
4
+ class TestSharedStringsTable < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @p = Axlsx::Package.new :use_shared_strings=>true
8
+ ws = @p.workbook.add_worksheet
9
+ ws.add_row ['a', 1, 'b']
10
+ ws.add_row ['b', 1, 'c']
11
+ ws.add_row ['c', 1, 'd']
12
+ end
13
+
14
+ def test_workbook_has_shared_strings
15
+ assert(@p.workbook.shared_strings.is_a?(Axlsx::SharedStringsTable), "shared string table was not created")
16
+ end
17
+
18
+ def test_count
19
+ sst = @p.workbook.shared_strings
20
+ assert_equal(sst.count, 6)
21
+ end
22
+
23
+ def test_unique_count
24
+ sst = @p.workbook.shared_strings
25
+ assert_equal(sst.unique_count, 4)
26
+ end
27
+
28
+ def test_valid_document
29
+ schema = Nokogiri::XML::Schema(File.open(Axlsx::SML_XSD))
30
+ doc = Nokogiri::XML(@p.workbook.shared_strings.to_xml)
31
+ errors = []
32
+ schema.validate(doc).each do |error|
33
+ puts error.message
34
+ errors << error
35
+ end
36
+ assert_equal(errors.size, 0, "sharedStirngs.xml Invalid" + errors.map{ |e| e.message }.to_s)
37
+ end
38
+
39
+ end
@@ -0,0 +1,8 @@
1
+ require 'test/unit'
2
+ require 'axlsx.rb'
3
+
4
+ class TestSharedStringsTable < Test::Unit::TestCase
5
+ def setup
6
+ p = Axlsx::Package.new
7
+ @wb = p.workbook
8
+ end
@@ -18,6 +18,12 @@ class TestWorkbook < Test::Unit::TestCase
18
18
  assert_equal(Axlsx::Workbook.date1904, @wb.date1904)
19
19
  end
20
20
 
21
+ def test_shared_strings
22
+ assert_equal(@wb.use_shared_strings, nil)
23
+ assert_raise(ArgumentError) {@wb.use_shared_strings = 'bpb'}
24
+ assert_nothing_raised {@wb.use_shared_strings = :true}
25
+ end
26
+
21
27
  def test_add_worksheet
22
28
  assert(@wb.worksheets.empty?, "worbook has no worksheets by default")
23
29
  ws = @wb.add_worksheet(:name=>"bob")
@@ -30,6 +36,8 @@ class TestWorkbook < Test::Unit::TestCase
30
36
  assert(@wb.relationships.size == 1)
31
37
  @wb.add_worksheet
32
38
  assert(@wb.relationships.size == 2)
39
+ @wb.use_shared_strings = true
40
+ assert(@wb.relationships.size == 3)
33
41
  end
34
42
 
35
43
  def test_to_xml