spreadsheet 0.6.2.1 → 0.6.3

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.
@@ -1,3 +1,21 @@
1
+ === 0.6.3 / 2009-01-14
2
+
3
+ * 1 Bugfix
4
+
5
+ * Fixes the issue reported by Corey Martella in
6
+ http://rubyforge.org/forum/message.php?msg_id=63651
7
+ as well as other issues engendered by the decision to always shorten
8
+ Rows to the last non-nil value.
9
+
10
+ * 2 minor enhancements
11
+
12
+ * Added bin/xlsopcodes, a tool for examining Excel files
13
+
14
+ * Documents created by Spreadsheet can now be Printed in Excel and
15
+ Excel-Viewer.
16
+ This issue was reported by Spencer Turner in
17
+ http://rubyforge.org/tracker/index.php?func=detail&aid=23287&group_id=678&atid=2677
18
+
1
19
  === 0.6.2.1 / 2008-12-18
2
20
 
3
21
  * 1 Bugfix
@@ -4,6 +4,7 @@ LICENSE.txt
4
4
  Manifest.txt
5
5
  README.txt
6
6
  Rakefile
7
+ bin/xlsopcodes
7
8
  lib/parseexcel.rb
8
9
  lib/parseexcel/parseexcel.rb
9
10
  lib/parseexcel/parser.rb
@@ -32,18 +33,23 @@ lib/spreadsheet/excel/writer/worksheet.rb
32
33
  lib/spreadsheet/font.rb
33
34
  lib/spreadsheet/format.rb
34
35
  lib/spreadsheet/formula.rb
36
+ lib/spreadsheet/helpers.rb
35
37
  lib/spreadsheet/link.rb
36
38
  lib/spreadsheet/row.rb
37
39
  lib/spreadsheet/workbook.rb
38
40
  lib/spreadsheet/worksheet.rb
39
41
  lib/spreadsheet/writer.rb
40
42
  test/data/test_copy.xls
43
+ test/data/test_datetime.xls
41
44
  test/data/test_empty.xls
42
45
  test/data/test_version_excel5.xls
43
46
  test/data/test_version_excel95.xls
44
47
  test/data/test_version_excel97.xls
45
48
  test/excel/row.rb
49
+ test/excel/writer/worksheet.rb
46
50
  test/font.rb
47
51
  test/integration.rb
52
+ test/row.rb
53
+ test/suite.rb
48
54
  test/workbook.rb
49
55
  test/worksheet.rb
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'spreadsheet'
4
+
5
+ source, target = ARGV
6
+
7
+ if source.nil?
8
+ puts "Usage: #{$0} <source> [<target>]"
9
+ exit -1
10
+ end
11
+
12
+ target = target ? File.open(target, 'w') : STDOUT
13
+
14
+ reader = Spreadsheet::Excel::Reader.new :print_opcodes => target
15
+ reader.setup source
16
+
17
+ while tuple = reader.get_next_chunk
18
+ end
@@ -42,7 +42,7 @@ module Spreadsheet
42
42
 
43
43
  ##
44
44
  # The version of Spreadsheet you are using.
45
- VERSION = '0.6.2.1'
45
+ VERSION = '0.6.3'
46
46
 
47
47
  ##
48
48
  # Default client Encoding. Change this value if your application uses a
@@ -98,8 +98,10 @@ module Internals
98
98
  :font => 'v5C3x',
99
99
  :labelsst => 'v3V',
100
100
  :number => "v3#{EIGHT_BYTE_DOUBLE}",
101
+ :pagesetup => "v8#{EIGHT_BYTE_DOUBLE}2v",
101
102
  :rk => 'v3V',
102
103
  :row => 'v4x4V',
104
+ :window2 => 'v4x2v2x4',
103
105
  :xf => 'v3C4V2v',
104
106
  }
105
107
  # From BIFF5 on, the built-in number formats will be omitted. The built-in
@@ -278,6 +280,11 @@ module Internals
278
280
  :useselfs => 0x0160, # ○ USESELFS (Natural Language Formulas) ➜ 6.105
279
281
  :dsf => 0x0161, # ○ DSF (Double Stream File) ➜ 6.32
280
282
  :refreshall => 0x01b7, # ○ REFRESHALL
283
+ ########################## ● Worksheet View Settings Block ➜ 5.5
284
+ :window2 => 0x023e, # ● WINDOW2 ➜ 5.110
285
+ :scl => 0x00a0, # ○ SCL ➜ 5.92 (BIFF4-BIFF8 only)
286
+ :pane => 0x0041, # ○ PANE ➜ 5.75
287
+ :selection => 0x001d, # ○○ SELECTION ➜ 5.93
281
288
  ########################## ○ Page Settings Block ➜ 5.4
282
289
  :hpagebreaks => 0x001b, # ○ HORIZONTALPAGEBREAKS ➜ 6.54
283
290
  :vpagebreaks => 0x001a, # ○ VERTICALPAGEBREAKS ➜ 6.107
@@ -290,7 +297,7 @@ module Internals
290
297
  :topmargin => 0x0028, # ○ TOPMARGIN ➜ 6.103
291
298
  :bottommargin => 0x0029, # ○ BOTTOMMARGIN ➜ 6.11
292
299
  # ○ PLS (opcode unknown)
293
- :setup => 0x00a1, # ○ SETUP ➜ 6.89 (BIFF4-BIFF8 only)
300
+ :pagesetup => 0x00a1, # ○ PAGESETUP ➜ 6.89 (BIFF4-BIFF8 only)
294
301
  :bitmap => 0x00e9, # ○ BITMAP ➜ 6.6 (Background-Bitmap, BIFF8 only)
295
302
  ##########################
296
303
  :printheaders => 0x002a, # ○ PRINTHEADERS ➜ 6.76
@@ -340,6 +347,8 @@ module Internals
340
347
  }
341
348
  NGILA_V_FX = XF_V_ALIGN.invert
342
349
  OPCODE_SIZE = 4
350
+ ROW_HEIGHT = 12.1
351
+ SST_CHUNKSIZE = 20
343
352
  def binfmt key
344
353
  BINARY_FORMATS[key]
345
354
  end
@@ -74,6 +74,23 @@ class Reader
74
74
  name
75
75
  end
76
76
  end
