spreadsheet 0.6.8 → 0.6.9

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.
@@ -0,0 +1,2 @@
1
+ tags
2
+ *.swp
@@ -1,3 +1,9 @@
1
+ === 0.6.9 / 28.04.2012
2
+
3
+ * Yield is more simple here too.
4
+ * No need to capture the block in Spreadsheet.open
5
+ * Rather than extending a core class, let's just use #rcompact from a helper module
6
+
1
7
  === 0.6.8 / 20.01.2012
2
8
 
3
9
  * adds the fix to allow the writing of empty rows, by ClemensP.
@@ -1,3 +1,4 @@
1
+ .gitignore
1
2
  GUIDE.txt
2
3
  History.txt
3
4
  LICENSE.txt
@@ -29,6 +30,7 @@ lib/spreadsheet/excel/worksheet.rb
29
30
  lib/spreadsheet/excel/writer.rb
30
31
  lib/spreadsheet/excel/writer/biff8.rb
31
32
  lib/spreadsheet/excel/writer/format.rb
33
+ lib/spreadsheet/excel/writer/n_worksheet.rb
32
34
  lib/spreadsheet/excel/writer/workbook.rb
33
35
  lib/spreadsheet/excel/writer/worksheet.rb
34
36
  lib/spreadsheet/font.rb
@@ -42,7 +42,7 @@ module Spreadsheet
42
42
 
43
43
  ##
44
44
  # The version of Spreadsheet you are using.
45
- VERSION = '0.6.8'
45
+ VERSION = '0.6.9'
46
46
 
47
47
  ##
48
48
  # Default client Encoding. Change this value if your application uses a
@@ -57,12 +57,12 @@ module Spreadsheet
57
57
  ##
58
58
  # Parses a Spreadsheet Document and returns a Workbook object. At present,
59
59
  # only Excel-Documents can be read.
60
- def open io_or_path, mode="rb+", &block
60
+ def open io_or_path, mode="rb+"
61
61
  if io_or_path.respond_to? :seek
62
62
  Excel::Workbook.open(io_or_path)
63
- elsif block
63
+ elsif block_given?
64
64
  File.open(io_or_path, mode) do |fh|
65
- block.call open(fh)
65
+ yield open(fh)
66
66
  end
67
67
  else
68
68
  open File.open(io_or_path, mode)
@@ -20,9 +20,9 @@ class Row < Spreadsheet::Row
20
20
  def datetime idx
21
21
  _datetime at(idx)
22
22
  end
23
- def each &block
23
+ def each
24
24
  size.times do |idx|
25
- block.call self[idx]
25
+ yield self[idx]
26
26
  end
27
27
  end
28
28
  ##
