surpass 0.0.3

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.
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