77
+ def get_next_chunk
78
+ pos = @pos
79
+ if pos < @data.size
80
+ op, len = @data[@pos,OPCODE_SIZE].unpack('v2')
81
+ @pos += OPCODE_SIZE
82
+ if len
83
+ work = @data[@pos,len]
84
+ @pos += len
85
+ code = SEDOCPO.fetch(op, op)
86
+ if io = @opts[:print_opcodes]
87
+ io.puts sprintf("0x%04x/%-16s %5i: %s",
88
+ op, code.inspect, len, work.inspect)
89
+ end
90
+ [ pos, code, len + OPCODE_SIZE, work]
91
+ end
92
+ end
93
+ end
77
94
  def in_row_block? op, previous
78
95
  if op == :row
79
96
  previous == op
@@ -98,17 +115,7 @@ class Reader
98
115
  # The entry-point for reading Excel-documents. Reads the Biff-Version and
99
116
  # loads additional reader-methods before proceeding with parsing the document.
100
117
  def read io
101
- @ole = Ole::Storage.open io
102
- @workbook = Workbook.new io, {}
103
- @book = @ole.file.open("Book") rescue @ole.file.open("Workbook")
104
- @data = @book.read
105
- read_bof
106
- @workbook.ole = @book
107
- @workbook.bof = @bof
108
- @workbook.version = @version
109
- biff = @workbook.biff_version
110
- extend_reader biff
111
- extend_internals biff
118
+ setup io
112
119
  read_workbook
113
120
  @workbook.default_format = @workbook.format 0
114
121
  @workbook.changes.clear
@@ -1047,6 +1054,19 @@ class Reader
1047
1054
  # TODO: Row spacing
1048
1055
  worksheet.set_row_address index, attrs
1049
1056
  end
1057
+ def setup io
1058
+ @ole = Ole::Storage.open io
1059
+ @workbook = Workbook.new io, {}
1060
+ @book = @ole.file.open("Book") rescue @ole.file.open("Workbook")
1061
+ @data = @book.read
1062
+ read_bof
1063
+ @workbook.ole = @book
1064
+ @workbook.bof = @bof
1065
+ @workbook.version = @version
1066
+ biff = @workbook.biff_version
1067
+ extend_reader biff
1068
+ extend_internals biff
1069
+ end
1050
1070
  private
1051
1071
  def extend_internals version
1052
1072
  require 'spreadsheet/excel/internals/biff%i' % version
@@ -1060,19 +1080,6 @@ class Reader
1060
1080
  extend Reader.const_get('Biff%i' % version)
1061
1081
  rescue LoadError
1062
1082
  end
1063
- def get_next_chunk
1064
- pos = @pos
1065
- op, len = @data[@pos,OPCODE_SIZE].unpack('v2')
1066
- @pos += OPCODE_SIZE
1067
- if len
1068
- work = @data[@pos,len]
1069
- @pos += len
1070
- code = SEDOCPO.fetch(op, op)
1071
- #puts "0x%04x/%-16s (0x%08x) %5i: %s" % [op, code.inspect, pos, len, work[0,16].inspect]
1072
- #puts "0x%04x/%-16s %5i: %s" % [op, code.inspect, len, work[0,32].inspect]
1073
- [ pos, code, len + OPCODE_SIZE, work]
1074
- end
1075
- end
1076
1083
  end
1077
1084
  end
1078
1085
  end
@@ -36,11 +36,11 @@ class Worksheet < Spreadsheet::Worksheet
36
36
  return if @row_addresses
37
37
  @dimensions = nil
38
38
  @row_addresses = []
39
- @reader.read_worksheet self, @offset
39
+ @reader.read_worksheet self, @offset if @reader
40
40
  end
41
41
  def row idx
42
- ensure_rows_read
43
42
  @rows[idx] or begin
43
+ ensure_rows_read
44
44
  if addr = @row_addresses[idx]
45
45
  row = @reader.read_row self, addr
46
46
  [:default_format, :height, :outline_level, :hidden, ].each do |key|
@@ -53,7 +53,7 @@ class Worksheet < Spreadsheet::Worksheet
53
53
  end
54
54
  end
55
55
  end
56
- def row_updated idx, row, args={}
56
+ def row_updated idx, row
57
57
  res = super
58
58
  @workbook.changes.store self, true
59
59
  @workbook.changes.store :boundsheets, true
@@ -83,11 +83,11 @@ class Worksheet < Spreadsheet::Worksheet
83
83
  index_of_first(@row_addresses) ].compact.min || 0
84
84
  @dimensions[1] = [ @rows.size, @row_addresses.size ].compact.max || 0
85
85
  compact = @rows.compact
86
- first_rows = compact.collect do |row| index_of_first row end.compact.min
86
+ first_rows = compact.collect do |row| row.first_used end.compact.min
87
87
  first_addrs = @row_addresses.compact.collect do |addr|
88
88
  addr[:first_used] end.min
89
89
  @dimensions[2] = [ first_rows, first_addrs ].compact.min || 0
90
- last_rows = compact.collect do |row| row.size end.max
90
+ last_rows = compact.collect do |row| row.first_unused end.max
91
91
  last_addrs = @row_addresses.compact.collect do |addr|
92
92
  addr[:first_unused] end.max
93
93
  @dimensions[3] = [last_rows, last_addrs].compact.max || 0
@@ -32,8 +32,8 @@ class Format < DelegateClass Spreadsheet::Format
32
32
  color :pattern_bg_color, :pattern_bg
33
33
  attr_accessor :xf_index
34
34
  attr_reader :format
35
- def initialize writer, workbook, format=workbook.default_format, type=:format
36
- @type = type.to_s.downcase
35
+ def initialize writer, workbook, format=workbook.default_format, opts={}
36
+ @opts = { :type => :format }.merge opts
37
37
  @format = format
38
38
  @writer = writer
39
39
  @workbook = workbook
@@ -62,7 +62,7 @@ class Format < DelegateClass Spreadsheet::Format
62
62
  writer.write [op,data.size].pack("v2")
63
63
  writer.write data
64
64
  end
65
- def write_xf writer, type=@type
65
+ def write_xf writer, type=@opts[:type]
66
66
  xf_type = xf_type_prot type
