rmasalov-surpass 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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