spreadsheet 0.6.8 → 0.6.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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