WriteExcel 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. data/.document +5 -0
  2. data/.gitignore +21 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +17 -0
  5. data/Rakefile +47 -0
  6. data/VERSION +1 -0
  7. data/examples/a_simple.rb +42 -0
  8. data/examples/autofilters.rb +266 -0
  9. data/examples/bigfile.rb +30 -0
  10. data/examples/copyformat.rb +51 -0
  11. data/examples/data_validate.rb +278 -0
  12. data/examples/date_time.rb +86 -0
  13. data/examples/demo.rb +118 -0
  14. data/examples/diag_border.rb +35 -0
  15. data/examples/formats.rb +489 -0
  16. data/examples/header.rb +136 -0
  17. data/examples/hidden.rb +28 -0
  18. data/examples/hyperlink.rb +42 -0
  19. data/examples/images.rb +52 -0
  20. data/examples/merge1.rb +39 -0
  21. data/examples/merge2.rb +44 -0
  22. data/examples/merge3.rb +65 -0
  23. data/examples/merge4.rb +82 -0
  24. data/examples/merge5.rb +79 -0
  25. data/examples/protection.rb +46 -0
  26. data/examples/regions.rb +52 -0
  27. data/examples/repeat.rb +42 -0
  28. data/examples/stats.rb +75 -0
  29. data/examples/stocks.rb +80 -0
  30. data/examples/tab_colors.rb +30 -0
  31. data/lib/WriteExcel.rb +30 -0
  32. data/lib/WriteExcel/biffwriter.rb +259 -0
  33. data/lib/WriteExcel/chart.rb +217 -0
  34. data/lib/WriteExcel/excelformula.y +138 -0
  35. data/lib/WriteExcel/excelformulaparser.rb +573 -0
  36. data/lib/WriteExcel/format.rb +1108 -0
  37. data/lib/WriteExcel/formula.rb +986 -0
  38. data/lib/WriteExcel/olewriter.rb +322 -0
  39. data/lib/WriteExcel/properties.rb +250 -0
  40. data/lib/WriteExcel/storage_lite.rb +590 -0
  41. data/lib/WriteExcel/workbook.rb +2602 -0
  42. data/lib/WriteExcel/worksheet.rb +6378 -0
  43. data/spec/WriteExcel_spec.rb +7 -0
  44. data/spec/spec.opts +1 -0
  45. data/spec/spec_helper.rb +9 -0
  46. data/test/tc_all.rb +31 -0
  47. data/test/tc_biff.rb +104 -0
  48. data/test/tc_chart.rb +22 -0
  49. data/test/tc_example_match.rb +1280 -0
  50. data/test/tc_format.rb +1264 -0
  51. data/test/tc_formula.rb +63 -0
  52. data/test/tc_ole.rb +110 -0
  53. data/test/tc_storage_lite.rb +102 -0
  54. data/test/tc_workbook.rb +115 -0
  55. data/test/tc_worksheet.rb +115 -0
  56. data/test/test_00_IEEE_double.rb +14 -0
  57. data/test/test_01_add_worksheet.rb +12 -0
  58. data/test/test_02_merge_formats.rb +58 -0
  59. data/test/test_04_dimensions.rb +397 -0
  60. data/test/test_05_rows.rb +182 -0
  61. data/test/test_06_extsst.rb +80 -0
  62. data/test/test_11_date_time.rb +484 -0
  63. data/test/test_12_date_only.rb +506 -0
  64. data/test/test_13_date_seconds.rb +486 -0
  65. data/test/test_21_escher.rb +629 -0
  66. data/test/test_22_mso_drawing_group.rb +739 -0
  67. data/test/test_23_note.rb +78 -0
  68. data/test/test_24_txo.rb +80 -0
  69. data/test/test_26_autofilter.rb +327 -0
  70. data/test/test_27_autofilter.rb +144 -0
  71. data/test/test_28_autofilter.rb +174 -0
  72. data/test/test_29_process_jpg.rb +131 -0
  73. data/test/test_30_validation_dval.rb +82 -0
  74. data/test/test_31_validation_dv_strings.rb +131 -0
  75. data/test/test_32_validation_dv_formula.rb +211 -0
  76. data/test/test_40_property_types.rb +191 -0
  77. data/test/test_41_properties.rb +238 -0
  78. data/test/test_42_set_properties.rb +430 -0
  79. data/test/ts_all.rb +34 -0
  80. metadata +154 -0
