parseexcel_mod 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # ParseExcel -- Spreadsheet -- 03.06.2003 -- hwyss@ywesee.com
3
+
4
+ require 'parseexcel_mod/olestorage'
5
+ require 'parseexcel_mod/parser'
6
+
7
+ module Spreadsheet
8
+ module ParseExcel
9
+ def parse(source, params={})
10
+ Parser.new(params).parse(source)
11
+ end
12
+ module_function :parse
13
+ end
14
+ end
@@ -0,0 +1,1013 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Spreadsheet::ParseExcel -- Extract Data from an Excel File
4
+ # Copyright (C) 2003 ywesee -- intellectual capital connected
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License as published by the Free Software Foundation; either
9
+ # version 2.1 of the License, or (at your option) any later version.
10
+ #
11
+ # This library is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public
17
+ # License along with this library; if not, write to the Free Software
18
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
+ #
20
+ # ywesee - intellectual capital connected, Winterthurerstrasse 52, CH-8006 Z�rich, Switzerland
21
+ # hwyss@ywesee.com
22
+ #
23
+ # Parser -- Spreadsheet::ParseExcel -- 10.06.2003 -- hwyss@ywesee.com
24
+
25
+ require 'parseexcel_mod/olestorage'
26
+ require 'parseexcel_mod/workbook'
27
+ require 'parseexcel_mod/worksheet'
28
+ require 'parseexcel_mod/format'
29
+
30
+ module Spreadsheet
31
+ module ParseExcel
32
+ class Parser
33
+ attr_reader :bigendian
34
+ CONTINUE = 0x03C
35
+ NOTE = 0x1B6
36
+ SST = 0x0FC
37
+ EVENT_TABLE = {
38
+ #Develpers' Kit P291
39
+ 0x14 => :header, # Header
40
+ 0x15 => :footer, # Footer
41
+ =begin
42
+ 0x18 => :name, # NAME(?)
43
+ 0x1A => :v_page_break, # Veritical Page Break
44
+ 0x1B => :h_page_break, # Horizontal Page Break
45
+ =end
46
+ 0x1C => :note, # Note
47
+ 0x22 => :flg_1904, # 1904 Flag
48
+ =begin
49
+ 0x26 => :margin, # Left Mergin
50
+ 0x27 => :margin, # Right Mergin
51
+ 0x28 => :margin, # Top Mergin
52
+ 0x29 => :margin, # Bottom Mergin
53
+ 0x2A => :print_headers, # Print Headers
54
+ 0x2B => :print_gridlines, # Print Gridlines
55
+ =end
56
+ CONTINUE=> :continue, # Continue
57
+ 0x42 => :codepage, # BIFF_CODEPAGE
58
+ 0x43 => :xf, # ExTended Format(?)
59
+ =begin
60
+ #Develpers' Kit P292
61
+ 0x55 => :def_col_width, # Consider
62
+ 0x5C => :write_access, # WRITEACCESS
63
+ 0x7D => :col_info, # Colinfo
64
+ =end
65
+ 0x7E => :rk, # RK
66
+ 0x81 => :ws_bool, # WSBOOL
67
+ =begin
68
+ 0x83 => :h_center, # HCENTER
69
+ 0x84 => :v_center, # VCENTER
70
+ =end
71
+ 0x85 => :bound_sheet, # BoundSheet
72
+ =begin
73
+ 0x92 => :palette, # Palette, fgp
74
+
75
+ 0x99 => :standard_width, # Standard Col
76
+ #Develpers' Kit P293
77
+ =end
78
+ 0xA1 => :setup, # SETUP
79
+ 0xBD => :mul_rk, # MULRK
80
+ 0xBE => :mul_blank, # MULBLANK
81
+ 0xD6 => :rstring, # RString
82
+ #Develpers' Kit P294
83
+ 0xe0 => :xf, # ExTended Format
84
+ =begin
85
+ 0xE5 => :merge_area, # MergeArea (Not Documented)
86
+ =end
87
+ SST => :sst, # Shared String Table
88
+ 0xFD => :label_sst, # Label SST
89
+ NOTE => :biff8_annotation,
90
+ #Develpers' Kit P295
91
+ 0x201 => :blank, # Blank
92
+
93
+ 0x202 => :integer, # Integer(Not Documented)
94
+ 0x203 => :number, # Number
95
+ 0x204 => :label , # Label
96
+ 0x205 => :bool_err, # BoolErr
97
+ 0x207 => :string, # STRING
98
+ 0x208 => :row, # RowData
99
+ 0x221 => :array, # Array (Consider)
100
+ 0x225 => :default_row_height,# Consider
101
+ =begin
102
+ 0x31 => :font, # Font
103
+ 0x231 => :font, # Font
104
+
105
+ =end
106
+ 0x27E => :rk, # RK
107
+ 0x41E => :format, # Format
108
+ 0x06 => :formula, # Formula
109
+ 0x406 => :formula, # Formula
110
+ 0x09 => :bof, # BOF(BIFF2)
111
+ 0x209 => :bof, # BOF(BIFF3)
112
+ 0x409 => :bof, # BOF(BIFF4)
113
+ 0x809 => :bof, # BOF(BIFF5-8)
114
+ }
115
+ UNIMPLEMENTED = {
116
+ #Develpers' Kit P291
117
+ 0x18 => :name, # NAME(?)
118
+ 0x1A => :v_page_break, # Veritical Page Break
119
+ 0x1B => :h_page_break, # Horizontal Page Break
120
+ 0x26 => :margin, # Left Mergin
121
+ 0x27 => :margin, # Right Mergin
122
+ 0x28 => :margin, # Top Mergin
123
+ 0x29 => :margin, # Bottom Mergin
124
+ 0x2A => :print_headers, # Print Headers
125
+ 0x2B => :print_gridlines, # Print Gridlines
126
+ #Develpers' Kit P292
127
+ 0x55 => :def_col_width, # Consider
128
+ 0x5C => :write_access, # WRITEACCESS
129
+ 0x7D => :col_info, # Colinfo
130
+ 0x81 => :ws_bool, # WSBOOL
131
+ 0x83 => :h_center, # HCENTER
132
+ 0x84 => :v_center, # VCENTER
133
+
134
+ 0x92 => :palette, # Palette, fgp
135
+
136
+ 0x99 => :standard_width, # Standard Col
137
+ #Develpers' Kit P293
138
+ 0xc1 => :add_menu, # BIFF_ADDMENU
139
+ 0xe1 => :interface_hdr, # BIFF_INTERFACEHDR
140
+ 0xe2 => :interface_end, # BIFF_INTERFACEEND
141
+ 0xE5 => :merge_area, # MergeArea (Not Documented)
142
+ 0x31 => :font, # Font
143
+ 0x161 => :dsf, # BIFF_DSF
144
+ 0x231 => :font, # Font
145
+ 0x293 => :style, # BIFF_STYLE
146
+ }
147
+ def initialize(params={})
148
+ #0. Check ENDIAN(Little: Intel etc. BIG: Sparc etc)
149
+ @bigendian = params.fetch(:bigendian) {
150
+ [2].pack('L').unpack('H8').first != '02000000'
151
+ }
152
+ @buff = ''
153
+ @sheet_names = {}
154
+ #1.2 Set Event Handler
155
+ set_event_handlers(params[:event_handlers] || EVENT_TABLE)
156
+ if(params[:add_handlers].is_a? Hash)
157
+ params[:add_handlers].each { |key, value|
158
+ set_event_handler(key, value)
159
+ }
160
+ end
161
+ =begin
162
+ #Experimental
163
+ $_CellHandler = $hParam{CellHandler} if($hParam{CellHandler});
164
+ $_NotSetCell = $hParam{NotSetCell};
165
+ $_Object = $hParam{Object};
166
+ =end
167
+ end
168
+ def parse(source, format=nil)
169
+ begin
170
+ #0. New $oBook
171
+ @workbook = Workbook.new
172
+ #1.Get content
173
+ @ole = OLE::Storage.new(source)
174
+ biff = @ole.search_pps(
175
+ [
176
+ OLE.asc2ucs('Book'),
177
+ OLE.asc2ucs('Workbook'),
178
+ ], true).first.data
179
+
180
+ #2. Ready for format
181
+ @workbook.format = (format || Format.new)
182
+
183
+ #3. Parse content
184
+ @global_pos = pos = 0
185
+ work = biff[pos, 4]
186
+ pos += 4
187
+ ef_flag = false
188
+ blen = biff.length
189
+ while(pos <= blen)
190
+ op, len = work.unpack('v2')
191
+ if(len)
192
+ work = biff[pos, len]
193
+ pos += len
194
+ end
195
+ #Check EF, EOF
196
+ if(op == 0xEF) #EF
197
+ ef_flag = op
198
+ elsif(op == 0x0A) #EOF
199
+ ef_flag = nil
200
+ end
201
+ unless(ef_flag)
202
+ #1. Formula String, but not string (0x207)
203
+ if(!@prev_pos.nil? && @proc_table.include?(op) && op != 0x207)
204
+ row, col, fmt = @prev_pos
205
+ @prev_pos = nil
206
+ params = {
207
+ :kind => :formula_string,
208
+ :value => '',
209
+ :format_no => fmt,
210
+ :numeric => false,
211
+ }
212
+ cell_factory(row, col, params)
213
+ end
214
+ if(prc = @proc_table[op])
215
+ #puts sprintf("%s 0x%03x %i ->%s<-", prc, op, len, work.inspect[0,30])
216
+ prc.call(op,len,work)
217
+ elsif(prc = UNIMPLEMENTED[op])
218
+ #puts sprintf("opcode not implemented: 0x%03x/%s", op.to_i, prc.to_s)
219
+ else
220
+ #puts sprintf("unknown opcode: 0x%03x (%s)", op.to_i, work.inspect[0,30])
221
+ end
222
+ (@prev_prc = op) unless(op == CONTINUE)
223
+ end
224
+ @global_pos = pos
225
+ work = biff[pos, 4] if((pos+4) <= blen)
226
+ pos += 4
227
+ if(@parse_abort)
228
+ return @workbook
229
+ end
230
+ end
231
+ @workbook
232
+ ensure
233
+ @ole.close if @ole
234
+ end
235
+ end
236
+ def set_event_handler(key, handler)
237
+ if(handler.is_a? Symbol)
238
+ handler = self.method(handler)
239
+ end
240
+ @proc_table.store(key, handler)
241
+ end
242
+ def set_event_handlers(hash)
243
+ @proc_table = {}
244
+ hash.each { |key, value|
245
+ set_event_handler(key, value)
246
+ }
247
+ end
248
+ private
249
+ VERSION_EXCEL95 = 0x500;
250
+ VERSION_EXCEL97 = 0x600;
251
+ VERSION_BIFF2 = 0x00;
252
+ VERSION_BIFF3 = 0x02;
253
+ VERSION_BIFF4 = 0x04;
254
+ VERSION_BIFF5 = 0x08;
255
+ VERSION_BIFF8 = 0x18; #Added (Not in BOOK)
256
+ CODEPAGES = {
257
+ 367 => "ASCII",
258
+ 437 => "IBM437", #(US)
259
+ 720 => "IBM720", #(OEM Arabic)
260
+ 737 => "IBM737", #(Greek)
261
+ 775 => "IBM775", #(Baltic)
262
+ 850 => "IBM850", #(Latin I)
263
+ 852 => "IBM852", #(Latin II (Central European))
264
+ 855 => "IBM855", #(Cyrillic)
265
+ 857 => "IBM857", #(Turkish)
266
+ 858 => "IBM858", #(Multilingual Latin I with Euro)
267
+ 860 => "IBM860", #(Portuguese)
268
+ 861 => "IBM861", #(Icelandic)
269
+ 862 => "IBM862", #(Hebrew)
270
+ 863 => "IBM863", #(Canadian (French))
271
+ 864 => "IBM864", #(Arabic)
272
+ 865 => "IBM865", #(Nordic)
273
+ 866 => "IBM866", #(Cyrillic (Russian))
274
+ 869 => "IBM869", #(Greek (Modern))
275
+ 874 => "WINDOWS-874", #(Thai)
276
+ 932 => "WINDOWS-932", #(Japanese Shift-JIS)
277
+ 936 => "WINDOWS-936", #(Chinese Simplified GBK)
278
+ 949 => "WINDOWS-949", #(Korean (Wansung))
279
+ 950 => "WINDOWS-950", #(Chinese Traditional BIG5)
280
+ 1200 => "UTF-16LE", #(BIFF8)
281
+ 1250 => "WINDOWS-1250", #(Latin II) (Central European)
282
+ 1251 => "WINDOWS-1251", #(Cyrillic)
283
+ 1252 => "WINDOWS-1252", #(Latin I) (BIFF4-BIFF7)
284
+ 1253 => "WINDOWS-1253", #(Greek)
285
+ 1254 => "WINDOWS-1254", #(Turkish)
286
+ 1255 => "WINDOWS-1255", #(Hebrew)
287
+ 1256 => "WINDOWS-1256", #(Arabic)
288
+ 1257 => "WINDOWS-1257", #(Baltic)
289
+ 1258 => "WINDOWS-1258", #(Vietnamese)
290
+ 1361 => "WINDOWS-1361", #(Korean (Johab))
291
+ 10000 => "MACINTOSH",
292
+ 32768 => "MACINTOSH",
293
+ 32769 => "WINDOWS-1252", #(Latin I) (BIFF2-BIFF3)
294
+ }
295
+ def ann_wk(work)
296
+ if(@annotation.length < @annotation_length)
297
+ @annotation << work[1,@annotation_length]
298
+ else
299
+ pkg_str = Worksheet::PkgString.new(@annotation, false, false, false)
300
+ @workbook.add_annotation(pkg_str)
301
+ end
302
+ end
303
+ def array(op, len, work) # DK:P297
304
+ warn "array is not implemented"
305
+ end
306
+ def biff8_annotation(op, len, work)
307
+ @annotation = ''
308
+ @annotation_length = work[10,2].unpack('v').first
309
+ end
310
+ def blank(op, len, work) # DK:P303
311
+ row, col, fmt = work.unpack('v3')
312
+ params = {
313
+ :kind => :blank,
314
+ :value => '',
315
+ :format_no => fmt,
316
+ :numeric => false,
317
+ }
318
+ cell_factory(row, col, params)
319
+ end
320
+ def bof(op, len, work) # Developers' Kit : P303
321
+ version, dtype = work.unpack('v2')
322
+
323
+ #Workbook Global
324
+ if(dtype == 0x5)
325
+ @workbook.version = version
326
+ @workbook.biffversion = if(version == VERSION_EXCEL95)
327
+ VERSION_BIFF5
328
+ else
329
+ VERSION_BIFF8
330
+ end
331
+ @current_sheet = nil
332
+ @curr_sheet_idx = nil
333
+ @prev_sheet_idx = -1
334
+
335
+ #Worksheet or Dialogsheet
336
+ elsif(dtype != 0x20)
337
+ unless(@prev_sheet_idx.nil?)
338
+ @curr_sheet_idx = @prev_sheet_idx += 1
339
+ @current_sheet = @workbook.worksheet(@curr_sheet_idx)
340
+ @current_sheet.name = @sheet_names[@global_pos]
341
+ if(work.length > 4)
342
+ @current_sheet.sheet_version,
343
+ @current_sheet.sheet_type, = work.unpack('v2')
344
+ end
345
+ else
346
+ @workbook.biffversion = (op/0x100).to_i
347
+ if([VERSION_BIFF2,
348
+ VERSION_BIFF3,
349
+ VERSION_BIFF4,
350
+ ].include?(@workbook.biffversion))
351
+ @workbook.version = @workbook.biffversion
352
+ @workbook.worksheet(@workbook.sheet_count)
353
+ @curr_sheet_idx = 0
354
+ @current_sheet = @workbook.worksheet(@curr_sheet_idx)
355
+ end
356
+ end
357
+ else
358
+ @prev_sheet_idx = @curr_sheet_idx || -1
359
+ @curr_sheet_idx = nil
360
+ @current_sheet = nil
361
+ end
362
+ end
363
+ def bool_err(op, len, work) # DK:P306
364
+ row, col, fmt = work.unpack('v3')
365
+ val, flg = work[6,2].unpack('cc')
366
+ txt = decode_bool_err(val, flg.nonzero?)
367
+ param = {
368
+ :kind => :bool_error,
369
+ :value => txt,
370
+ :format_no => fmt,
371
+ :numeric => false,
372
+ }
373
+ cell_factory(row, col, param)
374
+ end
375
+ def bound_sheet(op, len, work)
376
+ pos, = work[0,4].unpack('V')
377
+ str = ""
378
+ if(@workbook.biffversion.to_i >= VERSION_BIFF8)
379
+ str = conv_biff8_string(work[5..-1])
380
+ else
381
+ str = simple_string(work[6..-1])
382
+ end
383
+ @sheet_names.store(pos, str)
384
+ end
385
+ def cell_factory(row, col, params)
386
+ return if @current_sheet.nil?
387
+ fmt = params[:format_no]
388
+ format = params[:format] = @workbook.format(fmt)
389
+ params[:book] = @workbook
390
+ if(@encoding)
391
+ params[:encoding] = @encoding
392
+ end
393
+ cell = Worksheet::Cell.new(params)
394
+ @current_sheet.add_cell(row, col, cell)
395
+ end
396
+ def codepage(op, len, work)
397
+ codepage, = work.unpack('v')
398
+ @encoding = CODEPAGES[codepage]
399
+ end
400
+ def continue(op, len, work) #DK:P311
401
+ case @prev_prc
402
+ when SST # previous prc was Shared String Table (:sst)
403
+ str_wk(work, true)
404
+ when NOTE
405
+ ann_wk(work)
406
+ end
407
+ end
408
+ def conv_biff8(work, conv_flag=false)
409
+ chars, flg = work.unpack('vc')
410
+ ## in the usual (compressed) case, length of the string = number of chars
411
+ len = chars
412
+ uncompressed = ibool(flg & 0x01)
413
+ ext = ibool(flg & 0x04)
414
+ rich = ibool(flg & 0x08)
415
+ ecnt, rcnt, pos, str = 0, 0
416
+ #2. Rich and Ext
417
+ if(rich && ext)
418
+ pos = 9
419
+ rcnt, ecnt = work[3,6].unpack('vV')
420
+ elsif(rich) #Only Rich
421
+ pos = 5
422
+ rcnt, = work[3,2].unpack('v')
423
+ elsif(ext) #Only Ext
424
+ pos = 7
425
+ ecnt, = work[3,4].unpack('V')
426
+ else #Nothing Special
427
+ pos = 3
428
+ end
429
+ #3.Get String
430
+ if(uncompressed)
431
+ len = chars * 2
432
+ str = work[pos,len]
433
+ else
434
+ str = ''
435
+ work[pos,chars].each_byte { |byte|
436
+ str << byte.chr << "\0"
437
+ }
438
+ end
439
+ [str, uncompressed, pos, len, rcnt, ecnt]
440
+ end
441
+ def conv_biff8_data(work, conv_flag=false)
442
+ str, high, pos, len, rcnt, ecnt = conv_biff8(work, conv_flag)
443
+ #4. return
444
+ spos = pos + len + rcnt*4
445
+ epos = spos + ecnt
446
+ #4.1 Get Rich and Ext
447
+ if(work.length < epos)
448
+ [
449
+ [nil, high, nil, nil],
450
+ epos,
451
+ pos,
452
+ len,
453
+ ]
454
+ else
455
+ [
456
+ [str, high, work[spos..-1], work[spos, ecnt]],
457
+ epos,
458
+ pos,
459
+ len,
460
+ ]
461
+ end
462
+ end
463
+ def conv_biff8_string(work, conv_flag=false)
464
+ conv_biff8(work, conv_flag).first
465
+ end
466
+ def conv_dval(val)
467
+ val = val.unpack('c8').reverse.collect { |bit|
468
+ bit.to_i
469
+ }.pack('c8') if @bigendian
470
+ val.unpack('d').first
471
+ end
472
+ def decode_bool_err(val, flag=false) # DK:P306
473
+ if(flag) # ERROR
474
+ case val
475
+ when 0x00
476
+ '#NULL!'
477
+ when 0x07
478
+ '#DIV/0!'
479
+ when 0x0F
480
+ '#VALUE!'
481
+ when 0x17
482
+ '#REF!'
483
+ when 0x1D
484
+ '#NAME?'
485
+ when 0x24
486
+ '#NUM!'
487
+ when 0x2A
488
+ '#N/A!'
489
+ else
490
+ '#ERR'
491
+ end
492
+ else
493
+ (val.nonzero?) ? 'TRUE' : 'FALSE'
494
+ end
495
+ end
496
+ def default_row_height(op, len, work) # DK: P318
497
+ return if(@current_sheet.nil?)
498
+
499
+ #1. RowHeight
500
+ dum, hght = work.unpack('v2')
501
+ @current_sheet.default_row_height = hght/20.0
502
+ end
503
+ def flg_1904(op, len, work) # DK:P296
504
+ @workbook.flg_1904 = work.unpack('v').first.nonzero?
505
+ end
506
+ def footer(op, len, work) #DK:P335
507
+ return unless(@current_sheet)
508
+ @current_sheet.footer = simple_string(work)
509
+ end
510
+ def format(op, len, work) # DK:P336
511
+ fmt = if([
512
+ VERSION_BIFF2,
513
+ VERSION_BIFF3,
514
+ VERSION_BIFF4,
515
+ VERSION_BIFF5,
516
+ ].include?(@workbook.biffversion))
517
+ work[3, work[2,1].unpack('c').first]
518
+ else
519
+ conv_biff8_string(work[2..-1])
520
+ end
521
+ idx = work[0,2].unpack('v').first
522
+ @workbook.add_text_format(idx, fmt)
523
+ end
524
+ def formula(op, len, work) # DK:P336
525
+ row, col, fmt = work.unpack('v3')
526
+ flag = work[12,2].unpack('v')
527
+ if(flag == 0xffff)
528
+ kind = work[6,1].unpack('c')
529
+ val = work[8,1].unpack('c')
530
+ if((1..2).include?(kind))
531
+ txt = decode_bool_err(val, kind == 2)
532
+ params = {
533
+ :kind => :formula_bool,
534
+ :value => txt,
535
+ :format_no => fmt,
536
+ :numeric => false,
537
+ :code => nil,
538
+ }
539
+ cell_factory(row, col, params)
540
+ else
541
+ @prev_pos = [row, col, fmt]
542
+ end
543
+ else
544
+ dval = conv_dval(work[6,8])
545
+ params = {
546
+ :kind => :formula_number,
547
+ :value => dval,
548
+ :format_no => fmt,
549
+ :numeric => true,
550
+ :code => nil,
551
+ }
552
+ end
553
+ end
554
+ def header(op, len, work) # DK:P340
555
+ return unless(@current_sheet)
556
+ @current_sheet.header = simple_string(work)
557
+ end
558
+ def ibool(num)
559
+ (num != 0)
560
+ end
561
+ def integer(op, len, work) # Not in DK
562
+ row, col, fmt, dum, txt = work.unpack('v3cv')
563
+ params = {
564
+ :kind => :integer,
565
+ :value => txt,
566
+ :format_no => fmt,
567
+ :numeric => false,
568
+ }
569
+ cell_factory(row, col, params)
570
+ end
571
+ def label(op, len, work) # DK:P344
572
+ row, col, fmt = work.unpack('v3')
573
+ #BIFF8
574
+ label, code = nil
575
+ if(@workbook.biffversion && @workbook.biffversion >= VERSION_BIFF8)
576
+ buff, tln, pos, lens = conv_biff8_data(work[6..-1], true)
577
+ label = buff.at(0)
578
+ code = buff.at(1) ? :ucs2 : nil
579
+ #Before BIFF8
580
+ else
581
+ label = work[8..-1]
582
+ code = :_native_
583
+ end
584
+ params = {
585
+ :kind => :label,
586
+ :value => label,
587
+ :format_no => fmt,
588
+ :numeric => false,
589
+ :code => code,
590
+ }
591
+ cell_factory(row, col, params)
592
+ end
593
+ def label_sst(op, len, work) # DK: P345
594
+ row, col, fmt, idx = work.unpack('v3V')
595
+ reference = @workbook.pkg_str(idx) or return
596
+ code = (reference.unicode) ? :ucs2 : nil
597
+ params = {
598
+ :kind => :packed_idx,
599
+ :value => reference.text,
600
+ :format_no => fmt,
601
+ :numeric => false,
602
+ :code => code,
603
+ :rich => reference.rich
604
+ }
605
+ cell_factory(row, col, params)
606
+ end
607
+ def mul_rk(op, len, work) # DK:P349
608
+ return nil unless (@workbook.sheet_count > 0);
609
+
610
+ row, scol = work.unpack('v2')
611
+ ecol, = work[-2,2].unpack('v')
612
+ pos = 4
613
+
614
+ scol.upto(ecol) { |col|
615
+ fmt, val = unpack_rk_rec(work[pos,6])
616
+ params = {
617
+ :kind => :mul_rk,
618
+ :value => val,
619
+ :format_no => fmt,
620
+ :numeric => true,
621
+ }
622
+ cell_factory(row, col, params)
623
+ pos += 6
624
+ }
625
+ end
626
+ def mul_blank(op, len, work) # DK:P349
627
+ row, scol = work.unpack('v2')
628
+ ecol, = work[-2,2].unpack('v')
629
+ pos = 4
630
+
631
+ scol.upto(ecol) { |col|
632
+ if(snip = work[pos,2])
633
+ fmt, = snip.unpack('v')
634
+ params = {
635
+ :kind => :mul_blank,
636
+ :value => '',
637
+ :format_no => fmt,
638
+ :numeric => false,
639
+ }
640
+ cell_factory(row, col, params)
641
+ pos += 2
642
+ end
643
+ }
644
+ end
645
+ =begin
646
+ def name(op, len, work) # DK: P350
647
+ gr_bit, key, ch, cce, xals, tab, cust, dsc, hep, status = work.unpack('vc2v3c4')
648
+ #Builtin Name + Length == 1
649
+ if((gr_bit & 0x20).nonzero? && (ch == 1))
650
+ #BIFF8
651
+ if(@workbook.biffversion >= VERSION_BIFF8)
652
+ name = work[14,2].unpack('n').first
653
+ sheet = work[8,2].unpack('v').first - 1
654
+ sheet_w, area = parse_name_area(work[16..-1])
655
+ if(name == 6) #PrintArea
656
+ @workbook.print_area[sheet] = area
657
+ elsif(name == 7) #Title
658
+ title_r = []
659
+ title_c = []
660
+ area.each { |array|
661
+ if(array.at(3) == 0xFF) #Row Title
662
+ title_r.push([array.at(0), array.at(2)])
663
+ else #Col Title
664
+ title_c.push([array.at(1), array.at(3)])
665
+ end
666
+ }
667
+ @workbook.print_title[sheet] = {:row=>title_r, :column=>title_c}
668
+ end
669
+ else
670
+ name = work[14,1].unpack('c')
671
+ sheet, area = parse_name_area95(work[15..-1])
672
+ if(name == 6) #PrintArea
673
+ @workbook.print_area[sheet] = area
674
+ elsif(name == 7) #Title
675
+ title_r = []
676
+ title_c = []
677
+ area.each { |array|
678
+ if(array.at(3) == 0xFF) #Row Title
679
+ title_r.push([array.at(0), array.at(2)])
680
+ else #Col Title
681
+ title_c.push([array.at(1), array.at(3)])
682
+ end
683
+ }
684
+ @workbook.print_title[sheet] = {:row=>title_r, :column=>title_c}
685
+ end
686
+ end
687
+ end
688
+ end
689
+ =end
690
+ def note(op, len, work)
691
+ return if(@current_sheet.nil?)
692
+ ann = Annotation.new
693
+ row, col, clen = work.unpack('v3')
694
+ if(clen.nonzero?)
695
+ ann << work[6,clen]
696
+ else
697
+ idx, authlen = work[6,4].unpack('v2')
698
+ if(authlen > 0)
699
+ ann.author = work[11, authlen]
700
+ end
701
+ reference = @workbook.annotation(idx - 1) or return
702
+ ann << reference.text
703
+ end
704
+ @current_sheet.cell(row, col).annotation = ann
705
+ end
706
+ def number(op, len, work) # DK: P354
707
+ row, col, fmt = work.unpack('v3')
708
+ dval = conv_dval(work[6,8])
709
+ params = {
710
+ :kind => :number,
711
+ :value => dval,
712
+ :format_no => fmt,
713
+ :numeric => true,
714
+ }
715
+ cell_factory(row, col, params)
716
+ end
717
+ def rk(op, len, work) # DK:P401
718
+ row, col = work.unpack('v2')
719
+
720
+ fmt, txt = unpack_rk_rec(work[4,6])
721
+ params = {
722
+ :kind => :rk,
723
+ :value => txt,
724
+ :format_no => fmt,
725
+ :numeric => true,
726
+ :code => nil,
727
+ }
728
+ cell_factory(row, col, params)
729
+ end
730
+ def row(op, len, work) # DK:P403
731
+ return if(@current_sheet.nil?)
732
+
733
+ #0. Get Worksheet info (MaxRow, MaxCol, MinRow, MinCol)
734
+ row, scol, ecol, hght, nil1, nil2, gr, xf = work.unpack('v8')
735
+ ecol -= 1
736
+
737
+ #1. RowHeight
738
+ if(ibool(gr & 0x20)) # Height == 0
739
+ @current_sheet.set_row_height(row, 0)
740
+ else
741
+ @current_sheet.set_row_height(row, hght/20.0)
742
+ end
743
+ @current_sheet.set_dimensions(row, scol, ecol)
744
+ end
745
+ def rstring(op, len, work) # DK:P405
746
+ row, col, fmt, tln = work.unpack('v4')
747
+ params = {
748
+ :kind => :rstring,
749
+ :value => work[8,tln],
750
+ :format_no => fmt,
751
+ :numeric => false,
752
+ :code => :_native_,
753
+ }
754
+ #Has STRN
755
+ if(work.length > (8+tln))
756
+ params.store(:rich, work[8+tln..-1])
757
+ end
758
+ cell_factory(row, col, params)
759
+ end
760
+ def setup(op, len, work) # DK: P409
761
+ return unless(ws = @current_sheet)
762
+ ws.paper, ws.scale, ws.page_start, ws.fit_width, ws.fit_height, \
763
+ gr_bit, ws.resolution, ws.v_resolution = work.unpack('v8')
764
+
765
+ ws.header_margin = conv_dval(work[16,8]) * 127 / 50
766
+ ws.footer_margin = conv_dval(work[24,8]) * 127 / 50
767
+ ws.copies, = work[32,2].unpack('v') # $oWkS->{Copis}
768
+ ws.left_to_right = ibool(gr_bit & 0x01)
769
+ ws.landscape = ibool(gr_bit & 0x02)
770
+ ws.no_pls = ibool(gr_bit & 0x04)
771
+ ws.no_color = ibool(gr_bit & 0x08)
772
+ ws.draft = ibool(gr_bit & 0x10)
773
+ ws.notes = ibool(gr_bit & 0x20)
774
+ ws.no_orient = ibool(gr_bit & 0x40)
775
+ ws.use_page = ibool(gr_bit & 0x80)
776
+ end
777
+ def simple_string(work)
778
+ return "" if(work.empty?)
779
+ #BIFF8
780
+ if(@workbook.biffversion >= VERSION_BIFF8)
781
+ str = conv_biff8_string(work)
782
+ (str == "\x00\x00") ? nil : str
783
+ #Before BIFF8
784
+ else
785
+ len, = work.unpack('c')
786
+ str = work[1,len]
787
+ (str == "\x00\x00\x00") ? nil : str
788
+ end
789
+ end
790
+ def sst(op, len, work) # DK:P413
791
+ str_wk(work[8..-1])
792
+ end
793
+ def string(op, len, work) # DK:P414
794
+ #Position (not enough for ARRAY)
795
+ return if @prev_pos.nil?
796
+ row, col, fmt = @prev_pos
797
+ @prev_pos = nil
798
+
799
+ txt, code = nil
800
+ if(@workbook.biffversion == VERSION_BIFF8)
801
+ buff, = conv_biff8_data(work, true)
802
+ txt = buff.at(0)
803
+ code = (buff.at(1)) ? :ucs2 : nil
804
+ elsif(@workbook.biffversion == VERSION_BIFF5)
805
+ code = :_native_
806
+ tln, = work.unpack('v')
807
+ txt = work[2,tln]
808
+ else
809
+ code = :_native_
810
+ tln, = work.unpack('c')
811
+ txt = work[1,tln]
812
+ end
813
+ params = {
814
+ :kind => :string,
815
+ :value => txt,
816
+ :format_no => fmt,
817
+ :numeric => false,
818
+ :code => code,
819
+ }
820
+ cell_factory(row, col, params)
821
+ end
822
+ def str_wk(work, cnt=nil) # DK:P280
823
+ #1. Continue
824
+ #1.1 Before No Data No
825
+ if(cnt.nil? || @buff == '')
826
+ @buff << work
827
+ #1.1 No PrevCond
828
+ elsif(@prev_cond.nil?)
829
+ @buff << work[1..-1]
830
+ else
831
+ cnt1st = work[0] # 1st byte of Continue may be a GR byte
832
+ stp, lens = @prev_info
833
+ lenb = @buff.length
834
+
835
+ #1.1 Not in String
836
+ if(lenb >= (stp + lens))
837
+ @buff << work
838
+ #1.2 Same code (Unicode or ASCII)
839
+ elsif(((@prev_cond ? 1 : 0) & 0x01) == (cnt1st.bytes.first & 0x01))
840
+ @buff << work[1..-1]
841
+ #1.3 Diff code (Unicode or ASCII)
842
+ else
843
+ # if(ibool(cnt1st & 0x01))
844
+ if(ibool(cnt1st.bytes.first & 0x01))
845
+ dum, gr = @buff.unpack('vc')
846
+ @buff[2,1] = [gr | 0x01].pack('c')
847
+ (lenb-stp).downto(1) { |idx|
848
+ @buff[stp+idx,0] = "\x00"
849
+ }
850
+ else
851
+ diff = stp + lens - lenb
852
+ #(diff/2).downto(1) { |idx|
853
+ (work.length).downto(1) { |idx|
854
+ work[idx,0] = "\x00"
855
+ }
856
+ end
857
+ @buff << work[1..-1]
858
+ end
859
+ end
860
+ @prev_cond = nil
861
+ @prev_info = nil
862
+
863
+ while(@buff.length >= 4)
864
+ buff, len, stpos, lens = conv_biff8_data(@buff, true)
865
+ unless(buff[0].nil?)
866
+ pkg_str = Worksheet::PkgString.new(*buff)
867
+ @workbook.add_pkg_str(pkg_str)
868
+ @buff = @buff[len..-1]
869
+ else
870
+ @prev_cond = buff[1]
871
+ @prev_info = [stpos, lens]
872
+ break
873
+ end
874
+ end
875
+ end
876
+ def unpack_rk_rec(arg) # DK:P401
877
+ ef, = arg[0,2].unpack('v')
878
+ lwk, = arg[2,4]
879
+ swk = lwk.unpack('c4').reverse.pack('c4')
880
+ ptn = (swk[3,1].unpack('c').first & 0x03)
881
+ null = "\0\0\0\0"
882
+ res = nil
883
+ if(ptn == 0)
884
+ res, = ((@bigendian) ? swk + null : null + lwk).unpack('d')
885
+ elsif(ptn == 1)
886
+
887
+ swk[3] &&= [(swk[3,1].unpack('c').first & 0xFC)].pack('c')[0]
888
+ lwk[0] &&= [(lwk[0,1].unpack('c').first & 0xFC)].pack('c')[0]
889
+ res = ((@bigendian) ? swk + null : null + lwk).unpack('d').first.to_f / 100.0
890
+ elsif(ptn == 2)
891
+ bin, = swk.unpack('B32')
892
+ wklb = [((bin[0,1]*2) + bin[0,30])].pack('B32')
893
+ wkl = (@bigendian) ? wklb : wklb.unpack('c4').reverse.pack('c4')
894
+ res, = wkl.unpack('i')
895
+ else
896
+ ub, = swk.unpack('B32')
897
+ wklb = [((ub[0,1]*2) + ub[0,30])].pack('B32')
898
+ wkl = (@bigendian) ? wklb : wklb.unpack('c4').reverse.pack('c4')
899
+ res = wkl.unpack('i').first / 100.00
900
+ end
901
+ [ef, res]
902
+ end
903
+ def ws_bool(op, len, work) # DK: P452
904
+ return if(@current_sheet.nil?)
905
+ fit = ibool(work.unpack('v').first & 0x100)
906
+ @current_sheet.page_fit = fit
907
+ end
908
+ def xf(op, len, work) # DK:P453
909
+ fnt, idx, lock, hidden, style, i123, alh, wrap, alv, justl = nil
910
+ rotate, ind, shrink, merge, readdir, bdr_d, bdr_sl, bdr_sr = nil
911
+ bdr_st, bdr_sb, bdr_sd, bdr_cl, bdr_cr, bdr_ct, bdr_cb = nil
912
+ bdr_cd, fill_p, fill_cf, fill_cb = nil
913
+
914
+ if(@workbook.biffversion == VERSION_BIFF8)
915
+ fnt, idx, gen1, align, gen2,
916
+ bdr1, bdr2, bdr3, ptn = work.unpack('v7Vv')
917
+ lock = ibool(gen1 & 0x01)
918
+ hidden = ibool(gen1 & 0x02)
919
+ style = ibool(gen1 & 0x04)
920
+ i123 = ibool(gen1 & 0x08)
921
+
922
+ alh = (align & 0x07)
923
+ wrap = ibool(align & 0x08)
924
+ alv = (align & 0x70) / 0x10
925
+ justl = ibool(align & 0x80)
926
+
927
+ rotate = ((align & 0xFF00) / 0x100) & 0x00FF
928
+ rotate = 90 if(rotate == 255)
929
+ rotate = (90 - rotate) if(rotate > 90)
930
+
931
+ ind = (gen2 & 0x0F)
932
+ shrink = ibool(gen2 & 0x10)
933
+ merge = ibool(gen2 & 0x20)
934
+ readdir = ((gen2 & 0xC0) / 0x40) & 0x03
935
+
936
+ bdr_sl = bdr1 & 0x0F
937
+ bdr_sr = ((bdr1 & 0xF0) / 0x10) & 0x0F
938
+ bdr_st = ((bdr1 & 0xF00) / 0x100) & 0x0F
939
+ bdr_sb = ((bdr1 & 0xF000) / 0x1000) & 0x0F
940
+
941
+ bdr_cl = ((bdr2 & 0x7F)) & 0x7F
942
+ bdr_cr = ((bdr2 & 0x3F80) / 0x80) & 0x7F
943
+ bdr_d = ((bdr2 & 0xC000) / 0x4000) & 0x7F
944
+
945
+ bdr_ct = ((bdr3 & 0x7F)) & 0x7F
946
+ bdr_cb = ((bdr3 & 0x3F80) / 0x80) & 0x7F
947
+ bdr_cd = ((bdr2 & 0x1FC000) / 0x4000) & 0x7F
948
+ bdr_sd = ((bdr2 & 0x1E00000) / 0x200000) & 0x7F
949
+ fill_p = ((bdr2 & 0xFC000000) / 0x4000000) & 0x3F
950
+
951
+ fill_cf = ptn & 0x7F
952
+ fill_cb = ((ptn & 0x3F80) / 0x80) & 0x7F
953
+ else
954
+ fnt, idx, gen1, align, ptn1, ptn2, bdr1, bdr2 = work.unpack('v8')
955
+
956
+ lock = ibool(gen1 & 0x01)
957
+ hidden = ibool(gen1 & 0x02)
958
+ style = ibool(gen1 & 0x04)
959
+ i123 = ibool(gen1 & 0x08)
960
+
961
+ alh = (align & 0x07)
962
+ wrap = ibool(align & 0x08)
963
+ alv = (align & 0x70) / 0x10
964
+ justl = ibool(align & 0x80)
965
+
966
+ rotate = ((align & 0x300) / 0x100) & 0x03
967
+
968
+ fill_cf = ptn1 & 0x7F
969
+ fill_cb = ((ptn1 & 0x1F80) / 0x80) & 0x7F
970
+
971
+ fill_p = ptn2 & 0x3F
972
+ bdr_sb = ((ptn2 & 0x1C0) / 0x40) & 0x07
973
+ bdr_cb = ((ptn2 & 0xFE00) / 0x200) & 0x7F
974
+
975
+ bdr_st = bdr1 & 0x07
976
+ bdr_sl = ((bdr1 & 0x38) / 0x8) & 0x07
977
+ bdr_sr = ((bdr1 & 0x1C0) / 0x40) & 0x07
978
+ bdr_ct = ((bdr1 & 0xFE00) / 0x200) & 0x7F
979
+
980
+ bdr_cl = (bdr2 & 0x7F) & 0x7F
981
+ bdr_cr = ((bdr2 & 0x3F80) / 0x80) & 0x7F
982
+ end
983
+
984
+ params = {
985
+ :font_no => fnt,
986
+ #:font => workbook.fonts[fnt],
987
+ :fmt_idx => idx,
988
+ :lock => lock,
989
+ :hidden => hidden,
990
+ :style => style,
991
+ :key_123 => i123,
992
+ :align_h => alh,
993
+ :wrap => wrap,
994
+ :align_v => alv,
995
+ :just_last => justl,
996
+ :rotate => rotate,
997
+ :indent => ind,
998
+ :shrink => shrink,
999
+ :merge => merge,
1000
+ :read_dir => readdir,
1001
+ :border_style => [bdr_sl, bdr_sr, bdr_st, bdr_sb],
1002
+ :border_color => [bdr_cl, bdr_cr, bdr_ct, bdr_cb],
1003
+ :border_diag => [bdr_d, bdr_sd, bdr_cd],
1004
+ :fill => [fill_p, fill_cf, fill_cb],
1005
+ }
1006
+ if(@encoding)
1007
+ params.store(:encoding, @encoding)
1008
+ end
1009
+ @workbook.add_cell_format(Format.new(params))
1010
+ end
1011
+ end
1012
+ end
1013
+ end