67
67
  data = [
68
68
  font_index, # Index to FONT record (➜ 6.43)
@@ -56,7 +56,8 @@ class Workbook < Spreadsheet::Writer
56
56
  formats = []
57
57
  unless opts[:existing_document]
58
58
  15.times do
59
- formats.push Format.new(self, workbook, workbook.default_format, :style)
59
+ formats.push Format.new(self, workbook, workbook.default_format,
60
+ :type => :style)
60
61
  end
61
62
  formats.push Format.new(self, workbook)
62
63
  end
@@ -258,7 +259,7 @@ class Workbook < Spreadsheet::Writer
258
259
  write_op writer, 0x000a
259
260
  end
260
261
  def write_extsst workbook, offsets, writer
261
- header = [8].pack('v')
262
+ header = [SST_CHUNKSIZE].pack('v')
262
263
  data = offsets.collect do |pair| pair.push(0).pack('Vv2') end
263
264
  write_op writer, 0x00ff, header, data
264
265
  end
@@ -374,7 +375,9 @@ class Workbook < Spreadsheet::Writer
374
375
  buffer1 = StringIO.new ''
375
376
  # ● BOF Type = workbook globals (➜ 6.8)
376
377
  write_bof workbook, buffer1, :globals
377
- # ○ File Protection Block ➜ 5.19
378
+ # ○ File Protection Block ➜ 4.19
379
+ # ○ WRITEACCESS User name (BIFF3-BIFF8, ➜ 5.112)
380
+ # ○ FILESHARING File sharing options (BIFF3-BIFF8, ➜ 5.44)
378
381
  # ○ CODEPAGE ➜ 6.17
379
382
  write_encoding workbook, buffer1
380
383
  # ○ DSF ➜ 6.32
@@ -382,42 +385,46 @@ class Workbook < Spreadsheet::Writer
382
385
  # ○ TABID
383
386
  write_tabid workbook, buffer1
384
387
  # ○ FNGROUPCOUNT
385
- # ○ Workbook Protection Block ➜ 5.18
388
+ # ○ Workbook Protection Block ➜ 4.18
389
+ # ○ WINDOWPROTECT Window settings: 1 = protected (➜ 5.111)
390
+ # ○ PROTECT Cell contents: 1 = protected (➜ 5.82)
386
391
  write_protect workbook, buffer1
392
+ # ○ OBJECTPROTECT Embedded objects: 1 = protected (➜ 5.72)
393
+ # ○ PASSWORD Hash value of the password; 0 = No password (➜ 5.76)
387
394
  write_password workbook, buffer1
388
- # WINDOW16.108
395
+ # BACKUP5.5
396
+ # ○ HIDEOBJ ➜ 5.56
397
+ # ● WINDOW1 ➜ 5.109
389
398
  write_window1 workbook, buffer1
390
- # ○ BACKUP6.5
391
- # ○ HIDEOBJ ➜ 6.52
392
- # ○ DATEMODE ➜ 6.25
399
+ # ○ DATEMODE5.28
393
400
  write_datemode workbook, buffer1
394
- # ○ PRECISION ➜ 6.74
401
+ # ○ PRECISION ➜ 5.79
395
402
  write_precision workbook, buffer1
396
403
  # ○ REFRESHALL
397
404
  write_refreshall workbook, buffer1
398
- # ○ BOOKBOOL ➜ 6.9
405
+ # ○ BOOKBOOL ➜ 5.9
399
406
  write_bookbool workbook, buffer1
400
- # ●● FONT ➜ 6.43
407
+ # ●● FONT ➜ 5.45
401
408
  write_fonts workbook, buffer1
402
- # ○○ FORMAT ➜ 6.45
409
+ # ○○ FORMAT ➜ 5.49
403
410
  write_formats workbook, buffer1
404
- # ●● XF ➜ 6.115
411
+ # ●● XF ➜ 5.115
405
412
  write_xfs workbook, buffer1
406
- # ●● STYLE ➜ 6.99
413
+ # ●● STYLE ➜ 5.103
407
414
  write_styles workbook, buffer1
408
- # ○ PALETTE ➜ 6.70
409
- # ○ USESELFS ➜ 6.105
415
+ # ○ PALETTE ➜ 5.74
416
+ # ○ USESELFS ➜ 5.106
410
417
  buffer1.rewind
411
- # ●● BOUNDSHEET ➜ 6.12
418
+ # ●● BOUNDSHEET ➜ 5.95
412
419
  buffer2 = StringIO.new ''
413
- # ○ COUNTRY ➜ 6.23
414
- # ○ Link Table ➜ 5.10.3
420
+ # ○ COUNTRY ➜ 5.22
421
+ # ○ Link Table ➜ 4.10.3
415
422
  # ○○ NAME ➜ 6.66
416
- # ○ Shared String Table ➜ 5.11
417
- # ● SST ➜ 6.96
418
- # ● EXTSST ➜ 6.40
423
+ # ○ Shared String Table ➜ 4.11
424
+ # ● SST ➜ 5.100
425
+ # ● EXTSST ➜ 5.42
419
426
  write_sst workbook, buffer2, buffer1.size
420
- # ● EOF ➜ 6.36
427
+ # ● EOF ➜ 5.37
421
428
  write_eof workbook, buffer2
422
429
  buffer2.rewind
423
430
  # worksheet data can only be assembled after write_sst
@@ -482,7 +489,9 @@ class Workbook < Spreadsheet::Writer
482
489
  strings.each_with_index do |string, idx|
483
490
  sst.store string, idx
484
491
  op_offset = data.size + 4
485
- offsets.push [offset + writer.pos + op_offset, op_offset] if idx % 8 == 0
492
+ if idx % SST_CHUNKSIZE == 0
493
+ offsets.push [offset + writer.pos + op_offset, op_offset]
494
+ end
486
495
  header, packed, next_wide = _unicode_string string, 2
487
496
  # the first few bytes (header + first character) must not be split
488
497
  must_fit = header.size + wide + 1
@@ -546,9 +555,9 @@ class Workbook < Spreadsheet::Writer
546
555
  # 0x07 = Currency [0] (BIFF4-BIFF8)
547
556
  # 0x08 = Hyperlink (BIFF8)
548
557
  # 0x09 = Followed Hyperlink (BIFF8)
549
- 0xFF, # Level for RowLevel or ColLevel style (zero-based, lv),
550
- # 0xFF otherwise
551
- 0x00, # The RowLevel and ColLevel styles specify the formatting of
558
+ 0xff, # Level for RowLevel or ColLevel style (zero-based, lv),
559
+ # 0xff otherwise
560
+ # The RowLevel and ColLevel styles specify the formatting of
552
561
  # subtotal cells in a specific outline level. The level is
553
562
  # specified by the last field in the STYLE record. Valid values
554
563
  # are 0…6 for the outline levels 1…7.
@@ -158,7 +158,7 @@ class Worksheet
158
158
  write_op 0x000c, [count].pack('v')
159
159
  end
160
160
  def write_cell type, row, idx, *args
161
- xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx)
161
+ xf_idx = @workbook.xf_index @worksheet.workbook, row.formats[idx]
162
162
  data = [
163
163
  row.idx, # Index to row
164
164
  idx, # Index to column
@@ -178,6 +178,7 @@ class Worksheet
178
178
  # RK ➜ 6.82 (BIFF3-BIFF8)
179
179
  # RSTRING ➜ 6.84 (BIFF5/BIFF7)
180
180
  multiples, first_idx = nil
181
+ row = row.formatted
181
182
  row.each_with_index do |cell, idx|
182
183
  cell = nil if cell == ''
183
184
  ## it appears that there are limitations to RK precision, both for
@@ -357,7 +358,7 @@ class Worksheet
357
358
  # Write a cell with a Formula. May write an additional String record depending
358
359
  # on the stored result of the Formula.
359
360
  def write_formula row, idx
360
- xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx)
361
+ xf_idx = @workbook.xf_index @worksheet.workbook, row.formats[idx]
361
362
  cell = row[idx]
