slayer-surpass 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.Rakefile.swp +0 -0
- data/.bnsignore +26 -0
- data/History.txt +4 -0
- data/LICENSE.txt +110 -0
- data/README.txt +26 -0
- data/Rakefile +36 -0
- data/bin/surpass +8 -0
- data/lib/surpass.rb +64 -0
- data/lib/surpass/ExcelFormula.g +393 -0
- data/lib/surpass/ExcelFormula.tokens +32 -0
- data/lib/surpass/ExcelFormulaLexer.rb +1490 -0
- data/lib/surpass/ExcelFormulaParser.rb +1822 -0
- data/lib/surpass/biff_record.rb +2173 -0
- data/lib/surpass/bitmap.rb +218 -0
- data/lib/surpass/cell.rb +187 -0
- data/lib/surpass/chart.rb +16 -0
- data/lib/surpass/column.rb +40 -0
- data/lib/surpass/document.rb +406 -0
- data/lib/surpass/excel_magic.rb +1016 -0
- data/lib/surpass/formatting.rb +605 -0
- data/lib/surpass/formula.rb +25 -0
- data/lib/surpass/row.rb +173 -0
- data/lib/surpass/style.rb +194 -0
- data/lib/surpass/tokens.txt +2 -0
- data/lib/surpass/utilities.rb +118 -0
- data/lib/surpass/workbook.rb +207 -0
- data/lib/surpass/worksheet.rb +574 -0
- data/surpass.gemspec +39 -0
- metadata +119 -0
@@ -0,0 +1,2173 @@
|
|
1
|
+
class SharedStringTable
|
2
|
+
SST_ID = 0x00FC
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@sst_record = nil
|
6
|
+
@continues = []
|
7
|
+
@current_piece = [0,0].pack('V2')
|
8
|
+
|
9
|
+
@str_indexes = {} # TODO replace with array? or is hash more efficient?
|
10
|
+
@add_calls = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_str(s)
|
14
|
+
@add_calls += 1
|
15
|
+
index = @str_indexes[s]
|
16
|
+
if index.nil?
|
17
|
+
# This is a new string for the SST.
|
18
|
+
position = @str_indexes.length
|
19
|
+
@str_indexes[s] = position
|
20
|
+
index = position
|
21
|
+
add_to_sst(s)
|
22
|
+
end
|
23
|
+
index
|
24
|
+
end
|
25
|
+
|
26
|
+
def str_index(s)
|
27
|
+
@str_indexes[s]
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_biff
|
31
|
+
new_piece # flush the 'current' piece
|
32
|
+
result = [SST_ID, @sst_record.length, @add_calls, @str_indexes.length].pack('v2V2')
|
33
|
+
result += @sst_record[8..-1]
|
34
|
+
result += @continues.join
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_to_sst(s)
|
39
|
+
u_str = mock_unicode_string(s)
|
40
|
+
raise "very long string" if u_str.length > 0xFFFF
|
41
|
+
save_atom(u_str[0...4])
|
42
|
+
save_splitted(u_str[4..-1], false)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Store the @current_piece in @continues and initialize a new @current_piece
|
46
|
+
def new_piece
|
47
|
+
if @sst_record.nil?
|
48
|
+
# We get here when we first run out of space, or if that never happens then we end
|
49
|
+
# up here when everything is finished and we call to_biff for the first time.
|
50
|
+
@sst_record = @current_piece
|
51
|
+
else
|
52
|
+
@continues << [BiffRecord::CONTINUE_RECORD_ID, @current_piece.length].pack('v2') + @current_piece
|
53
|
+
end
|
54
|
+
@current_piece = ''
|
55
|
+
end
|
56
|
+
|
57
|
+
def save_atom(atom)
|
58
|
+
free_space = 0x2020 - @current_piece.length
|
59
|
+
new_piece if free_space < atom.length
|
60
|
+
@current_piece += atom
|
61
|
+
end
|
62
|
+
|
63
|
+
def save_splitted(s, is_unicode_str)
|
64
|
+
i = 0
|
65
|
+
while i < s.length do
|
66
|
+
free_space = 0x2020 - @current_piece.length
|
67
|
+
tail_length = s.length - i
|
68
|
+
need_more_space = free_space < tail_length
|
69
|
+
|
70
|
+
if !need_more_space
|
71
|
+
atom_length = tail_length
|
72
|
+
else
|
73
|
+
if is_unicode_str
|
74
|
+
atom_length = free_space & 0xFFFE
|
75
|
+
else
|
76
|
+
atom_length = free_space
|
77
|
+
end
|
78
|
+
end
|
79
|
+
@current_piece += s[i...(i+atom_length)]
|
80
|
+
|
81
|
+
if need_more_space
|
82
|
+
new_piece
|
83
|
+
if is_unicode_str
|
84
|
+
@current_piece += "\001"
|
85
|
+
else
|
86
|
+
@current_piece += "\000"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
i += atom_length
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class BiffRecord
|
96
|
+
attr_accessor :record_data
|
97
|
+
|
98
|
+
BIFF_LIMIT = 0x2020 # limit for BIFF7/8
|
99
|
+
CONTINUE_RECORD_ID = 0x003C
|
100
|
+
|
101
|
+
# By default, initialize to ''.
|
102
|
+
# May be overridden in subclass.
|
103
|
+
def initialize
|
104
|
+
@record_data = ''
|
105
|
+
end
|
106
|
+
|
107
|
+
### @export "biff-record"
|
108
|
+
def record_header
|
109
|
+
[self.class::RECORD_ID, @record_data.length].pack('v2')
|
110
|
+
end
|
111
|
+
|
112
|
+
def to_biff
|
113
|
+
if @record_data.length > BIFF_LIMIT
|
114
|
+
chunks = []
|
115
|
+
pos = 0
|
116
|
+
while pos < @record_data.length
|
117
|
+
chunk_pos = pos + BIFF_LIMIT
|
118
|
+
chunk = @record_data[pos...chunk_pos]
|
119
|
+
chunks << chunk
|
120
|
+
pos = chunk_pos
|
121
|
+
end
|
122
|
+
|
123
|
+
continues = [self.class::RECORD_ID, chunks[0].length].pack('v2') + chunks[0]
|
124
|
+
chunks.each_with_index do |c, i|
|
125
|
+
next if i == 0
|
126
|
+
continues += [CONTINUE_RECORD_ID, c.length].pack('v2') + c
|
127
|
+
end
|
128
|
+
continues
|
129
|
+
else
|
130
|
+
record_header + @record_data
|
131
|
+
end
|
132
|
+
end
|
133
|
+
### @end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Offset Size Contents
|
137
|
+
# 0 2 Version, contains 0600H for BIFF8 and BIFF8X
|
138
|
+
# 2 2 Type of the following data:
|
139
|
+
# 0005H = Workbook globals
|
140
|
+
# 0006H = Visual Basic module
|
141
|
+
# 0010H = Worksheet
|
142
|
+
# 0020H = Chart
|
143
|
+
# 0040H = Macro sheet
|
144
|
+
# 0100H = Workspace file
|
145
|
+
# 4 2 Build identifier
|
146
|
+
# 6 2 Build year
|
147
|
+
# 8 4 File history flags
|
148
|
+
# 12 4 Lowest Excel version that can read all records in this file
|
149
|
+
### @export "biffbof-record"
|
150
|
+
class Biff8BOFRecord < BiffRecord
|
151
|
+
RECORD_ID = 0x0809
|
152
|
+
|
153
|
+
# Stream Types
|
154
|
+
BOOK_GLOBAL = 0x0005
|
155
|
+
VB_MODULE = 0x0006
|
156
|
+
WORKSHEET = 0x0010
|
157
|
+
CHART = 0x0020
|
158
|
+
MACROSHEET = 0x0040
|
159
|
+
WORKSPACE = 0x0100
|
160
|
+
|
161
|
+
def initialize(rec_type)
|
162
|
+
version = 0x0600
|
163
|
+
build = 0x0DBB
|
164
|
+
year = 0x07CC
|
165
|
+
file_hist_flags = 0x00
|
166
|
+
ver_can_read = 0x06
|
167
|
+
|
168
|
+
@record_data = [version, rec_type, build, year, file_hist_flags, ver_can_read].pack('v4V2')
|
169
|
+
end
|
170
|
+
end
|
171
|
+
### @end
|
172
|
+
|
173
|
+
class InterfaceHeaderRecord < BiffRecord
|
174
|
+
RECORD_ID = 0x00E1
|
175
|
+
|
176
|
+
def initialize
|
177
|
+
@record_data = [0xB0, 0x04].pack('C2')
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class InterfaceEndRecord < BiffRecord
|
182
|
+
RECORD_ID = 0x00E2
|
183
|
+
end
|
184
|
+
|
185
|
+
class MMSRecord < BiffRecord
|
186
|
+
RECORD_ID = 0x00C1
|
187
|
+
def initialize
|
188
|
+
@record_data = [0x00].pack('v')
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# This record is part of the file protection. It contains the name of the
|
193
|
+
# user that has saved the file. The user name is always stored as an
|
194
|
+
# equal-sized string. All unused characters after the name are filled
|
195
|
+
# with space characters. It is not required to write the mentioned string
|
196
|
+
# length. Every other length will be accepted too.
|
197
|
+
class WriteAccessRecord < BiffRecord
|
198
|
+
RECORD_ID = 0x005C
|
199
|
+
|
200
|
+
# TODO Can we extend this to 0x70? I think 0x30 is a holdover from Biff7 but 112 chars is Biff8.
|
201
|
+
def initialize(owner)
|
202
|
+
@record_data = [owner[0, 0x30]].pack('A112')
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# This record specifies if the file contains an additional BIFF5/BIFF7
|
207
|
+
# workbook stream.
|
208
|
+
# Record DSF, BIFF8:
|
209
|
+
# Offset Size Contents
|
210
|
+
# 0 2 0 = Only the BIFF8 Workbook stream is present
|
211
|
+
# 1 = Additional BIFF5/BIFF7 Book stream is in the file
|
212
|
+
# A double stream file can be read by Excel 5.0 and Excel 95, and still
|
213
|
+
# contains all new features added to BIFF8 (which are left out in the
|
214
|
+
# BIFF5/BIFF7 Book stream).
|
215
|
+
class DSFRecord < BiffRecord
|
216
|
+
RECORD_ID = 0x0161
|
217
|
+
|
218
|
+
def initialize
|
219
|
+
@record_data = [0x00].pack('v')
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
class TabIDRecord < BiffRecord
|
224
|
+
RECORD_ID = 0x013D
|
225
|
+
|
226
|
+
def initialize(sheetcount)
|
227
|
+
@record_data = ''
|
228
|
+
for i in 1..sheetcount do
|
229
|
+
@record_data += [i].pack('v')
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
class FnGroupCountRecord < BiffRecord
|
235
|
+
RECORD_ID = 0x009C
|
236
|
+
|
237
|
+
def initialize
|
238
|
+
@record_data = [0x0E, 0X00].pack('C2')
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# This record is part of the worksheet/workbook protection. It determines
|
243
|
+
# whether the window configuration of this document is protected. Window
|
244
|
+
# protection is not active, if this record is omitted.
|
245
|
+
class WindowProtectRecord < BiffRecord
|
246
|
+
RECORD_ID = 0x0019
|
247
|
+
|
248
|
+
def initialize(protect)
|
249
|
+
@record_data = [protect].pack('v')
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# This record is part of the worksheet/workbook protection.
|
254
|
+
# It determines whether the objects of the current sheet are protected.
|
255
|
+
# Object protection is not active, if this record is omitted.
|
256
|
+
class ObjectProtectRecord < BiffRecord
|
257
|
+
RECORD_ID = 0x0063
|
258
|
+
|
259
|
+
def initialize(protect)
|
260
|
+
@record_data = [protect].pack('v')
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# This record is part of the worksheet/workbook protection. It
|
265
|
+
# determines whether the scenarios of the current sheet are protected.
|
266
|
+
# Scenario protection is not active, if this record is omitted.
|
267
|
+
class ScenarioProtectRecord < BiffRecord
|
268
|
+
RECORD_ID = 0x00DD
|
269
|
+
|
270
|
+
def initialize(protect)
|
271
|
+
@record_data = [protect].pack('v')
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
# This record is part of the worksheet/workbook protection. It
|
276
|
+
# determines whether the scenarios of the current sheet are protected.
|
277
|
+
# Scenario protection is not active, if this record is omitted.
|
278
|
+
class ProtectRecord < BiffRecord
|
279
|
+
RECORD_ID = 0x0012
|
280
|
+
|
281
|
+
def initialize(protect)
|
282
|
+
@record_data = [protect].pack('v')
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# This record is part of the worksheet/workbook protection. It
|
287
|
+
# stores a 16-bit hash value, calculated from the worksheet or workbook
|
288
|
+
# protection password.
|
289
|
+
class PasswordRecord < BiffRecord
|
290
|
+
RECORD_ID = 0x0013
|
291
|
+
|
292
|
+
def initialize(password = "")
|
293
|
+
@record_data = [password_hash(password)].pack('v')
|
294
|
+
end
|
295
|
+
|
296
|
+
# Based on the algorithm provided by Daniel Rentz of OpenOffice.
|
297
|
+
def password_hash(plaintext)
|
298
|
+
return 0 if plaintext === ""
|
299
|
+
hash = 0x0000
|
300
|
+
plaintext.unpack('C*').each_with_index do |t, i|
|
301
|
+
c = t << (i + 1)
|
302
|
+
low_15 = c & 0x7fff
|
303
|
+
high_15 = c & 0x7fff << 15
|
304
|
+
high_15 = high_15 >> 15
|
305
|
+
c = low_15 | high_15
|
306
|
+
hash = (hash ^ c)
|
307
|
+
end
|
308
|
+
hash = (hash ^ plaintext.length)
|
309
|
+
hash = (hash ^ 0xCE4B)
|
310
|
+
hash
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
class Prot4RevRecord < BiffRecord
|
315
|
+
RECORD_ID = 0x01AF
|
316
|
+
|
317
|
+
def initialize
|
318
|
+
@record_data = [0x00].pack('v')
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
class Prot4RevPassRecord < BiffRecord
|
323
|
+
RECORD_ID = 0x01BC
|
324
|
+
|
325
|
+
def initialize
|
326
|
+
@record_data = [0x00].pack('v')
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
# This record contains a Boolean value determining whether Excel makes
|
331
|
+
# a backup of the file while saving.
|
332
|
+
class BackupRecord < BiffRecord
|
333
|
+
RECORD_ID = 0x0040
|
334
|
+
|
335
|
+
def initialize(backup)
|
336
|
+
@record_data = [backup].pack('v')
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
# This record specifies whether and how to show objects in the workbook.
|
341
|
+
#
|
342
|
+
# Record HIDEOBJ, BIFF3-BIFF8:
|
343
|
+
# Offset Size Contents
|
344
|
+
# 0 2 Viewing mode for objects:
|
345
|
+
# 0 = Show all objects
|
346
|
+
# 1 = Show placeholders
|
347
|
+
# 2 = Do not show objects
|
348
|
+
class HideObjRecord < BiffRecord
|
349
|
+
RECORD_ID = 0x008D
|
350
|
+
|
351
|
+
def initialize
|
352
|
+
@record_data = [0x00].pack('v')
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
class RefreshAllRecord < BiffRecord
|
357
|
+
RECORD_ID = 0x01B7
|
358
|
+
|
359
|
+
def initialize
|
360
|
+
@record_data = [0x00].pack('v')
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
# This record contains a Boolean value determining whether to save values
|
365
|
+
# linked from external workbooks (CRN records and XCT records). In BIFF3
|
366
|
+
# and BIFF4 this option is stored in the WSBOOL record.
|
367
|
+
#
|
368
|
+
# Record BOOKBOOL, BIFF5-BIFF8:
|
369
|
+
#
|
370
|
+
# Offset Size Contents
|
371
|
+
# 0 2 0 = Save external linked values;
|
372
|
+
# 1 = Do not save external linked values
|
373
|
+
class BookBoolRecord < BiffRecord
|
374
|
+
RECORD_ID = 0x00DA
|
375
|
+
|
376
|
+
def initialize
|
377
|
+
@record_data = [0x00].pack('v')
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
# This record stores two Windows country identifiers. The first
|
382
|
+
# represents the user interface language of the Excel version that has
|
383
|
+
# saved the file, and the second represents the system regional settings
|
384
|
+
# at the time the file was saved.
|
385
|
+
#
|
386
|
+
# Record COUNTRY, BIFF3-BIFF8:
|
387
|
+
#
|
388
|
+
# Offset Size Contents
|
389
|
+
# 0 2 Windows country identifier of the user interface language of Excel
|
390
|
+
# 2 2 Windows country identifier of the system regional settings
|
391
|
+
#
|
392
|
+
# The following table shows most of the used country identifiers. Most
|
393
|
+
# of these identifiers are equal to the international country calling
|
394
|
+
# codes.
|
395
|
+
#
|
396
|
+
# 1 USA
|
397
|
+
# 2 Canada
|
398
|
+
# 7 Russia
|
399
|
+
class CountryRecord < BiffRecord
|
400
|
+
RECORD_ID = 0x00DA
|
401
|
+
|
402
|
+
def initialize(ui_id, sys_settings_id)
|
403
|
+
@record_data = [ui_id, sys_settings_id].pack('v2')
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
# This record specifies if the formulas in the workbook can use natural
|
408
|
+
# language formulasî. This type of formula can refer to cells by its
|
409
|
+
# content or the content of the column or row header cell.
|
410
|
+
#
|
411
|
+
# Record USESELFS, BIFF8:
|
412
|
+
#
|
413
|
+
# Offset Size Contents
|
414
|
+
# 0 2 0 = Do not use natural language formulas
|
415
|
+
# 1 = Use natural language formulas
|
416
|
+
class UseSelfsRecord < BiffRecord
|
417
|
+
RECORD_ID = 0x0160
|
418
|
+
|
419
|
+
def initialize
|
420
|
+
@record_data = [0x01].pack('v')
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
class EOFRecord < BiffRecord
|
425
|
+
RECORD_ID = 0x000A
|
426
|
+
end
|
427
|
+
|
428
|
+
# This record specifies the base date for displaying date values. All
|
429
|
+
# dates are stored as count of days past this base date. In BIFF2-BIFF4
|
430
|
+
# this record is part of the Calculation Settings Block.
|
431
|
+
# In BIFF5-BIFF8 it is stored in the Workbook Globals Substream.
|
432
|
+
#
|
433
|
+
# Record DATEMODE, BIFF2-BIFF8:
|
434
|
+
#
|
435
|
+
# Offset Size Contents
|
436
|
+
# 0 2 0 = Base is 1899-Dec-31 (the cell = 1 represents 1900-Jan-01)
|
437
|
+
# 1 = Base is 1904-Jan-01 (the cell = 1 represents 1904-Jan-02)
|
438
|
+
class DateModeRecord < BiffRecord
|
439
|
+
RECORD_ID = 0x0022
|
440
|
+
|
441
|
+
def initialize(boolean)
|
442
|
+
@record_data = boolean ? [1].pack('v') : [0].pack('v')
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
# This record stores if formulas use the real cell values for calculation
|
447
|
+
# or the values displayed on the screen. In BIFF2- BIFF4 this record
|
448
|
+
# is part of the Calculation Settings Block. In BIFF5-BIFF8 it is stored
|
449
|
+
# in the Workbook Globals Substream.
|
450
|
+
#
|
451
|
+
# Record PRECISION, BIFF2-BIFF8:
|
452
|
+
#
|
453
|
+
# Offset Size Contents
|
454
|
+
# 0 2 0 = Use displayed values;
|
455
|
+
# 1 = Use real cell values
|
456
|
+
class PrecisionRecord < BiffRecord
|
457
|
+
RECORD_ID = 0x000E
|
458
|
+
|
459
|
+
def initialize(boolean)
|
460
|
+
@record_data = boolean ? [1].pack('v') : [0].pack('v')
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
# This record stores the text encoding used to write byte strings, stored
|
465
|
+
# as MS Windows code page identifier. The CODEPAGE record in BIFF8 always
|
466
|
+
# contains the code page 1200 (UTF-16). Therefore it is not
|
467
|
+
# possible to obtain the encoding used for a protection password (it is
|
468
|
+
# not UTF-16).
|
469
|
+
#
|
470
|
+
# Record CODEPAGE, BIFF2-BIFF8:
|
471
|
+
#
|
472
|
+
# Offset Size Contents
|
473
|
+
# 0 2 Code page identifier used for byte string text encoding:
|
474
|
+
# 016FH = 367 = ASCII
|
475
|
+
# 01B5H = 437 = IBM PC CP-437 (US)
|
476
|
+
# 02D0H = 720 = IBM PC CP-720 (OEM Arabic)
|
477
|
+
# 02E1H = 737 = IBM PC CP-737 (Greek)
|
478
|
+
# 0307H = 775 = IBM PC CP-775 (Baltic)
|
479
|
+
# 0352H = 850 = IBM PC CP-850 (Latin I)
|
480
|
+
# 0354H = 852 = IBM PC CP-852 (Latin II (Central European))
|
481
|
+
# 0357H = 855 = IBM PC CP-855 (Cyrillic)
|
482
|
+
# 0359H = 857 = IBM PC CP-857 (Turkish)
|
483
|
+
# 035AH = 858 = IBM PC CP-858 (Multilingual Latin I with Euro)
|
484
|
+
# 035CH = 860 = IBM PC CP-860 (Portuguese)
|
485
|
+
# 035DH = 861 = IBM PC CP-861 (Icelandic)
|
486
|
+
# 035EH = 862 = IBM PC CP-862 (Hebrew)
|
487
|
+
# 035FH = 863 = IBM PC CP-863 (Canadian (French))
|
488
|
+
# 0360H = 864 = IBM PC CP-864 (Arabic)
|
489
|
+
# 0361H = 865 = IBM PC CP-865 (Nordic)
|
490
|
+
# 0362H = 866 = IBM PC CP-866 (Cyrillic (Russian))
|
491
|
+
# 0365H = 869 = IBM PC CP-869 (Greek (Modern))
|
492
|
+
# 036AH = 874 = Windows CP-874 (Thai)
|
493
|
+
# 03A4H = 932 = Windows CP-932 (Japanese Shift-JIS)
|
494
|
+
# 03A8H = 936 = Windows CP-936 (Chinese Simplified GBK)
|
495
|
+
# 03B5H = 949 = Windows CP-949 (Korean (Wansung))
|
496
|
+
# 03B6H = 950 = Windows CP-950 (Chinese Traditional BIG5)
|
497
|
+
# 04B0H = 1200 = UTF-16 (BIFF8)
|
498
|
+
# 04E2H = 1250 = Windows CP-1250 (Latin II) (Central European)
|
499
|
+
# 04E3H = 1251 = Windows CP-1251 (Cyrillic)
|
500
|
+
# 04E4H = 1252 = Windows CP-1252 (Latin I) (BIFF4-BIFF7)
|
501
|
+
# 04E5H = 1253 = Windows CP-1253 (Greek)
|
502
|
+
# 04E6H = 1254 = Windows CP-1254 (Turkish)
|
503
|
+
# 04E7H = 1255 = Windows CP-1255 (Hebrew)
|
504
|
+
# 04E8H = 1256 = Windows CP-1256 (Arabic)
|
505
|
+
# 04E9H = 1257 = Windows CP-1257 (Baltic)
|
506
|
+
# 04EAH = 1258 = Windows CP-1258 (Vietnamese)
|
507
|
+
# 0551H = 1361 = Windows CP-1361 (Korean (Johab))
|
508
|
+
# 2710H = 10000 = Apple Roman
|
509
|
+
# 8000H = 32768 = Apple Roman
|
510
|
+
# 8001H = 32769 = Windows CP-1252 (Latin I) (BIFF2-BIFF3)
|
511
|
+
class CodepageBiff8Record < BiffRecord
|
512
|
+
RECORD_ID = 0x0042
|
513
|
+
UTF_16 = 0x04B0
|
514
|
+
|
515
|
+
def initialize
|
516
|
+
@record_data = [UTF_16].pack('v')
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
# Offset Size Contents
|
521
|
+
# 0 2 Horizontal position of the document window (in twips = 1/20 of a point)
|
522
|
+
# 2 2 Vertical position of the document window (in twips = 1/20 of a point)
|
523
|
+
# 4 2 Width of the document window (in twips = 1/20 of a point)
|
524
|
+
# 6 2 Height of the document window (in twips = 1/20 of a point)
|
525
|
+
# 8 2 Option flags:
|
526
|
+
# Bits Mask Contents
|
527
|
+
# 0 0001H 0 = Window is visible 1 = Window is hidden
|
528
|
+
# 1 0002H 0 = Window is open 1 = Window is minimised
|
529
|
+
# 3 0008H 0 = Horizontal scroll bar hidden 1 = Horizontal scroll bar visible
|
530
|
+
# 4 0010H 0 = Vertical scroll bar hidden 1 = Vertical scroll bar visible
|
531
|
+
# 5 0020H 0 = Worksheet tab bar hidden 1 = Worksheet tab bar visible
|
532
|
+
# 10 2 Index to active (displayed) worksheet
|
533
|
+
# 12 2 Index of first visible tab in the worksheet tab bar
|
534
|
+
# 14 2 Number of selected worksheets (highlighted in the worksheet tab bar)
|
535
|
+
# 16 2 Width of worksheet tab bar (in 1/1000 of window width). The remaining space is used by the
|
536
|
+
# horizontal scrollbar.
|
537
|
+
class Window1Record < BiffRecord
|
538
|
+
RECORD_ID = 0x003D
|
539
|
+
|
540
|
+
def initialize(hpos_twips, vpos_twips, width_twips, height_twips, flags, active_sheet, first_tab_index, selected_tabs, tab_width)
|
541
|
+
args = [hpos_twips, vpos_twips, width_twips, height_twips, flags, active_sheet, first_tab_index, selected_tabs, tab_width]
|
542
|
+
@record_data = args.pack('v9')
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
# WARNING
|
547
|
+
# The font with index 4 is omitted in all BIFF versions.
|
548
|
+
# This means the first four fonts have zero-based indexes, and
|
549
|
+
# the fifth font and all following fonts are referenced with one-based
|
550
|
+
# indexes.
|
551
|
+
#
|
552
|
+
# Offset Size Contents
|
553
|
+
# 0 2 Height of the font (in twips = 1/20 of a point)
|
554
|
+
# 2 2 Option flags:
|
555
|
+
# Bit Mask Contents
|
556
|
+
# 0 0001H 1 = Characters are bold (redundant, see below)
|
557
|
+
# 1 0002H 1 = Characters are italic
|
558
|
+
# 2 0004H 1 = Characters are underlined (redundant, see below)
|
559
|
+
# 3 0008H 1 = Characters are struck out
|
560
|
+
# 0010H 1 = Outline
|
561
|
+
# 0020H 1 = Shadow
|
562
|
+
# 4 2 Colour index
|
563
|
+
# 6 2 Font weight (100-1000).
|
564
|
+
# Standard values are 0190H (400) for normal text and 02BCH
|
565
|
+
# (700) for bold text.
|
566
|
+
# 8 2 Escapement type:
|
567
|
+
# 0000H = None
|
568
|
+
# 0001H = Superscript
|
569
|
+
# 0002H = Subscript
|
570
|
+
# 10 1 Underline type:
|
571
|
+
# 00H = None
|
572
|
+
# 01H = Single
|
573
|
+
# 21H = Single accounting
|
574
|
+
# 02H = Double
|
575
|
+
# 22H = Double accounting
|
576
|
+
# 11 1 Font family:
|
577
|
+
# 00H = None (unknown or don't care)
|
578
|
+
# 01H = Roman (variable width, serifed)
|
579
|
+
# 02H = Swiss (variable width, sans-serifed)
|
580
|
+
# 03H = Modern (fixed width, serifed or sans-serifed)
|
581
|
+
# 04H = Script (cursive)
|
582
|
+
# 05H = Decorative (specialised, i.e. Old English, Fraktur)
|
583
|
+
# 12 1 Character set:
|
584
|
+
# 00H = 0 = ANSI Latin
|
585
|
+
# 01H = 1 = System default
|
586
|
+
# 02H = 2 = Symbol
|
587
|
+
# 4DH = 77 = Apple Roman
|
588
|
+
# 80H = 128 = ANSI Japanese Shift-JIS
|
589
|
+
# 81H = 129 = ANSI Korean (Hangul)
|
590
|
+
# 82H = 130 = ANSI Korean (Johab)
|
591
|
+
# 86H = 134 = ANSI Chinese Simplified GBK
|
592
|
+
# 88H = 136 = ANSI Chinese Traditional BIG5
|
593
|
+
# A1H = 161 = ANSI Greek
|
594
|
+
# A2H = 162 = ANSI Turkish
|
595
|
+
# A3H = 163 = ANSI Vietnamese
|
596
|
+
# B1H = 177 = ANSI Hebrew
|
597
|
+
# B2H = 178 = ANSI Arabic
|
598
|
+
# BAH = 186 = ANSI Baltic
|
599
|
+
# CCH = 204 = ANSI Cyrillic
|
600
|
+
# DEH = 222 = ANSI Thai
|
601
|
+
# EEH = 238 = ANSI Latin II (Central European)
|
602
|
+
# FFH = 255 = OEM Latin I
|
603
|
+
# 13 1 Not used
|
604
|
+
# 14 var. Font name:
|
605
|
+
# BIFF5/BIFF7: Byte string, 8-bit string length
|
606
|
+
# BIFF8: Unicode string, 8-bit string length
|
607
|
+
# The boldness and underline flags are still set in the options field,
|
608
|
+
# but not used on reading the font. Font weight and underline type
|
609
|
+
# are specified in separate fields instead.
|
610
|
+
class FontRecord < BiffRecord
|
611
|
+
RECORD_ID = 0x0031
|
612
|
+
|
613
|
+
def initialize(height, options, colour_index, weight, escapement, underline, family, charset, name)
|
614
|
+
#TODO implement upack1 fully
|
615
|
+
args = [height, options, colour_index, weight, escapement,underline, family, charset, 0x00]
|
616
|
+
@record_data = args.pack('v5C4') + [name.length, 0].pack('CC') + name
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
# Record FORMAT, BIFF8:
|
621
|
+
# Offset Size Contents
|
622
|
+
# 0 2 Format index used in other records
|
623
|
+
# 2 var. Number format string (Unicode string, 16-bit string length)
|
624
|
+
#
|
625
|
+
# From BIFF5 on, the built-in number formats will be omitted. The built-in
|
626
|
+
# formats are dependent on the current regional settings of the operating
|
627
|
+
# system. The following table shows which number formats are used by default
|
628
|
+
# in a US-English environment. All indexes from 0 to 163 are reserved for
|
629
|
+
# built-in formats. The first user-defined format starts at 164.
|
630
|
+
#
|
631
|
+
# The built-in number formats, BIFF5-BIFF8
|
632
|
+
#
|
633
|
+
# Index Type Format string
|
634
|
+
# 0 General General
|
635
|
+
# 1 Decimal 0
|
636
|
+
# 2 Decimal 0.00
|
637
|
+
# 3 Decimal #,##0
|
638
|
+
# 4 Decimal #,##0.00
|
639
|
+
# 5 Currency "$"#,##0_);("$"#,##
|
640
|
+
# 6 Currency "$"#,##0_);[Red]("$"#,##
|
641
|
+
# 7 Currency "$"#,##0.00_);("$"#,##
|
642
|
+
# 8 Currency "$"#,##0.00_);[Red]("$"#,##
|
643
|
+
# 9 Percent 0%
|
644
|
+
# 10 Percent 0.00%
|
645
|
+
# 11 Scientific 0.00E+00
|
646
|
+
# 12 Fraction # ?/?
|
647
|
+
# 13 Fraction # ??/??
|
648
|
+
# 14 Date M/D/YY
|
649
|
+
# 15 Date D-MMM-YY
|
650
|
+
# 16 Date D-MMM
|
651
|
+
# 17 Date MMM-YY
|
652
|
+
# 18 Time h:mm AM/PM
|
653
|
+
# 19 Time h:mm:ss AM/PM
|
654
|
+
# 20 Time h:mm
|
655
|
+
# 21 Time h:mm:ss
|
656
|
+
# 22 Date/Time M/D/YY h:mm
|
657
|
+
# 37 Account _(#,##0_);(#,##0)
|
658
|
+
# 38 Account _(#,##0_);[Red](#,##0)
|
659
|
+
# 39 Account _(#,##0.00_);(#,##0.00)
|
660
|
+
# 40 Account _(#,##0.00_);[Red](#,##0.00)
|
661
|
+
# 41 Currency _("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)
|
662
|
+
# 42 Currency _(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)
|
663
|
+
# 43 Currency _("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)
|
664
|
+
# 44 Currency _(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)
|
665
|
+
# 45 Time mm:ss
|
666
|
+
# 46 Time [h]:mm:ss
|
667
|
+
# 47 Time mm:ss.0
|
668
|
+
# 48 Scientific ##0.0E+0
|
669
|
+
# 49 Text @
|
670
|
+
class NumberFormatRecord < BiffRecord
|
671
|
+
RECORD_ID = 0x041E
|
672
|
+
|
673
|
+
def initialize(index, format_string)
|
674
|
+
@record_data = [index].pack('v') + mock_unicode_string(format_string)
|
675
|
+
end
|
676
|
+
end
|
677
|
+
|
678
|
+
# XF Substructures
|
679
|
+
# -------------------------------------------------------------------------
|
680
|
+
# XF_TYPE_PROT XF Type and Cell Protection (3 Bits), BIFF3-BIFF8
|
681
|
+
# These 3 bits are part of a specific data byte.
|
682
|
+
# Bit Mask Contents
|
683
|
+
# 0 01H 1 = Cell is locked
|
684
|
+
# 1 02H 1 = Formula is hidden
|
685
|
+
# 2 04H 0 = Cell XF; 1 = Style XF
|
686
|
+
#
|
687
|
+
# XF_USED_ATTRIB Attributes Used from Parent Style XF (6 Bits),
|
688
|
+
# BIFF3-BIFF8 Each bit describes the validity of a specific group
|
689
|
+
# of attributes. In cell XFs a cleared bit means the attributes of the
|
690
|
+
# parent style XF are used (but only if the attributes are valid there),
|
691
|
+
# a set bit means the attributes of this XF are used. In style XFs
|
692
|
+
# a cleared bit means the attribute setting is valid, a set bit means the
|
693
|
+
# attribute should be ignored.
|
694
|
+
# Bit Mask Contents
|
695
|
+
# 0 01H Flag for number format
|
696
|
+
# 1 02H Flag for font
|
697
|
+
# 2 04H Flag for horizontal and vertical alignment, text wrap, indentation, orientation, rotation, and
|
698
|
+
# text direction
|
699
|
+
# 3 08H Flag for border lines
|
700
|
+
# 4 10H Flag for background area style
|
701
|
+
# 5 20H Flag for cell protection (cell locked and formula hidden)
|
702
|
+
#
|
703
|
+
# XF_HOR_ALIGN Horizontal Alignment (3 Bits), BIFF2-BIFF8 The horizontal
|
704
|
+
# alignment consists of 3 bits and is part of a specific data byte.
|
705
|
+
# Value Horizontal alignment
|
706
|
+
# 00H General
|
707
|
+
# 01H Left
|
708
|
+
# 02H Centred
|
709
|
+
# 03H Right
|
710
|
+
# 04H Filled
|
711
|
+
# 05H Justified (BIFF4-BIFF8X)
|
712
|
+
# 06H Centred across selection (BIFF4-BIFF8X)
|
713
|
+
# 07H Distributed (BIFF8X)
|
714
|
+
#
|
715
|
+
# XF_VERT_ALIGN Vertical Alignment (2 or 3 Bits), BIFF4-BIFF8
|
716
|
+
# The vertical alignment consists of 2 bits (BIFF4) or 3 bits (BIFF5-BIFF8)
|
717
|
+
# and is part of a specific data byte. Vertical alignment is not available
|
718
|
+
# in BIFF2 and BIFF3.
|
719
|
+
# Value Vertical alignment
|
720
|
+
# 00H Top
|
721
|
+
# 01H Centred
|
722
|
+
# 02H Bottom
|
723
|
+
# 03H Justified (BIFF5-BIFF8X)
|
724
|
+
# 04H Distributed (BIFF8X)
|
725
|
+
#
|
726
|
+
# XF_ORIENTATION Text Orientation (2 Bits), BIFF4-BIFF7 In the BIFF
|
727
|
+
# versions BIFF4-BIFF7, text can be rotated in steps of 90 degrees
|
728
|
+
# or stacked. The orientation mode consists of 2 bits and is part of
|
729
|
+
# a specific data byte. In BIFF8 a rotation angle occurs instead of these
|
730
|
+
# flags.
|
731
|
+
# Value Text orientation
|
732
|
+
# 00H Not rotated
|
733
|
+
# 01H Letters are stacked top-to-bottom, but not rotated
|
734
|
+
# 02H Text is rotated 90 degrees counterclockwise
|
735
|
+
# 03H Text is rotated 90 degrees clockwise
|
736
|
+
#
|
737
|
+
# XF_ROTATION Text Rotation Angle (1 Byte), BIFF8
|
738
|
+
# Value Text rotation
|
739
|
+
# 0 Not rotated
|
740
|
+
# 1-90 1 to 90 degrees counterclockwise
|
741
|
+
# 91-180 1 to 90 degrees clockwise
|
742
|
+
# 255 Letters are stacked top-to-bottom, but not rotated
|
743
|
+
#
|
744
|
+
# XF_BORDER_34 Cell Border Style (4 Bytes), BIFF3-BIFF4 Cell borders
|
745
|
+
# contain a line style and a line colour for each line of the border.
|
746
|
+
# Bit Mask Contents
|
747
|
+
# 2-0 00000007H Top line style
|
748
|
+
# 7-3 000000F8H Colour index for top line colour
|
749
|
+
# 10-8 00000700H Left line style
|
750
|
+
# 15-11 0000F800H Colour index for left line colour
|
751
|
+
# 18-16 00070000H Bottom line style
|
752
|
+
# 23-19 00F80000H Colour index for bottom line colour
|
753
|
+
# 26-24 07000000H Right line style
|
754
|
+
# 31-27 F8000000H Colour index for right line colour
|
755
|
+
#
|
756
|
+
# XF_AREA_34 Cell Background Area Style (2 Bytes), BIFF3-BIFF4 A cell
|
757
|
+
# background area style contains an area pattern and a foreground and
|
758
|
+
# background colour.
|
759
|
+
# Bit Mask Contents
|
760
|
+
# 5-0 003FH Fill pattern
|
761
|
+
# 10-6 07C0H Colour index for pattern colour
|
762
|
+
# 15-11 F800H Colour index for pattern background
|
763
|
+
# ---------------------------------------------------------------------------------------------
|
764
|
+
# Record XF, BIFF8:
|
765
|
+
# Offset Size Contents
|
766
|
+
# 0 2 Index to FONT record
|
767
|
+
# 2 2 Index to FORMAT record
|
768
|
+
# 4 2 Bit Mask Contents
|
769
|
+
# 2-0 0007H XF_TYPE_PROT . XF type, cell protection (see above)
|
770
|
+
# 15-4 FFF0H Index to parent style XF (always FFFH in style XFs)
|
771
|
+
# 6 1 Bit Mask Contents
|
772
|
+
# 2-0 07H XF_HOR_ALIGN . Horizontal alignment (see above)
|
773
|
+
# 3 08H 1 = Text is wrapped at right border
|
774
|
+
# 6-4 70H XF_VERT_ALIGN . Vertical alignment (see above)
|
775
|
+
# 7 1 XF_ROTATION: Text rotation angle (see above)
|
776
|
+
# 8 1 Bit Mask Contents
|
777
|
+
# 3-0 0FH Indent level
|
778
|
+
# 4 10H 1 = Shrink content to fit into cell
|
779
|
+
# 5 merge
|
780
|
+
# 7-6 C0H Text direction (BIFF8X only)
|
781
|
+
# 00b = According to context
|
782
|
+
# 01b = Left-to-right
|
783
|
+
# 10b = Right-to-left
|
784
|
+
# 9 1 Bit Mask Contents
|
785
|
+
# 7-2 FCH XF_USED_ATTRIB . Used attributes (see above)
|
786
|
+
# 10 4 Cell border lines and background area:
|
787
|
+
# Bit Mask Contents
|
788
|
+
# 3-0 0000000FH Left line style
|
789
|
+
# 7-4 000000F0H Right line style
|
790
|
+
# 11-8 00000F00H Top line style
|
791
|
+
# 15-12 0000F000H Bottom line style
|
792
|
+
# 22-16 007F0000H Colour index for left line colour
|
793
|
+
# 29-23 3F800000H Colour index for right line colour
|
794
|
+
# 30 40000000H 1 = Diagonal line from top left to right bottom
|
795
|
+
# 31 80000000H 1 = Diagonal line from bottom left to right top
|
796
|
+
# 14 4 Bit Mask Contents
|
797
|
+
# 6-0 0000007FH Colour index for top line colour
|
798
|
+
# 13-7 00003F80H Colour index for bottom line colour
|
799
|
+
# 20-14 001FC000H Colour index for diagonal line colour
|
800
|
+
# 24-21 01E00000H Diagonal line style
|
801
|
+
# 31-26 FC000000H Fill pattern
|
802
|
+
# 18 2 Bit Mask Contents
|
803
|
+
# 6-0 007FH Colour index for pattern colour
|
804
|
+
# 13-7 3F80H Colour index for pattern background
|
805
|
+
class XFRecord < BiffRecord
|
806
|
+
RECORD_ID = 0x00E0
|
807
|
+
|
808
|
+
def initialize(xf, xf_type = 'cell')
|
809
|
+
font_xf_idx, fmt_str_xf_idx, alignment, borders, pattern, protection = xf
|
810
|
+
|
811
|
+
fnt = [font_xf_idx].pack('v')
|
812
|
+
fmt = [fmt_str_xf_idx].pack('v')
|
813
|
+
|
814
|
+
if xf_type === 'cell'
|
815
|
+
protection = ((protection.cell_locked & 0x01) << 0) | ((protection.formula_hidden & 0x01) << 1)
|
816
|
+
prt = [protection].pack('v')
|
817
|
+
else
|
818
|
+
prt = [0xFFF5].pack('v')
|
819
|
+
end
|
820
|
+
|
821
|
+
aln_value = ((alignment.horz & 0x07) << 0) | ((alignment.wrap & 0x01) << 3) | ((alignment.vert & 0x07) << 4)
|
822
|
+
aln = [aln_value].pack('C')
|
823
|
+
|
824
|
+
rot = [alignment.rota].pack('C')
|
825
|
+
|
826
|
+
txt_value = ((alignment.inde & 0x0F) << 0) | ((alignment.shri & 0x01) << 4) | ((alignment.merg & 0x01) << 5) | ((alignment.dire & 0x03) << 6)
|
827
|
+
txt = [txt_value].pack('C')
|
828
|
+
|
829
|
+
used_attr = (xf_type === 'cell') ? [0xF8].pack('C') : [0xF4].pack('C')
|
830
|
+
|
831
|
+
borders.left_colour = 0x00 if borders.left == Borders::NO_LINE
|
832
|
+
borders.right_colour = 0x00 if borders.right == Borders::NO_LINE
|
833
|
+
borders.top_colour = 0x00 if borders.top == Borders::NO_LINE
|
834
|
+
borders.bottom_colour = 0x00 if borders.bottom == Borders::NO_LINE
|
835
|
+
borders.diag_colour = 0x00 if borders.diag == Borders::NO_LINE
|
836
|
+
|
837
|
+
brd1_value = ((borders.left & 0x0F) << 0 ) |
|
838
|
+
((borders.right & 0x0F) << 4 ) |
|
839
|
+
((borders.top & 0x0F) << 8 ) |
|
840
|
+
((borders.bottom & 0x0F) << 12) |
|
841
|
+
((borders.left_colour & 0x7F) << 16) |
|
842
|
+
((borders.right_colour & 0x7F) << 23) |
|
843
|
+
((borders.need_diag1 & 0x01) << 30) |
|
844
|
+
((borders.need_diag2 & 0x01) << 31)
|
845
|
+
|
846
|
+
brd1 = [brd1_value].pack('V')
|
847
|
+
|
848
|
+
brd2_value = ((borders.top_colour & 0x7F) << 0 ) |
|
849
|
+
((borders.bottom_colour & 0x7F) << 7 ) |
|
850
|
+
((borders.diag_colour & 0x7F) << 14) |
|
851
|
+
((borders.diag & 0x0F) << 21) |
|
852
|
+
((pattern.pattern & 0x3F) << 26)
|
853
|
+
|
854
|
+
brd2 = [brd2_value].pack('V')
|
855
|
+
|
856
|
+
pat_value = ((pattern.pattern_fore_colour & 0x7F) << 0 ) |
|
857
|
+
((pattern.pattern_back_colour & 0x7F) << 7 )
|
858
|
+
pat = [pat_value].pack('v')
|
859
|
+
|
860
|
+
@record_data = fnt + fmt + prt + aln + rot + txt + used_attr + brd1 + brd2 + pat
|
861
|
+
end
|
862
|
+
end
|
863
|
+
|
864
|
+
# STYLE record for user-defined cell styles, BIFF3-BIFF8:
|
865
|
+
# Offset Size Contents
|
866
|
+
# 0 2 Bit Mask Contents
|
867
|
+
# 11-0 0FFFH Index to style XF record
|
868
|
+
# 15 8000H Always 0 for user-defined styles
|
869
|
+
# 2 var. BIFF2-BIFF7: Non-empty byte string, 8-bit string length
|
870
|
+
# BIFF8: Non-empty Unicode string, 16-bit string length
|
871
|
+
# STYLE record for built-in cell styles, BIFF3-BIFF8:
|
872
|
+
# Offset Size Contents
|
873
|
+
# 0 2 Bit Mask Contents
|
874
|
+
# 11-0 0FFFH Index to style XF record
|
875
|
+
# 15 8000H Always 1 for built-in styles
|
876
|
+
# 2 1 Identifier of the built-in cell style:
|
877
|
+
# 00H = Normal
|
878
|
+
# 01H = RowLevel_lv (see next field)
|
879
|
+
# 02H = ColLevel_lv (see next field)
|
880
|
+
# 03H = Comma
|
881
|
+
# 04H = Currency
|
882
|
+
# 05H = Percent
|
883
|
+
# 06H = Comma [0] (BIFF4-BIFF8)
|
884
|
+
# 07H = Currency [0] (BIFF4-BIFF8)
|
885
|
+
# 08H = Hyperlink (BIFF8)
|
886
|
+
# 09H = Followed Hyperlink (BIFF8)
|
887
|
+
# 3 1 Level for RowLevel or ColLevel style
|
888
|
+
# (zero-based, lv), FFH otherwise
|
889
|
+
# The RowLevel and ColLevel styles specify the formatting of subtotal
|
890
|
+
# cells in a specific outline level. The level is specified by the last
|
891
|
+
# field in the STYLE record. Valid values are 0-6 for the outline levels
|
892
|
+
# 1-7.
|
893
|
+
class StyleRecord < BiffRecord
|
894
|
+
RECORD_ID = 0x0293
|
895
|
+
|
896
|
+
def initialize
|
897
|
+
@record_data = [0x8000, 0x00, 0xFF].pack('vCC')
|
898
|
+
# TODO: implement user-defined styles???
|
899
|
+
end
|
900
|
+
end
|
901
|
+
|
902
|
+
# This record contains the definition of all user-defined colours
|
903
|
+
# available for cell and object formatting.
|
904
|
+
#
|
905
|
+
# Record PALETTE, BIFF3-BIFF8:
|
906
|
+
#
|
907
|
+
# Offset Size Contents
|
908
|
+
# 0 2 Number of following colours (nm). Contains 16 in BIFF3-BIFF4 and 56 in BIFF5-BIFF8.
|
909
|
+
# 2 4*nm List of nm RGB colours
|
910
|
+
#
|
911
|
+
# The following table shows how colour indexes are used in other records:
|
912
|
+
#
|
913
|
+
# Colour index Resulting colour or internal list index
|
914
|
+
# 00H Built-in Black (R = 00H, G = 00H, B = 00H)
|
915
|
+
# 01H Built-in White (R = FFH, G = FFH, B = FFH)
|
916
|
+
# 02H Built-in Red (R = FFH, G = 00H, B = 00H)
|
917
|
+
# 03H Built-in Green (R = 00H, G = FFH, B = 00H)
|
918
|
+
# 04H Built-in Blue (R = 00H, G = 00H, B = FFH)
|
919
|
+
# 05H Built-in Yellow (R = FFH, G = FFH, B = 00H)
|
920
|
+
# 06H Built-in Magenta (R = FFH, G = 00H, B = FFH)
|
921
|
+
# 07H Built-in Cyan (R = 00H, G = FFH, B = FFH)
|
922
|
+
# 08H First user-defined colour from the PALETTE record (entry 0 from record colour list)
|
923
|
+
# .........................
|
924
|
+
#
|
925
|
+
# 17H (BIFF3-BIFF4) Last user-defined colour from the PALETTE record (entry 15 or 55 from record colour list)
|
926
|
+
# 3FH (BIFF5-BIFF8)
|
927
|
+
#
|
928
|
+
# 18H (BIFF3-BIFF4) System window text colour for border lines (used in records XF, CF, and
|
929
|
+
# 40H (BIFF5-BIFF8) WINDOW2 (BIFF8 only))
|
930
|
+
#
|
931
|
+
# 19H (BIFF3-BIFF4) System window background colour for pattern background (used in records XF, and CF)
|
932
|
+
# 41H (BIFF5-BIFF8)
|
933
|
+
#
|
934
|
+
# 43H System face colour (dialogue background colour)
|
935
|
+
# 4DH System window text colour for chart border lines
|
936
|
+
# 4EH System window background colour for chart areas
|
937
|
+
# 4FH Automatic colour for chart border lines (seems to be always Black)
|
938
|
+
# 50H System ToolTip background colour (used in note objects)
|
939
|
+
# 51H System ToolTip text colour (used in note objects)
|
940
|
+
# 7FFFH System window text colour for fonts (used in records FONT, EFONT, and CF)
|
941
|
+
class PaletteRecord < BiffRecord
|
942
|
+
RECORD_ID = 0x0092
|
943
|
+
end
|
944
|
+
|
945
|
+
# This record is located in the workbook globals area and represents
|
946
|
+
# a sheet inside of the workbook. For each sheet a BOUNDSHEET record
|
947
|
+
# is written. It stores the sheet name and a stream offset to the BOF
|
948
|
+
# record within the workbook stream. The record is also known
|
949
|
+
# as BUNDLESHEET.
|
950
|
+
#
|
951
|
+
# Record BOUNDSHEET, BIFF5-BIFF8:
|
952
|
+
# Offset Size Contents
|
953
|
+
# 0 4 Absolute stream position of the BOF record of the sheet represented by this record. This
|
954
|
+
# field is never encrypted in protected files.
|
955
|
+
# 4 1 Visibility:
|
956
|
+
# 00H = Visible
|
957
|
+
# 01H = Hidden
|
958
|
+
# 02H = Strong hidden
|
959
|
+
# 5 1 Sheet type:
|
960
|
+
# 00H = Worksheet
|
961
|
+
# 02H = Chart
|
962
|
+
# 06H = Visual Basic module
|
963
|
+
# 6 var. Sheet name:
|
964
|
+
# BIFF5/BIFF7: Byte string, 8-bit string length
|
965
|
+
# BIFF8: Unicode string, 8-bit string length
|
966
|
+
class BoundSheetRecord < BiffRecord
|
967
|
+
RECORD_ID = 0x0085
|
968
|
+
|
969
|
+
def initialize(stream_pos, visibility, name)
|
970
|
+
@record_data = [stream_pos, visibility, 0x00].pack('VCC') + [name.length, 0].pack('CC') + name
|
971
|
+
end
|
972
|
+
end
|
973
|
+
|
974
|
+
# Whenever the content of a record exceeds the given limits (see table),
|
975
|
+
# the record must be split. Several CONTINUE records containing the
|
976
|
+
# additional data are added after the parent record.
|
977
|
+
#
|
978
|
+
# BIFF version Maximum data size of a record
|
979
|
+
# BIFF2-BIFF7 2080 bytes (2084 bytes including record header)
|
980
|
+
# BIFF8 8224 bytes (8228 bytes including record header) (0x2020)
|
981
|
+
#
|
982
|
+
# Record CONTINUE, BIFF2-BIFF8:
|
983
|
+
# Offset Size Contents
|
984
|
+
# 0 var. Data continuation of the previous record
|
985
|
+
#
|
986
|
+
# Unicode strings are split in a special way. At the beginning of each
|
987
|
+
# CONTINUE record the option flags byte is repeated. Only the character
|
988
|
+
# size flag will be set in this flags byte, the Rich-Text flag and the
|
989
|
+
# Far-East flag are set to zero. In each CONTINUE record it is possible
|
990
|
+
# that the character size changes from 8-bit characters to 16-bit
|
991
|
+
# characters and vice versa.
|
992
|
+
#
|
993
|
+
# Never a Unicode string is split until and including the first
|
994
|
+
# character. That means, all header fields (string length, option flags,
|
995
|
+
# optional Rich-Text size, and optional Far-East data size) and the first
|
996
|
+
# character of the string have to occur together in the leading record,
|
997
|
+
# or have to be moved completely into the CONTINUE record. Formatting
|
998
|
+
# runs cannot be split between their components (character index and FONT
|
999
|
+
# record index). If a string is split between two formatting runs, the
|
1000
|
+
# option flags field will not be repeated in the CONTINUE record.
|
1001
|
+
class ContinueRecord < BiffRecord
|
1002
|
+
RECORD_ID = 0x003C
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
# This record contains a list of all strings used anywhere in the
|
1006
|
+
# workbook. Each string occurs only once. The workbook uses indexes into
|
1007
|
+
# the list to reference the strings.
|
1008
|
+
#
|
1009
|
+
# Record SST, BIFF8:
|
1010
|
+
# Offset Size Contents
|
1011
|
+
# 0 4 Total number of strings in the workbook (see below)
|
1012
|
+
# 4 4 Number of following strings (nm)
|
1013
|
+
# 8 var. List of nm Unicode strings, 16-bit string length
|
1014
|
+
#
|
1015
|
+
# The first field of the SST record counts the total occurrence
|
1016
|
+
# of strings in the workbook. For instance, the string AAA is used
|
1017
|
+
# 3 times and the string BBB is used 2 times. The first field contains
|
1018
|
+
# 5 and the second field contains 2, followed by the two strings.
|
1019
|
+
class SSTRecord < BiffRecord
|
1020
|
+
RECORD_ID = 0x00FC
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
# This record occurs in conjunction with the SST record. It is used
|
1024
|
+
# by Excel to create a hash table with stream offsets to the SST record
|
1025
|
+
# to optimise string search operations. Excel may not shorten this record
|
1026
|
+
# if strings are deleted from the shared string table, so the last part
|
1027
|
+
# might contain invalid data. The stream indexes in this record divide
|
1028
|
+
# the SST into portions containing a constant number of strings.
|
1029
|
+
#
|
1030
|
+
# Record EXTSST, BIFF8:
|
1031
|
+
#
|
1032
|
+
# Offset Size Contents
|
1033
|
+
# 0 2 Number of strings in a portion, this number is >=8
|
1034
|
+
# 2 var. List of OFFSET structures for all portions. Each OFFSET contains the following data:
|
1035
|
+
# Offset Size Contents
|
1036
|
+
# 0 4 Absolute stream position of first string of the portion
|
1037
|
+
# 4 2 Position of first string of the portion inside of current record,
|
1038
|
+
# including record header. This counter restarts at zero, if the SST
|
1039
|
+
# record is continued with a CONTINUE record.
|
1040
|
+
# 6 2 Not used
|
1041
|
+
class ExtSSTRecord < BiffRecord
|
1042
|
+
RECORD_ID = 0x00FF
|
1043
|
+
|
1044
|
+
def initialize(sst_stream_pos, str_placement, portions_len)
|
1045
|
+
extsst = {}
|
1046
|
+
abs_stream_pos = sst_stream_pos
|
1047
|
+
str_counter = 0
|
1048
|
+
portion_counter = 0
|
1049
|
+
|
1050
|
+
while (str_counter < str_placement.length) do
|
1051
|
+
str_chunk_num, pos_in_chunk = str_placement[str_counter]
|
1052
|
+
if str_chunk_num != portion_counter
|
1053
|
+
portion_counter = str_chunk_num
|
1054
|
+
abs_stream_pos += portions_len[portion_counter-1]
|
1055
|
+
end
|
1056
|
+
str_stream_pos = abs_stream_pos + pos_in_chunk + 4 # header
|
1057
|
+
extsst[str_counter] = [pos_in_chunk, str_stream_pos]
|
1058
|
+
str_counter += 1
|
1059
|
+
end
|
1060
|
+
|
1061
|
+
exsst_str_count_delta = [8, str_placement.length*8/0x2000].max
|
1062
|
+
@record_data = [exsst_str_count_delta].pack('v')
|
1063
|
+
|
1064
|
+
str_counter = 0
|
1065
|
+
while (str_counter < str_placement.length) do
|
1066
|
+
@record_data += [extsst[str_counter][1], extsst[str_counter][0], 0].pack('Vvv')
|
1067
|
+
str_counter += exsst_str_count_delta
|
1068
|
+
end
|
1069
|
+
end
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
|
1073
|
+
# Record DIMENSIONS, BIFF8:
|
1074
|
+
#
|
1075
|
+
# Offset Size Contents
|
1076
|
+
# 0 4 Index to first used row
|
1077
|
+
# 4 4 Index to last used row, increased by 1
|
1078
|
+
# 8 2 Index to first used column
|
1079
|
+
# 10 2 Index to last used column, increased by 1
|
1080
|
+
# 12 2 Not used
|
1081
|
+
class DimensionsRecord < BiffRecord
|
1082
|
+
RECORD_ID = 0x0200
|
1083
|
+
|
1084
|
+
def initialize(first_used_row, last_used_row, first_used_col, last_used_col)
|
1085
|
+
@record_data = [first_used_row, last_used_row + 1, first_used_col, last_used_col + 1, 0x00].pack('V2v3')
|
1086
|
+
end
|
1087
|
+
end
|
1088
|
+
|
1089
|
+
|
1090
|
+
# Record WINDOW2, BIFF8:
|
1091
|
+
#
|
1092
|
+
# Offset Size Contents
|
1093
|
+
# 0 2 Option flags (see below)
|
1094
|
+
# 2 2 Index to first visible row
|
1095
|
+
# 4 2 Index to first visible column
|
1096
|
+
# 6 2 Colour index of grid line colour. Note that in BIFF2-BIFF7 an RGB colour is
|
1097
|
+
# written instead.
|
1098
|
+
# 8 2 Not used
|
1099
|
+
# 10 2 Cached magnification factor in page break preview (in percent); 0 = Default (60%)
|
1100
|
+
# 12 2 Cached magnification factor in normal view (in percent); 0 = Default (100%)
|
1101
|
+
# 14 4 Not used
|
1102
|
+
#
|
1103
|
+
# In BIFF8 this record stores used magnification factors for page break
|
1104
|
+
# preview and normal view. These values are used to restore the
|
1105
|
+
# magnification, when the view is changed. The real magnification of the
|
1106
|
+
# currently active view is stored in the SCL record. The type of the
|
1107
|
+
# active view is stored in the option flags field (see below).
|
1108
|
+
#
|
1109
|
+
# 0 0001H 0 = Show formula results 1 = Show formulas
|
1110
|
+
# 1 0002H 0 = Do not show grid lines 1 = Show grid lines
|
1111
|
+
# 2 0004H 0 = Do not show sheet headers 1 = Show sheet headers
|
1112
|
+
# 3 0008H 0 = Panes are not frozen 1 = Panes are frozen (freeze)
|
1113
|
+
# 4 0010H 0 = Show zero values as empty cells 1 = Show zero values
|
1114
|
+
# 5 0020H 0 = Manual grid line colour 1 = Automatic grid line colour
|
1115
|
+
# 6 0040H 0 = Columns from left to right 1 = Columns from right to left
|
1116
|
+
# 7 0080H 0 = Do not show outline symbols 1 = Show outline symbols
|
1117
|
+
# 8 0100H 0 = Keep splits if pane freeze is removed 1 = Remove splits if pane freeze is removed
|
1118
|
+
# 9 0200H 0 = Sheet not selected 1 = Sheet selected (BIFF5-BIFF8)
|
1119
|
+
# 10 0400H 0 = Sheet not visible 1 = Sheet visible (BIFF5-BIFF8)
|
1120
|
+
# 11 0800H 0 = Show in normal view 1 = Show in page break preview (BIFF8)
|
1121
|
+
#
|
1122
|
+
# The freeze flag specifies, if a following PANE record describes unfrozen or frozen panes.
|
1123
|
+
#
|
1124
|
+
# *** This class appends the optional SCL record ***
|
1125
|
+
#
|
1126
|
+
# Record SCL, BIFF4-BIFF8:
|
1127
|
+
#
|
1128
|
+
# This record stores the magnification of the active view of the current worksheet.
|
1129
|
+
# In BIFF8 this can be either the normal view or the page break preview.
|
1130
|
+
# This is determined in the WINDOW2 record. The SCL record is part of the
|
1131
|
+
# Sheet View Settings Block.
|
1132
|
+
#
|
1133
|
+
# Offset Size Contents
|
1134
|
+
# 0 2 Numerator of the view magnification fraction (num)
|
1135
|
+
# 2 2 Denumerator [denominator] of the view magnification fraction (den)
|
1136
|
+
# The magnification is stored as reduced fraction. The magnification results from num/den.
|
1137
|
+
#
|
1138
|
+
# SJM note: Excel expresses (e.g.) 25% in reduced form i.e. 1/4. Reason unknown. This code
|
1139
|
+
# writes 25/100, and Excel is happy with that.
|
1140
|
+
class Window2Record < BiffRecord
|
1141
|
+
RECORD_ID = 0x023E
|
1142
|
+
|
1143
|
+
def initialize(options, first_visible_row, first_visible_col, grid_colour, preview_magn, normal_magn, scl_magn)
|
1144
|
+
scl_rec = ''
|
1145
|
+
scl_rec = [0x00A0, 4, scl_magn, 100].pack('v4') unless scl_magn == 0
|
1146
|
+
|
1147
|
+
args = [options, first_visible_row, first_visible_col, grid_colour, 0x00, preview_magn, normal_magn, 0x00]
|
1148
|
+
@record_data = args.pack('v7V') + scl_rec
|
1149
|
+
end
|
1150
|
+
end
|
1151
|
+
|
1152
|
+
# This record stores the position of window panes. It is part of the Sheet
|
1153
|
+
# View Settings Block. If the sheet does not contain any splits, this
|
1154
|
+
# record will not occur.
|
1155
|
+
# A sheet can be split in two different ways, with unfrozen panes or with
|
1156
|
+
# frozen panes. A flag in the WINDOW2 record specifies, if the panes are
|
1157
|
+
# frozen, which affects the contents of this record.
|
1158
|
+
#
|
1159
|
+
# Record PANE, BIFF2-BIFF8:
|
1160
|
+
# Offset Size Contents
|
1161
|
+
# 0 2 Position of the vertical split
|
1162
|
+
# (px, 0 = No vertical split):
|
1163
|
+
# Unfrozen pane: Width of the left pane(s)
|
1164
|
+
# (in twips = 1/20 of a point)
|
1165
|
+
# Frozen pane: Number of visible
|
1166
|
+
# columns in left pane(s)
|
1167
|
+
# 2 2 Position of the horizontal split
|
1168
|
+
# (py, 0 = No horizontal split):
|
1169
|
+
# Unfrozen pane: Height of the top pane(s)
|
1170
|
+
# (in twips = 1/20 of a point)
|
1171
|
+
# Frozen pane: Number of visible
|
1172
|
+
# rows in top pane(s)
|
1173
|
+
# 4 2 Index to first visible row
|
1174
|
+
# in bottom pane(s)
|
1175
|
+
# 6 2 Index to first visible column
|
1176
|
+
# in right pane(s)
|
1177
|
+
# 8 1 Identifier of pane with active
|
1178
|
+
# cell cursor
|
1179
|
+
# [9] 1 Not used (BIFF5-BIFF8 only, not written
|
1180
|
+
# in BIFF2-BIFF4)
|
1181
|
+
#
|
1182
|
+
# If the panes are frozen, pane†0 is always active, regardless
|
1183
|
+
# of the cursor position. The correct identifiers for all possible
|
1184
|
+
# combinations of visible panes are shown in the following pictures.
|
1185
|
+
#
|
1186
|
+
# px = 0, py = 0 px = 0, py > 0
|
1187
|
+
# -------------------------- ------------|-------------
|
1188
|
+
# | | | |
|
1189
|
+
# | | | 3 |
|
1190
|
+
# | | | |
|
1191
|
+
# - 3 - --------------------------
|
1192
|
+
# | | | |
|
1193
|
+
# | | | 2 |
|
1194
|
+
# | | | |
|
1195
|
+
# -------------------------- ------------|-------------
|
1196
|
+
#
|
1197
|
+
# px > 0, py = 0 px > 0, py > 0
|
1198
|
+
# ------------|------------- ------------|-------------
|
1199
|
+
# | | | | | |
|
1200
|
+
# | | | | 3 | 2 |
|
1201
|
+
# | | | | | |
|
1202
|
+
# - 3 | 1 - --------------------------
|
1203
|
+
# | | | | | |
|
1204
|
+
# | | | | 1 | 0 |
|
1205
|
+
# | | | | | |
|
1206
|
+
# ------------|------------- ------------|-------------
|
1207
|
+
class PanesRecord < BiffRecord
|
1208
|
+
RECORD_ID = 0x0041
|
1209
|
+
|
1210
|
+
def initialize(px, py, first_row_bottom, first_col_right, active_pane)
|
1211
|
+
@record_data = [px, py, first_row_bottom, first_col_right, active_pane].pack('v5')
|
1212
|
+
end
|
1213
|
+
end
|
1214
|
+
|
1215
|
+
|
1216
|
+
# This record contains the properties of a single row in a sheet. Rows
|
1217
|
+
# and cells in a sheet are divided into blocks of 32 rows.
|
1218
|
+
#
|
1219
|
+
# Record ROW, BIFF3-BIFF8:
|
1220
|
+
#
|
1221
|
+
# Offset Size Contents
|
1222
|
+
# 0 2 Index of this row
|
1223
|
+
# 2 2 Index to column of the first cell which is described by a cell record
|
1224
|
+
# 4 2 Index to column of the last cell which is described by a cell record,
|
1225
|
+
# increased by 1
|
1226
|
+
# 6 2 Bit Mask Contents
|
1227
|
+
# 14-0 7FFFH Height of the row, in twips = 1/20 of a point
|
1228
|
+
# 15 8000H 0 = Row has custom height; 1 = Row has default height
|
1229
|
+
# 8 2 Not used
|
1230
|
+
# 10 2 In BIFF3-BIFF4 this field contains a relative offset
|
1231
|
+
# to calculate stream position of the first cell record
|
1232
|
+
# for this row. In BIFF5-BIFF8 this field is not used
|
1233
|
+
# anymore, but the DBCELL record instead.
|
1234
|
+
# 12 4 Option flags and default row formatting:
|
1235
|
+
# Bit Mask Contents
|
1236
|
+
# 2-0 00000007H Outline level of the row
|
1237
|
+
# 4 00000010H 1 = Outline group starts or ends here (depending
|
1238
|
+
# on where the outline buttons are located,
|
1239
|
+
# see WSBOOL record), and is collapsed
|
1240
|
+
# 5 00000020H 1 = Row is hidden (manually, or by a filter or outline group)
|
1241
|
+
# 6 00000040H 1 = Row height and default font height do not match
|
1242
|
+
# 7 00000080H 1 = Row has explicit default format (fl)
|
1243
|
+
# 8 00000100H Always 1
|
1244
|
+
# 27-16 0FFF0000H If fl=1: Index to default XF record
|
1245
|
+
# 28 10000000H 1 = Additional space above the row. This flag is set,
|
1246
|
+
# if the upper border of at least one cell in this row
|
1247
|
+
# or if the lower border of at least one cell in the row
|
1248
|
+
# above is formatted with a thick line style.
|
1249
|
+
# Thin and medium line styles are not taken into account.
|
1250
|
+
# 29 20000000H 1 = Additional space below the row. This flag is set,
|
1251
|
+
# if the lower border of at least one cell in this row
|
1252
|
+
# or if the upper border of at least one cell in the row
|
1253
|
+
# below is formatted with a medium or thick line style.
|
1254
|
+
# Thin line styles are not taken into account.
|
1255
|
+
class RowRecord < BiffRecord
|
1256
|
+
RECORD_ID = 0x0208
|
1257
|
+
|
1258
|
+
def initialize(index, first_col, last_col, height_options, options)
|
1259
|
+
@record_data = [index, first_col, last_col + 1, height_options, 0x00, 0x00, options].pack('v6V')
|
1260
|
+
end
|
1261
|
+
end
|
1262
|
+
|
1263
|
+
# This record represents a cell that contains a string. It replaces the
|
1264
|
+
# LABEL record and RSTRING record used in BIFF2-BIFF7.
|
1265
|
+
class LabelSSTRecord < BiffRecord
|
1266
|
+
RECORD_ID = 0x00FD
|
1267
|
+
|
1268
|
+
def initialize(row, col, xf_idx, sst_idx)
|
1269
|
+
@record_data = [row, col, xf_idx, sst_idx].pack('v3V')
|
1270
|
+
end
|
1271
|
+
end
|
1272
|
+
|
1273
|
+
# This record contains all merged cell ranges of the current sheet.
|
1274
|
+
#
|
1275
|
+
# Record MERGEDCELLS, BIFF8:
|
1276
|
+
#
|
1277
|
+
# Offset Size Contents
|
1278
|
+
# 0 var. Cell range address list with all merged ranges
|
1279
|
+
#
|
1280
|
+
# ------------------------------------------------------------------
|
1281
|
+
#
|
1282
|
+
# A cell range address list consists of a field with the number of ranges
|
1283
|
+
# and the list of the range addresses.
|
1284
|
+
#
|
1285
|
+
# Cell range address list, BIFF8:
|
1286
|
+
#
|
1287
|
+
# Offset Size Contents
|
1288
|
+
# 0 2 Number of following cell range addresses (nm)
|
1289
|
+
# 2 8*nm List of nm cell range addresses
|
1290
|
+
#
|
1291
|
+
# ---------------------------------------------------------------------
|
1292
|
+
# Cell range address, BIFF8:
|
1293
|
+
#
|
1294
|
+
# Offset Size Contents
|
1295
|
+
# 0 2 Index to first row
|
1296
|
+
# 2 2 Index to last row
|
1297
|
+
# 4 2 Index to first column
|
1298
|
+
# 6 2 Index to last column
|
1299
|
+
class MergedCellsRecord < BiffRecord
|
1300
|
+
RECORD_ID = 0x00E5
|
1301
|
+
|
1302
|
+
def initialize(merged_list)
|
1303
|
+
@record_data = ''
|
1304
|
+
|
1305
|
+
i = merged_list.length - 1
|
1306
|
+
while i >= 0 do
|
1307
|
+
j = 0
|
1308
|
+
merged = ''
|
1309
|
+
while (i >= 0) && (j < 0x403) do
|
1310
|
+
r1, r2, c1, c2 = merged_list[i]
|
1311
|
+
merged += [r1, r2, c1, c2].pack('v4')
|
1312
|
+
i -= 1
|
1313
|
+
j += 1
|
1314
|
+
end
|
1315
|
+
@record_data += [RECORD_ID, merged.length + 2, j].pack('v3') + merged
|
1316
|
+
end
|
1317
|
+
end
|
1318
|
+
|
1319
|
+
# for some reason Excel doesn't use CONTINUE
|
1320
|
+
def to_biff
|
1321
|
+
@record_data
|
1322
|
+
end
|
1323
|
+
end
|
1324
|
+
|
1325
|
+
# This record represents a cell range of empty cells. All cells are
|
1326
|
+
# located in the same row.
|
1327
|
+
#
|
1328
|
+
# Record MULBLANK, BIFF5-BIFF8:
|
1329
|
+
#
|
1330
|
+
# Offset Size Contents
|
1331
|
+
# 0 2 Index to row
|
1332
|
+
# 2 2 Index to first column (fc)
|
1333
|
+
# 4 2*nc List of nc=lc-fc+1 16-bit indexes to XF records
|
1334
|
+
# 4+2*nc 2 Index to last column (lc)
|
1335
|
+
class MulBlankRecord < BiffRecord
|
1336
|
+
RECORD_ID = 0x00BE
|
1337
|
+
|
1338
|
+
def initialize(row, first_col, last_col, xf_index)
|
1339
|
+
blanks_count = last_col-first_col+1
|
1340
|
+
blanks = ([xf_index]*blanks_count).pack('v*')
|
1341
|
+
@record_data = [row, first_col].pack('v2') + blanks + [last_col].pack('v')
|
1342
|
+
end
|
1343
|
+
end
|
1344
|
+
|
1345
|
+
# This record represents an empty cell.
|
1346
|
+
#
|
1347
|
+
# Record BLANK, BIFF5-BIFF8:
|
1348
|
+
#
|
1349
|
+
# Offset Size Contents
|
1350
|
+
# 0 2 Index to row
|
1351
|
+
# 2 2 Index to first column (fc)
|
1352
|
+
# 4 2 indexes to XF record
|
1353
|
+
class BlankRecord < BiffRecord
|
1354
|
+
RECORD_ID = 0x0201
|
1355
|
+
|
1356
|
+
def initialize(row, col, xf_index)
|
1357
|
+
@record_data = [row, col, xf_index].pack('v3')
|
1358
|
+
end
|
1359
|
+
end
|
1360
|
+
|
1361
|
+
# This record represents a cell that contains an RK value (encoded integer or
|
1362
|
+
# floating-point value). If a floating-point value cannot be encoded to an RK value,
|
1363
|
+
# a NUMBER record will be written.
|
1364
|
+
class RKRecord < BiffRecord
|
1365
|
+
RECORD_ID = 0x027E
|
1366
|
+
|
1367
|
+
def initialize(row, col, xf_index, rk_encoded)
|
1368
|
+
@record_data = [row, col, xf_index, rk_encoded].pack('v3V')
|
1369
|
+
end
|
1370
|
+
end
|
1371
|
+
|
1372
|
+
# This record represents a cell that contains an IEEE-754 floating-point value.
|
1373
|
+
class NumberRecord < BiffRecord
|
1374
|
+
RECORD_ID = 0x0203
|
1375
|
+
|
1376
|
+
def initialize(row, col, xf_index, number)
|
1377
|
+
@record_data = [row, col, xf_index, number].pack('v3E')
|
1378
|
+
end
|
1379
|
+
end
|
1380
|
+
|
1381
|
+
# This record represents a cell that contains a boolean or error value. '<3HBB'
|
1382
|
+
class BoolErrRecord < BiffRecord
|
1383
|
+
RECORD_ID = 0x0205
|
1384
|
+
|
1385
|
+
def initialize(row, col, xf_index, number, is_error)
|
1386
|
+
@record_data = [row, col, xf_index, number, is_error].pack('v3C2')
|
1387
|
+
end
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
# Offset Size Contents
|
1391
|
+
# 0 2 Index to row
|
1392
|
+
# 2 2 Index to column
|
1393
|
+
# 4 2 Index to XF record
|
1394
|
+
# 6 8 Result of the formula
|
1395
|
+
# 14 2 Option flags:
|
1396
|
+
# Bit Mask Contents
|
1397
|
+
# 0 0001H 1 = Recalculate always
|
1398
|
+
# 1 0002H 1 = Calculate on open
|
1399
|
+
# 3 0008H 1 = Part of a shared formula
|
1400
|
+
# 16 4 Not used
|
1401
|
+
# 20 var. Formula data (RPN token array)
|
1402
|
+
### @export "formula-record"
|
1403
|
+
class FormulaRecord < BiffRecord
|
1404
|
+
RECORD_ID = 0x0006
|
1405
|
+
|
1406
|
+
def initialize(row, col, xf_index, rpn, calc_flags = 0)
|
1407
|
+
@record_data = [row, col, xf_index, 0xFFFF000000000003, calc_flags & 0x03, 0].pack('v3QvV') + rpn
|
1408
|
+
end
|
1409
|
+
end
|
1410
|
+
### @end
|
1411
|
+
|
1412
|
+
# This record contains information about the layout of outline symbols.
|
1413
|
+
#
|
1414
|
+
# Record GUTS, BIFF3-BIFF8:
|
1415
|
+
#
|
1416
|
+
# Offset Size Contents
|
1417
|
+
# 0 2 Width of the area to display row outlines (left of the sheet), in pixel
|
1418
|
+
# 2 2 Height of the area to display column outlines (above the sheet), in pixel
|
1419
|
+
# 4 2 Number of visible row outline levels (used row levels + 1; or 0, if not used)
|
1420
|
+
# 6 2 Number of visible column outline levels (used column levels + 1; or 0, if not used)
|
1421
|
+
class GutsRecord < BiffRecord
|
1422
|
+
RECORD_ID = 0x0080
|
1423
|
+
|
1424
|
+
def initialize(row_gut_width, col_gut_height, row_visible_levels, col_visible_levels)
|
1425
|
+
@record_data = [row_gut_width, col_gut_height, row_visible_levels, col_visible_levels].pack('v4')
|
1426
|
+
end
|
1427
|
+
end
|
1428
|
+
|
1429
|
+
# This record stores a 16 bit value with Boolean options for the current
|
1430
|
+
# sheet. From BIFF5 on the "Save external linked values" option is moved
|
1431
|
+
# to the record BOOKBOOL.
|
1432
|
+
#
|
1433
|
+
# Option flags of record WSBOOL, BIFF3-BIFF8:
|
1434
|
+
#
|
1435
|
+
# Bit Mask Contents
|
1436
|
+
# 0 0001H 0 = Do not show automatic page breaks
|
1437
|
+
# 1 = Show automatic page breaks
|
1438
|
+
# 4 0010H 0 = Standard sheet
|
1439
|
+
# 1 = Dialogue sheet (BIFF5-BIFF8)
|
1440
|
+
# 5 0020H 0 = No automatic styles in outlines
|
1441
|
+
# 1 = Apply automatic styles to outlines
|
1442
|
+
# 6 0040H 0 = Outline buttons above outline group
|
1443
|
+
# 1 = Outline buttons below outline group
|
1444
|
+
# 7 0080H 0 = Outline buttons left of outline group
|
1445
|
+
# 1 = Outline buttons right of outline group
|
1446
|
+
# 8 0100H 0 = Scale printout in percent
|
1447
|
+
# 1 = Fit printout to number of pages
|
1448
|
+
# 9 0200H 0 = Save external linked values (BIFF3?BIFF4 only)
|
1449
|
+
# 1 = Do not save external linked values (BIFF3?BIFF4 only)
|
1450
|
+
# 10 0400H 0 = Do not show row outline symbols
|
1451
|
+
# 1 = Show row outline symbols
|
1452
|
+
# 11 0800H 0 = Do not show column outline symbols
|
1453
|
+
# 1 = Show column outline symbols
|
1454
|
+
# 13-12 3000H These flags specify the arrangement of windows.
|
1455
|
+
# They are stored in BIFF4 only.
|
1456
|
+
# 00 = Arrange windows tiled
|
1457
|
+
# 01 = Arrange windows horizontal
|
1458
|
+
# 10 = Arrange windows vertical112 = Arrange windows cascaded
|
1459
|
+
# The following flags are valid for BIFF4-BIFF8 only:
|
1460
|
+
# 14 4000H 0 = Standard expression evaluation
|
1461
|
+
# 1 = Alternative expression evaluation
|
1462
|
+
# 15 8000H 0 = Standard formula entries
|
1463
|
+
# 1 = Alternative formula entries
|
1464
|
+
class WSBoolRecord < BiffRecord
|
1465
|
+
RECORD_ID = 0x0081
|
1466
|
+
|
1467
|
+
def initialize(options)
|
1468
|
+
@record_data = [options].pack('v')
|
1469
|
+
end
|
1470
|
+
end
|
1471
|
+
|
1472
|
+
# This record specifies the width for a given range of columns.
|
1473
|
+
# If a column does not have a corresponding COLINFO record,
|
1474
|
+
# the width specified in the record STANDARDWIDTH is used. If
|
1475
|
+
# this record is also not present, the contents of the record
|
1476
|
+
# DEFCOLWIDTH is used instead.
|
1477
|
+
# This record also specifies a default XF record to use for
|
1478
|
+
# cells in the columns that are not described by any cell record
|
1479
|
+
# (which contain the XF index for that cell). Additionally,
|
1480
|
+
# the option flags field contains hidden, outline, and collapsed
|
1481
|
+
# options applied at the columns.
|
1482
|
+
#
|
1483
|
+
# Record COLINFO, BIFF3-BIFF8:
|
1484
|
+
#
|
1485
|
+
# Offset Size Contents
|
1486
|
+
# 0 2 Index to first column in the range
|
1487
|
+
# 2 2 Index to last column in the range
|
1488
|
+
# 4 2 Width of the columns in 1/256 of the width of the zero character, using default font
|
1489
|
+
# (first FONT record in the file)
|
1490
|
+
# 6 2 Index to XF record for default column formatting
|
1491
|
+
# 8 2 Option flags:
|
1492
|
+
# Bits Mask Contents
|
1493
|
+
# 0 0001H 1 = Columns are hidden
|
1494
|
+
# 10-8 0700H Outline level of the columns (0 = no outline)
|
1495
|
+
# 12 1000H 1 = Columns are collapsed
|
1496
|
+
# 10 2 Not used
|
1497
|
+
class ColInfoRecord < BiffRecord
|
1498
|
+
RECORD_ID = 0x007D
|
1499
|
+
|
1500
|
+
def initialize(first_col, last_col, width, xf_index, options)
|
1501
|
+
@record_data = [first_col, last_col, width, xf_index, options, 0].pack('v6')
|
1502
|
+
end
|
1503
|
+
end
|
1504
|
+
|
1505
|
+
# This record is part of the Calculation Settings Block.
|
1506
|
+
# It specifies whether to calculate formulas manually,
|
1507
|
+
# automatically or automatically except for multiple table operations.
|
1508
|
+
#
|
1509
|
+
# Record CALCMODE, BIFF2-BIFF8:
|
1510
|
+
#
|
1511
|
+
# Offset Size Contents
|
1512
|
+
# 0 2 FFFFH = automatic except for multiple table operations
|
1513
|
+
# 0000H = manually
|
1514
|
+
# 0001H = automatically (default)
|
1515
|
+
class CalcModeRecord < BiffRecord
|
1516
|
+
RECORD_ID = 0x000D
|
1517
|
+
|
1518
|
+
def initialize(calc_mode)
|
1519
|
+
@record_data = [calc_mode].pack('v')
|
1520
|
+
end
|
1521
|
+
end
|
1522
|
+
|
1523
|
+
# This record is part of the Calculation Settings Block. It specifies the maximum
|
1524
|
+
# number of times the formulas should be iteratively calculated. This is a fail-safe
|
1525
|
+
# against mutually recursive formulas locking up a spreadsheet application.
|
1526
|
+
#
|
1527
|
+
# Record CALCCOUNT, BIFF2-BIFF8:
|
1528
|
+
#
|
1529
|
+
# Offset Size Contents
|
1530
|
+
# 0 2 Maximum number of iterations allowed in circular references
|
1531
|
+
class CalcCountRecord < BiffRecord
|
1532
|
+
RECORD_ID = 0x000C
|
1533
|
+
|
1534
|
+
def initialize(calc_count)
|
1535
|
+
@record_data = [calc_count].pack('v')
|
1536
|
+
end
|
1537
|
+
end
|
1538
|
+
|
1539
|
+
# This record is part of the Calculation Settings Block.
|
1540
|
+
# It stores which method is used to show cell addresses in formulas.
|
1541
|
+
# The ìRCî mode uses numeric indexes for rows and columns,
|
1542
|
+
# i.e. ìR(1)C(-1)î, or ìR1C1:R2C2î.
|
1543
|
+
# The ìA1î mode uses characters for columns and numbers for rows,
|
1544
|
+
# i.e. ìB1î, or ì$A$1:$B$2î.
|
1545
|
+
#
|
1546
|
+
# Record REFMODE, BIFF2-BIFF8:
|
1547
|
+
#
|
1548
|
+
# Offset Size Contents
|
1549
|
+
# 0 2 0 = RC mode; 1 = A1 mode
|
1550
|
+
class RefModeRecord < BiffRecord
|
1551
|
+
RECORD_ID = 0x00F
|
1552
|
+
|
1553
|
+
def initialize(ref_mode)
|
1554
|
+
@record_data = [ref_mode].pack('v')
|
1555
|
+
end
|
1556
|
+
end
|
1557
|
+
|
1558
|
+
# This record is part of the Calculation Settings Block.
|
1559
|
+
# It stores if iterations are allowed while calculating recursive formulas.
|
1560
|
+
#
|
1561
|
+
# Record ITERATION, BIFF2-BIFF8:
|
1562
|
+
#
|
1563
|
+
# Offset Size Contents
|
1564
|
+
# 0 2 0 = Iterations off; 1 = Iterations on
|
1565
|
+
class IterationRecord < BiffRecord
|
1566
|
+
RECORD_ID = 0x011
|
1567
|
+
|
1568
|
+
def initialize(iterations_on)
|
1569
|
+
@record_data = [iterations_on].pack('v')
|
1570
|
+
end
|
1571
|
+
end
|
1572
|
+
|
1573
|
+
# This record is part of the Calculation Settings Block.
|
1574
|
+
# It stores the maximum change of the result to exit an iteration.
|
1575
|
+
#
|
1576
|
+
# Record DELTA, BIFF2-BIFF8:
|
1577
|
+
#
|
1578
|
+
# Offset Size Contents
|
1579
|
+
# 0 8 Maximum change in iteration
|
1580
|
+
# (IEEE 754 floating-point value,
|
1581
|
+
# 64bit double precision)
|
1582
|
+
class DeltaRecord < BiffRecord
|
1583
|
+
RECORD_ID = 0x010
|
1584
|
+
|
1585
|
+
def initialize(delta)
|
1586
|
+
@record_data = [delta].pack('E')
|
1587
|
+
end
|
1588
|
+
end
|
1589
|
+
|
1590
|
+
# This record is part of the Calculation Settings Block.
|
1591
|
+
# It contains the ìRecalculate before saveî option in
|
1592
|
+
# Excel's calculation settings dialogue.
|
1593
|
+
#
|
1594
|
+
# Record SAVERECALC, BIFF3-BIFF8:
|
1595
|
+
#
|
1596
|
+
# Offset Size Contents
|
1597
|
+
# 0 2 0 = Do not recalculate;
|
1598
|
+
# 1 = Recalculate before saving the document
|
1599
|
+
class SaveRecalcRecord < BiffRecord
|
1600
|
+
RECORD_ID = 0x05F
|
1601
|
+
|
1602
|
+
def initialize(recalc)
|
1603
|
+
@record_data = [recalc].pack('v')
|
1604
|
+
end
|
1605
|
+
end
|
1606
|
+
|
1607
|
+
# This record stores if the row and column headers
|
1608
|
+
# (the areas with row numbers and column letters) will be printed.
|
1609
|
+
#
|
1610
|
+
# Record PRINTHEADERS, BIFF2-BIFF8:
|
1611
|
+
#
|
1612
|
+
# Offset Size Contents
|
1613
|
+
# 0 2 0 = Do not print row/column headers;
|
1614
|
+
# 1 = Print row/column headers
|
1615
|
+
class PrintHeadersRecord < BiffRecord
|
1616
|
+
RECORD_ID = 0x02A
|
1617
|
+
|
1618
|
+
def initialize(print_headers)
|
1619
|
+
@record_data = [print_headers].pack('v')
|
1620
|
+
end
|
1621
|
+
end
|
1622
|
+
|
1623
|
+
# This record stores if sheet grid lines will be printed.
|
1624
|
+
#
|
1625
|
+
# Record PRINTGRIDLINES, BIFF2-BIFF8:
|
1626
|
+
#
|
1627
|
+
# Offset Size Contents
|
1628
|
+
# 0 2 0 = Do not print sheet grid lines;
|
1629
|
+
# 1 = Print sheet grid lines
|
1630
|
+
class PrintGridLinesRecord < BiffRecord
|
1631
|
+
RECORD_ID = 0x02B
|
1632
|
+
|
1633
|
+
def initialize(print_grid)
|
1634
|
+
@record_data = [print_grid].pack('v')
|
1635
|
+
end
|
1636
|
+
end
|
1637
|
+
|
1638
|
+
# This record specifies if the option to print sheet grid lines
|
1639
|
+
# (record PRINTGRIDLINES) has ever been changed.
|
1640
|
+
#
|
1641
|
+
# Record GRIDSET, BIFF3-BIFF8:
|
1642
|
+
#
|
1643
|
+
# Offset Size Contents
|
1644
|
+
# 0 2 0 = Print grid lines option never changed
|
1645
|
+
# 1 = Print grid lines option changed
|
1646
|
+
class GridSetRecord < BiffRecord
|
1647
|
+
RECORD_ID = 0x082
|
1648
|
+
|
1649
|
+
def initialize(print_grid_changed)
|
1650
|
+
@record_data = [print_grid_changed].pack('v')
|
1651
|
+
end
|
1652
|
+
end
|
1653
|
+
|
1654
|
+
# This record specifies the default height and default flags
|
1655
|
+
# for rows that do not have a corresponding ROW record.
|
1656
|
+
#
|
1657
|
+
# Record DEFAULTROWHEIGHT, BIFF3-BIFF8:
|
1658
|
+
#
|
1659
|
+
# Offset Size Contents
|
1660
|
+
# 0 2 Option flags:
|
1661
|
+
# Bit Mask Contents
|
1662
|
+
# 0 0001H 1 = Row height and default font height do not match
|
1663
|
+
# 1 0002H 1 = Row is hidden
|
1664
|
+
# 2 0004H 1 = Additional space above the row
|
1665
|
+
# 3 0008H 1 = Additional space below the row
|
1666
|
+
# 2 2 Default height for unused rows, in twips = 1/20 of a point
|
1667
|
+
class DefaultRowHeight < BiffRecord
|
1668
|
+
RECORD_ID = 0x0225
|
1669
|
+
|
1670
|
+
def initialize(options, def_height)
|
1671
|
+
@record_data = [options, def_height].pack('v2')
|
1672
|
+
end
|
1673
|
+
end
|
1674
|
+
|
1675
|
+
# This record specifies the default column width for columns that
|
1676
|
+
# do not have a specific width set using the record COLINFO or COLWIDTH.
|
1677
|
+
# This record has no effect, if a STANDARDWIDTH record is present in the file.
|
1678
|
+
#
|
1679
|
+
# Record DEFCOLWIDTH, BIFF2-BIFF8:
|
1680
|
+
#
|
1681
|
+
# Offset Size Contents
|
1682
|
+
# 0 2 Column width in characters, using the width of the zero
|
1683
|
+
# character from default font (first FONT record in the file)
|
1684
|
+
class DefColWidthRecord < BiffRecord
|
1685
|
+
RECORD_ID = 0x0055
|
1686
|
+
|
1687
|
+
def initialize(def_width)
|
1688
|
+
@record_data = [def_width].pack('v')
|
1689
|
+
end
|
1690
|
+
end
|
1691
|
+
|
1692
|
+
# This record is part of the Page Settings Block. It contains all
|
1693
|
+
# horizontal manual page breaks.
|
1694
|
+
#
|
1695
|
+
# Record HORIZONTALPAGEBREAKS, BIFF8:
|
1696
|
+
# Offset Size Contents
|
1697
|
+
# 0 2 Number of following row index structures (nm)
|
1698
|
+
# 2 6nm List of nm row index structures. Each row index
|
1699
|
+
# structure contains:
|
1700
|
+
# Offset Size Contents
|
1701
|
+
# 0 2 Index to first row below the page break
|
1702
|
+
# 2 2 Index to first column of this page break
|
1703
|
+
# 4 2 Index to last column of this page break
|
1704
|
+
#
|
1705
|
+
# The row indexes in the lists must be ordered ascending.
|
1706
|
+
# If in BIFF8 a row contains several page breaks, they must be ordered
|
1707
|
+
# ascending by start column index.
|
1708
|
+
class HorizontalPageBreaksRecord < BiffRecord
|
1709
|
+
RECORD_ID = 0x001B
|
1710
|
+
|
1711
|
+
def initialize(breaks_list)
|
1712
|
+
@record_data = [breaks_list.length].pack('v')
|
1713
|
+
breaks_list.each do |r, c1, c2|
|
1714
|
+
@record_data += [r, c1, c2].pack('v3')
|
1715
|
+
end
|
1716
|
+
end
|
1717
|
+
end
|
1718
|
+
|
1719
|
+
# This record is part of the Page Settings Block. It contains all
|
1720
|
+
# vertical manual page breaks.
|
1721
|
+
#
|
1722
|
+
# Record VERTICALPAGEBREAKS, BIFF8:
|
1723
|
+
# Offset Size Contents
|
1724
|
+
# 0 2 Number of following column index structures (nm)
|
1725
|
+
# 2 6nm List of nm column index structures. Each column index
|
1726
|
+
# structure contains:
|
1727
|
+
# Offset Size Contents
|
1728
|
+
# 0 2 Index to first column following the page
|
1729
|
+
# break
|
1730
|
+
# 2 2 Index to first row of this page break
|
1731
|
+
# 4 2 Index to last row of this page break
|
1732
|
+
#
|
1733
|
+
# The column indexes in the lists must be ordered ascending.
|
1734
|
+
# If in BIFF8 a column contains several page breaks, they must be ordered
|
1735
|
+
# ascending by start row index.
|
1736
|
+
class VerticalPageBreaksRecord < BiffRecord
|
1737
|
+
RECORD_ID = 0x001A
|
1738
|
+
|
1739
|
+
def initialize(breaks_list)
|
1740
|
+
@record_data = [breaks_list.length].pack('v')
|
1741
|
+
breaks_list.each do |c, r1, r2|
|
1742
|
+
@record_data += [c, r1, r2].pack('v3')
|
1743
|
+
end
|
1744
|
+
end
|
1745
|
+
end
|
1746
|
+
|
1747
|
+
# This record is part of the Page Settings Block. It specifies the
|
1748
|
+
# page header string for the current worksheet. If this record is not
|
1749
|
+
# present or completely empty (record size is 0), the sheet does not
|
1750
|
+
# contain a page header.
|
1751
|
+
#
|
1752
|
+
# Record HEADER for non-empty page header, BIFF2-BIFF8:
|
1753
|
+
# Offset Size Contents
|
1754
|
+
# 0 var. Page header string
|
1755
|
+
# BIFF2-BIFF7: Non-empty byte string, 8bit string
|
1756
|
+
# length
|
1757
|
+
# BIFF8: Non-empty Unicode string, 16bit string length
|
1758
|
+
# The header string may contain special commands, i.e. placeholders for
|
1759
|
+
# the page number, current date, or text formatting attributes. These
|
1760
|
+
# fields are represented by single letters (exception: font name and
|
1761
|
+
# size, see below) with a leading ampersand ("&"). If the ampersand
|
1762
|
+
# is part of the regular header text, it will be duplicated ("&&"). The
|
1763
|
+
# page header is divided into 3 sections: the left, the centred, and the
|
1764
|
+
# right section. Each section is introduced by a special command. All
|
1765
|
+
# text and all commands following are part of the selected section. Each
|
1766
|
+
# section starts with the text formatting specified in the default font
|
1767
|
+
# (first FONT record in the file). Active formatting attributes from
|
1768
|
+
# a previous section do not go into the next section.
|
1769
|
+
#
|
1770
|
+
# The following table shows all available commands:
|
1771
|
+
#
|
1772
|
+
# Command Contents
|
1773
|
+
# && The "&" character itself
|
1774
|
+
# &L Start of the left section
|
1775
|
+
# &C Start of the centred section
|
1776
|
+
# &R Start of the right section
|
1777
|
+
# &P Current page number
|
1778
|
+
# &N Page count
|
1779
|
+
# &D Current date
|
1780
|
+
# &T Current time
|
1781
|
+
# &A Sheet name (BIFF5-BIFF8)
|
1782
|
+
# &F File name without path
|
1783
|
+
# &Z File path without file name (BIFF8X)
|
1784
|
+
# &G Picture (BIFF8X)
|
1785
|
+
# &B Bold on/off (BIFF2-BIFF4)
|
1786
|
+
# &I Italic on/off (BIFF2-BIFF4)
|
1787
|
+
# &U Underlining on/off
|
1788
|
+
# &E Double underlining on/off (BIFF5-BIFF8)
|
1789
|
+
# &S Strikeout on/off
|
1790
|
+
# &X Superscript on/off (BIFF5-BIFF8)
|
1791
|
+
# &Y Subscript on/off (BIFF5-BIFF8)
|
1792
|
+
# &"<fontname>" Set new font <fontname>
|
1793
|
+
# &"<fontname>,<fontstyle>"
|
1794
|
+
# Set new font with specified style <fontstyle>.
|
1795
|
+
# The style <fontstyle> is in most cases one of
|
1796
|
+
# "Regular", "Bold", "Italic", or "Bold Italic".
|
1797
|
+
# But this setting is dependent on the used font,
|
1798
|
+
# it may differ (localised style names, or "Standard",
|
1799
|
+
# "Oblique", ...). (BIFF5-BIFF8)
|
1800
|
+
# &<fontheight> Set font height in points (<fontheight> is a decimal value).
|
1801
|
+
# If this command is followed by a plain number to be printed
|
1802
|
+
# in the header, it will be separated from the font height
|
1803
|
+
# with a space character.
|
1804
|
+
class HeaderRecord < BiffRecord
|
1805
|
+
RECORD_ID = 0x0014
|
1806
|
+
|
1807
|
+
def initialize(str)
|
1808
|
+
@record_data = [str.length, 0].pack('vC') + str
|
1809
|
+
end
|
1810
|
+
end
|
1811
|
+
|
1812
|
+
# Semantic is equal to HEADER record
|
1813
|
+
class FooterRecord < BiffRecord
|
1814
|
+
RECORD_ID = 0x0015
|
1815
|
+
|
1816
|
+
def initialize(str)
|
1817
|
+
@record_data = [str.length, 0].pack('vC') + str
|
1818
|
+
end
|
1819
|
+
end
|
1820
|
+
|
1821
|
+
# This record is part of the Page Settings Block. It specifies if the
|
1822
|
+
# sheet is centred horizontally when printed.
|
1823
|
+
#
|
1824
|
+
# Record HCENTER, BIFF3-BIFF8:
|
1825
|
+
#
|
1826
|
+
# Offset Size Contents
|
1827
|
+
# 0 2 0 = Print sheet left aligned
|
1828
|
+
# 1 = Print sheet centred horizontally
|
1829
|
+
class HCenterRecord < BiffRecord
|
1830
|
+
RECORD_ID = 0x0083
|
1831
|
+
|
1832
|
+
def initialize(is_horz_center)
|
1833
|
+
@record_data = [is_horz_center].pack('v')
|
1834
|
+
end
|
1835
|
+
end
|
1836
|
+
|
1837
|
+
# This record is part of the Page Settings Block. It specifies if the
|
1838
|
+
# sheet is centred vertically when printed.
|
1839
|
+
#
|
1840
|
+
# Record VCENTER, BIFF3-BIFF8:
|
1841
|
+
#
|
1842
|
+
# Offset Size Contents
|
1843
|
+
# 0 2 0 = Print sheet aligned at top page border
|
1844
|
+
# 1 = Print sheet vertically centred
|
1845
|
+
class VCenterRecord < BiffRecord
|
1846
|
+
RECORD_ID = 0x0084
|
1847
|
+
|
1848
|
+
def initialize(is_vert_center)
|
1849
|
+
@record_data = [is_vert_center].pack('v')
|
1850
|
+
end
|
1851
|
+
end
|
1852
|
+
|
1853
|
+
# This record is part of the Page Settings Block. It contains the left
|
1854
|
+
# page margin of the current worksheet.
|
1855
|
+
#
|
1856
|
+
# Record LEFTMARGIN, BIFF2-BIFF8:
|
1857
|
+
#
|
1858
|
+
# Offset Size Contents
|
1859
|
+
# 0 8 Left page margin in inches
|
1860
|
+
# (IEEE 754 floating-point value, 64bit double precision)
|
1861
|
+
class LeftMarginRecord < BiffRecord
|
1862
|
+
RECORD_ID = 0x0026
|
1863
|
+
|
1864
|
+
def initialize(margin)
|
1865
|
+
@record_data = [margin].pack('E')
|
1866
|
+
end
|
1867
|
+
end
|
1868
|
+
|
1869
|
+
# This record is part of the Page Settings Block. It contains the right
|
1870
|
+
# page margin of the current worksheet.
|
1871
|
+
#
|
1872
|
+
# Offset Size Contents
|
1873
|
+
# 0 8 Right page margin in inches
|
1874
|
+
# (IEEE 754 floating-point value, 64?bit double precision)
|
1875
|
+
class RightMarginRecord < BiffRecord
|
1876
|
+
RECORD_ID = 0x0027
|
1877
|
+
|
1878
|
+
def initialize(margin)
|
1879
|
+
@record_data = [margin].pack('E')
|
1880
|
+
end
|
1881
|
+
end
|
1882
|
+
|
1883
|
+
# This record is part of the Page Settings Block. It contains the top
|
1884
|
+
# page margin of the current worksheet.
|
1885
|
+
#
|
1886
|
+
# Offset Size Contents
|
1887
|
+
# 0 8 Top page margin in inches
|
1888
|
+
# (IEEE 754 floating-point value, 64?bit double precision)
|
1889
|
+
class TopMarginRecord < BiffRecord
|
1890
|
+
RECORD_ID = 0x0028
|
1891
|
+
|
1892
|
+
def initialize(margin)
|
1893
|
+
@record_data = [margin].pack('E')
|
1894
|
+
end
|
1895
|
+
end
|
1896
|
+
|
1897
|
+
# This record is part of the Page Settings Block. It contains the bottom
|
1898
|
+
# page margin of the current worksheet.
|
1899
|
+
#
|
1900
|
+
# Offset Size Contents
|
1901
|
+
# 0 8 Bottom page margin in inches
|
1902
|
+
# (IEEE 754 floating-point value, 64?bit double precision)
|
1903
|
+
class BottomMarginRecord < BiffRecord
|
1904
|
+
RECORD_ID = 0x0029
|
1905
|
+
|
1906
|
+
def initialize(margin)
|
1907
|
+
@record_data = [margin].pack('E')
|
1908
|
+
end
|
1909
|
+
end
|
1910
|
+
|
1911
|
+
# This record is part of the Page Settings Block. It stores the page
|
1912
|
+
# format settings of the current sheet. The pages may be scaled in
|
1913
|
+
# percent or by using an absolute number of pages. This setting is
|
1914
|
+
# located in the WSBOOL record. If pages are scaled in percent,
|
1915
|
+
# the scaling factor in this record is used, otherwise the "Fit to
|
1916
|
+
# pages" values. One of the "Fit to pages" values may be 0. In this case
|
1917
|
+
# the sheet is scaled to fit only to the other value.
|
1918
|
+
#
|
1919
|
+
# Record SETUP, BIFF5-BIFF8:
|
1920
|
+
#
|
1921
|
+
# Offset Size Contents
|
1922
|
+
# 0 2 Paper size (see below)
|
1923
|
+
# 2 2 Scaling factor in percent
|
1924
|
+
# 4 2 Start page number
|
1925
|
+
# 6 2 Fit worksheet width to this number of pages
|
1926
|
+
# (0 = use as many as needed)
|
1927
|
+
# 8 2 Fit worksheet height to this number of pages
|
1928
|
+
# (0 = use as many as needed)
|
1929
|
+
# 10 2 Option flags:
|
1930
|
+
# Bit Mask Contents
|
1931
|
+
# 0 0001H 0 = Print pages in columns
|
1932
|
+
# 1 = Print pages in rows
|
1933
|
+
# 1 0002H 0 = Landscape
|
1934
|
+
# 1 = Portrait
|
1935
|
+
# 2 0004H 1 = Paper size, scaling factor,
|
1936
|
+
# paper orientation (portrait/landscape),
|
1937
|
+
# print resolution and number of copies
|
1938
|
+
# are not initialised
|
1939
|
+
# 3 0008H 0 = Print coloured
|
1940
|
+
# 1 = Print black and white
|
1941
|
+
# 4 0010H 0 = Default print quality
|
1942
|
+
# 1 = Draft quality
|
1943
|
+
# 5 0020H 0 = Do not print cell notes
|
1944
|
+
# 1 = Print cell notes
|
1945
|
+
# 6 0040H 0 = Paper orientation setting is valid
|
1946
|
+
# 1 = Paper orientation setting not
|
1947
|
+
# initialised
|
1948
|
+
# 7 0080H 0 = Automatic page numbers
|
1949
|
+
# 1 = Use start page number
|
1950
|
+
# The following flags are valid for BIFF8 only:
|
1951
|
+
# 9 0200H 0 = Print notes as displayed
|
1952
|
+
# 1 = Print notes at end of sheet
|
1953
|
+
# 11-10 0C00H 00 = Print errors as displayed
|
1954
|
+
# 01 = Do not print errors
|
1955
|
+
# 10 = Print errors as "--"
|
1956
|
+
# 11 = Print errors as "#N/A!"
|
1957
|
+
# 12 2 Print resolution in dpi
|
1958
|
+
# 14 2 Vertical print resolution in dpi
|
1959
|
+
# 16 8 Header margin (IEEE 754 floating-point value,
|
1960
|
+
# 64bit double precision)
|
1961
|
+
# 24 8 Footer margin (IEEE 754 floating-point value,
|
1962
|
+
# 64bit double precision)
|
1963
|
+
# 32 2 Number of copies to print
|
1964
|
+
#
|
1965
|
+
#
|
1966
|
+
# PAPER TYPES:
|
1967
|
+
#
|
1968
|
+
# Index Paper type Paper size
|
1969
|
+
# 0 Undefined
|
1970
|
+
# 1 Letter 8 1/2" x 11"
|
1971
|
+
# 2 Letter small 8 1/2" x 11"
|
1972
|
+
# 3 Tabloid 11" x 17"
|
1973
|
+
# 4 Ledger 17" x 11"
|
1974
|
+
# 5 Legal 8 1/2" x 14"
|
1975
|
+
# 6 Statement 5 1/2" x 8 1/2"
|
1976
|
+
# 7 Executive 7 1/4" x 10 1/2"
|
1977
|
+
# 8 A3 297mm x 420mm
|
1978
|
+
# 9 A4 210mm x 297mm
|
1979
|
+
# 10 A4 small 210mm x 297mm
|
1980
|
+
# 11 A5 148mm x 210mm
|
1981
|
+
# 12 B4 (JIS) 257mm x 364mm
|
1982
|
+
# 13 B5 (JIS) 182mm x 257mm
|
1983
|
+
# 14 Folio 8 1/2" x 13"
|
1984
|
+
# 15 Quarto 215mm x 275mm
|
1985
|
+
# 16 10x14 10" x 14"
|
1986
|
+
# 17 11x17 11" x 17"
|
1987
|
+
# 18 Note 8 1/2" x 11"
|
1988
|
+
# 19 Envelope #9 3 7/8" x 8 7/8"
|
1989
|
+
# 20 Envelope #10 4 1/8" x 9 1/2"
|
1990
|
+
# 21 Envelope #11 4 1/2" x 10 3/8"
|
1991
|
+
# 22 Envelope #12 4 3/4" x 11"
|
1992
|
+
# 23 Envelope #14 5" x 11 1/2"
|
1993
|
+
# 24 C 17" x 22"
|
1994
|
+
# 25 D 22" x 34"
|
1995
|
+
# 26 E 34" x 44"
|
1996
|
+
# 27 Envelope DL 110mm x 220mm
|
1997
|
+
# 28 Envelope C5 162mm x 229mm
|
1998
|
+
# 29 Envelope C3 324mm x 458mm
|
1999
|
+
# 30 Envelope C4 229mm x 324mm
|
2000
|
+
# 31 Envelope C6 114mm x 162mm
|
2001
|
+
# 32 Envelope C6/C5 114mm x 229mm
|
2002
|
+
# 33 B4 (ISO) 250mm x 353mm
|
2003
|
+
# 34 B5 (ISO) 176mm x 250mm
|
2004
|
+
# 35 B6 (ISO) 125mm x 176mm
|
2005
|
+
# 36 Envelope Italy 110mm x 230mm
|
2006
|
+
# 37 Envelope Monarch 3 7/8" x 7 1/2"
|
2007
|
+
# 38 63/4 Envelope 3 5/8" x 6 1/2"
|
2008
|
+
# 39 US Standard Fanfold 14 7/8" x 11"
|
2009
|
+
# 40 German Std. Fanfold 8 1/2" x 12"
|
2010
|
+
# 41 German Legal Fanfold 8 1/2" x 13"
|
2011
|
+
# 42 B4 (ISO) 250mm x 353mm
|
2012
|
+
# 43 Japanese Postcard 100mm x 148mm
|
2013
|
+
# 44 9x11 9" x 11"
|
2014
|
+
# 45 10x11 10" x 11"
|
2015
|
+
# 46 15x11 15" x 11"
|
2016
|
+
# 47 Envelope Invite 220mm x 220mm
|
2017
|
+
# 48 Undefined
|
2018
|
+
# 49 Undefined
|
2019
|
+
# 50 Letter Extra 9 1/2" x 12"
|
2020
|
+
# 51 Legal Extra 9 1/2" x 15"
|
2021
|
+
# 52 Tabloid Extra 11 11/16" x 18"
|
2022
|
+
# 53 A4 Extra 235mm x 322mm
|
2023
|
+
# 54 Letter Transverse 8 1/2" x 11"
|
2024
|
+
# 55 A4 Transverse 210mm x 297mm
|
2025
|
+
# 56 Letter Extra Transv. 9 1/2" x 12"
|
2026
|
+
# 57 Super A/A4 227mm x 356mm
|
2027
|
+
# 58 Super B/A3 305mm x 487mm
|
2028
|
+
# 59 Letter Plus 8 1/2" x 12 11/16"
|
2029
|
+
# 60 A4 Plus 210mm x 330mm
|
2030
|
+
# 61 A5 Transverse 148mm x 210mm
|
2031
|
+
# 62 B5 (JIS) Transverse 182mm x 257mm
|
2032
|
+
# 63 A3 Extra 322mm x 445mm
|
2033
|
+
# 64 A5 Extra 174mm x 235mm
|
2034
|
+
# 65 B5 (ISO) Extra 201mm x 276mm
|
2035
|
+
# 66 A2 420mm x 594mm
|
2036
|
+
# 67 A3 Transverse 297mm x 420mm
|
2037
|
+
# 68 A3 Extra Transverse 322mm x 445mm
|
2038
|
+
# 69 Dbl. Japanese Postcard 200mm x 148mm
|
2039
|
+
# 70 A6 105mm x 148mm
|
2040
|
+
# 71
|
2041
|
+
# 72
|
2042
|
+
# 73
|
2043
|
+
# 74
|
2044
|
+
# 75 Letter Rotated 11" x 8 1/2"
|
2045
|
+
# 76 A3 Rotated 420mm x 297mm
|
2046
|
+
# 77 A4 Rotated 297mm x 210mm
|
2047
|
+
# 78 A5 Rotated 210mm x 148mm
|
2048
|
+
# 79 B4 (JIS) Rotated 364mm x 257mm
|
2049
|
+
# 80 B5 (JIS) Rotated 257mm x 182mm
|
2050
|
+
# 81 Japanese Postcard Rot. 148mm x 100mm
|
2051
|
+
# 82 Dbl. Jap. Postcard Rot. 148mm x 200mm
|
2052
|
+
# 83 A6 Rotated 148mm x 105mm
|
2053
|
+
# 84
|
2054
|
+
# 85
|
2055
|
+
# 86
|
2056
|
+
# 87
|
2057
|
+
# 88 B6 (JIS) 128mm x 182mm
|
2058
|
+
# 89 B6 (JIS) Rotated 182mm x 128mm
|
2059
|
+
# 90 12x11 12" x 11"
|
2060
|
+
class SetupPageRecord < BiffRecord
|
2061
|
+
RECORD_ID = 0x00A1
|
2062
|
+
|
2063
|
+
def initialize(paper, scaling, start_num, fit_width_to, fit_height_to, options, hres, vres, header_margin, footer_margin, num_copies)
|
2064
|
+
args = [paper, scaling, start_num, fit_width_to, fit_height_to, options, hres, vres, header_margin, footer_margin, num_copies]
|
2065
|
+
@record_data = args.pack('v8E2v')
|
2066
|
+
end
|
2067
|
+
end
|
2068
|
+
|
2069
|
+
# This record is part of a Link Table. It contains the name and the token
|
2070
|
+
# array of an internal defined name. Token arrays of defined names
|
2071
|
+
# contain tokens with aberrant token classes.
|
2072
|
+
#
|
2073
|
+
# Record NAME, BIFF5/BIFF7:
|
2074
|
+
# Offset Size Contents
|
2075
|
+
# 0 2 Option flags, see below
|
2076
|
+
# 2 1 Keyboard shortcut (only for command macro names, see below)
|
2077
|
+
# 3 1 Length of the name (character count, ln)
|
2078
|
+
# 4 2 Size of the formula data (sz)
|
2079
|
+
# 6 2 0 = Global name, otherwise index to EXTERNSHEET record (one-based)
|
2080
|
+
# 8 2 0 = Global name, otherwise index to sheet (one-based)
|
2081
|
+
# 10 1 Length of menu text (character count, lm)
|
2082
|
+
# 11 1 Length of description text (character count, ld)
|
2083
|
+
# 12 1 Length of help topic text (character count, lh)
|
2084
|
+
# 13 1 Length of status bar text (character count, ls)
|
2085
|
+
# 14 ln Character array of the name
|
2086
|
+
# 14+ln sz Formula data (RPN token array without size field, 4)
|
2087
|
+
# 14+ln+sz lm Character array of menu text
|
2088
|
+
# var. ld Character array of description text
|
2089
|
+
# var. lh Character array of help topic text
|
2090
|
+
# var. ls Character array of status bar text
|
2091
|
+
#
|
2092
|
+
# Record NAME, BIFF8:
|
2093
|
+
# Offset Size Contents
|
2094
|
+
# 0 2 Option flags, see below
|
2095
|
+
# 2 1 Keyboard shortcut (only for command macro names, see below)
|
2096
|
+
# 3 1 Length of the name (character count, ln)
|
2097
|
+
# 4 2 Size of the formula data (sz)
|
2098
|
+
# 6 2 Not used
|
2099
|
+
# 8 2 0 = Global name, otherwise index to sheet (one-based)
|
2100
|
+
# 10 1 Length of menu text (character count, lm)
|
2101
|
+
# 11 1 Length of description text (character count, ld)
|
2102
|
+
# 12 1 Length of help topic text (character count, lh)
|
2103
|
+
# 13 1 Length of status bar text (character count, ls)
|
2104
|
+
# 14 var. Name (Unicode string without length field, 3.4)
|
2105
|
+
# var. sz Formula data (RPN token array without size field, 4)
|
2106
|
+
# [var.] var. (optional, only if lm > 0) Menu text (Unicode string without length field, 3.4)
|
2107
|
+
# [var.] var. (optional, only if ld > 0) Description text (Unicode string without length field, 3.4)
|
2108
|
+
# [var.] var. (optional, only if lh > 0) Help topic text (Unicode string without length field, 3.4)
|
2109
|
+
# [var.] var. (optional, only if ls > 0) Status bar text (Unicode string without length field, 3.4)
|
2110
|
+
class NameRecord < BiffRecord
|
2111
|
+
RECORD_ID = 0x0018
|
2112
|
+
|
2113
|
+
def initialize(options, keyboard_shortcut, name, sheet_index, rpn, menu_text='', desc_text='', help_text='', status_text='')
|
2114
|
+
if name.is_a?(Integer)
|
2115
|
+
unicode_name = [name].pack('c')
|
2116
|
+
else
|
2117
|
+
unicode_name = name
|
2118
|
+
end
|
2119
|
+
|
2120
|
+
args = [options, keyboard_shortcut, unicode_name.length, rpn.length]
|
2121
|
+
args += [0x0000, sheet_index, 0x00]
|
2122
|
+
args += [menu_text.length, desc_text.length, help_text.length, status_text.length]
|
2123
|
+
args += [unicode_name, rpn]
|
2124
|
+
|
2125
|
+
@record_data = args.pack('vCCv vvC vvvv b*') + menu_text + desc_text + help_text + status_text
|
2126
|
+
end
|
2127
|
+
end
|
2128
|
+
|
2129
|
+
# In BIFF8 the record stores a list with indexes to SUPBOOK
|
2130
|
+
# records (list of REF structures, 6.100). See 5.10.3 for
|
2131
|
+
# details about external references in BIFF8.
|
2132
|
+
#
|
2133
|
+
# Record EXTERNSHEET, BIFF8:
|
2134
|
+
# Offset Size Contents
|
2135
|
+
# 0 2 Number of following REF structures (nm)
|
2136
|
+
# 2 6nm List of nm REF structures. Each REF contains the following data:
|
2137
|
+
# Offset Size Contents
|
2138
|
+
# 0 2 Index to SUPBOOK record
|
2139
|
+
# 2 2 Index to first SUPBOOK sheet
|
2140
|
+
# 4 2 Index to last SUPBOOK sheet
|
2141
|
+
class ExternSheetRecord < BiffRecord
|
2142
|
+
RECORD_ID = 0x0017
|
2143
|
+
|
2144
|
+
def initialize(refs = [])
|
2145
|
+
# do we always need this ref? or only if there are no refs?
|
2146
|
+
# AN: I am assuming only needed if no other refs TODO test
|
2147
|
+
refs = [[0,0,0]] if refs.empty?
|
2148
|
+
ref_data = refs.collect {|r| r.pack('v3')}.join
|
2149
|
+
@record_data = [refs.length].pack('v') + ref_data
|
2150
|
+
end
|
2151
|
+
end
|
2152
|
+
|
2153
|
+
# This record mainly stores the URL of an external document
|
2154
|
+
# and a list of sheet names inside this document. Furthermore
|
2155
|
+
# it is used to store DDE and OLE object links, or to indicate
|
2156
|
+
# an internal 3D reference or an add-in function. See 5.10.3
|
2157
|
+
# for details about external references in BIFF8.
|
2158
|
+
class SupBookRecord < BiffRecord
|
2159
|
+
RECORD_ID = 0x01AE
|
2160
|
+
end
|
2161
|
+
|
2162
|
+
# In each file occurs a SUPBOOK that is used for internal 3D
|
2163
|
+
# references. It stores the number of sheets of the own document.
|
2164
|
+
#
|
2165
|
+
# Record SUPBOOK for 3D references, BIFF8:
|
2166
|
+
# Offset Size Contents
|
2167
|
+
# 0 2 Number of sheets in this document
|
2168
|
+
# 2 2 01H 04H (relict of BIFF5/BIFF7, the byte string "<04H>", see 3.9.1)
|
2169
|
+
class InternalReferenceSupBookRecord < SupBookRecord
|
2170
|
+
def initialize(num_sheets)
|
2171
|
+
@record_data = [num_sheets, 0x01, 0x04].pack('vCC')
|
2172
|
+
end
|
2173
|
+
end
|