@@ -0,0 +1,886 @@
1
+ require 'stringio'
2
+ require 'spreadsheet/excel/writer/biff8'
3
+ require 'spreadsheet/excel/internals'
4
+ require 'spreadsheet/excel/internals/biff8'
5
+
6
+ module Spreadsheet
7
+ module Excel
8
+ module Writer
9
+ ##
10
+ # Writer class for Excel Worksheets. Most write_* method correspond to an
11
+ # Excel-Record/Opcode. You should not need to call any of its methods directly.
12
+ # If you think you do, look at #write_worksheet
13
+ class Worksheet
14
+ include Spreadsheet::Excel::Writer::Biff8
15
+ include Spreadsheet::Excel::Internals
16
+ include Spreadsheet::Excel::Internals::Biff8
17
+ attr_reader :worksheet
18
+ def initialize workbook, worksheet
19
+ @workbook = workbook
20
+ @worksheet = worksheet
21
+ @io = StringIO.new ''
22
+ @biff_version = 0x0600
23
+ @bof = 0x0809
24
+ @build_id = 3515
25
+ @build_year = 1996
26
+ @bof_types = {
27
+ :globals => 0x0005,
28
+ :visual_basic => 0x0006,
29
+ :worksheet => 0x0010,
30
+ :chart => 0x0020,
31
+ :macro_sheet => 0x0040,
32
+ :workspace => 0x0100,
33
+ }
34
+ end
35
+ ##
36
+ # The number of bytes needed to write a Boundsheet record for this Worksheet
37
+ # Used by Writer::Worksheet to calculate various offsets.
38
+ def boundsheet_size
39
+ name.size + 10
40
+ end
41
+ def data
42
+ @io.rewind
43
+ @io.read
44
+ end
45
+ def encode_date date
46
+ return date if date.is_a? Numeric
47
+ if date.is_a? Time
48
+ date = DateTime.new date.year, date.month, date.day,
49
+ date.hour, date.min, date.sec
50
+ end
51
+ base = @workbook.date_base
52
+ value = date - base
53
+ if LEAP_ERROR > base
54
+ value += 1
55
+ end
56
+ value
57
+ end
58
+ def encode_rk value
59
+ # Bit Mask Contents
60
+ # 0 0x00000001 0 = Value not changed 1 = Value is multiplied by 100
61
+ # 1 0x00000002 0 = Floating-point value 1 = Signed integer value
62
+ # 31-2 0xFFFFFFFC Encoded value
63
+ cent = 0
64
+ int = 2
65
+ higher = value * 100
66
+ if higher.is_a?(Float) && higher < 0xfffffffc
67
+ cent = 1
68
+ if higher == higher.to_i
69
+ value = higher.to_i
70
+ else
71
+ value = higher
72
+ end
73
+ end
74
+ if value.is_a?(Integer)
75
+ ## although not documented as signed, 'V' appears to correctly pack
76
+ # negative numbers.
77
+ value <<= 2
78
+ else
79
+ # FIXME: precision of small numbers
80
+ int = 0
81
+ value, = [value].pack(EIGHT_BYTE_DOUBLE).unpack('x4V')
82
+ value &= 0xfffffffc
83
+ end
84
+ value | cent | int
85
+ end
86
+ def name
87
+ unicode_string @worksheet.name
88
+ end
89
+ def need_number? cell
90
+ if cell.is_a?(Numeric) && cell.abs > 0x1fffffff
91
+ true
92
+ elsif cell.is_a?(Float) and not cell.nan?
93
+ higher = cell * 100
94
+ if higher == higher.to_i
95
+ need_number? higher.to_i
96
+ else
97
+ test1, test2 = [cell * 100].pack(EIGHT_BYTE_DOUBLE).unpack('V2')
98
+ test1 > 0 || need_number?(test2)
99
+ end
100
+ else
101
+ false
102
+ end
103
+ end
104
+ def row_blocks
105
+ # All cells in an Excel document are divided into blocks of 32 consecutive
106
+ # rows, called Row Blocks. The first Row Block starts with the first used
107
+ # row in that sheet. Inside each Row Block there will occur ROW records
108
+ # describing the properties of the rows, and cell records with all the cell
109
+ # contents in this Row Block.
110
+ blocks = []
111
+ @worksheet.reject do |row| row.empty? end.each_with_index do |row, idx|
112
+ blocks << [] if idx % 32 == 0
113
+ blocks.last << row
114
+ end
115
+ blocks
116
+ end
117
+ def size
118
+ @io.size
119
+ end
120
+ def strings
121
+ @worksheet.inject [] do |memo, row|
122
+ strings = row.select do |cell| cell.is_a?(String) && !cell.empty? end
123
+ memo.concat strings
124
+ end
125
+ end
126
+ ##
127
+ # Write a blank cell
128
+ def write_blank row, idx
129
+ write_cell :blank, row, idx
130
+ end
131
+ def write_bof
132
+ data = [
133
+ @biff_version, # BIFF version (always 0x0600 for BIFF8)
134
+ 0x0010, # Type of the following data:
135
+ # 0x0005 = Workbook globals
136
+ # 0x0006 = Visual Basic module
137
+ # 0x0010 = Worksheet
138
+ # 0x0020 = Chart
139
+ # 0x0040 = Macro sheet
140
+ # 0x0100 = Workspace file
141
+ @build_id, # Build identifier
142
+ @build_year, # Build year
143
+ 0x000, # File history flags
144
+ 0x006, # Lowest Excel version that can read
145
+ # all records in this file
146
+ ]
147
+ write_op @bof, data.pack("v4V2")
148
+ end
149
+ ##
150
+ # Write a cell with a Boolean or Error value
151
+ def write_boolerr row, idx
152
+ value = row[idx]
153
+ type = 0
154
+ numval = 0
155
+ if value.is_a? Error
156
+ type = 1
157
+ numval = value.code
158
+ elsif value
159
+ numval = 1
160
+ end
161
+ data = [
162
+ numval, # Boolean or error value (type depends on the following byte)
163
+ type # 0 = Boolean value; 1 = Error code
164
+ ]
165
+ write_cell :boolerr, row, idx, *data
166
+ end
167
+ def write_calccount
168
+ count = 100 # Maximum number of iterations allowed in circular references
169
+ write_op 0x000c, [count].pack('v')
170
+ end
171
+ def write_cell type, row, idx, *args
172
+ xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx)
173
+ data = [
174
+ row.idx, # Index to row
175
+ idx, # Index to column
176
+ xf_idx, # Index to XF record (➜ 6.115)
177
+ ].concat args
178
+ write_op opcode(type), data.pack(binfmt(type))
179
+ end
180
+ def write_cellblocks row
181
+ # BLANK ➜ 6.7
182
+ # BOOLERR ➜ 6.10
183
+ # INTEGER ➜ 6.56 (BIFF2 only)
184
+ # LABEL ➜ 6.59 (BIFF2-BIFF7)
185
+ # LABELSST ➜ 6.61 (BIFF8 only)
186
+ # MULBLANK ➜ 6.64 (BIFF5-BIFF8)
187
+ # MULRK ➜ 6.65 (BIFF5-BIFF8)
188
+ # NUMBER ➜ 6.68
189
+ # RK ➜ 6.82 (BIFF3-BIFF8)
190
+ # RSTRING ➜ 6.84 (BIFF5/BIFF7)
191
+ multiples, first_idx = nil
192
+ row = row.formatted
193
+ row.each_with_index do |cell, idx|
194
+ cell = nil if cell == ''
195
+ ## it appears that there are limitations to RK precision, both for
196
+ # Integers and Floats, that lie well below 2^30 significant bits, or
197
+ # Ruby's Bignum threshold. In that case we'll just write a Number
198
+ # record
199
+ need_number = need_number? cell
200
+ if multiples && (!multiples.last.is_a?(cell.class) || need_number)
201
+ write_multiples row, first_idx, multiples
202
+ multiples, first_idx = nil
203
+ end
204
+ nxt = idx + 1
205
+ case cell
206
+ when NilClass
207
+ if multiples
208
+ multiples.push cell
209
+ elsif nxt < row.size && row[nxt].nil?
210
+ multiples = [cell]
211
+ first_idx = idx
212
+ else
213
+ write_blank row, idx
214
+ end
215
+ when TrueClass, FalseClass, Error
216
+ write_boolerr row, idx
217
+ when String
218
+ write_labelsst row, idx
219
+ when Numeric
220
+ ## RK encodes Floats with 30 significant bits, which is a bit more than
221
+ # 10^9. Not sure what is a good rule of thumb here, but it seems that
222
+ # Decimal Numbers with more than 4 significant digits are not represented
223
+ # with sufficient precision by RK
224
+ if need_number
225
+ write_number row, idx
226
+ elsif multiples
227
+ multiples.push cell
228
+ elsif nxt < row.size && row[nxt].is_a?(Numeric)
229
+ multiples = [cell]
230
+ first_idx = idx
231
+ else
232
+ write_rk row, idx
233
+ end
234
+ when Formula
235
+ write_formula row, idx
236
+ when Date, Time
237
+ write_number row, idx
238
+ end
239
+ end
240
+ write_multiples row, first_idx, multiples if multiples
241
+ end
242
+ def write_changes reader, endpos, sst_status
243
+
244
+ ## FIXME this is not smart solution to update outline_level.
245
+ # without this process, outlines in row disappear in MS Excel.
246
+ @worksheet.row_count.times do |i|
247
+ if @worksheet.row(i).outline_level > 0
248
+ @worksheet.row(i).outline_level = @worksheet.row(i).outline_level
249
+ end
250
+ end
251
+
252
+ reader.seek @worksheet.offset
253
+ blocks = row_blocks
254
+ lastpos = reader.pos
255
+ offsets = {}
256
+ row_offsets = []
257
+ changes = @worksheet.changes
258
+ @worksheet.offsets.each do |key, pair|
259
+ if changes.include?(key) \
260
+ || (sst_status == :complete_update && key.is_a?(Integer))
261
+ offsets.store pair, key
262
+ end
263
+ end
264
+ ## FIXME it may be smarter to simply write all rowblocks, instead of doing a
265
+ # song-and-dance routine for every row...
266
+ work = offsets.invert
267
+ work.each do |key, (pos, len)|
268
+ case key
269
+ when Integer
270
+ row_offsets.push [key, [pos, len]]
271
+ when :dimensions
272
+ row_offsets.push [-1, [pos, len]]
273
+ end
274
+ end
275
+ row_offsets.sort!
276
+ row_offsets.reverse!
277
+ control = changes.size
278
+ @worksheet.each do |row|
279
+ key = row.idx
280
+ if changes.include?(key) && !work.include?(key)
281
+ row, pair = row_offsets.find do |idx, _| idx <= key end
282
+ work.store key, pair
283
+ end
284
+ end
285
+ if changes.size > control
286
+ warn <<-EOS
287
+ Your Worksheet was modified while it was being written. This should not happen.
288
+ Please contact the author (hannes dot wyss at gmail dot com) with a sample file
289
+ and minimal code that generates this warning. Thanks!
290
+ EOS
291
+ end
292
+ work = work.sort_by do |key, (pos, len)|
293
+ [pos, key.is_a?(Integer) ? key : -1]
294
+ end
295
+ work.each do |key, (pos, len)|
296
+ @io.write reader.read(pos - lastpos) if pos > lastpos
297
+ if key.is_a?(Integer)
298
+ if block = blocks.find do |rows| rows.any? do |row| row.idx == key end end
299
+ write_rowblock block
300
+ blocks.delete block
301
+ end
302
+ else
303
+ send "write_#{key}"
304
+ end
305
+ lastpos = pos + len
306
+ reader.seek lastpos
307
+ end
308
+
309
+ # Necessary for outline (grouping) and hiding functions
310
+ # but these below are not necessary to run
311
+ # if [Row|Column]#hidden? = false and [Row|Column]#outline_level == 0
312
+ write_colinfos
313
+ write_guts
314
+
315
+ @io.write reader.read(endpos - lastpos)
316
+ end
317
+ def write_colinfo bunch
318
+ col = bunch.first
319
+ width = col.width.to_f * 256
320
+ xf_idx = @workbook.xf_index @worksheet.workbook, col.default_format
321
+ opts = 0
322
+ opts |= 0x0001 if col.hidden?
323
+ opts |= col.outline_level.to_i << 8
324
+ opts |= 0x1000 if col.collapsed?
325
+ data = [
326
+ col.idx, # Index to first column in the range
327
+ bunch.last.idx, # Index to last column in the range
328
+ width.to_i, # Width of the columns in 1/256 of the width of the zero
329
+ # character, using default font (first FONT record in the
330
+ # file)
331
+ xf_idx.to_i, # Index to XF record (➜ 6.115) for default column formatting
332
+ opts, # Option flags:
333
+ # Bits Mask Contents
334
+ # 0 0x0001 1 = Columns are hidden
335
+ # 10-8 0x0700 Outline level of the columns
336
+ # (0 = no outline)
337
+ # 12 0x1000 1 = Columns are collapsed
338
+ ]
339
+ write_op opcode(:colinfo), data.pack(binfmt(:colinfo))
340
+ end
341
+ def write_colinfos
342
+ cols = @worksheet.columns
343
+ bunch = []
344
+ cols.each_with_index do |column, idx|
345
+ if column
346
+ bunch << column
347
+ if cols[idx.next] != column
348
+ write_colinfo bunch
349
+ bunch.clear
350
+ end
351
+ end
352
+ end
353
+ end
354
+ def write_defaultrowheight
355
+ data = [
356
+ 0x00, # Option flags:
357
+ # Bit Mask Contents
358
+ # 0 0x01 1 = Row height and default font height do not match
359
+ # 1 0x02 1 = Row is hidden
360
+ # 2 0x04 1 = Additional space above the row
361
+ # 3 0x08 1 = Additional space below the row
362
+ 0xf2, # Default height for unused rows, in twips = 1/20 of a point
363
+ ]
364
+ write_op 0x0225, data.pack('v2')
365
+ end
366
+ def write_defcolwidth
367
+ # Offset Size Contents
368
+ # 0 2 Column width in characters, using the width of the zero
369
+ # character from default font (first FONT record in the
370
+ # file). Excel adds some extra space to the default width,
371
+ # depending on the default font and default font size. The
372
+ # algorithm how to exactly calculate the resulting column
373
+ # width is not known.
374
+ #
375
+ # Example: The default width of 8 set in this record results
376
+ # in a column width of 8.43 using Arial font with a size of
377
+ # 10 points.
378
+ write_op 0x0055, [8].pack('v')
379
+ end
380
+ def write_dimensions
381
+ # Offset Size Contents
382
+ # 0 4 Index to first used row
383
+ # 4 4 Index to last used row, increased by 1
384
+ # 8 2 Index to first used column
385
+ # 10 2 Index to last used column, increased by 1
386
+ # 12 2 Not used
387
+ write_op 0x0200, @worksheet.dimensions.pack(binfmt(:dimensions))
388
+ end
389
+ def write_eof
390
+ write_op 0x000a
391
+ end
392
+ ##
393
+ # Write a cell with a Formula. May write an additional String record depending
394
+ # on the stored result of the Formula.
395
+ def write_formula row, idx
396
+ xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx)
397
+ cell = row[idx]
398
+ data1 = [
399
+ row.idx, # Index to row
400
+ idx, # Index to column
401
+ xf_idx, # Index to XF record (➜ 6.115)
402
+ ].pack 'v3'
403
+ data2 = nil
404
+ case value = cell.value
405
+ when Numeric # IEEE 754 floating-point value (64-bit double precision)
406
+ data2 = [value].pack EIGHT_BYTE_DOUBLE
407
+ when String
408
+ data2 = [
409
+ 0x00, # (identifier for a string value)
410
+ 0xffff, #
411
+ ].pack 'Cx5v'
412
+ when true, false
413
+ value = value ? 1 : 0
414
+ data2 = [
415
+ 0x01, # (identifier for a Boolean value)
416
+ value, # 0 = FALSE, 1 = TRUE
417
+ 0xffff, #
418
+ ].pack 'CxCx3v'
419
+ when Error
420
+ data2 = [
421
+ 0x02, # (identifier for an error value)
422
+ value.code, # Error code
423
+ 0xffff, #
424
+ ].pack 'CxCx3v'
425
+ when nil
426
+ data2 = [
427
+ 0x03, # (identifier for an empty cell)
428
+ 0xffff, #
429
+ ].pack 'Cx5v'
430
+ else
431
+ data2 = [
432
+ 0x02, # (identifier for an error value)
433
+ 0x2a, # Error code: #N/A! Argument or function not available
434
+ 0xffff, #
435
+ ].pack 'CxCx3v'
436
+ end
437
+ opts = 0x03
438
+ opts |= 0x08 if cell.shared
439
+ data3 = [
440
+ opts # Option flags:
441
+ # Bit Mask Contents
442
+ # 0 0x0001 1 = Recalculate always
443
+ # 1 0x0002 1 = Calculate on open
444
+ # 3 0x0008 1 = Part of a shared formula
445
+ ].pack 'vx4'
446
+ write_op opcode(:formula), data1, data2, data3, cell.data
447
+ if cell.value.is_a?(String)
448
+ write_op opcode(:string), unicode_string(cell.value, 2)
449
+ end
450
+ end
451
+ ##
452
+ # Write a new Worksheet.
453
+ def write_from_scratch
454
+ # ● BOF Type = worksheet (➜ 5.8)
455
+ write_bof
456
+ # ○ UNCALCED ➜ 5.105
457
+ # ○ INDEX ➜ 4.7 (Row Blocks), ➜ 5.59
458
+ # ○ Calculation Settings Block ➜ 4.3
459
+ write_calccount
460
+ write_refmode
461
+ write_iteration
462
+ write_saverecalc
463
+ # ○ PRINTHEADERS ➜ 5.81
464
+ # ○ PRINTGRIDLINES ➜ 5.80
465
+ # ○ GRIDSET ➜ 5.52
466
+ # ○ GUTS ➜ 5.53
467
+ write_guts
468
+ # ○ DEFAULTROWHEIGHT ➜ 5.31
469
+ write_defaultrowheight
470
+ # ○ WSBOOL ➜ 5.113
471
+ write_wsbool
472
+ # ○ Page Settings Block ➜ 4.4
473
+ # ○ Worksheet Protection Block ➜ 4.18
474
+ # ○ DEFCOLWIDTH ➜ 5.32
475
+ write_defcolwidth
476
+ # ○○ COLINFO ➜ 5.18
477
+ write_colinfos
478
+ # ○ SORT ➜ 5.99
479
+ # ● DIMENSIONS ➜ 5.35
480
+ write_dimensions
481
+ # ○○ Row Blocks ➜ 4.7
482
+ write_rows
483
+ # ● Worksheet View Settings Block ➜ 4.5
484
+ # ● WINDOW2 ➜ 5.110
485
+ write_window2
486
+ # ○ SCL ➜ 5.92 (BIFF4-BIFF8 only)
487
+ # ○ PANE ➜ 5.75
488
+ # ○○ SELECTION ➜ 5.93
489
+ # ○ STANDARDWIDTH ➜ 5.101
490
+ # ○○ MERGEDCELLS ➜ 5.67
491
+ # ○ LABELRANGES ➜ 5.64
492
+ # ○ PHONETIC ➜ 5.77
493
+ # ○ Conditional Formatting Table ➜ 4.12
494
+ # ○ Hyperlink Table ➜ 4.13
495
+ write_hyperlink_table
496
+ # ○ Data Validity Table ➜ 4.14
497
+ # ○ SHEETLAYOUT ➜ 5.96 (BIFF8X only)
498
+ # ○ SHEETPROTECTION Additional protection, ➜ 5.98 (BIFF8X only)
499
+ # ○ RANGEPROTECTION Additional protection, ➜ 5.84 (BIFF8X only)
500
+ # ● EOF ➜ 5.36
501
+ write_eof
502
+ end
503
+ ##
504
+ # Write record that contains information about the layout of outline symbols.
505
+ def write_guts
506
+ # find the maximum outline_level in rows and columns
507
+ row_outline_level = 0
508
+ col_outline_level = 0
509
+ if(row = @worksheet.rows.select{|x| x!=nil}.max{|a,b| a.outline_level <=> b.outline_level})
510
+ row_outline_level = row.outline_level
511
+ end
512
+ if(col = @worksheet.columns.select{|x| x!=nil}.max{|a,b| a.outline_level <=> b.outline_level})
513
+ col_outline_level = col.outline_level
514
+ end
515
+ # set data
516
+ data = [
517
+ 0, # Width of the area to display row outlines (left of the sheet), in pixel
518
+ 0, # Height of the area to display column outlines (above the sheet), in pixel
519
+ row_outline_level+1, # Number of visible row outline levels (used row levels+1; or 0,if not used)
520
+ col_outline_level+1 # Number of visible column outline levels (used column levels+1; or 0,if not used)
521
+ ]
522
+ # write record
523
+ write_op opcode(:guts), data.pack('v4')
524
+ end
525
+ def write_hlink row, col, link
526
+ # FIXME: only Hyperlinks are supported at present.
527
+ cell_range = [
528
+ row, row, # Cell range address of all cells containing this hyperlink
529
+ col, col, # (➜ 3.13.1)
530
+ ].pack 'v4'
531
+ guid = [
532
+ # GUID of StdLink:
533
+ # D0 C9 EA 79 F9 BA CE 11 8C 82 00 AA 00 4B A9 0B
534
+ # (79EAC9D0-BAF9-11CE-8C82-00AA004BA90B)
535
+ "d0c9ea79f9bace118c8200aa004ba90b",
536
+ ].pack 'H32'
537
+ opts = 0x01
538
+ opts |= 0x02
539
+ opts |= 0x14 unless link == link.url
540
+ opts |= 0x08 if link.fragment
541
+ opts |= 0x80 if link.target_frame
542
+ # TODO: UNC support
543
+ options = [
544
+ 2, # Unknown value: 0x00000002
545
+ opts, # Option flags
546
+ # Bit Mask Contents
547
+ # 0 0x00000001 0 = No link extant
548
+ # 1 = File link or URL
549
+ # 1 0x00000002 0 = Relative file path
550
+ # 1 = Absolute path or URL
551
+ # 2 and 4 0x00000014 0 = No description
552
+ # 1 (both bits) = Description
553
+ # 3 0x00000008 0 = No text mark
554
+ # 1 = Text mark
555
+ # 7 0x00000080 0 = No target frame
556
+ # 1 = Target frame
557
+ # 8 0x00000100 0 = File link or URL
558
+ # 1 = UNC path (incl. server name)
559
+
560
+ ].pack('V2')
561
+ tail = []
562
+ ## call internal to get the correct internal encoding in Ruby 1.9
563
+ nullstr = internal "\000"
564
+ unless link == link.url
565
+ desc = internal(link).dup << nullstr
566
+ tail.push [desc.size / 2].pack('V'), desc
567
+ end
568
+ if link.target_frame
569
+ frme = internal(link.target_frame).dup << nullstr
570
+ tail.push [frme.size / 2].pack('V'), frme
571
+ end
572
+ url = internal(link.url).dup << nullstr
573
+ tail.push [
574
+ # 6.53.2 Hyperlink containing a URL (Uniform Resource Locator)
575
+ # These data fields occur for links which are not local files or files
576
+ # in the local network (for instance HTTP and FTP links and e-mail
577
+ # addresses). The lower 9 bits of the option flags field must be
578
+ # 0.x00x.xx112 (x means optional, depending on hyperlink content). The
579
+ # GUID could be used to distinguish a URL from a file link.
580
+ # GUID of URL Moniker:
581
+ # E0 C9 EA 79 F9 BA CE 11 8C 82 00 AA 00 4B A9 0B
582
+ # (79EAC9E0-BAF9-11CE-8C82-00AA004BA90B)
583
+ 'e0c9ea79f9bace118c8200aa004ba90b',
584
+ url.size # Size of character array of the URL, including trailing zero
585
+ # word (us). There are us/2-1 characters in the following
586
+ # string.
587
+ ].pack('H32V'), url
588
+ if link.fragment
589
+ frag = internal(link.fragment).dup << nullstr
590
+ tail.push [frag.size / 2].pack('V'), frag
591
+ end
592
+ write_op opcode(:hlink), cell_range, guid, options, *tail
593
+ end
594
+ def write_hyperlink_table
595
+ # TODO: theoretically it's possible to write fewer records by combining
596
+ # identical neighboring links in cell-ranges
597
+ links = []
598
+ @worksheet.each do |row|
599
+ row.each_with_index do |cell, idx|
600
+ if cell.is_a? Link
601
+ write_hlink row.idx, idx, cell
602
+ end
603
+ end
604
+ end
605
+ end
606
+ def write_iteration
607
+ its = 0 # 0 = Iterations off; 1 = Iterations on
608
+ write_op 0x0011, [its].pack('v')
609
+ end
610
+ ##
611
+ # Write a cell with a String value. The String must have been stored in the
612
+ # Shared String Table.
613
+ def write_labelsst row, idx
614
+ write_cell :labelsst, row, idx, @workbook.sst_index(self, row[idx])
615
+ end
616
+ ##
617
+ # Write multiple consecutive blank cells.
618
+ def write_mulblank row, idx, multiples
619
+ data = [
620
+ row.idx, # Index to row
621
+ idx, # Index to first column (fc)
622
+ ]
623
+ # List of nc=lc-fc+1 16-bit indexes to XF records (➜ 6.115)
624
+ multiples.each_with_index do |blank, cell_idx|
625
+ xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx + cell_idx)
626
+ data.push xf_idx
627
+ end
628
+ # Index to last column (lc)
629
+ data.push idx + multiples.size - 1
630
+ write_op opcode(:mulblank), data.pack('v*')
631
+ end
632
+ ##
633
+ # Write multiple consecutive cells with RK values (see #write_rk)
634
+ def write_mulrk row, idx, multiples
635
+ fmt = 'v2'
636
+ data = [
637
+ row.idx, # Index to row
638
+ idx, # Index to first column (fc)
639
+ ]
640
+ # List of nc=lc-fc+1 16-bit indexes to XF records (➜ 6.115)
641
+ multiples.each_with_index do |cell, cell_idx|
642
+ xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx + cell_idx)
643
+ data.push xf_idx, encode_rk(cell)
644
+ fmt << 'vV'
645
+ end
646
+ # Index to last column (lc)
647
+ data.push idx + multiples.size - 1
648
+ write_op opcode(:mulrk), data.pack(fmt << 'v')
649
+ end
650
+ def write_multiples row, idx, multiples
651
+ case multiples.last
652
+ when NilClass
653
+ write_mulblank row, idx, multiples
654
+ when Numeric
655
+ if multiples.size > 1
656
+ write_mulrk row, idx, multiples
657
+ else
658
+ write_rk row, idx
659
+ end
660
+ end
661
+ end
662
+ ##
663
+ # Write a cell with a 64-bit double precision Float value
664
+ def write_number row, idx
665
+ # Offset Size Contents
666
+ # 0 2 Index to row
667
+ # 2 2 Index to column
668
+ # 4 2 Index to XF record (➜ 6.115)
669
+ # 6 8 IEEE 754 floating-point value (64-bit double precision)
670
+ value = row[idx]
671
+ case value
672
+ when Date, Time
673
+ value = encode_date(value)
674
+ end
675
+ write_cell :number, row, idx, value
676
+ end
677
+ def write_op op, *args
678
+ data = args.join
679
+ @io.write [op,data.size].pack("v2")
680
+ @io.write data
681
+ end
682
+ def write_refmode
683
+ # • The “RC” mode uses numeric indexes for rows and columns, for example
684
+ # “R(1)C(-1)”, or “R1C1:R2C2”.
685
+ # • The “A1” mode uses characters for columns and numbers for rows, for
686
+ # example “B1”, or “$A$1:$B$2”.
687
+ mode = 1 # 0 = RC mode; 1 = A1 mode
688
+ write_op 0x000f, [mode].pack('v')
689
+ end
690
+ ##
691
+ # Write a cell with a Numeric or Date value.
692
+ def write_rk row, idx
693
+ write_cell :rk, row, idx, encode_rk(row[idx])
694
+ end
695
+ def write_row row
696
+ # Offset Size Contents
697
+ # 0 2 Index of this row
698
+ # 2 2 Index to column of the first cell which
699
+ # is described by a cell record
700
+ # 4 2 Index to column of the last cell which is
701
+ # described by a cell record, increased by 1
702
+ # 6 2 Bit Mask Contents
703
+ # 14-0 0x7fff Height of the row, in twips = 1/20 of a point
704
+ # 15 0x8000 0 = Row has custom height;
705
+ # 1 = Row has default height
706
+ # 8 2 Not used
707
+ # 10 2 In BIFF3-BIFF4 this field contains a relative offset to
708
+ # calculate stream position of the first cell record for this
709
+ # row (➜ 5.7.1). In BIFF5-BIFF8 this field is not used
710
+ # anymore, but the DBCELL record (➜ 6.26) instead.
711
+ # 12 4 Option flags and default row formatting:
712
+ # Bit Mask Contents
713
+ # 2-0 0x00000007 Outline level of the row
714
+ # 4 0x00000010 1 = Outline group starts or ends here
715
+ # (depending on where the outline
716
+ # buttons are located, see WSBOOL
717
+ # record, ➜ 6.113), and is collapsed
718
+ # 5 0x00000020 1 = Row is hidden (manually, or by a
719
+ # filter or outline group)
720
+ # 6 0x00000040 1 = Row height and default font height
721
+ # do not match
722
+ # 7 0x00000080 1 = Row has explicit default format (fl)
723
+ # 8 0x00000100 Always 1
724
+ # 27-16 0x0fff0000 If fl = 1: Index to default XF record
725
+ # (➜ 6.115)
726
+ # 28 0x10000000 1 = Additional space above the row.
727
+ # This flag is set, if the upper
728
+ # border of at least one cell in this
729
+ # row or if the lower border of at
730
+ # least one cell in the row above is
731
+ # formatted with a thick line style.
732
+ # Thin and medium line styles are not
733
+ # taken into account.
734
+ # 29 0x20000000 1 = Additional space below the row.
735
+ # This flag is set, if the lower
736
+ # border of at least one cell in this
737
+ # row or if the upper border of at
738
+ # least one cell in the row below is
739
+ # formatted with a medium or thick
740
+ # line style. Thin line styles are
741
+ # not taken into account.
742
+ height = row.height || ROW_HEIGHT
743
+ opts = row.outline_level & 0x00000007
744
+ opts |= 0x00000010 if row.collapsed?
745
+ opts |= 0x00000020 if row.hidden?
746
+ opts |= 0x00000040 if height != ROW_HEIGHT
747
+ if fmt = row.default_format
748
+ xf_idx = @workbook.xf_index @worksheet.workbook, fmt
749
+ opts |= 0x00000080
750
+ opts |= xf_idx << 16
751
+ end
752
+ opts |= 0x00000100
753
+ height = if height == ROW_HEIGHT
754
+ (height * TWIPS).to_i | 0x8000
755
+ else
756
+ height * TWIPS
757
+ end
758
+
759
+ attrs = [
760
+ row.idx,
761
+ row.first_used,
762
+ row.first_unused,
763
+ height,
764
+ opts]
765
+
766
+ return if attrs.any?(&:nil?)
767
+
768
+ # TODO: Row spacing
769
+ data = attrs.pack binfmt(:row)
770
+ write_op opcode(:row), data
771
+ end
772
+ def write_rowblock block
773
+ # ●● ROW Properties of the used rows
774
+ # ○○ Cell Block(s) Cell records for all used cells
775
+ # ○ DBCELL Stream offsets to the cell records of each row
776
+ block.each do |row|
777
+ write_row row
778
+ end
779
+ block.each do |row|
780
+ write_cellblocks row
781
+ end
782
+ end
783
+ def write_rows
784
+ row_blocks.each do |block|
785
+ write_rowblock block
786
+ end
787
+ end
788
+ def write_saverecalc
789
+ # 0 = Do not recalculate; 1 = Recalculate before saving the document
790
+ write_op 0x005f, [1].pack('v')
791
+ end
792
+ def write_window2
793
+ # This record contains additional settings for the document window
794
+ # (BIFF2-BIFF4) or for the window of a specific worksheet (BIFF5-BIFF8).
795
+ # It is part of the Sheet View Settings Block (➜ 4.5).
796
+ # Offset Size Contents
797
+ # 0 2 Option flags:
798
+ # Bits Mask Contents
799
+ # 0 0x0001 0 = Show formula results
800
+ # 1 = Show formulas
801
+ # 1 0x0002 0 = Do not show grid lines
802
+ # 1 = Show grid lines
803
+ # 2 0x0004 0 = Do not show sheet headers
804
+ # 1 = Show sheet headers
805
+ # 3 0x0008 0 = Panes are not frozen
806
+ # 1 = Panes are frozen (freeze)
807
+ # 4 0x0010 0 = Show zero values as empty cells
808
+ # 1 = Show zero values
809
+ # 5 0x0020 0 = Manual grid line colour
810
+ # 1 = Automatic grid line colour
811
+ # 6 0x0040 0 = Columns from left to right
812
+ # 1 = Columns from right to left
813
+ # 7 0x0080 0 = Do not show outline symbols
814
+ # 1 = Show outline symbols
815
+ # 8 0x0100 0 = Keep splits if pane freeze is removed
816
+ # 1 = Remove splits if pane freeze is removed
817
+ # 9 0x0200 0 = Sheet not selected
818
+ # 1 = Sheet selected (BIFF5-BIFF8)
819
+ # 10 0x0400 0 = Sheet not active
820
+ # 1 = Sheet active (BIFF5-BIFF8)
821
+ # 11 0x0800 0 = Show in normal view
822
+ # 1 = Show in page break preview (BIFF8)
823
+ # 2 2 Index to first visible row
824
+ # 4 2 Index to first visible column
825
+ # 6 2 Colour index of grid line colour (➜ 5.74).
826
+ # Note that in BIFF2-BIFF5 an RGB colour is written instead.
827
+ # 8 2 Not used
828
+ # 10 2 Cached magnification factor in page break preview (in percent)
829
+ # 0 = Default (60%)
830
+ # 12 2 Cached magnification factor in normal view (in percent)
831
+ # 0 = Default (100%)
832
+ # 14 4 Not used
833
+ flags = 0x0536 # Show grid lines, sheet headers, zero values. Automatic
834
+ # grid line colour, Remove slits if pane freeze is removed,
835
+ # Sheet is active.
836
+ if @worksheet.selected
837
+ flags |= 0x0200
838
+ end
839
+ flags |= 0x0080 # Show outline symbols,
840
+ # but if [Row|Column]#outline_level = 0 the symbols are not shown.
841
+ data = [ flags, 0, 0, 0, 0, 0 ].pack binfmt(:window2)
842
+ write_op opcode(:window2), data
843
+ end
844
+ def write_wsbool
845
+ bits = [
846
+ # Bit Mask Contents
847
+ 1, # 0 0x0001 0 = Do not show automatic page breaks
848
+ # 1 = Show automatic page breaks
849
+ 0, # 4 0x0010 0 = Standard sheet
850
+ # 1 = Dialogue sheet (BIFF5-BIFF8)
851
+ 0, # 5 0x0020 0 = No automatic styles in outlines
852
+ # 1 = Apply automatic styles to outlines
853
+ 1, # 6 0x0040 0 = Outline buttons above outline group
854
+ # 1 = Outline buttons below outline group
855
+ 1, # 7 0x0080 0 = Outline buttons left of outline group
856
+ # 1 = Outline buttons right of outline group
857
+ 0, # 8 0x0100 0 = Scale printout in percent (➜ 6.89)
858
+ # 1 = Fit printout to number of pages (➜ 6.89)
859
+ 0, # 9 0x0200 0 = Save external linked values
860
+ # (BIFF3-BIFF4 only, ➜ 5.10)
861
+ # 1 = Do not save external linked values
862
+ # (BIFF3-BIFF4 only, ➜ 5.10)
863
+ 1, # 10 0x0400 0 = Do not show row outline symbols
864
+ # 1 = Show row outline symbols
865
+ 0, # 11 0x0800 0 = Do not show column outline symbols
866
+ # 1 = Show column outline symbols
867
+ 0, # 13-12 0x3000 These flags specify the arrangement of windows.
868
+ # They are stored in BIFF4 only.
869
+ # 00 = Arrange windows tiled
870
+ # 01 = Arrange windows horizontal
871
+ 0, # 10 = Arrange windows vertical
872
+ # 11 = Arrange windows cascaded
873
+ # The following flags are valid for BIFF4-BIFF8 only:
874
+ 0, # 14 0x4000 0 = Standard expression evaluation
875
+ # 1 = Alternative expression evaluation
876
+ 0, # 15 0x8000 0 = Standard formula entries
877
+ # 1 = Alternative formula entries
878
+ ]
879
+ weights = [4,5,6,7,8,9,10,11,12,13,14,15]
880
+ value = bits.inject do |a, b| a | (b << weights.shift) end
881
+ write_op 0x0081, [value].pack('v')
882
+ end
883
+ end
884
+ end
885
+ end
886
+ end
@@ -1,11 +1,11 @@
1
- class Array
2
- def rcompact
3
- dup.rcompact!
4
- end
5
- def rcompact!
6
- while !empty? && last.nil?
7
- pop
1
+ module Spreadsheet
2
+ module Helpers
3
+ def rcompact(array)
4
+ while !array.empty? && array.last.nil?
5
+ array.pop
6
+ end
7
+ array
8
8
  end
