spreadsheet 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/GUIDE.txt +209 -0
- data/History.txt +8 -0
- data/LICENSE.txt +619 -0
- data/Manifest.txt +46 -0
- data/README.txt +54 -0
- data/Rakefile +15 -0
- data/lib/parseexcel.rb +27 -0
- data/lib/parseexcel/parseexcel.rb +75 -0
- data/lib/parseexcel/parser.rb +11 -0
- data/lib/spreadsheet.rb +79 -0
- data/lib/spreadsheet/datatypes.rb +99 -0
- data/lib/spreadsheet/encodings.rb +49 -0
- data/lib/spreadsheet/excel.rb +75 -0
- data/lib/spreadsheet/excel/error.rb +26 -0
- data/lib/spreadsheet/excel/internals.rb +322 -0
- data/lib/spreadsheet/excel/internals/biff5.rb +17 -0
- data/lib/spreadsheet/excel/internals/biff8.rb +19 -0
- data/lib/spreadsheet/excel/offset.rb +37 -0
- data/lib/spreadsheet/excel/reader.rb +798 -0
- data/lib/spreadsheet/excel/reader/biff5.rb +22 -0
- data/lib/spreadsheet/excel/reader/biff8.rb +168 -0
- data/lib/spreadsheet/excel/row.rb +67 -0
- data/lib/spreadsheet/excel/sst_entry.rb +45 -0
- data/lib/spreadsheet/excel/workbook.rb +76 -0
- data/lib/spreadsheet/excel/worksheet.rb +85 -0
- data/lib/spreadsheet/excel/writer.rb +1 -0
- data/lib/spreadsheet/excel/writer/biff8.rb +66 -0
- data/lib/spreadsheet/excel/writer/format.rb +270 -0
- data/lib/spreadsheet/excel/writer/workbook.rb +586 -0
- data/lib/spreadsheet/excel/writer/worksheet.rb +556 -0
- data/lib/spreadsheet/font.rb +86 -0
- data/lib/spreadsheet/format.rb +172 -0
- data/lib/spreadsheet/formula.rb +9 -0
- data/lib/spreadsheet/row.rb +87 -0
- data/lib/spreadsheet/workbook.rb +120 -0
- data/lib/spreadsheet/worksheet.rb +215 -0
- data/lib/spreadsheet/writer.rb +29 -0
- data/test/data/test_copy.xls +0 -0
- data/test/data/test_version_excel5.xls +0 -0
- data/test/data/test_version_excel95.xls +0 -0
- data/test/data/test_version_excel97.xls +0 -0
- data/test/excel/row.rb +29 -0
- data/test/font.rb +163 -0
- data/test/integration.rb +1021 -0
- data/test/workbook.rb +21 -0
- data/test/worksheet.rb +62 -0
- metadata +113 -0
@@ -0,0 +1,556 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'spreadsheet/excel/writer/biff8'
|
3
|
+
require 'spreadsheet/excel/internals/biff8'
|
4
|
+
|
5
|
+
module Spreadsheet
|
6
|
+
module Excel
|
7
|
+
module Writer
|
8
|
+
##
|
9
|
+
# Writer class for Excel Worksheets. Most write_* method correspond to an
|
10
|
+
# Excel-Record/Opcode. You should not need to call any of its methods directly.
|
11
|
+
# If you think you do, look at #write_worksheet
|
12
|
+
class Worksheet
|
13
|
+
include Biff8
|
14
|
+
include Internals
|
15
|
+
include Internals::Biff8
|
16
|
+
attr_reader :worksheet
|
17
|
+
def initialize workbook, worksheet
|
18
|
+
@workbook = workbook
|
19
|
+
@worksheet = worksheet
|
20
|
+
@io = StringIO.new ''
|
21
|
+
@biff_version = 0x0600
|
22
|
+
@bof = 0x0809
|
23
|
+
@build_id = 3515
|
24
|
+
@build_year = 1996
|
25
|
+
@bof_types = {
|
26
|
+
:globals => 0x0005,
|
27
|
+
:visual_basic => 0x0006,
|
28
|
+
:worksheet => 0x0010,
|
29
|
+
:chart => 0x0020,
|
30
|
+
:macro_sheet => 0x0040,
|
31
|
+
:workspace => 0x0100,
|
32
|
+
}
|
33
|
+
end
|
34
|
+
##
|
35
|
+
# The number of bytes needed to write a Boundsheet record for this Worksheet
|
36
|
+
# Used by Writer::Worksheet to calculate various offsets.
|
37
|
+
def boundsheet_size
|
38
|
+
name.size + 10
|
39
|
+
end
|
40
|
+
def data
|
41
|
+
@io.rewind
|
42
|
+
@io.read
|
43
|
+
end
|
44
|
+
def encode_date date
|
45
|
+
return date if date.is_a? Numeric
|
46
|
+
if date.is_a? Time
|
47
|
+
date = DateTime.new date.year, date.month, date.day,
|
48
|
+
date.hour, date.min, date.sec
|
49
|
+
end
|
50
|
+
value = date - @worksheet.workbook.date_base
|
51
|
+
if date > LEAP_ERROR
|
52
|
+
value += 1
|
53
|
+
end
|
54
|
+
value
|
55
|
+
end
|
56
|
+
def encode_rk value
|
57
|
+
# Bit Mask Contents
|
58
|
+
# 0 0x00000001 0 = Value not changed 1 = Value is multiplied by 100
|
59
|
+
# 1 0x00000002 0 = Floating-point value 1 = Signed integer value
|
60
|
+
# 31-2 0xFFFFFFFC Encoded value
|
61
|
+
cent = 0
|
62
|
+
int = 2
|
63
|
+
higher = value * 100
|
64
|
+
if higher == higher.to_i
|
65
|
+
value = higher.to_i
|
66
|
+
cent = 1
|
67
|
+
end
|
68
|
+
if value.is_a?(Integer)
|
69
|
+
shifted = [value].pack 'l'
|
70
|
+
## I can't find a format for packing a little endian signed integer
|
71
|
+
shifted.reverse! if @bigendian
|
72
|
+
value, = shifted.unpack 'V'
|
73
|
+
value <<= 2
|
74
|
+
else
|
75
|
+
# FIXME: precision of small numbers
|
76
|
+
int = 0
|
77
|
+
value, = [value].pack(EIGHT_BYTE_DOUBLE).unpack('x4V')
|
78
|
+
value &= 0xfffffffc
|
79
|
+
end
|
80
|
+
value | cent | int
|
81
|
+
end
|
82
|
+
def name
|
83
|
+
unicode_string @worksheet.name
|
84
|
+
end
|
85
|
+
def row_blocks
|
86
|
+
# All cells in an Excel document are divided into blocks of 32 consecutive
|
87
|
+
# rows, called Row Blocks. The first Row Block starts with the first used
|
88
|
+
# row in that sheet. Inside each Row Block there will occur ROW records
|
89
|
+
# describing the properties of the rows, and cell records with all the cell
|
90
|
+
# contents in this Row Block.
|
91
|
+
blocks = []
|
92
|
+
@worksheet.reject do |row| row.empty? end.each_with_index do |row, idx|
|
93
|
+
blocks << [] if idx % 32 == 0
|
94
|
+
blocks.last << row
|
95
|
+
end
|
96
|
+
blocks
|
97
|
+
end
|
98
|
+
def size
|
99
|
+
@io.size
|
100
|
+
end
|
101
|
+
def strings
|
102
|
+
@worksheet.inject [] do |memo, row|
|
103
|
+
strings = row.select do |cell| cell.is_a? String end
|
104
|
+
memo.concat strings
|
105
|
+
end
|
106
|
+
end
|
107
|
+
##
|
108
|
+
# Write a blank cell
|
109
|
+
def write_blank row, idx
|
110
|
+
write_cell :blank, row, idx
|
111
|
+
end
|
112
|
+
def write_bof
|
113
|
+
data = [
|
114
|
+
@biff_version, # BIFF version (always 0x0600 for BIFF8)
|
115
|
+
0x0010, # Type of the following data:
|
116
|
+
# 0x0005 = Workbook globals
|
117
|
+
# 0x0006 = Visual Basic module
|
118
|
+
# 0x0010 = Worksheet
|
119
|
+
# 0x0020 = Chart
|
120
|
+
# 0x0040 = Macro sheet
|
121
|
+
# 0x0100 = Workspace file
|
122
|
+
@build_id, # Build identifier
|
123
|
+
@build_year, # Build year
|
124
|
+
0x000, # File history flags
|
125
|
+
0x006, # Lowest Excel version that can read
|
126
|
+
# all records in this file
|
127
|
+
]
|
128
|
+
write_op @bof, data.pack("v4V2")
|
129
|
+
end
|
130
|
+
##
|
131
|
+
# Write a cell with a Boolean or Error value
|
132
|
+
def write_boolerr row, idx
|
133
|
+
value = row[idx]
|
134
|
+
type = 0
|
135
|
+
numval = 0
|
136
|
+
if value.is_a? Error
|
137
|
+
type = 1
|
138
|
+
numval = value.code
|
139
|
+
elsif value
|
140
|
+
numval = 1
|
141
|
+
end
|
142
|
+
data = [
|
143
|
+
numval, # Boolean or error value (type depends on the following byte)
|
144
|
+
type # 0 = Boolean value; 1 = Error code
|
145
|
+
]
|
146
|
+
write_cell :boolerr, row, idx, *data
|
147
|
+
end
|
148
|
+
def write_calccount
|
149
|
+
count = 100 # Maximum number of iterations allowed in circular references
|
150
|
+
write_op 0x000c, [count].pack('v')
|
151
|
+
end
|
152
|
+
def write_cell type, row, idx, *args
|
153
|
+
xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx)
|
154
|
+
data = [
|
155
|
+
row.idx, # Index to row
|
156
|
+
idx, # Index to column
|
157
|
+
xf_idx, # Index to XF record (➜ 6.115)
|
158
|
+
].concat args
|
159
|
+
write_op opcode(type), data.pack(binfmt(type))
|
160
|
+
end
|
161
|
+
def write_cellblocks row
|
162
|
+
# BLANK ➜ 6.7
|
163
|
+
# BOOLERR ➜ 6.10
|
164
|
+
# INTEGER ➜ 6.56 (BIFF2 only)
|
165
|
+
# LABEL ➜ 6.59 (BIFF2-BIFF7)
|
166
|
+
# LABELSST ➜ 6.61 (BIFF8 only)
|
167
|
+
# MULBLANK ➜ 6.64 (BIFF5-BIFF8)
|
168
|
+
# MULRK ➜ 6.65 (BIFF5-BIFF8)
|
169
|
+
# NUMBER ➜ 6.68
|
170
|
+
# RK ➜ 6.82 (BIFF3-BIFF8)
|
171
|
+
# RSTRING ➜ 6.84 (BIFF5/BIFF7)
|
172
|
+
multiples, first_idx = nil
|
173
|
+
row.each_with_index do |cell, idx|
|
174
|
+
if multiples && (!multiples.last.is_a?(cell.class) \
|
175
|
+
|| (cell.is_a?(Numeric) && cell.abs < 0.1))
|
176
|
+
write_multiples row, first_idx, multiples
|
177
|
+
multiples, first_idx = nil
|
178
|
+
end
|
179
|
+
nxt = idx + 1
|
180
|
+
case cell
|
181
|
+
when NilClass
|
182
|
+
if multiples
|
183
|
+
multiples.push cell
|
184
|
+
elsif nxt < row.size && row[nxt].nil?
|
185
|
+
multiples = [cell]
|
186
|
+
first_idx = idx
|
187
|
+
else
|
188
|
+
write_blank row, idx
|
189
|
+
end
|
190
|
+
when TrueClass, FalseClass, Error
|
191
|
+
write_boolerr row, idx
|
192
|
+
when String
|
193
|
+
write_labelsst row, idx
|
194
|
+
when Numeric
|
195
|
+
## RK encodes Floats with 30 significant bits, which is a bit more than
|
196
|
+
# 10^9. Not sure what is a good rule of thumb here, but it seems that
|
197
|
+
# Decimal Numbers with more than 4 significant digits are not represented
|
198
|
+
# with sufficient precision by RK
|
199
|
+
if cell.is_a?(Float) && cell.to_s.length > 5
|
200
|
+
write_number row, idx
|
201
|
+
elsif multiples
|
202
|
+
multiples.push cell
|
203
|
+
elsif nxt < row.size && row[nxt].is_a?(Numeric)
|
204
|
+
multiples = [cell]
|
205
|
+
first_idx = idx
|
206
|
+
else
|
207
|
+
write_rk row, idx
|
208
|
+
end
|
209
|
+
when Formula
|
210
|
+
write_formula row, idx
|
211
|
+
when Date
|
212
|
+
write_rk row, idx
|
213
|
+
end
|
214
|
+
end
|
215
|
+
write_multiples row, first_idx, multiples if multiples
|
216
|
+
end
|
217
|
+
def write_changes reader, endpos, sst_status
|
218
|
+
reader.seek @worksheet.offset
|
219
|
+
blocks = row_blocks
|
220
|
+
lastpos = reader.pos
|
221
|
+
offsets = {}
|
222
|
+
@worksheet.offsets.each do |key, pair|
|
223
|
+
if @worksheet.changes.include?(key) \
|
224
|
+
|| (sst_status == :complete_update && key.is_a?(Integer))
|
225
|
+
offsets.store pair, key
|
226
|
+
end
|
227
|
+
end
|
228
|
+
offsets.invert.sort_by do |key, (pos, len)|
|
229
|
+
pos
|
230
|
+
end.each do |key, (pos, len)|
|
231
|
+
@io.write reader.read(pos - lastpos)
|
232
|
+
if key.is_a?(Integer)
|
233
|
+
block = blocks.find do |rows| rows.any? do |row| row.idx == key end end
|
234
|
+
write_rowblock block
|
235
|
+
else
|
236
|
+
send "write_#{key}"
|
237
|
+
end
|
238
|
+
lastpos = pos + len
|
239
|
+
reader.seek lastpos
|
240
|
+
end
|
241
|
+
@io.write reader.read(endpos - lastpos)
|
242
|
+
end
|
243
|
+
def write_defaultrowheight
|
244
|
+
data = [
|
245
|
+
0x00, # Option flags:
|
246
|
+
# Bit Mask Contents
|
247
|
+
# 0 0x01 1 = Row height and default font height do not match
|
248
|
+
# 1 0x02 1 = Row is hidden
|
249
|
+
# 2 0x04 1 = Additional space above the row
|
250
|
+
# 3 0x08 1 = Additional space below the row
|
251
|
+
0xf2, # Default height for unused rows, in twips = 1/20 of a point
|
252
|
+
]
|
253
|
+
write_op 0x0225, data.pack('v2')
|
254
|
+
end
|
255
|
+
def write_dimensions
|
256
|
+
# Offset Size Contents
|
257
|
+
# 0 4 Index to first used row
|
258
|
+
# 4 4 Index to last used row, increased by 1
|
259
|
+
# 8 2 Index to first used column
|
260
|
+
# 10 2 Index to last used column, increased by 1
|
261
|
+
# 12 2 Not used
|
262
|
+
write_op 0x0200, @worksheet.dimensions.pack(binfmt(:dimensions))
|
263
|
+
end
|
264
|
+
def write_eof
|
265
|
+
write_op 0x000a
|
266
|
+
end
|
267
|
+
##
|
268
|
+
# Write a cell with a Formula. May write an additional String record depending
|
269
|
+
# on the stored result of the Formula.
|
270
|
+
def write_formula row, idx
|
271
|
+
cell = row[idx]
|
272
|
+
data1 = [
|
273
|
+
row.idx, # Index to row
|
274
|
+
idx, # Index to column
|
275
|
+
0, # Index to XF record (➜ 6.115)
|
276
|
+
].pack 'v3'
|
277
|
+
data2 = nil
|
278
|
+
case value = cell.value
|
279
|
+
when Numeric # IEEE 754 floating-point value (64-bit double precision)
|
280
|
+
data2 = [value].pack EIGHT_BYTE_DOUBLE
|
281
|
+
when String
|
282
|
+
data2 = [
|
283
|
+
0x00, # (identifier for a string value)
|
284
|
+
0xffff, #
|
285
|
+
].pack 'Cx5v'
|
286
|
+
when true, false
|
287
|
+
value = value ? 1 : 0
|
288
|
+
data2 = [
|
289
|
+
0x01, # (identifier for a Boolean value)
|
290
|
+
value, # 0 = FALSE, 1 = TRUE
|
291
|
+
0xffff, #
|
292
|
+
].pack 'CxCx3v'
|
293
|
+
when Error
|
294
|
+
data2 = [
|
295
|
+
0x02, # (identifier for an error value)
|
296
|
+
value.code, # Error code
|
297
|
+
0xffff, #
|
298
|
+
].pack 'CxCx3v'
|
299
|
+
when nil
|
300
|
+
data2 = [
|
301
|
+
0x03, # (identifier for an empty cell)
|
302
|
+
0xffff, #
|
303
|
+
].pack 'Cx5v'
|
304
|
+
else
|
305
|
+
data2 = [
|
306
|
+
0x02, # (identifier for an error value)
|
307
|
+
0x2a, # Error code: #N/A! Argument or function not available
|
308
|
+
0xffff, #
|
309
|
+
].pack 'CxCx3v'
|
310
|
+
end
|
311
|
+
opts = 0x03
|
312
|
+
opts |= 0x08 if cell.shared
|
313
|
+
data3 = [
|
314
|
+
opts # Option flags:
|
315
|
+
# Bit Mask Contents
|
316
|
+
# 0 0x0001 1 = Recalculate always
|
317
|
+
# 1 0x0002 1 = Calculate on open
|
318
|
+
# 3 0x0008 1 = Part of a shared formula
|
319
|
+
].pack 'vx4'
|
320
|
+
write_op opcode(:formula), data1, data2, data3, cell.data
|
321
|
+
if cell.value.is_a?(String)
|
322
|
+
write_op opcode(:string), unicode_string(cell.value, 2)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
##
|
326
|
+
# Write a new Worksheet.
|
327
|
+
def write_from_scratch
|
328
|
+
# ● BOF Type = worksheet (➜ 6.8)
|
329
|
+
write_bof
|
330
|
+
# ○ UNCALCED ➜ 6.104
|
331
|
+
# ○ INDEX ➜ 5.7 (Row Blocks), ➜ 6.55
|
332
|
+
# ○ Calculation Settings Block ➜ 5.3
|
333
|
+
write_calccount
|
334
|
+
write_refmode
|
335
|
+
write_iteration
|
336
|
+
write_saverecalc
|
337
|
+
# ○ PRINTHEADERS ➜ 6.76
|
338
|
+
# ○ PRINTGRIDLINES ➜ 6.75
|
339
|
+
# ○ GRIDSET ➜ 6.48
|
340
|
+
# ○ GUTS ➜ 6.49
|
341
|
+
# ○ DEFAULTROWHEIGHT ➜ 6.28
|
342
|
+
write_defaultrowheight
|
343
|
+
# ○ WSBOOL ➜ 6.113
|
344
|
+
write_wsbool
|
345
|
+
# ○ Page Settings Block ➜ 5.4
|
346
|
+
# ○ Worksheet Protection Block ➜ 5.18
|
347
|
+
# ○ DEFCOLWIDTH ➜ 6.29
|
348
|
+
# ○○ COLINFO ➜ 6.18
|
349
|
+
# ○ SORT ➜ 6.95
|
350
|
+
# ● DIMENSIONS ➜ 6.31
|
351
|
+
write_dimensions
|
352
|
+
# ○○ Row Blocks ➜ 5.7
|
353
|
+
write_rows
|
354
|
+
# ● Worksheet View Settings Block ➜ 5.5
|
355
|
+
# ○ STANDARDWIDTH ➜ 6.97
|
356
|
+
# ○○ MERGEDCELLS ➜ 6.63
|
357
|
+
# ○ LABELRANGES ➜ 6.60
|
358
|
+
# ○ PHONETIC ➜ 6.73
|
359
|
+
# ○ Conditional Formatting Table ➜ 5.12
|
360
|
+
# ○ Hyperlink Table ➜ 5.13
|
361
|
+
# ○ Data Validity Table ➜ 5.14
|
362
|
+
# ○ SHEETLAYOUT ➜ 6.91 (BIFF8X only)
|
363
|
+
# ○ SHEETPROTECTION Additional protection, ➜ 6.92 (BIFF8X only)
|
364
|
+
# ○ RANGEPROTECTION Additional protection, ➜ 6.79 (BIFF8X only)
|
365
|
+
# ● EOF ➜ 6.36
|
366
|
+
write_eof
|
367
|
+
end
|
368
|
+
def write_iteration
|
369
|
+
its = 0 # 0 = Iterations off; 1 = Iterations on
|
370
|
+
write_op 0x0011, [its].pack('v')
|
371
|
+
end
|
372
|
+
##
|
373
|
+
# Write a cell with a String value. The String must have been stored in the
|
374
|
+
# Shared String Table.
|
375
|
+
def write_labelsst row, idx
|
376
|
+
write_cell :labelsst, row, idx, @workbook.sst_index(self, row[idx])
|
377
|
+
end
|
378
|
+
##
|
379
|
+
# Write multiple consecutive blank cells.
|
380
|
+
def write_mulblank row, idx, multiples
|
381
|
+
data = [
|
382
|
+
row.idx, # Index to row
|
383
|
+
idx, # Index to first column (fc)
|
384
|
+
]
|
385
|
+
# List of nc=lc-fc+1 16-bit indexes to XF records (➜ 6.115)
|
386
|
+
multiples.each_with_index do |blank, cell_idx|
|
387
|
+
xf_idx = @workbook.xf_index @worksheet.workbook, row.format(idx + cell_idx)
|
388
|
+
data.push xf_idx
|
389
|
+
end
|
390
|
+
# Index to last column (lc)
|
391
|
+
data.push idx + multiples.size
|
392
|
+
write_op opcode(:mulblank), data.pack('v*')
|
393
|
+
end
|
394
|
+
##
|
395
|
+
# Write multiple consecutive cells with RK values (see #write_rk)
|
396
|
+
def write_mulrk row, idx, multiples
|
397
|
+
fmt = 'v2'
|
398
|
+
data = [
|
399
|
+
row.idx, # Index to row
|
400
|
+
idx, # Index to first column (fc)
|
401
|
+
]
|
402
|
+
# List of nc=lc-fc+1 16-bit indexes to XF records (➜ 6.115)
|
403
|
+
multiples.each do |cell|
|
404
|
+
# TODO: XF indices
|
405
|
+
data.push 0, encode_rk(cell)
|
406
|
+
fmt << 'vV'
|
407
|
+
end
|
408
|
+
# Index to last column (lc)
|
409
|
+
data.push idx + multiples.size
|
410
|
+
write_op opcode(:mulrk), data.pack(fmt << 'v')
|
411
|
+
end
|
412
|
+
def write_multiples row, idx, multiples
|
413
|
+
case multiples.last
|
414
|
+
when NilClass
|
415
|
+
write_mulblank row, idx, multiples
|
416
|
+
when Numeric
|
417
|
+
write_mulrk row, idx, multiples
|
418
|
+
end
|
419
|
+
end
|
420
|
+
##
|
421
|
+
# Write a cell with a 64-bit double precision Float value
|
422
|
+
def write_number row, idx
|
423
|
+
# Offset Size Contents
|
424
|
+
# 0 2 Index to row
|
425
|
+
# 2 2 Index to column
|
426
|
+
# 4 2 Index to XF record (➜ 6.115)
|
427
|
+
# 6 8 IEEE 754 floating-point value (64-bit double precision)
|
428
|
+
write_cell :number, row, idx, row[idx]
|
429
|
+
end
|
430
|
+
def write_op op, *args
|
431
|
+
data = args.join
|
432
|
+
@io.write [op,data.size].pack("v2")
|
433
|
+
@io.write data
|
434
|
+
end
|
435
|
+
def write_refmode
|
436
|
+
# • The “RC” mode uses numeric indexes for rows and columns, for example
|
437
|
+
# “R(1)C(-1)”, or “R1C1:R2C2”.
|
438
|
+
# • The “A1” mode uses characters for columns and numbers for rows, for
|
439
|
+
# example “B1”, or “$A$1:$B$2”.
|
440
|
+
mode = 1 # 0 = RC mode; 1 = A1 mode
|
441
|
+
write_op 0x000f, [mode].pack('v')
|
442
|
+
end
|
443
|
+
##
|
444
|
+
# Write a cell with a Numeric or Date value.
|
445
|
+
def write_rk row, idx
|
446
|
+
value = row[idx]
|
447
|
+
case value
|
448
|
+
when Date, DateTime
|
449
|
+
value = encode_date(value)
|
450
|
+
end
|
451
|
+
write_cell :rk, row, idx, encode_rk(value)
|
452
|
+
end
|
453
|
+
def write_row row
|
454
|
+
# Offset Size Contents
|
455
|
+
# 0 2 Index of this row
|
456
|
+
# 2 2 Index to column of the first cell which
|
457
|
+
# is described by a cell record
|
458
|
+
# 4 2 Index to column of the last cell which is
|
459
|
+
# described by a cell record, increased by 1
|
460
|
+
# 6 2 Bit Mask Contents
|
461
|
+
# 14-0 0x7fff Height of the row, in twips = 1/20 of a point
|
462
|
+
# 15 0x8000 0 = Row has custom height;
|
463
|
+
# 1 = Row has default height
|
464
|
+
# 8 2 Not used
|
465
|
+
# 10 1 0 = No defaults written;
|
466
|
+
# 1 = Default row attribute field and XF index occur below (fl)
|
467
|
+
# 11 2 Relative offset to calculate stream position of the first
|
468
|
+
# cell record for this row (➜ 5.7.1)
|
469
|
+
# [13] 3 (written only if fl = 1) Default row attributes (➜ 3.12)
|
470
|
+
# [16] 2 (written only if fl = 1) Index to XF record (➜ 6.115)
|
471
|
+
has_defaults = row.default_format ? 1 : 0
|
472
|
+
data = [
|
473
|
+
row.idx,
|
474
|
+
row.first_used,
|
475
|
+
row.first_unused,
|
476
|
+
row.height * TWIPS,
|
477
|
+
0, # Not used
|
478
|
+
has_defaults,
|
479
|
+
0, # OOffice does not set this - ignore until someone complains
|
480
|
+
]
|
481
|
+
# OpenOffice apparently can't read Rows with a length other than 16 Bytes
|
482
|
+
fmt = binfmt(:row) + 'x3'
|
483
|
+
=begin
|
484
|
+
if format = row.default_format
|
485
|
+
fmt = fmt + 'xv'
|
486
|
+
data.concat [
|
487
|
+
#0, # Row attributes should only matter in BIFF2
|
488
|
+
workbook.xf_index(@worksheet.workbook, format),
|
489
|
+
]
|
490
|
+
end
|
491
|
+
=end
|
492
|
+
write_op opcode(:row), data.pack(fmt)
|
493
|
+
end
|
494
|
+
def write_rowblock block
|
495
|
+
# ●● ROW Properties of the used rows
|
496
|
+
# ○○ Cell Block(s) Cell records for all used cells
|
497
|
+
# ○ DBCELL Stream offsets to the cell records of each row
|
498
|
+
block.each do |row|
|
499
|
+
write_row row
|
500
|
+
end
|
501
|
+
block.each do |row|
|
502
|
+
write_cellblocks row
|
503
|
+
end
|
504
|
+
end
|
505
|
+
def write_rows
|
506
|
+
row_blocks.each do |block|
|
507
|
+
write_rowblock block
|
508
|
+
end
|
509
|
+
end
|
510
|
+
def write_saverecalc
|
511
|
+
# 0 = Do not recalculate; 1 = Recalculate before saving the document
|
512
|
+
write_op 0x005f, [1].pack('v')
|
513
|
+
end
|
514
|
+
def write_wsbool
|
515
|
+
bits = [
|
516
|
+
# Bit Mask Contents
|
517
|
+
1, # 0 0x0001 0 = Do not show automatic page breaks
|
518
|
+
# 1 = Show automatic page breaks
|
519
|
+
0, # 4 0x0010 0 = Standard sheet
|
520
|
+
# 1 = Dialogue sheet (BIFF5-BIFF8)
|
521
|
+
0, # 5 0x0020 0 = No automatic styles in outlines
|
522
|
+
# 1 = Apply automatic styles to outlines
|
523
|
+
1, # 6 0x0040 0 = Outline buttons above outline group
|
524
|
+
# 1 = Outline buttons below outline group
|
525
|
+
1, # 7 0x0080 0 = Outline buttons left of outline group
|
526
|
+
# 1 = Outline buttons right of outline group
|
527
|
+
0, # 8 0x0100 0 = Scale printout in percent (➜ 6.89)
|
528
|
+
# 1 = Fit printout to number of pages (➜ 6.89)
|
529
|
+
0, # 9 0x0200 0 = Save external linked values
|
530
|
+
# (BIFF3-BIFF4 only, ➜ 5.10)
|
531
|
+
# 1 = Do not save external linked values
|
532
|
+
# (BIFF3-BIFF4 only, ➜ 5.10)
|
533
|
+
1, # 10 0x0400 0 = Do not show row outline symbols
|
534
|
+
# 1 = Show row outline symbols
|
535
|
+
0, # 11 0x0800 0 = Do not show column outline symbols
|
536
|
+
# 1 = Show column outline symbols
|
537
|
+
0, # 13-12 0x3000 These flags specify the arrangement of windows.
|
538
|
+
# They are stored in BIFF4 only.
|
539
|
+
# 00 = Arrange windows tiled
|
540
|
+
# 01 = Arrange windows horizontal
|
541
|
+
0, # 10 = Arrange windows vertical
|
542
|
+
# 11 = Arrange windows cascaded
|
543
|
+
# The following flags are valid for BIFF4-BIFF8 only:
|
544
|
+
0, # 14 0x4000 0 = Standard expression evaluation
|
545
|
+
# 1 = Alternative expression evaluation
|
546
|
+
0, # 15 0x8000 0 = Standard formula entries
|
547
|
+
# 1 = Alternative formula entries
|
548
|
+
]
|
549
|
+
weights = [4,5,6,7,8,9,10,11,12,13,14,15]
|
550
|
+
value = bits.inject do |a, b| a | (b << weights.shift) end
|
551
|
+
write_op 0x0081, [value].pack('v')
|
552
|
+
end
|
553
|
+
end
|
554
|
+
end
|
555
|
+
end
|
556
|
+
end
|