362
363
  data1 = [
363
364
  row.idx, # Index to row
@@ -415,47 +416,52 @@ class Worksheet
415
416
  ##
416
417
  # Write a new Worksheet.
417
418
  def write_from_scratch
418
- # ● BOF Type = worksheet (➜ 6.8)
419
+ # ● BOF Type = worksheet (➜ 5.8)
419
420
  write_bof
420
- # ○ UNCALCED ➜ 6.104
421
- # ○ INDEX ➜ 5.7 (Row Blocks), ➜ 6.55
422
- # ○ Calculation Settings Block ➜ 5.3
421
+ # ○ UNCALCED ➜ 5.105
422
+ # ○ INDEX ➜ 4.7 (Row Blocks), ➜ 5.59
423
+ # ○ Calculation Settings Block ➜ 4.3
423
424
  write_calccount
424
425
  write_refmode
425
426
  write_iteration
426
427
  write_saverecalc
427
- # ○ PRINTHEADERS ➜ 6.76
428
- # ○ PRINTGRIDLINES ➜ 6.75
429
- # ○ GRIDSET ➜ 6.48
430
- # ○ GUTS ➜ 6.49
431
- # ○ DEFAULTROWHEIGHT ➜ 6.28
428
+ # ○ PRINTHEADERS ➜ 5.81
429
+ # ○ PRINTGRIDLINES ➜ 5.80
430
+ # ○ GRIDSET ➜ 5.52
431
+ # ○ GUTS ➜ 5.53
432
+ # ○ DEFAULTROWHEIGHT ➜ 5.31
432
433
  write_defaultrowheight
433
- # ○ WSBOOL ➜ 6.113
434
+ # ○ WSBOOL ➜ 5.113
434
435
  write_wsbool
435
- # ○ Page Settings Block ➜ 5.4
436
- # ○ Worksheet Protection Block ➜ 5.18
437
- # ○ DEFCOLWIDTH ➜ 6.29
436
+ # ○ Page Settings Block ➜ 4.4
437
+ # ○ Worksheet Protection Block ➜ 4.18
438
+ # ○ DEFCOLWIDTH ➜ 5.32
438
439
  write_defcolwidth
439
- # ○○ COLINFO ➜ 6.18
440
+ # ○○ COLINFO ➜ 5.18
440
441
  write_colinfos
441
- # ○ SORT ➜ 6.95
442
- # ● DIMENSIONS ➜ 6.31
442
+ # ○ SORT ➜ 5.99
443
+ # ● DIMENSIONS ➜ 5.35
443
444
  write_dimensions
444
- # ○○ Row Blocks ➜ 5.7
445
+ # ○○ Row Blocks ➜ 4.7
445
446
  write_rows
446
- # ● Worksheet View Settings Block ➜ 5.5
447
- # STANDARDWIDTH6.97
448
- # ○○ MERGEDCELLS ➜ 6.63
449
- # ○ LABELRANGES6.60
450
- # ○ PHONETIC6.73
451
- # ○ Conditional Formatting Table ➜ 5.12
452
- # ○ Hyperlink Table ➜ 5.13
447
+ # ● Worksheet View Settings Block ➜ 4.5
448
+ # WINDOW25.110
449
+ write_window2
450
+ # ○ SCL5.92 (BIFF4-BIFF8 only)
451
+ # ○ PANE5.75
452
+ # ○○ SELECTION ➜ 5.93
453
+ # ○ STANDARDWIDTH ➜ 5.101
454
+ # ○○ MERGEDCELLS ➜ 5.67
455
+ # ○ LABELRANGES ➜ 5.64
456
+ # ○ PHONETIC ➜ 5.77
457
+ # ○ Conditional Formatting Table ➜ 4.12
458
+ # ○ Hyperlink Table ➜ 4.13
453
459
  write_hyperlink_table
454
- # ○ Data Validity Table ➜ 5.14
455
- # ○ SHEETLAYOUT ➜ 6.91 (BIFF8X only)
456
- # ○ SHEETPROTECTION Additional protection, ➜ 6.92 (BIFF8X only)
457
- # ○ RANGEPROTECTION Additional protection, ➜ 6.79 (BIFF8X only)
458
- # ● EOF ➜ 6.36
460
+ # ○ Data Validity Table ➜ 4.14
461
+ # ○ SHEETLAYOUT ➜ 5.96 (BIFF8X only)
462
+ # ○ SHEETPROTECTION Additional protection, ➜ 5.98 (BIFF8X only)
463
+ # ○ RANGEPROTECTION Additional protection, ➜ 5.84 (BIFF8X only)
464
+ # ● EOF ➜ 5.36
459
465
  write_eof
460
466
  end
461
467
  def write_hlink row, col, link
@@ -556,11 +562,11 @@ class Worksheet
556
562
  ]