9
- self
9
+ module_function :rcompact
10
10
  end
11
11
  end
@@ -90,7 +90,7 @@ module Spreadsheet
90
90
  # This is primarily a helper-function for the writer classes.
91
91
  def formatted
92
92
  copy = dup
93
- @formats.rcompact!
93
+ Helpers.rcompact(@formats)
94
94
  if copy.length < @formats.size
95
95
  copy.concat Array.new(@formats.size - copy.length)
96
96
  end
@@ -99,7 +99,7 @@ module Spreadsheet
99
99
  ##
100
100
  # Same as Row#size, but takes into account formatted empty cells
101
101
  def formatted_size
102
- @formats.rcompact!
102
+ Helpers.rcompact(@formats)
103
103
  sz = size
104
104
  fs = @formats.size
105
105
  fs > sz ? fs : sz
@@ -108,9 +108,9 @@ module Spreadsheet
108
108
  # If the argument skip is given, #each iterates from that row until but
109
109
  # omitting the first unused Row, effectively skipping the first _skip_ Rows
110
110
  # from the top of the Worksheet.
111
- def each skip=dimensions[0], &block
111
+ def each skip=dimensions[0]
112
112
  skip.upto(dimensions[1] - 1) do |idx|
113
- block.call row(idx)
113
+ yield row(idx)
114
114
  end
