surpass 0.0.3

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