557
563
  # List of nc=lc-fc+1 16-bit indexes to XF records (➜ 6.115)
558
564
  multiples.each_with_index do |blank, cell_idx|
559
- xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx + cell_idx)
565
+ xf_idx = @workbook.xf_index @worksheet.workbook, row.formats[idx + cell_idx]
560
566
  data.push xf_idx
561
567
  end
562
568
  # Index to last column (lc)
563
- data.push idx + multiples.size
569
+ data.push idx + multiples.size - 1
564
570
  write_op opcode(:mulblank), data.pack('v*')
565
571
  end
566
572
  ##
@@ -673,23 +679,28 @@ class Worksheet
673
679
  # formatted with a medium or thick
674
680
  # line style. Thin line styles are
675
681
  # not taken into account.
676
- height = row.height || 12 # FIXME: where is the default font height?
682
+ height = row.height || ROW_HEIGHT
677
683
  opts = row.outline_level & 0x00000007
678
684
  opts |= 0x00000010 if row.collapsed?
679
685
  opts |= 0x00000020 if row.hidden?
680
- opts |= 0x00000040 if height != 12 # FIXME: where is the default font height?
686
+ opts |= 0x00000040 if height != ROW_HEIGHT
681
687
  if fmt = row.default_format
682
688
  xf_idx = @workbook.xf_index @worksheet.workbook, fmt
683
689
  opts |= 0x00000080
684
690
  opts |= xf_idx << 16
685
691
  end
686
692
  opts |= 0x00000100
693
+ height = if height == ROW_HEIGHT
694
+ height * TWIPS
695
+ else
696
+ ( height * TWIPS ) | 0x8000
697
+ end
687
698
  # TODO: Row spacing
688
699
  data = [
689
700
  row.idx,
690
701
  row.first_used,
691
702
  row.first_unused,
692
- height * TWIPS,
703
+ height,
693
704
  opts,
694
705
  ].pack binfmt(:row)
695
706
  write_op opcode(:row), data
@@ -714,6 +725,54 @@ class Worksheet
714
725
  # 0 = Do not recalculate; 1 = Recalculate before saving the document
715
726
  write_op 0x005f, [1].pack('v')
716
727
  end
728
+ def write_window2
729
+ # This record contains additional settings for the document window
730
+ # (BIFF2-BIFF4) or for the window of a specific worksheet (BIFF5-BIFF8).
731
+ # It is part of the Sheet View Settings Block (➜ 4.5).
732
+ # Offset Size Contents
733
+ # 0 2 Option flags:
734
+ # Bits Mask Contents
735
+ # 0 0x0001 0 = Show formula results
736
+ # 1 = Show formulas
737
+ # 1 0x0002 0 = Do not show grid lines
738
+ # 1 = Show grid lines
739
+ # 2 0x0004 0 = Do not show sheet headers
740
+ # 1 = Show sheet headers
741
+ # 3 0x0008 0 = Panes are not frozen
742
+ # 1 = Panes are frozen (freeze)
743
+ # 4 0x0010 0 = Show zero values as empty cells
744
+ # 1 = Show zero values
745
+ # 5 0x0020 0 = Manual grid line colour
746
+ # 1 = Automatic grid line colour
747
+ # 6 0x0040 0 = Columns from left to right
748
+ # 1 = Columns from right to left
749
+ # 7 0x0080 0 = Do not show outline symbols
750
+ # 1 = Show outline symbols
751
+ # 8 0x0100 0 = Keep splits if pane freeze is removed
752
+ # 1 = Remove splits if pane freeze is removed
753
+ # 9 0x0200 0 = Sheet not selected
754
+ # 1 = Sheet selected (BIFF5-BIFF8)
755
+ # 10 0x0400 0 = Sheet not active
756
+ # 1 = Sheet active (BIFF5-BIFF8)
757
+ # 11 0x0800 0 = Show in normal view
758
+ # 1 = Show in page break preview (BIFF8)
759
+ # 2 2 Index to first visible row
760
+ # 4 2 Index to first visible column
761
+ # 6 2 Colour index of grid line colour (➜ 5.74).
762
+ # Note that in BIFF2-BIFF5 an RGB colour is written instead.
763
+ # 8 2 Not used
764
+ # 10 2 Cached magnification factor in page break preview (in percent)
765
+ # 0 = Default (60%)
766
+ # 12 2 Cached magnification factor in normal view (in percent)
767
+ # 0 = Default (100%)
768
+ # 14 4 Not used
769
+ flags = 310
770
+ if @worksheet.active
771
+ flags |= 0x0200
772
+ end
773
+ data = [ flags, 0, 0, 0, 0, 0 ].pack binfmt(:window2)
774
+ write_op opcode(:window2), data
775
+ end
717
776
  def write_wsbool