@@ -0,0 +1,590 @@
1
+ #require 'tempfile'
2
+ #require 'stringio'
3
+
4
+ class OLEStorageLite
5
+ PPS_TYPE_ROOT = 5
6
+ PPS_TYPE_DIR = 1
7
+ PPS_TYPE_FILE = 2
8
+ DATA_SIZE_SMALL = 0x1000
9
+ LONG_INT_SIZE = 4
10
+ PPS_SIZE = 0x80
11
+
12
+ def asc2ucs(str)
13
+ str.split(//).join("\0") + "\0"
14
+ end
15
+
16
+ def ucs2asc(str)
17
+ ary = str.unpack('v*').map { |s| [s].pack('c')}
18
+ ary.join('')
19
+ end
20
+
21
+ def localDate2OLE(localtime)
22
+ return "\x00" * 8 unless localtime
23
+
24
+ # Convert from localtime (actually gmtime) to seconds.
25
+ args = localtime.reverse
26
+ args[0] += 1900 # year
27
+ args[1] += 1 # month
28
+ time = Time.gm(*args)
29
+
30
+ # Add the number of seconds between the 1601 and 1970 epochs.
31
+ time = time.to_i + 11644473600
32
+
33
+ # The FILETIME seconds are in units of 100 nanoseconds.
34
+ nanoseconds = time * 1E7
35
+
36
+ # Pack the total nanoseconds into 64 bits...
37
+ hi, lo = nanoseconds.divmod 1 << 32
38
+
39
+ oletime = [lo, hi].pack("VV")
40
+ return oletime
41
+ end
42
+ end
43
+
44
+ class OLEStorageLitePPS < OLEStorageLite
45
+ attr_accessor :no, :name, :type, :prev_pps, :next_pps, :dir_pps
46
+ attr_accessor :time_1st, :time_2nd, :start_block, :size, :data, :child
47
+ attr_reader :pps_file
48
+
49
+ def initialize(iNo, sNm, iType, iPrev, iNext, iDir,
50
+ raTime1st, raTime2nd, iStart, iSize, sData, raChild)
51
+ @no = iNo
52
+ @name = sNm
53
+ @type = iType
54
+ @prev_pps = iPrev
55
+ @next_pps = iNext
56
+ @dir_pps = iDir
57
+ @time_1st = raTime1st
58
+ @time_2nd = raTime2nd
59
+ @start_block = iStart
60
+ @size = iSize
61
+ @data = sData
62
+ @child = raChild
63
+ @pps_file = nil
64
+ end
65
+
66
+ def _datalen
67
+ return 0 if @data.nil?
68
+ if @pps_file
69
+ return @pps_file.lstat.size
70
+ else
71
+ return @data.size
72
+ end
73
+ end
74
+ protected :_datalen
75
+
76
+ def _makeSmallData(aList, rh_info)
77
+ file = rh_info[:fileh]
78
+ iSmBlk = 0
79
+ sRes = ''
80
+
81
+ aList.each do |pps|
82
+ #1. Make SBD, small data string
83
+ if pps.type == PPS_TYPE_FILE
84
+ next if pps.size <= 0
85
+ if pps.size < rh_info[:small_size]
86
+ iSmbCnt = pps.size / rh_info[:small_block_size]
87
+ iSmbCnt += 1 if pps.size % rh_info[:small_block_size] > 0
88
+ #1.1 Add to SBD
89
+ 0.upto(iSmbCnt-1-1) do |i|
90
+ file.write([i + iSmBlk+1].pack("V"))
91
+ end
92
+ file.write([-2].pack("V"))
93
+
94
+ #1.2 Add to Data String(this will be written for RootEntry)
95
+ #Check for update
96
+ if pps.pps_file
97
+ pps.pps_file.seek(0) #To The Top
98
+ while sBuff = pps.pps_file.read(4096)
99
+ sRes << sBuff
100
+ end
101
+ else
102
+ sRes << pps.data
103
+ end
104
+ if pps.size % rh_info[:small_block_size] > 0
105
+ cnt = rh_info[:small_block_size] - (pps.size % rh_info[:small_block_size])
106
+ sRes << "\0" * cnt
107
+ end
108
+ #1.3 Set for PPS
109
+ pps.start_block = iSmBlk
110
+ iSmBlk += iSmbCnt
111
+ end
112
+ end
113
+ end
114
+ iSbCnt = rh_info[:big_block_size] / LONG_INT_SIZE
115
+ file.write([-1].pack("V") * (iSbCnt - (iSmBlk % iSbCnt))) if iSmBlk % iSbCnt > 0
116
+ #2. Write SBD with adjusting length for block
117
+ return sRes
118
+ end
119
+ private :_makeSmallData
120
+
121
+ def _savePpsWk(rh_info)
122
+ #1. Write PPS
123
+ file = rh_info[:fileh]
124
+ file.write(
125
+ @name +
126
+ ("\x00" * (64 - @name.length)) + #64
127
+ [@name.length + 2].pack("v") + #66
128
+ [@type].pack("c") + #67
129
+ [0x00].pack("c") + #UK #68
130
+ [@prev_pps].pack("V") + #Prev #72
131
+ [@next_pps].pack("V") + #Next #76
132
+ [@dir_pps].pack("V") + #Dir #80
133
+ "\x00\x09\x02\x00" + #84
134
+ "\x00\x00\x00\x00" + #88
135
+ "\xc0\x00\x00\x00" + #92
136
+ "\x00\x00\x00\x46" + #96
137
+ "\x00\x00\x00\x00" + #100
138
+ localDate2OLE(@time_1st) + #108
139
+ localDate2OLE(@time_2nd) #116
140
+ )
141
+ if @start_block != 0
142
+ file.write([@start_block].pack('V'))
143
+ else
144
+ file.write([0].pack('V'))
145
+ end
146
+ if @size != 0 #124
147
+ file.write([@size].pack('V'))
148
+ else
149
+ file.write([0].pack('V'))
150
+ end
151
+ file.write([0].pack('V')) #128
152
+ end
153
+ protected :_savePpsWk
154
+ end
155
+
156
+ class OLEStorageLitePPSRoot < OLEStorageLitePPS
157
+ def initialize(raTime1st, raTime2nd, raChild)
158
+ super(
159
+ nil,
160
+ asc2ucs('Root Entry'),
161
+ PPS_TYPE_ROOT,
162
+ nil,
163
+ nil,
164
+ nil,
165
+ raTime1st,
166
+ raTime2nd,
167
+ nil,
168
+ nil,
169
+ nil,
170
+ raChild)
171
+ end
172
+
173
+ def save(sFile, bNoAs = nil, rh_info = nil)
174
+ #0.Initial Setting for saving
175
+ rh_info = Hash.new unless rh_info
176
+ if rh_info[:big_block_size]
177
+ rh_info[:big_block_size] = 2 ** adjust2(rh_info[:big_block_size])
178
+ else
179
+ rh_info[:big_block_size] = 2 ** 9
180
+ end
181
+ if rh_info[:small_block_size]
182
+ rh_info[:small_block_size] = 2 ** adjust2(rh_info[:small_block_size])
183
+ else
184
+ rh_info[:small_block_size] = 2 ** 6
185
+ end
186
+ rh_info[:small_size] = 0x1000
187
+ rh_info[:pps_size] = 0x80
188
+
189
+ close_file = true
190
+
191
+ #1.Open File
192
+ #1.1 sFile is Ref of scalar
193
+ if sFile.kind_of?(String)
194
+ rh_info[:fileh] = open(sFile, "wb")
195
+ else
196
+ rh_info[:fileh] = sFile.binmode
197
+ end
198
+
199
+ iBlk = 0
200
+ #1. Make an array of PPS (for Save)
201
+ aList=[]
202
+ if bNoAs
203
+ _savePpsSetPnt2([self], aList, rh_info)
204
+ else
205
+ _savePpsSetPnt([self], aList, rh_info)
206
+ end
207
+ iSBDcnt, iBBcnt, iPPScnt = _calcSize(aList, rh_info)
208
+
209
+ #2.Save Header
210
+ _saveHeader(rh_info, iSBDcnt, iBBcnt, iPPScnt)
211
+
212
+ #3.Make Small Data string (write SBD)
213
+ # Small Datas become RootEntry Data
214
+ @data = _makeSmallData(aList, rh_info)
215
+
216
+ #4. Write BB
217
+ iBBlk = iSBDcnt
218
+ _saveBigData(iBBlk, aList, rh_info)
219
+
220
+ #5. Write PPS
221
+ _savePps(aList, rh_info)
222
+
223
+ #6. Write BD and BDList and Adding Header informations
224
+ _saveBbd(iSBDcnt, iBBcnt, iPPScnt, rh_info)
225
+
226
+ #7.Close File
227
+ return rh_info[:fileh].close if close_file != 0
228
+ end
229
+
230
+ def _calcSize(aList, rh_info)
231
+ #0. Calculate Basic Setting
232
+ iSBDcnt, iBBcnt, iPPScnt = [0,0,0]
233
+ iSmallLen = 0
234
+ iSBcnt = 0
235
+ aList.each do |pps|
236
+ if pps.type == PPS_TYPE_FILE
237
+ pps.size = pps._datalen #Mod
238
+ if pps.size < rh_info[:small_size]
239
+ iSBcnt += pps.size / rh_info[:small_block_size]
240
+ iSBcnt += 1 if pps.size % rh_info[:small_block_size] > 0
241
+ else
242
+ iBBcnt += pps.size / rh_info[:big_block_size]
243
+ iBBcnt += 1 if pps.size % rh_info[:big_block_size] > 0
244
+ end
245
+ end
246
+ end
247
+ iSmallLen = iSBcnt * rh_info[:small_block_size]
248
+ iSlCnt = rh_info[:big_block_size] / LONG_INT_SIZE
249
+ iSBDcnt = iSBcnt / iSlCnt
250
+ iSBDcnt += 1 if iSBcnt % iSlCnt > 0
251
+ iBBcnt += iSmallLen / rh_info[:big_block_size]
252
+ iBBcnt += 1 if iSmallLen % rh_info[:big_block_size] > 0
253
+ iCnt = aList.size
254
+ iBdCnt = rh_info[:big_block_size] / PPS_SIZE
255
+ iPPScnt = iCnt / iBdCnt
256
+ iPPScnt += 1 if iCnt % iBdCnt > 0
257
+ return [iSBDcnt, iBBcnt, iPPScnt]
258
+ end
259
+ private :_calcSize
260
+
261
+ def _adjust2(i2)
262
+ iWk = Math.log(i2)/Math.log(2)
263
+ return iWk > Integer(iWk) ? Integer(iWk) + 1 : iWk
264
+ end
265
+ private :_adjust2
266
+
267
+ def _saveHeader(rh_info, iSBDcnt, iBBcnt, iPPScnt)
268
+ file = rh_info[:fileh]
269
+
270
+ #0. Calculate Basic Setting
271
+ iBlCnt = rh_info[:big_block_size] / LONG_INT_SIZE
272
+ i1stBdL = (rh_info[:big_block_size] - 0x4C) / LONG_INT_SIZE
273
+ i1stBdMax = i1stBdL * iBlCnt - i1stBdL
274
+ iBdExL = 0
275
+ iAll = iBBcnt + iPPScnt + iSBDcnt
276
+ iAllW = iAll
277
+ iBdCntW = iAllW / iBlCnt
278
+ iBdCntW += 1 if iAllW % iBlCnt > 0
279
+ iBdCnt = 0
280
+ #0.1 Calculate BD count
281
+ iBlCnt -= 1 #the BlCnt is reduced in the count of the last sect is used for a pointer the next Bl
282
+ iBBleftover = iAll - i1stBdMax
283
+ if iAll >i1stBdMax
284
+ while true
285
+ iBdCnt = iBBleftover / iBlCnt
286
+ iBdCnt += 1 if iBBleftover % iBlCnt > 0
287
+ iBdExL = iBdCnt / iBlCnt
288
+ iBdExL += 1 if iBdCnt % iBlCnt > 0
289
+ iBBleftover += iBdExL
290
+ break if iBdCnt == iBBleftover / iBlCnt + (iBBleftover % iBlCnt > 0 ? 1 : 0)
291
+ end
292
+ end
293
+ iBdCnt += i1stBdL
294
+ #print "iBdCnt = iBdCnt \n"
295
+
296
+ #1.Save Header
297
+ file.write(
298
+ "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" +
299
+ "\x00\x00\x00\x00" * 4 +
300
+ [0x3b].pack("v") +
301
+ [0x03].pack("v") +
302
+ [-2].pack("v") +
303
+ [9].pack("v") +
304
+ [6].pack("v") +
305
+ [0].pack("v") +
306
+ "\x00\x00\x00\x00" * 2 +
307
+ [iBdCnt].pack("V") +
308
+ [iBBcnt+iSBDcnt].pack("V") + #ROOT START
309
+ [0].pack("V") +
310
+ [0x1000].pack("V") +
311
+ [0].pack("V") + #Small Block Depot
312
+ [1].pack("V")
313
+ )
314
+ #2. Extra BDList Start, Count
315
+ if iAll <= i1stBdMax
316
+ file.write(
317
+ [-2].pack("V") + #Extra BDList Start
318
+ [0].pack("V") #Extra BDList Count
319
+ )
320
+ else
321
+ file.write(
322
+ [iAll + iBdCnt].pack("V") +
323
+ [iBdExL].pack("V")
324
+ )
325
+ end
326
+
327
+ #3. BDList
328
+ cnt = i1stBdL
329
+ cnt = iBdCnt if iBdCnt < i1stBdL
330
+ 0.upto(cnt-1) do |i|
331
+ file.write([iAll + i].pack("V"))
332
+ end
333
+ file.write([-1].pack("V") * (i1stBdL - cnt)) if cnt < i1stBdL
334
+ end
335
+ private :_saveHeader
336
+
337
+ def _saveBigData(iStBlk, aList, rh_info)
338
+ iRes = 0
339
+ file = rh_info[:fileh]
340
+
341
+ #1.Write Big (ge 0x1000) Data into Block
342
+ aList.each do |pps|
343
+ if pps.type != PPS_TYPE_DIR
344
+ #print "PPS: pps DEF:", defined(pps->{Data}), "\n"
345
+ pps.size = pps._datalen #Mod
346
+ if (pps.size >= rh_info[:small_size]) ||
347
+ ((pps.type == PPS_TYPE_ROOT) && !pps.data.nil?)
348
+ #1.1 Write Data
349
+ #Check for update
350
+ if pps.pps_file
351
+ iLen = 0
352
+ pps.pps_file.seek(0, 0) #To The Top
353
+ while sBuff = pps.pps_file.read(4096)
354
+ iLen += sBuff.length
355
+ file.write(sBuff) #Check for update
356
+ end
357
+ else
358
+ file.write(pps.data)
359
+ end
360
+ if pps.size % rh_info[:big_block_size] > 0
361
+ file.write(
362
+ "\x00" *
363
+ (rh_info[:big_block_size] -
364
+ (pps.size % rh_info[:big_block_size]))
365
+ )
366
+ end
367
+ #1.2 Set For PPS
368
+ pps.start_block = iStBlk
369
+ iStBlk += pps.size / rh_info[:big_block_size]
370
+ iStBlk += 1 if pps.size % rh_info[:big_block_size] > 0
371
+ end
372
+ end
373
+ end
374
+ end
375
+
376
+ def _savePps(aList, rh_info)
377
+ #0. Initial
378
+ file = rh_info[:fileh]
379
+ #2. Save PPS
380
+ aList.each do |oItem|
381
+ oItem._savePpsWk(rh_info)
382
+ end
383
+ #3. Adjust for Block
384
+ iCnt = aList.size
385
+ iBCnt = rh_info[:big_block_size] / rh_info[:pps_size]
386
+ if iCnt % iBCnt > 0
387
+ file.write("\x00" * ((iBCnt - (iCnt % iBCnt)) * rh_info[:pps_size]))
388
+ end
389
+ return (iCnt / iBCnt) + ((iCnt % iBCnt) > 0 ? 1: 0)
390
+ end
391
+ private :_savePps
392
+
393
+ def _savePpsSetPnt(pps_array, aList, rh_info)
394
+ #1. make Array as Children-Relations
395
+ #1.1 if No Children
396
+ bpp=1
397
+ if pps_array.nil? || pps_array.size == 0
398
+ return 0xFFFFFFFF
399
+ #1.2 Just Only one
400
+ elsif pps_array.size == 1
401
+ aList << pps_array[0]
402
+ pps_array[0].no = aList.size - 1
403
+ pps_array[0].prev_pps = 0xFFFFFFFF
404
+ pps_array[0].next_pps = 0xFFFFFFFF
405
+ pps_array[0].dir_pps = _savePpsSetPnt(pps_array[0].child, aList, rh_info)
406
+ return pps_array[0].no
407
+ #1.3 Array
408
+ else
409
+ iCnt = pps_array.size
410
+ #1.3.1 Define Center
411
+ iPos = Integer(iCnt / 2.0) #$iCnt
412
+
413
+ aList.push(pps_array[iPos])
414
+ pps_array[iPos].no = aList.size - 1
415
+
416
+ aWk = pps_array.dup
417
+ #1.3.2 Devide a array into Previous,Next
418
+ aPrev = aWk[0, iPos]
419
+ aWk[0..iPos-1] = nil
420
+ aNext = aWk[1, iCnt - iPos - 1]
421
+ aWk[1..(1 + iCnt - iPos -1 -1)] = nil
422
+ pps_array[iPos].prev_pps = _savePpsSetPnt(aPrev, aList, rh_info)
423
+ pps_array[iPos].next_pps = _savePpsSetPnt(aNext, aList, rh_info)
424
+ pps_array[iPos].dir_pps = _savePpsSetPnt(pps_array[iPos].child, aList, rh_info)
425
+ return pps_array[iPos].no
426
+ end
427
+ end
428
+ private :_savePpsSetPnt
429
+
430
+ def _savePpsSetPnt2(pps_array, aList, rh_info)
431
+ #1. make Array as Children-Relations
432
+ #1.1 if No Children
433
+ if pps_array.nil? || pps_array.size == 0
434
+ return 0xFFFFFFFF
435
+ #1.2 Just Only one
436
+ elsif pps_array.size == 1
437
+ aList << pps_array[0]
438
+ pps_array[0].no = aList.size - 1
439
+ pps_array[0].prev_pps = 0xFFFFFFFF
440
+ pps_array[0].next_pps = 0xFFFFFFFF
441
+ pps_array[0].dir_pps = _savePpsSetPnt2(pps_array[0].child, aList, rh_info)
442
+ return pps_array[0].no
443
+ #1.3 Array
444
+ else
445
+ iCnt = pps_array.size
446
+ #1.3.1 Define Center
447
+ iPos = 0 #int($iCnt/ 2); #$iCnt
448
+
449
+ aWk = pps_array.dup
450
+ aPrev = aWk[1, 1]
451
+ aWk[1..1] = nil
452
+ aNext = aWk[1..aWk.size] #, $iCnt - $iPos -1);
453
+ pps_array[iPos].prev_pps = _savePpsSetPnt2(pps_array, aList, rh_info)
454
+ aList.push(pps_array[iPos])
455
+ pps_array[iPos].no = aList.size
456
+
457
+ #1.3.2 Devide a array into Previous,Next
458
+ pps_array[iPos].next_pps = _savePpsSetPnt2(aNext, aList, rh_info)
459
+ pps_array[iPos].dir_pps = _savePpsSetPnt2(pps_array[iPos].child, aList, rh_info)
460
+ return pps_array[iPos].no
461
+ end
462
+ end
463
+ private :_savePpsSetPnt2
464
+
465
+ def _saveBbd(iSbdSize, iBsize, iPpsCnt, rh_info)
466
+ file = rh_info[:fileh]
467
+ #0. Calculate Basic Setting
468
+ iBbCnt = rh_info[:big_block_size] / LONG_INT_SIZE
469
+ iBlCnt = iBbCnt - 1
470
+ i1stBdL = (rh_info[:big_block_size] - 0x4C) / LONG_INT_SIZE
471
+ i1stBdMax = i1stBdL * iBbCnt - i1stBdL
472
+ iBdExL = 0
473
+ iAll = iBsize + iPpsCnt + iSbdSize
474
+ iAllW = iAll
475
+ iBdCntW = iAllW / iBbCnt
476
+ iBdCntW += 1 if iAllW % iBbCnt > 0
477
+ iBdCnt = 0
478
+ #0.1 Calculate BD count
479
+ iBBleftover = iAll - i1stBdMax
480
+ if iAll >i1stBdMax
481
+ while true
482
+ iBdCnt = iBBleftover / iBlCnt
483
+ iBdCnt += 1 if iBBleftover % iBlCnt > 0
484
+
485
+ iBdExL = iBdCnt / iBlCnt
486
+ iBdExL += 1 if iBdCnt % iBlCnt > 0
487
+ iBBleftover = iBBleftover + iBdExL
488
+ break if iBdCnt == (iBBleftover / iBlCnt + ((iBBleftover % iBlCnt) > 0 ? 1: 0))
489
+ end
490
+ end
491
+ iAllW += iBdExL
492
+ iBdCnt += i1stBdL
493
+ #print "iBdCnt = iBdCnt \n"
494
+
495
+ #1. Making BD
496
+ #1.1 Set for SBD
497
+ if iSbdSize > 0
498
+ 0.upto(iSbdSize-1-1) do |i|
499
+ file.write([i + 1].pack('V'))
500
+ end
501
+ file.write([-2].pack('V'))
502
+ end
503
+ #1.2 Set for B
504
+ 0.upto(iBsize-1-1) do |i|
505
+ file.write([i + iSbdSize + 1].pack('V'))
506
+ end
507
+ file.write([-2].pack('V'))
508
+
509
+ #1.3 Set for PPS
510
+ 0.upto(iPpsCnt-1-1) do |i|
511
+ file.write([i+iSbdSize+iBsize+1].pack("V"))
512
+ end
513
+ file.write([-2].pack('V'))
514
+ #1.4 Set for BBD itself ( 0xFFFFFFFD : BBD)
515
+ 0.upto(iBdCnt-1) do |i|
516
+ file.write([0xFFFFFFFD].pack("V"))
517
+ end
518
+ #1.5 Set for ExtraBDList
519
+ 0.upto(iBdExL-1) do |i|
520
+ file.write([0xFFFFFFFC].pack("V"))
521
+ end
522
+ #1.6 Adjust for Block
523
+ if (iAllW + iBdCnt) % iBbCnt > 0
524
+ file.write([-1].pack('V') * (iBbCnt - ((iAllW + iBdCnt) % iBbCnt)))
525
+ end
526
+ #2.Extra BDList
527
+ if iBdCnt > i1stBdL
528
+ iN = 0
529
+ iNb = 0
530
+ i1stBdL.upto(iBdCnt-1) do |i|
531
+ if iN >= iBbCnt-1
532
+ iN = 0
533
+ iNb += 1
534
+ file.write([iAll+iBdCnt+iNb].pack("V"))
535
+ end
536
+ file.write([iBsize+iSbdSize+iPpsCnt+i].pack("V"))
537
+ iN += 1
538
+ end
539
+ if (iBdCnt-i1stBdL) % (iBbCnt-1) > 0
540
+ file.write([-1].pack("V") * ((iBbCnt-1) - ((iBdCnt-i1stBdL) % (iBbCnt-1))))
541
+ end
542
+ file.write([-2].pack('V'))
543
+ end
544
+ end
545
+ end
546
+
547
+ class OLEStorageLitePPSFile < OLEStorageLitePPS
548
+ def initialize(sNm, data = '')
549
+ super(
550
+ nil,
551
+ sNm || '',
552
+ PPS_TYPE_FILE,
553
+ nil,
554
+ nil,
555
+ nil,
556
+ nil,
557
+ nil,
558
+ nil,
559
+ nil,
560
+ data || '',
561
+ nil
562
+ )
563
+ end
564
+
565
+ def set_file(sFile = '')
566
+ if sFile.nil? or sFile == ''
567
+ @pps_file = Tempfile.new('OLEStorageLitePPSFile')
568
+ elsif sFile.kind_of?(IO) || sFile.kind_of?(StringIO)
569
+ @pps_file = sFile
570
+ elsif sFile.kind_of?(String)
571
+ #File Name
572
+ @pps_file = open(sFile, "r+")
573
+ return nil unless @pps_file
574
+ else
575
+ return nil
576
+ end
577
+ @pps_file.seek(0, IO::SEEK_END)
578
+ @pps_file.binmode
579
+ end
580
+
581
+ def append (data)
582
+ return if data.nil?
583
+ if @pps_file
584
+ @pps_file << data
585
+ @pps_file.flush
586
+ else
587
+ @data << data
588
+ end
589
+ end
590
+ end