parseexcel 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # ParseExcel -- Spreadsheet -- 03.06.2003 -- hwyss@ywesee.com
3
+
4
+ require 'parseexcel/olestorage'
5
+ require 'parseexcel/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,1032 @@
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/olestorage'
26
+ require 'parseexcel/workbook'
27
+ require 'parseexcel/worksheet'
28
+ require 'parseexcel/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
+ 0x85 => :bound_sheet, # BoundSheet
71
+
72
+ 0x92 => :palette, # Palette, fgp
73
+
74
+ 0x99 => :standard_width, # Standard Col
75
+ #Develpers' Kit P293
76
+ =end
77
+ 0xA1 => :setup, # SETUP
78
+ 0xBD => :mul_rk, # MULRK
79
+ 0xBE => :mul_blank, # MULBLANK
80
+ 0xD6 => :rstring, # RString
81
+ #Develpers' Kit P294
82
+ 0xe0 => :xf, # ExTended Format
83
+ =begin
84
+ 0xE5 => :merge_area, # MergeArea (Not Documented)
85
+ =end
86
+ SST => :sst, # Shared String Table
87
+ 0xFD => :label_sst, # Label SST
88
+ NOTE => :biff8_annotation,
89
+ #Develpers' Kit P295
90
+ 0x201 => :blank, # Blank
91
+
92
+ 0x202 => :integer, # Integer(Not Documented)
93
+ 0x203 => :number, # Number
94
+ 0x204 => :label , # Label
95
+ 0x205 => :bool_err, # BoolErr
96
+ 0x207 => :string, # STRING
97
+ 0x208 => :row, # RowData
98
+ 0x221 => :array, # Array (Consider)
99
+ 0x225 => :default_row_height,# Consider
100
+ =begin
101
+ 0x31 => :font, # Font
102
+ 0x231 => :font, # Font
103
+
104
+ =end
105
+ 0x27E => :rk, # RK
106
+ 0x41E => :format, # Format
107
+ 0x06 => :formula, # Formula
108
+ 0x406 => :formula, # Formula
109
+ 0x09 => :bof, # BOF(BIFF2)
110
+ 0x209 => :bof, # BOF(BIFF3)
111
+ 0x409 => :bof, # BOF(BIFF4)
112
+ 0x809 => :bof, # BOF(BIFF5-8)
113
+ }
114
+ UNIMPLEMENTED = {
115
+ #Develpers' Kit P291
116
+ 0x18 => :name, # NAME(?)
117
+ 0x1A => :v_page_break, # Veritical Page Break
118
+ 0x1B => :h_page_break, # Horizontal Page Break
119
+ 0x26 => :margin, # Left Mergin
120
+ 0x27 => :margin, # Right Mergin
121
+ 0x28 => :margin, # Top Mergin
122
+ 0x29 => :margin, # Bottom Mergin
123
+ 0x2A => :print_headers, # Print Headers
124
+ 0x2B => :print_gridlines, # Print Gridlines
125
+ #Develpers' Kit P292
126
+ 0x55 => :def_col_width, # Consider
127
+ 0x5C => :write_access, # WRITEACCESS
128
+ 0x7D => :col_info, # Colinfo
129
+ 0x81 => :ws_bool, # WSBOOL
130
+ 0x83 => :h_center, # HCENTER
131
+ 0x84 => :v_center, # VCENTER
132
+ 0x85 => :bound_sheet, # BoundSheet
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
+ #1.2 Set Event Handler
154
+ set_event_handlers(params[:event_handlers] || EVENT_TABLE)
155
+ if(params[:add_handlers].is_a? Hash)
156
+ params[:add_handlers].each { |key, value|
157
+ set_event_handler(key, value)
158
+ }
159
+ end
160
+ =begin
161
+ #Experimental
162
+ $_CellHandler = $hParam{CellHandler} if($hParam{CellHandler});
163
+ $_NotSetCell = $hParam{NotSetCell};
164
+ $_Object = $hParam{Object};
165
+ =end
166
+ end
167
+ def parse(source, format=nil)
168
+ begin
169
+ #0. New $oBook
170
+ @workbook = Workbook.new
171
+ #1.Get content
172
+ @ole = OLE::Storage.new(source)
173
+ biff = @ole.search_pps(
174
+ [
175
+ OLE.asc2ucs('Book'),
176
+ OLE.asc2ucs('Workbook'),
177
+ ], true).first.data
178
+
179
+ #2. Ready for format
180
+ @workbook.format = (format || Format.new)
181
+
182
+ #3. Parse content
183
+ pos = 0
184
+ work = biff[pos, 4]
185
+ pos += 4
186
+ ef_flag = false
187
+ blen = biff.length
188
+ while(pos <= blen)
189
+ op, len = work.unpack('v2')
190
+ #puts "*"*33
191
+ #puts sprintf("0x%03x %i ->%s<-", op, len, work.inspect[0,200])
192
+ #p "#{biff.length} / #{pos}"
193
+ #p work, op, len
194
+ if(len)
195
+ work = biff[pos, len]
196
+ pos += len
197
+ end
198
+ #Check EF, EOF
199
+ if(op == 0xEF) #EF
200
+ ef_flag = op
201
+ elsif(op == 0x0A) #EOF
202
+ ef_flag = nil
203
+ end
204
+ #puts "ef_flag: =>#{ef_flag}<="
205
+ unless(ef_flag)
206
+ #1. Formula String, but not string (0x207)
207
+ if(!@prev_pos.nil? && @proc_table.include?(op) && op != 0x207)
208
+ row, col, fmt = @prev_pos
209
+ @prev_pos = nil
210
+ params = {
211
+ :kind => :formula_string,
212
+ :value => '',
213
+ :format_no => fmt,
214
+ :numeric => false,
215
+ }
216
+ cell_factory(row, col, params)
217
+ end
218
+ if(prc = @proc_table[op])
219
+ #puts sprintf("%s 0x%03x %i ->%s<-", prc, op, len, work.inspect[0,30])
220
+ prc.call(op,len,work)
221
+ elsif(prc = UNIMPLEMENTED[op])
222
+ #puts sprintf("opcode not implemented: 0x%03x/%s", op.to_i, prc.to_s)
223
+ else
224
+ #puts sprintf("unknown opcode: 0x%03x (%s)", op.to_i, work.inspect[0,30])
225
+ end
226
+ (@prev_prc = op) unless(op == CONTINUE)
227
+ end
228
+ work = biff[pos, 4] if((pos+4) <= blen)
229
+ pos += 4
230
+ if(@parse_abort)
231
+ return @workbook
232
+ end
233
+ end
234
+ @workbook
235
+ ensure
236
+ @ole.close if @ole
237
+ end
238
+ end
239
+ def set_event_handler(key, handler)
240
+ if(handler.is_a? Symbol)
241
+ handler = self.method(handler)
242
+ end
243
+ @proc_table.store(key, handler)
244
+ end
245
+ def set_event_handlers(hash)
246
+ @proc_table = {}
247
+ hash.each { |key, value|
248
+ set_event_handler(key, value)
249
+ }
250
+ end
251
+ private
252
+ VERSION_EXCEL95 = 0x500;
253
+ VERSION_EXCEL97 = 0x600;
254
+ VERSION_BIFF2 = 0x00;
255
+ VERSION_BIFF3 = 0x02;
256
+ VERSION_BIFF4 = 0x04;
257
+ VERSION_BIFF5 = 0x08;
258
+ VERSION_BIFF8 = 0x18; #Added (Not in BOOK)
259
+ CODEPAGES = {
260
+ 367 => "ASCII",
261
+ 437 => "IBM437", #(US)
262
+ 720 => "IBM720", #(OEM Arabic)
263
+ 737 => "IBM737", #(Greek)
264
+ 775 => "IBM775", #(Baltic)
265
+ 850 => "IBM850", #(Latin I)
266
+ 852 => "IBM852", #(Latin II (Central European))
267
+ 855 => "IBM855", #(Cyrillic)
268
+ 857 => "IBM857", #(Turkish)
269
+ 858 => "IBM858", #(Multilingual Latin I with Euro)
270
+ 860 => "IBM860", #(Portuguese)
271
+ 861 => "IBM861", #(Icelandic)
272
+ 862 => "IBM862", #(Hebrew)
273
+ 863 => "IBM863", #(Canadian (French))
274
+ 864 => "IBM864", #(Arabic)
275
+ 865 => "IBM865", #(Nordic)
276
+ 866 => "IBM866", #(Cyrillic (Russian))
277
+ 869 => "IBM869", #(Greek (Modern))
278
+ 874 => "WINDOWS-874", #(Thai)
279
+ 932 => "WINDOWS-932", #(Japanese Shift-JIS)
280
+ 936 => "WINDOWS-936", #(Chinese Simplified GBK)
281
+ 949 => "WINDOWS-949", #(Korean (Wansung))
282
+ 950 => "WINDOWS-950", #(Chinese Traditional BIG5)
283
+ 1200 => "UTF-16LE", #(BIFF8)
284
+ 1250 => "WINDOWS-1250", #(Latin II) (Central European)
285
+ 1251 => "WINDOWS-1251", #(Cyrillic)
286
+ 1252 => "WINDOWS-1252", #(Latin I) (BIFF4-BIFF7)
287
+ 1253 => "WINDOWS-1253", #(Greek)
288
+ 1254 => "WINDOWS-1254", #(Turkish)
289
+ 1255 => "WINDOWS-1255", #(Hebrew)
290
+ 1256 => "WINDOWS-1256", #(Arabic)
291
+ 1257 => "WINDOWS-1257", #(Baltic)
292
+ 1258 => "WINDOWS-1258", #(Vietnamese)
293
+ 1361 => "WINDOWS-1361", #(Korean (Johab))
294
+ 10000 => "MACINTOSH",
295
+ 32768 => "MACINTOSH",
296
+ 32769 => "WINDOWS-1252", #(Latin I) (BIFF2-BIFF3)
297
+ }
298
+ def ann_wk(work)
299
+ if(@annotation.length < @annotation_length)
300
+ @annotation << work[1,@annotation_length]
301
+ else
302
+ pkg_str = Worksheet::PkgString.new(@annotation, false, false, false)
303
+ @workbook.add_annotation(pkg_str)
304
+ end
305
+ end
306
+ def array(op, len, work) # DK:P297
307
+ warn "array is not implemented"
308
+ end
309
+ def biff8_annotation(op, len, work)
310
+ @annotation = ''
311
+ @annotation_length = work[10,2].unpack('v').first
312
+ end
313
+ def blank(op, len, work) # DK:P303
314
+ row, col, fmt = work.unpack('v3')
315
+ params = {
316
+ :kind => :blank,
317
+ :value => '',
318
+ :format_no => fmt,
319
+ :numeric => false,
320
+ }
321
+ cell_factory(row, col, params)
322
+ end
323
+ def bof(op, len, work) # Developers' Kit : P303
324
+ version, dtype = work.unpack('v2')
325
+
326
+ #Workbook Global
327
+ if(dtype == 0x5)
328
+ #puts "dtype: #{dtype}(0x5)"
329
+ @workbook.version = version
330
+ @workbook.biffversion = if(version == VERSION_EXCEL95)
331
+ VERSION_BIFF5
332
+ else
333
+ VERSION_BIFF8
334
+ end
335
+ @current_sheet = nil
336
+ @curr_sheet_idx = nil
337
+ @prev_sheet_idx = -1
338
+
339
+ #Worksheet or Dialogsheet
340
+ elsif(dtype != 0x20)
341
+ #puts "dtype: #{dtype}(!0x20)"
342
+ unless(@prev_sheet_idx.nil?)
343
+ #puts "we have a prev_sheet_index - make a new sheet"
344
+ @curr_sheet_idx = @prev_sheet_idx += 1
345
+ @current_sheet = @workbook.worksheet(@curr_sheet_idx)
346
+ if(work.length > 4)
347
+ @current_sheet.sheet_version,
348
+ @current_sheet.sheet_type, = work.unpack('v2')
349
+ end
350
+ else
351
+ #puts "no current sheet_index so far..."
352
+ @workbook.biffversion = (op/0x100).to_i
353
+ if([VERSION_BIFF2,
354
+ VERSION_BIFF3,
355
+ VERSION_BIFF4,
356
+ ].include?(@workbook.biffversion))
357
+ #puts "found biffversion #{sprintf('%04x', @workbook.biffversion)}"
358
+ @workbook.version = @workbook.biffversion
359
+ @workbook.worksheet(@workbook.sheet_count)
360
+ @curr_sheet_idx = 0
361
+ @current_sheet = @workbook.worksheet(@curr_sheet_idx)
362
+ end
363
+ end
364
+ else
365
+ @prev_sheet_idx = @curr_sheet_idx || -1
366
+ @curr_sheet_idx = nil
367
+ @current_sheet = nil
368
+ end
369
+ end
370
+ def bool_err(op, len, work) # DK:P306
371
+ row, col, fmt = work.unpack('v3')
372
+ val, flg = work[6,2].unpack('cc')
373
+ txt = decode_bool_err(val, flg.nonzero?)
374
+ param = {
375
+ :kind => :bool_error,
376
+ :value => txt,
377
+ :format_no => fmt,
378
+ :numeric => false,
379
+ }
380
+ cell_factory(row, col, param)
381
+ end
382
+ def cell_factory(row, col, params)
383
+ return if @current_sheet.nil?
384
+ fmt = params[:format_no]
385
+ format = params[:format] = @workbook.format(fmt)
386
+ params[:book] = @workbook
387
+ if(@encoding)
388
+ params[:encoding] = @encoding
389
+ end
390
+ cell = Worksheet::Cell.new(params)
391
+ #p format
392
+ #cell.type = @workbook.format.cell_type(cell) unless format.nil?
393
+ @current_sheet.add_cell(row, col, cell)
394
+ end
395
+ def codepage(op, len, work)
396
+ codepage, = work.unpack('v')
397
+ @encoding = CODEPAGES[codepage]
398
+ end
399
+ def continue(op, len, work) #DK:P311
400
+ case @prev_prc
401
+ when SST # previous prc was Shared String Table (:sst)
402
+ str_wk(work, true)
403
+ when NOTE
404
+ ann_wk(work)
405
+ end
406
+ end
407
+ def conv_biff8(work, conv_flag=false)
408
+ chars, flg = work.unpack('vc')
409
+ ## in the usual (compressed) case, length of the string = number of chars
410
+ len = chars
411
+ uncompressed = ibool(flg & 0x01)
412
+ ext = ibool(flg & 0x04)
413
+ rich = ibool(flg & 0x08)
414
+ ecnt, rcnt, pos, str = 0, 0
415
+ #2. Rich and Ext
416
+ if(rich && ext)
417
+ pos = 9
418
+ rcnt, ecnt = work[3,6].unpack('vV')
419
+ elsif(rich) #Only Rich
420
+ pos = 5
421
+ rcnt, = work[3,2].unpack('v')
422
+ elsif(ext) #Only Ext
423
+ pos = 7
424
+ ecnt, = work[3,4].unpack('V')
425
+ else #Nothing Special
426
+ pos = 3
427
+ end
428
+ #3.Get String
429
+ if(uncompressed)
430
+ len = chars * 2
431
+ str = work[pos,len]
432
+ else
433
+ str = ''
434
+ work[pos,chars].each_byte { |byte|
435
+ str << byte.chr << "\0"
436
+ }
437
+ end
438
+ #puts [uncompressed, pos, len, rcnt, ecnt].inspect
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
+ #puts "work: #{work.length} < epos: #{epos} ?"
448
+ if(work.length < epos)
449
+ [
450
+ [nil, high, nil, nil],
451
+ epos,
452
+ pos,
453
+ len,
454
+ ]
455
+ else
456
+ [
457
+ [str, high, work[spos..-1], work[spos, ecnt]],
458
+ epos,
459
+ pos,
460
+ len,
461
+ ]
462
+ end
463
+ end
464
+ def conv_biff8_string(work, conv_flag=false)
465
+ conv_biff8(work, conv_flag).first
466
+ end
467
+ def conv_dval(val)
468
+ val = val.unpack('c8').reverse.collect { |bit|
469
+ bit.to_i
470
+ }.pack('c8') if @bigendian
471
+ val.unpack('d').first
472
+ end
473
+ def decode_bool_err(val, flag=false) # DK:P306
474
+ if(flag) # ERROR
475
+ case val
476
+ when 0x00
477
+ '#NULL!'
478
+ when 0x07
479
+ '#DIV/0!'
480
+ when 0x0F
481
+ '#VALUE!'
482
+ when 0x17
483
+ '#REF!'
484
+ when 0x1D
485
+ '#NAME?'
486
+ when 0x24
487
+ '#NUM!'
488
+ when 0x2A
489
+ '#N/A!'
490
+ else
491
+ '#ERR'
492
+ end
493
+ else
494
+ (val.nonzero?) ? 'TRUE' : 'FALSE'
495
+ end
496
+ end
497
+ def default_row_height(op, len, work) # DK: P318
498
+ return if(@current_sheet.nil?)
499
+
500
+ #1. RowHeight
501
+ dum, hght = work.unpack('v2')
502
+ @current_sheet.default_row_height = hght/20.0
503
+ end
504
+ def flg_1904(op, len, work) # DK:P296
505
+ @workbook.flg_1904 = work.unpack('v').first.nonzero?
506
+ end
507
+ def footer(op, len, work) #DK:P335
508
+ return unless(@current_sheet)
509
+ @current_sheet.footer = simple_string(work)
510
+ end
511
+ def format(op, len, work) # DK:P336
512
+ fmt = if([
513
+ VERSION_BIFF2,
514
+ VERSION_BIFF3,
515
+ VERSION_BIFF4,
516
+ VERSION_BIFF5,
517
+ ].include?(@workbook.biffversion))
518
+ work[3, work[2,1].unpack('c').first]
519
+ else
520
+ conv_biff8_string(work[2..-1])
521
+ end
522
+ idx = work[0,2].unpack('v').first
523
+ @workbook.add_text_format(idx, fmt)
524
+ end
525
+ def formula(op, len, work) # DK:P336
526
+ row, col, fmt = work.unpack('v3')
527
+ flag = work[12,2].unpack('v')
528
+ if(flag == 0xffff)
529
+ kind = work[6,1].unpack('c')
530
+ val = work[8,1].unpack('c')
531
+ if(1..2.include?(kind))
532
+ txt = decode_bool_err(val, kind == 2)
533
+ params = {
534
+ :kind => :formula_bool,
535
+ :value => txt,
536
+ :format_no => fmt,
537
+ :numeric => false,
538
+ :code => nil,
539
+ }
540
+ cell_factory(row, col, params)
541
+ else
542
+ @prev_pos = [row, col, fmt]
543
+ end
544
+ else
545
+ dval = conv_dval(work[6,8])
546
+ params = {
547
+ :kind => :formula_number,
548
+ :value => dval,
549
+ :format_no => fmt,
550
+ :numeric => true,
551
+ :code => nil,
552
+ }
553
+ end
554
+ end
555
+ def header(op, len, work) # DK:P340
556
+ return unless(@current_sheet)
557
+ @current_sheet.header = simple_string(work)
558
+ end
559
+ def ibool(num)
560
+ (num != 0)
561
+ end
562
+ def integer(op, len, work) # Not in DK
563
+ row, col, fmt, dum, txt = work.unpack('v3cv')
564
+ params = {
565
+ :kind => :integer,
566
+ :value => txt,
567
+ :format_no => fmt,
568
+ :numeric => false,
569
+ }
570
+ cell_factory(row, col, params)
571
+ end
572
+ def label(op, len, work) # DK:P344
573
+ row, col, fmt = work.unpack('v3')
574
+ #BIFF8
575
+ label, code = nil
576
+ if(@workbook.biffversion && @workbook.biffversion >= VERSION_BIFF8)
577
+ buff, tln, pos, lens = conv_biff8_data(work[6..-1], true)
578
+ label = buff.at(0)
579
+ code = buff.at(1) ? :ucs2 : nil
580
+ #Before BIFF8
581
+ else
582
+ label = work[8..-1]
583
+ code = :_native_
584
+ end
585
+ params = {
586
+ :kind => :label,
587
+ :value => label,
588
+ :format_no => fmt,
589
+ :numeric => false,
590
+ :code => code,
591
+ }
592
+ cell_factory(row, col, params)
593
+ end
594
+ def label_sst(op, len, work) # DK: P345
595
+ row, col, fmt, idx = work.unpack('v3V')
596
+ reference = @workbook.pkg_str(idx) or return
597
+ code = (reference.unicode) ? :ucs2 : nil
598
+ params = {
599
+ :kind => :packed_idx,
600
+ :value => reference.text,
601
+ :format_no => fmt,
602
+ :numeric => false,
603
+ :code => code,
604
+ :rich => reference.rich
605
+ }
606
+ cell_factory(row, col, params)
607
+ end
608
+ def mul_rk(op, len, work) # DK:P349
609
+ return nil unless (@workbook.sheet_count > 0);
610
+
611
+ row, scol = work.unpack('v2')
612
+ ecol, = work[-2,2].unpack('v')
613
+ pos = 4
614
+
615
+ scol.upto(ecol) { |col|
616
+ #puts "unpacking: #{work[pos,6].inspect}"
617
+ fmt, val = unpack_rk_rec(work[pos,6])
618
+ params = {
619
+ :kind => :mul_rk,
620
+ :value => val,
621
+ :format_no => fmt,
622
+ :numeric => true,
623
+ }
624
+ cell_factory(row, col, params)
625
+ pos += 6
626
+ }
627
+ end
628
+ def mul_blank(op, len, work) # DK:P349
629
+ row, scol = work.unpack('v2')
630
+ ecol, = work[-2,2].unpack('v')
631
+ pos = 4
632
+
633
+ scol.upto(ecol) { |col|
634
+ fmt, = work[pos,2].unpack('v')
635
+ params = {
636
+ :kind => :mul_blank,
637
+ :value => '',
638
+ :format_no => fmt,
639
+ :numeric => false,
640
+ }
641
+ cell_factory(row, col, params)
642
+ pos += 2
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
+ #puts "cnt was nil or buff was empty"
827
+ @buff << work
828
+ #1.1 No PrevCond
829
+ elsif(@prev_cond.nil?)
830
+ #puts "no prev_cond, adding work to buffer"
831
+ @buff << work[1..-1]
832
+ else
833
+ #puts "else..."
834
+ cnt1st = work[0] # 1st byte of Continue may be a GR byte
835
+ stp, lens = @prev_info
836
+ lenb = @buff.length
837
+
838
+ #puts "cnt1st, @prev_cond"
839
+ #p cnt1st, @prev_cond
840
+
841
+ #1.1 Not in String
842
+ if(lenb >= (stp + lens))
843
+ #puts "lenb (#{lenb}) >= stp + lens (#{stp+lens})"
844
+ @buff << work
845
+ #1.2 Same code (Unicode or ASCII)
846
+ elsif(((@prev_cond ? 1 : 0) & 0x01) == (cnt1st & 0x01))
847
+ #puts "same code"
848
+ @buff << work[1..-1]
849
+ #1.3 Diff code (Unicode or ASCII)
850
+ else
851
+ #puts "codes differ"
852
+ diff = stp + lens - lenb
853
+ if(ibool(cnt1st & 0x01))
854
+ #puts "new code is unicode"
855
+ dum, gr = @buff.unpack('vc')
856
+ @buff[2,1] = [gr | 0x01].pack('c')
857
+ (lenb-stp).downto(1) { |idx|
858
+ @buff[stp+idx,0] = "\x00"
859
+ }
860
+ else
861
+ #puts "old code is unicode"
862
+ (diff/2).downto(1) { |idx|
863
+ work[idx+1,0] = "\x00"
864
+ }
865
+ end
866
+ @buff << work[1..-1]
867
+ end
868
+ end
869
+ @prev_cond = nil
870
+ @prev_info = nil
871
+
872
+ while(@buff.length >= 4)
873
+ buff, len, stpos, lens = conv_biff8_data(@buff, true)
874
+ #puts buff.inspect
875
+ unless(buff[0].nil?)
876
+ pkg_str = Worksheet::PkgString.new(*buff)
877
+ @workbook.add_pkg_str(pkg_str)
878
+ #puts pkg_str
879
+ @buff = @buff[len..-1]
880
+ else
881
+ #puts "code convert, breaking with @prev_cond: #{buff[1]} and @prev_info: [#{stpos}, #{lens}]"
882
+ @prev_cond = buff[1]
883
+ @prev_info = [stpos, lens]
884
+ break
885
+ end
886
+ end
887
+ end
888
+ def unpack_rk_rec(arg) # DK:P401
889
+ ef, = arg[0,2].unpack('v')
890
+ lwk, = arg[2,4]
891
+ swk = lwk.unpack('c4').reverse.pack('c4')
892
+ ptn = (swk[3,1].unpack('c').first & 0x03)
893
+ null = "\0\0\0\0"
894
+ res = nil
895
+ if(ptn == 0)
896
+ #puts "ptn==0"
897
+ res, = ((@bigendian) ? swk + null : null + lwk).unpack('d')
898
+ elsif(ptn == 1)
899
+ #puts "ptn==1"
900
+ swk[3] &= [(swk[3,1].unpack('c').first & 0xFC)].pack('c')[0]
901
+ lwk[0] &= [(lwk[0,1].unpack('c').first & 0xFC)].pack('c')[0]
902
+ res = ((@bigendian) ? swk + null : null + lwk).unpack('d').first.to_f / 100.0
903
+ elsif(ptn == 2)
904
+ #puts "ptn==2"
905
+ bin, = swk.unpack('B32')
906
+ wklb = [((bin[0,1]*2) + bin[0,30])].pack('B32')
907
+ wkl = (@bigendian) ? wklb : wklb.unpack('c4').reverse.pack('c4')
908
+ res, = wkl.unpack('i')
909
+ else
910
+ #puts "ptn==#{ptn}"
911
+ ub, = swk.unpack('B32')
912
+ wklb = [((ub[0,1]*2) + ub[0,30])].pack('B32')
913
+ wkl = (@bigendian) ? wklb : wklb.unpack('c4').reverse.pack('c4')
914
+ res = wkl.unpack('i').first / 100.00
915
+ end
916
+ #p lwk, swk, swk[3,1], res if([5,12].include? res)
917
+ [ef, res]
918
+ end
919
+ def ws_bool(op, len, work) # DK: P452
920
+ return if(@current_sheet.nil?)
921
+ fit = ibool(work.unpack('v').first & 0x100)
922
+ @current_sheet.page_fit = fit
923
+ end
924
+ def xf(op, len, work) # DK:P453
925
+ fnt, idx, lock, hidden, style, i123, alh, wrap, alv, justl = nil
926
+ rotate, ind, shrink, merge, readdir, bdr_d, bdr_sl, bdr_sr = nil
927
+ bdr_st, bdr_sb, bdr_sd, bdr_cl, bdr_cr, bdr_ct, bdr_cb = nil
928
+ bdr_cd, fill_p, fill_cf, fill_cb = nil
929
+
930
+ if(@workbook.biffversion == VERSION_BIFF8)
931
+ fnt, idx, gen1, align, gen2,
932
+ bdr1, bdr2, bdr3, ptn = work.unpack('v7Vv')
933
+ lock = ibool(gen1 & 0x01)
934
+ hidden = ibool(gen1 & 0x02)
935
+ style = ibool(gen1 & 0x04)
936
+ i123 = ibool(gen1 & 0x08)
937
+
938
+ alh = (align & 0x07)
939
+ wrap = ibool(align & 0x08)
940
+ alv = (align & 0x70) / 0x10
941
+ justl = ibool(align & 0x80)
942
+
943
+ rotate = ((align & 0xFF00) / 0x100) & 0x00FF
944
+ rotate = 90 if(rotate == 255)
945
+ rotate = (90 - rotate) if(rotate > 90)
946
+
947
+ ind = (gen2 & 0x0F)
948
+ shrink = ibool(gen2 & 0x10)
949
+ merge = ibool(gen2 & 0x20)
950
+ readdir = ((gen2 & 0xC0) / 0x40) & 0x03
951
+
952
+ bdr_sl = bdr1 & 0x0F
953
+ bdr_sr = ((bdr1 & 0xF0) / 0x10) & 0x0F
954
+ bdr_st = ((bdr1 & 0xF00) / 0x100) & 0x0F
955
+ bdr_sb = ((bdr1 & 0xF000) / 0x1000) & 0x0F
956
+
957
+ bdr_cl = ((bdr2 & 0x7F)) & 0x7F
958
+ bdr_cr = ((bdr2 & 0x3F80) / 0x80) & 0x7F
959
+ bdr_d = ((bdr2 & 0xC000) / 0x4000) & 0x7F
960
+
961
+ bdr_ct = ((bdr3 & 0x7F)) & 0x7F
962
+ bdr_cb = ((bdr3 & 0x3F80) / 0x80) & 0x7F
963
+ bdr_cd = ((bdr2 & 0x1FC000) / 0x4000) & 0x7F
964
+ bdr_sd = ((bdr2 & 0x1E00000) / 0x200000) & 0x7F
965
+ fill_p = ((bdr2 & 0xFC000000) / 0x4000000) & 0x3F
966
+
967
+ fill_cf = ptn & 0x7F
968
+ fill_cb = ((ptn & 0x3F80) / 0x80) & 0x7F
969
+ else
970
+ fnt, idx, gen1, align, ptn1, ptn2, bdr1, bdr2 = work.unpack('v8')
971
+
972
+ lock = ibool(gen1 & 0x01)
973
+ hidden = ibool(gen1 & 0x02)
974
+ style = ibool(gen1 & 0x04)
975
+ i123 = ibool(gen1 & 0x08)
976
+
977
+ alh = (align & 0x07)
978
+ wrap = ibool(align & 0x08)
979
+ alv = (align & 0x70) / 0x10
980
+ justl = ibool(align & 0x80)
981
+
982
+ rotate = ((align & 0x300) / 0x100) & 0x03
983
+
984
+ fill_cf = ptn1 & 0x7F
985
+ fill_cb = ((ptn1 & 0x1F80) / 0x80) & 0x7F
986
+
987
+ fill_p = ptn2 & 0x3F
988
+ bdr_sb = ((ptn2 & 0x1C0) / 0x40) & 0x07
989
+ bdr_cb = ((ptn2 & 0xFE00) / 0x200) & 0x7F
990
+
991
+ bdr_st = bdr1 & 0x07
992
+ bdr_sl = ((bdr1 & 0x38) / 0x8) & 0x07
993
+ bdr_sr = ((bdr1 & 0x1C0) / 0x40) & 0x07
994
+ bdr_ct = ((bdr1 & 0xFE00) / 0x200) & 0x7F
995
+
996
+ bdr_cl = (bdr2 & 0x7F) & 0x7F
997
+ bdr_cr = ((bdr2 & 0x3F80) / 0x80) & 0x7F
998
+ end
999
+
1000
+ params = {
1001
+ :font_no => fnt,
1002
+ #:font => workbook.fonts[fnt],
1003
+ :fmt_idx => idx,
1004
+ :lock => lock,
1005
+ :hidden => hidden,
1006
+ :style => style,
1007
+ :key_123 => i123,
1008
+ :align_h => alh,
1009
+ :wrap => wrap,
1010
+ :align_v => alv,
1011
+ :just_last => justl,
1012
+ :rotate => rotate,
1013
+ :indent => ind,
1014
+ :shrink => shrink,
1015
+ :merge => merge,
1016
+ :read_dir => readdir,
1017
+ :border_style => [bdr_sl, bdr_sr, bdr_st, bdr_sb],
1018
+ :border_color => [bdr_cl, bdr_cr, bdr_ct, bdr_cb],
1019
+ :border_diag => [bdr_d, bdr_sd, bdr_cd],
1020
+ :fill => [fill_p, fill_cf, fill_cb],
1021
+ }
1022
+ if(@encoding)
1023
+ params.store(:encoding, @encoding)
1024
+ end
1025
+ #p "**"*33
1026
+ #p work
1027
+ #p idx
1028
+ @workbook.add_cell_format(Format.new(params))
1029
+ end
1030
+ end
1031
+ end
1032
+ end