718
777
  bits = [
719
778
  # Bit Mask Contents
@@ -0,0 +1,11 @@
1
+ class Array
2
+ def rcompact
3
+ dup.rcompact!
4
+ end
5
+ def rcompact!
6
+ while !empty? && last.nil?
7
+ pop
8
+ end
9
+ self
10
+ end
11
+ end
@@ -1,3 +1,5 @@
1
+ require 'spreadsheet/helpers'
2
+
1
3
  module Spreadsheet
2
4
  ##
3
5
  # The Row class. Encapsulates Cell data and formatting.
@@ -23,10 +25,7 @@ module Spreadsheet
23
25
  alias_method :"unupdated_#{key}=", :"#{key}="
24
26
  define_method "#{key}=" do |value|
25
27
  send "unupdated_#{key}=", value
26
- if @worksheet
27
- @formatted = true
28
- @worksheet.row_updated @idx, self, :formatted => true
29
- end
28
+ @worksheet.row_updated @idx, self if @worksheet
30
29
  value
31
30
  end
32
31
  end
@@ -36,9 +35,7 @@ module Spreadsheet
36
35
  keys.each do |key|
37
36
  define_method key do |*args|
38
37
  res = super
39
- if @worksheet
40
- @worksheet.row_updated @idx, self, :formatted => @formatted
41
- end
38
+ @worksheet.row_updated @idx, self if @worksheet
42
39
  res
43
40
  end
44
41
  end
@@ -55,31 +52,22 @@ module Spreadsheet
55
52
  def initialize worksheet, idx, cells=[]
56
53
  @worksheet = worksheet
57
54
  @idx = idx
58
- while !cells.empty? && !cells.last
59
- cells.pop
60
- end
61
55
  super cells
62
- @first_used ||= index_of_first self
63
- @first_unused ||= size
64
56
  @formats = []
65
- @height = 12
57
+ @height = 12.1
66
58
  end
67
59
  ##
68
60
  # Set the default Format used when writing a Cell if no explicit Format is
69
61
  # stored for the cell.
70
62
  def default_format= format
71
63
  @worksheet.add_format format if @worksheet
72
- @worksheet.row_updated @idx, self, :formatted => true if @worksheet
64
+ @worksheet.row_updated @idx, self if @worksheet
73
65
  @default_format = format
74
66
  end
75
67
  ##
76
- # #first_unused (really last used + 1) - the 0-based index of the first of
77
- # all remaining contiguous blank Cells.
78
- alias :first_unused :size
79
- ##
80
68
  # #first_used the 0-based index of the first non-blank Cell.
81
69
  def first_used
82
- index_of_first self
70
+ [ index_of_first(self), index_of_first(@formats) ].compact.min
83
71
  end
84
72
  ##
85
73
  # The Format for the Cell at _idx_ (0-based), or the first valid Format in
@@ -89,22 +77,48 @@ module Spreadsheet
89
77
  || @worksheet.column(idx).default_format if @worksheet
90
78
  end
91
79
  ##
92
- # Set the Format for the Cell at _idx_ (0-based).
93
- def set_format idx, fmt
94
- @formats[idx] = fmt
95
- @worksheet.add_format fmt
96
- @worksheet.row_updated @idx, self, :formatted => true if @worksheet
97
- fmt
80
+ # Returns a copy of self with nil-values appended for empty cells that have
81
+ # an associated Format.
82
+ # This is primarily a helper-function for the writer classes.
83
+ def formatted
84
+ copy = dup
85
+ @formats.rcompact!
86
+ if copy.length < @formats.size
87
+ copy.concat Array.new(@formats.size - copy.length)
88
+ end
89
+ copy
90
+ end
91
+ ##
92
+ # Same as Row#size, but takes into account formatted empty cells
93
+ def formatted_size
94
+ @formats.rcompact!
95
+ sz = size
96
+ fs = @formats.size
97
+ fs > sz ? fs : sz
98
98
  end
99
+ ##
100
+ # #first_unused (really last used + 1) - the 0-based index of the first of
101
+ # all remaining contiguous blank Cells.
102
+ alias :first_unused :formatted_size
99
103
  def inspect
100
104
  variables = instance_variables.collect do |name|
101
105
  "%s=%s" % [name, instance_variable_get(name)]
102
106
  end.join(' ')
103
107
  sprintf "#<%s:0x%014x %s %s>", self.class, object_id, variables, super
104
108
  end
109
+ ##
110
+ # Set the Format for the Cell at _idx_ (0-based).
111
+ def set_format idx, fmt
112
+ @formats[idx] = fmt
113
+ @worksheet.add_format fmt
114
+ @worksheet.row_updated @idx, self if @worksheet
115
+ fmt
116
+ end
105
117
  private
106
118
  def index_of_first ary # :nodoc:
107
- ary.index(ary.find do |elm| elm end)
119
+ if first = ary.find do |elm| !elm.nil? end
120
+ ary.index first
121
+ end
108
122
  end
109
123
  end
110
124
  end
@@ -13,7 +13,7 @@ module Spreadsheet
13
13
  class Workbook
14
14
  include Spreadsheet::Encodings
15
15
  attr_reader :io, :worksheets, :formats, :fonts
16
- attr_accessor :encoding, :version, :default_format
16
+ attr_accessor :active_worksheet, :encoding, :default_format, :version
17
17
  def initialize io = nil, opts={:default_format => Format.new}
18
18
  @worksheets = []
19
19
  @io = io
@@ -19,12 +19,19 @@ module Spreadsheet
19
19
  # instances may appear at more than one position in #columns.
20
20
  # If you modify a Column directly, your changes will be
21
21
  # reflected in all those positions.
22
+ # #active :: When a user chooses to print a Workbook, Excel will include
23
+ # all active Worksheets. Defaults to true. If you want to
24
+ # exclude a Worksheet from the default print-list, set this
25
+ # to false. Warning: Excel will display a cryptic Error
26
+ # Message if a user tries to print a Workbook that has no
27
+ # active Worksheets.
22
28
  class Worksheet
23
29
  include Spreadsheet::Encodings
24
30
  include Enumerable
25
- attr_accessor :name, :workbook
31
+ attr_accessor :active, :name, :workbook
26
32
  attr_reader :rows, :columns
27
33
  def initialize opts={}
34
+ @active = true
28
35
  @dimensions = [0,0,0,0]
29
36
  @name = opts[:name] || 'Worksheet'
30
37
  @workbook = opts[:workbook]
@@ -173,11 +180,8 @@ module Spreadsheet
173
180
  ##
174
181
  # Tell Worksheet that the Row at _idx_ has been updated and the #dimensions
175
182
  # need to be recalculated. You should not need to call this directly.
176
- def row_updated idx, row, opts={}
183
+ def row_updated idx, row
177
184
  @dimensions = nil
178
- unless opts[:formatted]
179
- row = shorten(row)
180
- end
181
185
  @rows[idx] = row
182
186
  format_dates row
183
187
  row
@@ -188,7 +192,7 @@ module Spreadsheet
188
192
  res = if row = @rows[idx]
189
193
  row[0, cells.size] = cells
190
194
  row
191
- elsif cells = shorten(cells)
195
+ else
192
196
  Row.new self, idx, cells
193
197
  end
194
198
  row_updated idx, res
@@ -251,8 +255,9 @@ module Spreadsheet
251
255
  @dimensions[0] = index_of_first(@rows) || 0
252
256
  @dimensions[1] = @rows.size
253
257
  compact = @rows.compact
254
- @dimensions[2] = compact.collect do |row| index_of_first row end.compact.min || 0
255
- @dimensions[3] = compact.collect do |row| row.size end.max || 0
258
+ @dimensions[2] = compact.collect do |row| row.first_used end.compact.min || 0
259
+ @dimensions[3] = compact.collect do |row| row.first_unused end.max || 0
260
+ puts caller if @dimensions.nil?
256
261
  @dimensions
257
262
  end
258
263
  def shorten ary # :nodoc:
@@ -19,8 +19,8 @@ class TestRow < Test::Unit::TestCase
19
19
  assert_equal Date.new(1975,8,21), row.date(1)
20
20
  end
21
21
  def test_datetime
22
- row = Row.new @worksheet, 0, [nil, 27627.6789]
23
- d1 = DateTime.new(1975,8,21) + 0.6789
22
+ row = Row.new @worksheet, 0, [nil, 27627.765]
23
+ d1 = DateTime.new(1975,8,21) + 0.765
24
24
  d2 = row.datetime 1
25
25
  assert_equal d1, d2
26
26
  end
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+ # Excel::Writer::TestWorksheet -- Spreadheet -- 21.11.2007 -- hwyss@ywesee.com
3
+
4
+ require 'test/unit'
5
+ require 'spreadsheet/excel/writer/worksheet'
6
+
7
+ module Spreadsheet
8
+ module Excel
9
+ module Writer
10
+ class TestWorksheet < Test::Unit::TestCase
11
+ def test_need_number
12
+ sheet = Worksheet.new nil, nil
13
+ assert_equal false, sheet.need_number?(10)
14
+ assert_equal false, sheet.need_number?(114.55)
15
+ assert_equal false, sheet.need_number?(0.1)
16
+ assert_equal false, sheet.need_number?(0.01)
17
+ assert_equal true, sheet.need_number?(0.001)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -63,7 +63,7 @@ module Spreadsheet
63
63
  enc = Encoding.find enc
64
64
  end
65
65
  assert_equal enc, book.encoding
66
- assert_equal 24, book.formats.size
66
+ assert_equal 25, book.formats.size
67
67
  assert_equal 5, book.fonts.size
68
68
  str1 = book.shared_string 0
69
69
  assert_equal @@iconv.iconv('Shared String'), str1
@@ -92,10 +92,10 @@ module Spreadsheet
92
92
  end
93
93
  assert_equal long, str4
94
94
  sheet = book.worksheet 0
95
- assert_equal 10, sheet.row_count
96
- assert_equal 11, sheet.column_count
97
- useds = [0,0,0,0,0,0,0,1,0,0]
98
- unuseds = [2,2,1,1,1,2,1,11,1,2]
95
+ assert_equal 11, sheet.row_count
96
+ assert_equal 12, sheet.column_count
97
+ useds = [0,0,0,0,0,0,0,1,0,0,11]
98
+ unuseds = [2,2,1,1,1,2,1,11,1,2,12]
99
99
  sheet.each do |row|
100
100
  assert_equal useds.shift, row.first_used
101
101
  assert_equal unuseds.shift, row.first_unused
@@ -154,6 +154,7 @@ module Spreadsheet
154
154
  assert_equal 0.0001, row[0]
155
155
  row = sheet.row 9
156
156
  assert_equal 0.00009, row[0]
157
+ assert_equal :green, sheet.row(10).format(11).pattern_fg_color
157
158
  end
158
159
  def test_version_excel97__ooffice
159
160
  path = File.join @data, 'test_version_excel97.xls'
@@ -166,7 +167,7 @@ module Spreadsheet
166
167
  enc = Encoding.find enc
167
168
  end
168
169
  assert_equal enc, book.encoding
169
- assert_equal 24, book.formats.size
170
+ assert_equal 25, book.formats.size
170
171
  assert_equal 5, book.fonts.size
171
172
  str1 = book.shared_string 0
172
173
  assert_equal 'Shared String', str1
@@ -195,10 +196,10 @@ module Spreadsheet
195
196
  end
196
197
  assert_equal long, str4
197
198
  sheet = book.worksheet 0
198
- assert_equal 10, sheet.row_count
199
- assert_equal 11, sheet.column_count
200
- useds = [0,0,0,0,0,0,0,1,0,0]
201
- unuseds = [2,2,1,1,1,2,1,11,1,2]
199
+ assert_equal 11, sheet.row_count
200
+ assert_equal 12, sheet.column_count
201
+ useds = [0,0,0,0,0,0,0,1,0,0,11]
202
+ unuseds = [2,2,1,1,1,2,1,11,1,2,12]
202
203
  sheet.each do |row|
203
204
  assert_equal useds.shift, row.first_used
204
205
  assert_equal unuseds.shift, row.first_unused
@@ -577,10 +578,10 @@ module Spreadsheet
577
578
  book.write path
578
579
  assert_nothing_raised do book = Spreadsheet.open path end
579
580
  sheet = book.worksheet 0
580
- assert_equal 10, sheet.row_count
581
- assert_equal 11, sheet.column_count
582
- useds = [0,0,0,0,0,0,0,1,0,0]
583
- unuseds = [2,2,1,1,1,2,1,11,1,2]
581
+ assert_equal 11, sheet.row_count
582
+ assert_equal 12, sheet.column_count
583
+ useds = [0,0,0,0,0,0,0,1,0,0,11]
584
+ unuseds = [2,2,1,1,1,2,1,11,1,2,12]
584
585
  sheet.each do |row|
585
586
  assert_equal useds.shift, row.first_used
586
587
  assert_equal unuseds.shift, row.first_unused
@@ -686,10 +687,10 @@ module Spreadsheet
686
687
  assert_equal str3, book.shared_string(2)
687
688
  assert_equal str4, book.shared_string(3)
688
689
  sheet = book.worksheet 0
689
- assert_equal 10, sheet.row_count
690
- assert_equal 11, sheet.column_count
691
- useds = [0,0,0,0,0,0,0,1,0,0]
692
- unuseds = [2,2,1,1,1,2,1,11,1,2]
690
+ assert_equal 11, sheet.row_count
691
+ assert_equal 12, sheet.column_count
692
+ useds = [0,0,0,0,0,0,0,1,0,0,11]
693
+ unuseds = [2,2,1,1,1,2,1,11,1,2,12]
693
694
  sheet.each do |row|
694
695
  assert_equal useds.shift, row.first_used
695
696
  assert_equal unuseds.shift, row.first_unused
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+ # TestRow -- Spreadsheet -- 08.01.2009 -- hwyss@ywesee.com
3
+
4
+ $: << File.expand_path('../../lib', File.dirname(__FILE__))
5
+
6
+ require 'test/unit'
7
+ require 'spreadsheet'
8
+
9
+ module Spreadsheet
10
+ class TestRow < Test::Unit::TestCase
11
+ def setup
12
+ @workbook = Excel::Workbook.new
13
+ @worksheet = Excel::Worksheet.new
14
+ @workbook.add_worksheet @worksheet
15
+ end
16
+ def test_formatted
17
+ row = Row.new @worksheet, 0, [nil, 1]
18
+ assert_equal 2, row.formatted.size
19
+ row.set_format 3, Format.new
20
+ assert_equal 4, row.formatted.size
21
+ end
22
+ def test_concat
23
+ row = Row.new @worksheet, 0, [nil, 1, nil]
24
+ assert_equal [nil, 1, nil], row
25
+ row.concat [2, nil]
26
+ assert_equal [nil, 1, nil, 2, nil], row
27
+ row.concat [3]
28
+ assert_equal [nil, 1, nil, 2, nil, 3], row
29
+ row.concat [nil, 4]
30
+ assert_equal [nil, 1, nil, 2, nil, 3, nil, 4], row
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # suite.rb -- oddb -- 08.01.2009 -- hwyss@ywesee.com
3
+
4
+ require 'find'
5
+
6
+ here = File.dirname(__FILE__)
7
+
8
+ $: << here
9
+
10
+ Find.find(here) do |file|
11
+ if /(?<!suite)\.rb$/o.match(file)
12
+ require file
13
+ end
14
+ end
@@ -28,10 +28,10 @@ module Spreadsheet
28
28
  assert_equal 2, @sheet.row_count
29
29
  @sheet[1,0] = nil
30
30
  assert_equal 2, @sheet.column_count
31
- assert_equal 1, @sheet.row_count
31
+ assert_equal 2, @sheet.row_count
32
32
  @sheet[0,1] = nil
33
- assert_equal 1, @sheet.column_count
34
- assert_equal 1, @sheet.row_count
33
+ assert_equal 2, @sheet.column_count
34
+ assert_equal 2, @sheet.row_count
35
35
  end
36
36
  def test_column_count
37
37
  assert_equal 0, @sheet.column_count
@@ -44,7 +44,7 @@ module Spreadsheet
44
44
  @sheet.replace_row 5, nil, 'something', 4, 7, nil
45
45
  assert_equal 4, @sheet.column_count
46
46
  @sheet.replace_row 3
47
- assert_equal 3, @sheet.column_count
47
+ assert_equal 4, @sheet.column_count
48
48
  end
49
49
  def test_row_count
50
50
  assert_equal 0, @sheet.row_count
@@ -57,7 +57,15 @@ module Spreadsheet
57
57
  @sheet.replace_row 5, nil, 'something', 4, 7, nil
58
58
  assert_equal 6, @sheet.row_count
59
59
  @sheet.replace_row 3
60
+ assert_equal 6, @sheet.row_count
61
+ @sheet.delete_row 3
62
+ assert_equal 5, @sheet.row_count
63
+ @sheet.delete_row 3
64
+ assert_equal 4, @sheet.row_count
65
+ @sheet.delete_row 2
60
66
  assert_equal 4, @sheet.row_count
67
+ @sheet.delete_row 2
68
+ assert_equal 3, @sheet.row_count
61
69
  end
62
70
  def test_modify_column
63
71
  assert_equal 10, @sheet.column(0).width
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spreadsheet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2.1
4
+ version: 0.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hannes Wyss
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-19 00:00:00 +01:00
12
+ date: 2009-01-14 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -35,8 +35,8 @@ dependencies:
35
35
  description: The Spreadsheet Library is designed to read and write Spreadsheet Documents. As of version 0.6.0, only Microsoft Excel compatible spreadsheets are supported. Spreadsheet is a combination/complete rewrite of the Spreadsheet::Excel Library by Daniel J. Berger and the ParseExcel Library by Hannes Wyss. Spreadsheet can read, write and modify Spreadsheet Documents.
36
36
  email:
37
37
  - hannes.wyss@gmail.com
38
- executables: []
39
-
38
+ executables:
39
+ - xlsopcodes
40
40
  extensions: []
41
41
 
42
42
  extra_rdoc_files:
@@ -52,6 +52,7 @@ files:
52
52
  - Manifest.txt
53
53
  - README.txt
54
54
  - Rakefile
55
+ - bin/xlsopcodes
55
56
  - lib/parseexcel.rb
56
57
  - lib/parseexcel/parseexcel.rb
57
58
  - lib/parseexcel/parser.rb
@@ -80,19 +81,24 @@ files:
80
81
  - lib/spreadsheet/font.rb
81
82
  - lib/spreadsheet/format.rb
82
83
  - lib/spreadsheet/formula.rb
84
+ - lib/spreadsheet/helpers.rb
83
85
  - lib/spreadsheet/link.rb
84
86
  - lib/spreadsheet/row.rb
85
87
  - lib/spreadsheet/workbook.rb
86
88
  - lib/spreadsheet/worksheet.rb
87
89
  - lib/spreadsheet/writer.rb
88
90
  - test/data/test_copy.xls
91
+ - test/data/test_datetime.xls
89
92
  - test/data/test_empty.xls
90
93
  - test/data/test_version_excel5.xls
91
94
  - test/data/test_version_excel95.xls
92
95
  - test/data/test_version_excel97.xls
93
96
  - test/excel/row.rb
97
+ - test/excel/writer/worksheet.rb
94
98
  - test/font.rb
95
99
  - test/integration.rb
100
+ - test/row.rb
101
+ - test/suite.rb
96
102
  - test/workbook.rb
97
103
  - test/worksheet.rb
98
104
  has_rdoc: true