axlsx 1.0.14 → 1.0.15

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.
data/README.md CHANGED
@@ -7,10 +7,10 @@ Axlsx: Office Open XML Spreadsheet Generation
7
7
  **Author**: Randy Morgan
8
8
  **Copyright**: 2011
9
9
  **License**: MIT License
10
- **Latest Version**: 1.0.14
10
+ **Latest Version**: 1.0.15
11
11
  **Ruby Version**: 1.8.7, 1.9.2, 1.9.3
12
12
 
13
- **Release Date**: December 14th 2011
13
+ **Release Date**: January 6th 2012
14
14
 
15
15
  Synopsis
16
16
  --------
@@ -262,8 +262,14 @@ This gem has 100% test coverage using test/unit. To execute tests for this gem,
262
262
 
263
263
  #Changelog
264
264
  ---------
265
+ - **January.6.12**: 1.0.15 release
266
+ - Bug fix add_style specified number formats must be explicity applied for libraOffice
267
+ - performance improvements from ochko when creating cells with options.
268
+ - Bug fix setting types=>[:n] when adding a row incorrectly determines the cell type to be string as the value is null during creation.
269
+ - Release in preparation for password protection merge
270
+
265
271
  - **December.14.11**: 1.0.14 release
266
- - Added support for mergin cells
272
+ - Added support for merging cells
267
273
  - Added support for auto filters
268
274
  - Improved auto width calculations
269
275
  - Improved charts
@@ -288,7 +294,9 @@ This gem has 100% test coverage using test/unit. To execute tests for this gem,
288
294
 
289
295
  Please see the {file:CHANGELOG.md} document for past release information.
290
296
 
291
-
297
+ #Thanks!
298
+ --------
299
+ ochko https://github.com/ochko
292
300
  #Copyright and License
293
301
  ----------
294
302
 
@@ -6,8 +6,9 @@ require 'axlsx/version.rb'
6
6
  require 'axlsx/util/simple_typed_list.rb'
7
7
  require 'axlsx/util/constants.rb'
8
8
  require 'axlsx/util/validators.rb'
9
-
10
- require 'axlsx/util/ms_off_crypto.rb'
9
+ # require 'axlsx/util/storage.rb'
10
+ # require 'axlsx/util/cbf.rb'
11
+ # require 'axlsx/util/ms_off_crypto.rb'
11
12
 
12
13
 
13
14
  # to be included with parsable intitites.
@@ -4,6 +4,7 @@ module Axlsx
4
4
  # xlsx document including valdation and serialization.
5
5
  class Package
6
6
 
7
+ # plain text password
7
8
  # provides access to the app doc properties for this package
8
9
  # see App
9
10
  attr_reader :app
@@ -87,6 +88,11 @@ module Axlsx
87
88
  true
88
89
  end
89
90
 
91
+ # Encrypt the package into a CFB using the password provided
92
+ # def encrypt(file_name, password)
93
+ # moc = MsOffCrypto.new(file_name, password)
94
+ # moc.save
95
+ # end
90
96
 
91
97
  # Validate all parts of the package against xsd schema.
92
98
  # @return [Array] An array of all validation errors found.
@@ -222,6 +222,8 @@ module Axlsx
222
222
  applyProtection = (options[:hidden] || options[:locked]) ? 1 : 0
223
223
 
224
224
  xf = Xf.new(:fillId => fill, :fontId=>fontId, :applyFill=>1, :applyFont=>1, :numFmtId=>numFmtId, :borderId=>borderId, :applyProtection=>applyProtection)
225
+
226
+ xf.applyNumberFormat = true if xf.numFmtId > 0
225
227
 
226
228
  if options[:alignment]
227
229
  xf.alignment = CellAlignment.new(options[:alignment])