115
115
  end
116
116
  def encoding # :nodoc:
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spreadsheet
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 6
9
- - 8
10
- version: 0.6.8
9
+ - 9
10
+ version: 0.6.9
11
11
  platform: ruby
12
12
  authors:
13
13
  - Masaomi Hatakeyama, Zeno R.R. Davatz
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-01-20 00:00:00 Z
18
+ date: 2012-04-28 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: ruby-ole
@@ -55,11 +55,11 @@ dependencies:
55
55
  requirements:
56
56
  - - ~>
57
57
  - !ruby/object:Gem::Version
58
- hash: 27
58
+ hash: 25
59
59
  segments:
60
60
  - 2
61
- - 12
62
- version: "2.12"
61
+ - 13
62
+ version: "2.13"
63
63
  type: :development
64
64
  version_requirements: *id003
65
65
  description: |-
@@ -81,6 +81,7 @@ extra_rdoc_files:
81
81
  - Manifest.txt
82
82
  - README.txt
83
83
  files:
84
+ - .gitignore
84
85
  - GUIDE.txt
85
86
  - History.txt
86
87
  - LICENSE.txt
@@ -112,6 +113,7 @@ files:
112
113
  - lib/spreadsheet/excel/writer.rb
113
114
  - lib/spreadsheet/excel/writer/biff8.rb
114
115
  - lib/spreadsheet/excel/writer/format.rb
116
+ - lib/spreadsheet/excel/writer/n_worksheet.rb
115
117
  - lib/spreadsheet/excel/writer/workbook.rb
116
118
  - lib/spreadsheet/excel/writer/worksheet.rb
117
119
  - lib/spreadsheet/font.rb