@@ -0,0 +1,297 @@
1
+ module Axlsx
2
+
3
+ # The Cfb class is a MS-OFF-CRYPTOGRAPHY specific OLE (MS-CBF) writer implementation. No attempt is made to re-invent the wheel for read/write of compound binary files.
4
+ class Cbf
5
+
6
+ # the serialization for the CBF FAT
7
+ FAT_PACKING = "s128"
8
+
9
+ # the serialization for the MS-OFF-CRYPTO version stream
10
+ VERSION_PACKING = 'l s30 l3'
11
+
12
+ # The serialization for the MS-OFF-CRYPTO dataspace map stream
13
+ DATA_SPACE_MAP_PACKING = 'l6 s16 l s25'
14
+
15
+ # The serialization for the MS-OFF-CRYPTO strong encrytion data space stream
16
+ STRONG_ENCRYPTION_DATA_SPACE_PACKING = 'l3 s25'
17
+
18
+ # The serialization for the MS-OFF-CRYPTO primary stream
19
+ PRIMARY_PACKING = 'l3 s38 l s39 l3 x12 l x2'
20
+
21
+ # The cutoff size that determines if a stream should be in the mini-fat or the fat
22
+ MINI_CUTOFF = 4096
23
+
24
+ # The serialization for CBF header
25
+ HEADER_PACKING = "q x16 l s3 x10 l l x4 l*"
26
+
27
+ # Creates a new Cbf object based on the ms_off_crypto object provided.
28
+ # @param [MsOffCrypto] ms_off_crypto
29
+ def initialize(ms_off_crypto)
30
+ @file_name = ms_off_crypto.file_name
31
+ create_storages
32
+ mini_fat_stream
33
+ mini_fat
34
+ fat
35
+ header
36
+ end
37
+
38
+ # creates or returns the version storage
39
+ # @return [Storage]
40
+ def version
41
+ @version ||= create_version
42
+ end
43
+
44
+ # returns the data space map storage
45
+ # @return [Storage]
46
+ def data_space_map
47
+ @data_space_map ||= create_data_space_map
48
+ end
49
+
50
+ # returns the primary storage
51
+ # @return [Storgae]
52
+ def primary
53
+ @primary ||= create_primary
54
+ end
55
+
56
+ # returns the summary information storage
57
+ # @return [Storage]
58
+ def summary_information
59
+ @summary_information ||= create_summary_information
60
+ end
61
+
62
+ # returns the document summary information
63
+ # @return [Storage]
64
+ def document_summary_information
65
+ @document_summary_information ||= create_document_summary_information
66
+ end
67
+
68
+ # returns the stream of data allocated in the fat
69
+ # @return [String]
70
+ def fat_stream
71
+ @fat_stream ||= create_fat_stream
72
+ end
73
+
74
+ # returns the stream allocated in the mini fat.
75
+ # return [String]
76
+ def mini_fat_stream
77
+ @mini_fat_stream ||= create_mini_fat_stream
78
+ end
79
+
80
+ # returns the mini fat
81
+ # return [String]
82
+ def mini_fat
83
+ @mini_fat ||= create_mini_fat
84
+ end
85
+
86
+ # returns the fat
87
+ # @return [String]
88
+ def fat
89
+ @fat ||= create_fat
90
+ end
91
+
92
+ # returns the CFB header
93
+ # @return [String]
94
+ def header
95
+ @header ||= create_header
96
+ end
97
+
98
+
99
+ # writes the compound binary file to disk
100
+ def save
101
+ ole = File.open(@file_name, 'w')
102
+ ole << header
103
+ ole << fat
104
+ @storages.each { |s| ole << s.to_s }
105
+ ole << Array.new((512-(ole.pos % 512)), 0).pack('c*')
106
+ ole << mini_fat
107
+ ole << mini_fat_stream
108
+ ole << fat_stream
109
+ ole.close
110
+ end
111
+
112
+ private
113
+
114
+ # Generates the storages required for ms-office-cryptography cfb
115
+ def create_storages
116
+ @storages = []
117
+ @encryption_info = ms_off_crypto.encryption_info
118
+ @encrypted_package = ms_off_crypto.encrypted_package
119
+ @storages << Storage.new('R', :type=>Storage::TYPES[:root], :color=>Storage::COLORS[:red], :child=>1, :modified=>129685612742510730)
120
+ @storages.last.name_size = 2
121
+ @storages << Storage.new('EncryptionInfo', :data=>@encryption_info, :left=>3, :size => @encryption_info.size) # example shows right child. do we need the summary info????
122
+ @storages << Storage.new('EncryptedPackage', :data=>@encrypted_package, :color=>Storage::COLORS[:red], :size=>@encrypted_package.size)
123
+ @storages << Storage.new([6].pack("c")+"DataSpaces", :child=>5, :modified =>129685612740945580, :created=>129685612740819979)
124
+ @storages << version
125
+ @storages << data_space_map
126
+ @storages << Storage.new('DataSpaceInfo', :right=>8, :child=>7, :created=>129685612740828880,:modified=>129685612740831800)
127
+ @storages << strong_encryption_data_space
128
+ @storages << Storage.new('TransformInfo', :color => Storage::COLORS[:red], :child=>9, :created=>129685612740834130, :modified=>129685612740943959)
129
+ @storages << Storage.new('StrongEncryptionTransform', :child=>10, :created=>129685612740834169, :modified=>129685612740942280)
130
+ @storages << primary
131
+ end
132
+
133
+ # generates the mini fat stream
134
+ # @return [String]
135
+ def create_mini_fat_stream
136
+ mfs = []
137
+ @storages.select{ |s| s.type == Storage::TYPES[:stream] && s.size < MINI_CUTOFF}.each_with_index do |stream, index|
138
+ mfs.concat stream.data
139
+ mfs.concat Array.new(64 - (mfs.size % 64), 0) if mfs.size % 64
140
+ end
141
+ @storages[0].size = mfs.size
142
+ mfs.concat(Array.new(512 - (mfs.size % 512), 0))
143
+ mfs.pack 'c*'
144
+ end
145
+
146
+ # generates the fat stream.
147
+ # @return [String]
148
+ def create_fat_stream
149
+ mfs = []
150
+ @storages.select{ |s| s.type == Storage::TYPES[:stream] && s.size >= MINI_CUTOFF}.each_with_index do |stream, index|
151
+ mfs.concat stream.data
152
+ mfs.concat Array.new(512 - (mfs.size % 512), 0) if mfs.size % 512
153
+ end
154
+ mfs.pack 'c*'
155
+ end
156
+
157
+ # creates the mini fat
158
+ # @return [String]
159
+ def create_mini_fat
160
+ v_mf = []
161
+ @storages.select{ |s| s.type == Storage::TYPES[:stream] && s.size < MINI_CUTOFF}.each do |stream|
162
+ allocate_stream(v_mf, stream, 64)
163
+ end
164
+ v_mf.concat Array.new(128 - v_mf.size, -1)
165
+ v_mf.pack 'l*'
166
+ end
167
+
168
+ # creates the fat
169
+ # @return [String]
170
+ def create_fat
171
+ v_fat = [-3]
172
+ # storages four per sector, allocation forces directories to start at sector ID 0
173
+ allocate_stream(v_fat, @storages, 4)
174
+ # fat entry for minifat
175
+ allocate_stream(v_fat, 0, 512)
176
+ # fat entry for minifat stream
177
+ @storages[0].sector = v_fat.size
178
+ allocate_stream(v_fat, mini_fat_stream, 512)
179
+ # fat entries for encrypted package storage
180
+ # what to do about DIFAT for larger packages...
181
+ if @encrypted_package.size > (109 - v_fat.size) * 512
182
+ raise ArgumentError, "Your package is too big!"
183
+ end
184
+
185
+ if @encrypted_package.size >= MINI_CUTOFF
186
+ allocate_stream(v_fat, @encrypted_package, 512)
187
+ end
188
+
189
+ v_fat.concat Array.new(128 - v_fat.size, -1) if v_fat.size < 128 #pack in unused sectors
190
+ v_fat.pack 'l*'
191
+ end
192
+
193
+ # Creates the version storage
194
+ # @return [Storage]
195
+ def create_version
196
+ v_stream= [60, "Microsoft.Container.DataSpaces".bytes.to_a, 1, 1, 1].flatten!.pack VERSION_PACKING
197
+ Storage.new('Version', :data=>v_stream, :size=>v_stream.size)
198
+ end
199
+
200
+ # returns the strong encryption data space storage
201
+ # @return [Storgae]
202
+ def strong_encryption_data_space
203
+ @strong_encryption_data_space ||= create_strong_encryption_data_space
204
+ end
205
+
206
+ # Creates the data space map storage
207
+ # @return [Storgae]
208
+ def create_data_space_map
209
+ v_stream = [8,1,104, 1,0, 32, "EncryptedPackage".bytes.to_a, 50, "StrongEncryptionDataSpace".bytes.to_a].flatten!.pack DATA_SPACE_MAP_PACKING
210
+ Storage.new('DataSpaceMap', :data=>v_stream, :left => 4, :right => 6, :size=>v_stream.size)
211
+ end
212
+
213
+
214
+ # creates the stron encryption data space storage
215
+ # @return [Storgae]
216
+ def create_strong_encryption_data_space
217
+ v_stream = [8,1,50,"StrongEncryptionTransform".bytes.to_a].flatten.pack STRONG_ENCRYPTION_DATA_SPACE_PACKING
218
+ Storage.new("StrongEncryptionDataSpace", :data=>v_stream, :size => v_stream.size)
219
+ end
220
+
221
+ # creates the primary storage
222
+ # @return [Storgae]
223
+ def create_primary
224
+ v_stream = [88,1,76,"{FF9A3F03-56EF-4613-BDD5-5A41C1D07246}".bytes.to_a].flatten
225
+ v_stream.concat [78, "Microsoft.Container.EncryptionTransform".bytes.to_a,1,1,1,4].flatten
226
+ v_stream = v_stream.pack PRIMARY_PACKING
227
+ Storage.new([6].pack("c")+"Primary", :data=>v_stream)
228
+ end
229
+
230
+
231
+ SUMMARY_INFORMATION_PACKING = ""
232
+ # creates the summary information storage
233
+ # @return [Storage]
234
+ def create_summary_information
235
+ # FEFF 0000 030A 0100 0000 0000 0000 0000
236
+ # 0000 0000 0000 0000 0100 0000 E085 9FF2
237
+ # F94F 6810 AB91 0800 2B27 B3D9 3000 0000
238
+ # AC00 0000 0700 0000 0100 0000 4000 0000
239
+ # 0400 0000 4800 0000 0800 0000 5800 0000
240
+ # 1200 0000 6800 0000 0C00 0000 8C00 0000
241
+ # 0D00 0000 9800 0000 1300 0000 A400 0000
242
+ # 0200 0000 E9FD 0000 1E00 0000 0800 0000
243
+ # 7261 6E64 796D 0000 1E00 0000 0800 0000
244
+ # 7261 6E64 796D 0000 1E00 0000 1C00 0000
245
+ # 4D69 6372 6F73 6F66 7420 4D61 6369 6E74
246
+ # 6F73 6820 4578 6365 6C00 0000 4000 0000
247
+ # 10AC 5396 60BC CC01 4000 0000 40F4 FDAF
248
+ # 60BC CC01 0300 0000 0100 0000
249
+ v_stream = []
250
+ v_stream = v_stream.pack SUMMARY_INFORMATION_PACKING
251
+ Storage.new([5].pack('c')+"SummaryInformation", :data=>v_stream)
252
+ end
253
+
254
+ DOCUMENT_SUMMARY_INFORMATION_PACKING = ""
255
+ # creates the document summary information storage
256
+ # @return [Storage]
257
+ def create_document_summary_information
258
+ v_stream = []
259
+ v_stream = v_stream.pack DOCUMENT_SUMMARY_INFORMATION_PACKING
260
+ Storage.new([5].pack('c')+"DocumentSummaryInformation", :data=>v_stream)
261
+ end
262
+
263
+ # Creates the header
264
+ # @return [String]
265
+ def create_header
266
+ header = []
267
+ header << -2226271756974174256 # identifier pack as q
268
+ header << 196670 # version pack as L
269
+ header << 65534 # byte order pack as s
270
+ header << 9 # sector shift
271
+ header << 6 # mini-sector shift
272
+ header << (fat.size/512.0).ceil # this is the number of FAT sectors in the file at index 6 pack as L
273
+ header << header.last # this is the first directory sector, index of 7 pack as L
274
+ header << MINI_CUTOFF # minfat cutoff pack as L
275
+ # MiniFat starts after directories
276
+ header << (fat.size/512.0).ceil + (@storages.size/4.0).ceil # this is the sector id for the first minifat index 10 pack as L
277
+ header << (mini_fat.size/512.0).ceil # minifat sector count index 11 pack as L
278
+ header << -2 # the first DIFAT - set to end of chain until we exceed a single FAT pack as L
279
+ header << 0 # number of DIFAT sectors, unless we go beyond 109 FAT sectors this will always be 0 pack as L
280
+ header << 0 # first FAT sector defined in the DIFAT pack as L
281
+ header.concat Array.new(108, -1) # Difat sectors pack as L108
282
+ header.pack(HEADER_PACKING)
283
+ end
284
+
285
+ # Allocates sector chains in a allocation table based on the sector size and stream provided
286
+ # If a storage obeject is provided, the starting sector value for the storage is updated based on the allocation performed here.
287
+ # @param [Array] table Allocation table array
288
+ # @param [Storage | String] stream
289
+ # @param [Integer] size The cutoff size for the stream.
290
+ def allocate_stream(table, stream, size)
291
+ stream.sector = table.size if stream.respond_to?(:sector)
292
+ ((stream.size / size.to_f).ceil).times { table << table.size }
293
+ table[table.size-1] = -2 # this is the CBF chain terminator
294
+ end
295
+
296
+ end
297
+ end
@@ -0,0 +1,201 @@
1
+ module Axlsx
2
+
3
+ # The Cfb class is a MS-OFF-CRYPTOGRAPHY specific OLE (MS-CBF) writer implementation. No attempt is made to re-invent the wheel for read/write of compound binary files.
4
+ class Cbf
5
+
6
+ # the serialization for the CBF FAT
7
+ FAT_PACKING = "s128"
8
+
9
+ # the serialization for the MS-OFF-CRYPTO version stream
10
+ VERSION_PACKING = 'l s30 l3'
11
+
12
+ # The serialization for the MS-OFF-CRYPTO dataspace map stream
13
+ DATA_SPACE_MAP_PACKING = 'l6 s16 l s25'
14
+
15
+ # The serialization for the MS-OFF-CRYPTO strong encrytion data space stream
16
+ STRONG_ENCRYPTION_DATA_SPACE_PACKING = 'l3 s25'
17
+
18
+ # The serialization for the MS-OFF-CRYPTO primary stream
19
+ PRIMARY_PACKING = 'l3 s38 l s39 l3 x12 l x2'
20
+
21
+ # The cutoff size that determines if a stream should be in the mini-fat or the fat
22
+ MINI_CUTOFF = 4096
23
+
24
+ # Creates a new Cbf object based on a package.
25
+ def initialize(ms_off_crypto)
26
+ @file_name = ms_off_crypto.file_name
27
+ @storages = []
28
+ @encryption_info = ms_off_crypto.encryption_info
29
+ @encrypted_package = ms_off_crypto.encrypted_package
30
+ @storages << Storage.new('R', :type=>Storage::TYPES[:root], :color=>Storage::COLORS[:red], :child=>1, :modified=>129685612742510730)
31
+ @storages.last.name_size = 2
32
+ @storages << Storage.new('EncryptionInfo', :data=>@encryption_info, :left=>3, :size => @encryption_info.size) # example shows right child. do we need the summary info????
33
+ @storages << Storage.new('EncryptedPackage', :data=>@encrypted_package, :color=>Storage::COLORS[:red], :size=>@encrypted_package.size)
34
+ @storages << Storage.new([6].pack("c")+"DataSpaces", :child=>5, :modified =>129685612740945580, :created=>129685612740819979)
35
+ @storages << version
36
+ @storages << data_space_map
37
+ @storages << Storage.new('DataSpaceInfo', :right=>8, :child=>7, :created=>129685612740828880,:modified=>129685612740831800)
38
+ @storages << strong_encryption_data_space
39
+ @storages << Storage.new('TransformInfo', :color => Storage::COLORS[:red], :child=>9, :created=>129685612740834130, :modified=>129685612740943959)
40
+ @storages << Storage.new('StrongEncryptionTransform', :child=>10, :created=>129685612740834169, :modified=>129685612740942280)
41
+ @storages << primary
42
+ mini_fat_stream
43
+ mini_fat
44
+ fat
45
+ header
46
+ end
47
+
48
+ def version
49
+ @version ||= create_version
50
+ end
51
+
52
+ def data_space_map
53
+ @data_space_map ||= create_data_space_map
54
+ end
55
+
56
+ def strong_encryption_data_space
57
+ @strong_encryption_data_space ||= create_strong_encryption_data_space
58
+ end
59
+
60
+ def primary
61
+ @primary ||= create_primary
62
+ end
63
+
64
+
65
+ def create_primary
66
+ v_stream = [88,1,76,"{FF9A3F03-56EF-4613-BDD5-5A41C1D07246}".bytes.to_a].flatten
67
+ v_stream.concat [78, "Microsoft.Container.EncryptionTransform".bytes.to_a,1,1,1,4].flatten
68
+ v_stream = v_stream.pack PRIMARY_PACKING
69
+ Storage.new([6].pack("c")+"Primary", :data=>v_stream, :size=>v_stream.size)
70
+ end
71
+
72
+ def create_strong_encryption_data_space
73
+ v_stream = [8,1,50,"StrongEncryptionTransform".bytes.to_a].flatten.pack STRONG_ENCRYPTION_DATA_SPACE_PACKING
74
+ Storage.new("StrongEncryptionDataSpace", :data=>v_stream, :size => v_stream.size)
75
+ end
76
+
77
+ # Create the version storage
78
+ def create_version
79
+ v_stream= [60, "Microsoft.Container.DataSpaces".bytes.to_a, 1, 1, 1].flatten!.pack VERSION_PACKING
80
+ Storage.new('Version', :data=>v_stream, :size=>v_stream.size)
81
+ end
82
+
83
+ def create_data_space_map
84
+ v_stream = [8,1,104, 1,0, 32, "EncryptedPackage".bytes.to_a, 50, "StrongEncryptionDataSpace".bytes.to_a].flatten!.pack DATA_SPACE_MAP_PACKING
85
+ Storage.new('DataSpaceMap', :data=>v_stream, :left => 4, :right => 6, :size=>v_stream.size)
86
+ end
87
+
88
+ def allocate_stream(table, stream, size)
89
+ stream.sector = table.size if stream.respond_to?(:sector)
90
+ ((stream.size / size.to_f).ceil).times { table << table.size }
91
+ table[table.size-1] = -2
92
+ end
93
+
94
+ def fat_stream
95
+ @fat_stream ||= create_fat_stream
96
+ end
97
+ def create_fat_stream
98
+ mfs = []
99
+ @storages.select{ |s| s.type == Storage::TYPES[:stream] && s.size >= MINI_CUTOFF}.each_with_index do |stream, index|
100
+ mfs.concat stream.data
101
+ mfs.concat Array.new(512 - (mfs.size % 512), 0) if mfs.size % 512
102
+ end
103
+ mfs.pack 'c*'
104
+ end
105
+
106
+ def mini_fat_stream
107
+ @mini_fat_stream ||= create_mini_fat_stream
108
+ end
109
+
110
+ def create_mini_fat_stream
111
+ mfs = []
112
+ @storages.select{ |s| s.type == Storage::TYPES[:stream] && s.size < MINI_CUTOFF}.each_with_index do |stream, index|
113
+ mfs.concat stream.data
114
+ mfs.concat Array.new(64 - (mfs.size % 64), 0) if mfs.size % 64
115
+ end
116
+ @storages[0].size = mfs.size
117
+ mfs.concat(Array.new(512 - (mfs.size % 512), 0))
118
+ mfs.pack 'c*'
119
+ end
120
+
121
+ def mini_fat
122
+ @mini_fat ||= create_mini_fat
123
+ end
124
+
125
+ def create_mini_fat
126
+ v_mf = []
127
+ @storages.select{ |s| s.type == Storage::TYPES[:stream] && s.size < MINI_CUTOFF}.each do |stream|
128
+ allocate_stream(v_mf, stream, 64)
129
+ end
130
+ v_mf.concat Array.new(128 - v_mf.size, -1)
131
+ v_mf.pack 'l*'
132
+ end
133
+
134
+ def fat
135
+ @fat ||= create_fat
136
+ end
137
+
138
+ def create_fat
139
+ v_fat = [-3]
140
+ # storages four per sector, allocation forces directories to start at sector ID 0
141
+ allocate_stream(v_fat, @storages, 4)
142
+ # fat entry for minifat
143
+ allocate_stream(v_fat, 0, 512)
144
+ # fat entry for minifat stream
145
+ @storages[0].sector = v_fat.size
146
+ allocate_stream(v_fat, mini_fat_stream, 512)
147
+ # fat entries for encrypted package storage
148
+ # what to do about DIFAT for larger packages...
149
+ if @encrypted_package.size > (109 - v_fat.size) * 512
150
+ raise ArgumentError, "Your package is too big!"
151
+ end
152
+
153
+ if @encrypted_package.size >= MINI_CUTOFF
154
+ allocate_stream(v_fat, @encrypted_package, 512)
155
+ end
156
+
157
+ v_fat.concat Array.new(128 - v_fat.size, -1) if v_fat.size < 128 #pack in unused sectors
158
+ v_fat.pack 'l*'
159
+ end
160
+
161
+ def header
162
+ @header ||= create_header
163
+ end
164
+
165
+ # The serialization for CBF header
166
+ HEADER_PACKING = "q x16 l s3 x10 l l x4 l*"
167
+
168
+ def create_header
169
+ header = []
170
+ header << -2226271756974174256 # identifier pack as q
171
+ header << 196670 # version pack as L
172
+ header << 65534 # byte order pack as s
173
+ header << 9 # sector shift
174
+ header << 6 # mini-sector shift
175
+ header << (fat.size/512.0).ceil # this is the number of FAT sectors in the file at index 6 pack as L
176
+ header << header.last # this is the first directory sector, index of 7 pack as L
177
+ header << MINI_CUTOFF # minfat cutoff pack as L
178
+ # MiniFat starts after directories
179
+ header << (fat.size/512.0).ceil + (@storages.size/4.0).ceil # this is the sector id for the first minifat index 10 pack as L
180
+ header << (mini_fat.size/512.0).ceil # minifat sector count index 11 pack as L
181
+ header << -2 # the first DIFAT - set to end of chain until we exceed a single FAT pack as L
182
+ header << 0 # number of DIFAT sectors, unless we go beyond 109 FAT sectors this will always be 0 pack as L
183
+ header << 0 # first FAT sector defined in the DIFAT pack as L
184
+ header.concat Array.new(108, -1) # Difat sectors pack as L108
185
+ end
186
+
187
+ def save
188
+
189
+ ole = File.open(@file_name, 'w')
190
+ ole << header.pack(HEADER_PACKING)
191
+ ole << fat
192
+ @storages.each { |s| ole << s.to_s }
193
+ ole << Array.new((512-(ole.pos % 512)), 0).pack('c*')
194
+ ole << mini_fat
195
+ ole << mini_fat_stream
196
+ ole << fat_stream
197
+ ole.close
198
+ end
199
+
200
+ end
201
+ end