File Coverage

blib/lib/Spreadsheet/WriteExcel/OLEwriter.pm
Criterion Covered Total %
statement 197 203 97.0
branch 22 30 73.3
condition n/a
subroutine 17 17 100.0
pod 0 5 0.0
total 236 255 92.5


line stmt bran cond sub pod time code
1             package Spreadsheet::WriteExcel::OLEwriter;
2              
3             ###############################################################################
4             #
5             # OLEwriter - A writer class to store BIFF data in a OLE compound storage file.
6             #
7             #
8             # Used in conjunction with Spreadsheet::WriteExcel
9             #
10             # Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
11             #
12             # Documentation after __END__
13             #
14              
15 30     30   393 use Exporter;
  30         54  
  30         1400  
16 30     30   154 use strict;
  30         77  
  30         869  
17 30     30   153 use Carp;
  30         51  
  30         4063  
18 30     30   32415 use FileHandle;
  30         598097  
  30         231  
19              
20              
21              
22              
23              
24 30     30   18815 use vars qw($VERSION @ISA);
  30         72  
  30         94984  
25             @ISA = qw(Exporter);
26              
27             $VERSION = '2.40';
28              
29             ###############################################################################
30             #
31             # new()
32             #
33             # Constructor
34             #
35             sub new {
36              
37 60     60 0 131 my $class = shift;
38 60         1018 my $self = {
39             _olefilename => $_[0],
40             _filehandle => "",
41             _fileclosed => 0,
42             _internal_fh => 0,
43             _biff_only => 0,
44             _size_allowed => 0,
45             _biffsize => 0,
46             _booksize => 0,
47             _big_blocks => 0,
48             _list_blocks => 0,
49             _root_start => 0,
50             _block_count => 4,
51             };
52              
53 60         188 bless $self, $class;
54 60         250 $self->_initialize();
55 60         174 return $self;
56             }
57              
58              
59             ###############################################################################
60             #
61             # _initialize()
62             #
63             # Create a new filehandle or use the provided filehandle.
64             #
65             sub _initialize {
66              
67 60     60   127 my $self = shift;
68 60         356 my $olefile = $self->{_olefilename};
69 60         114 my $fh;
70              
71             # If the filename is a reference it is assumed that it is a valid
72             # filehandle, if not we create a filehandle.
73             #
74 60 50       282 if (ref($olefile)) {
75 60         123 $fh = $olefile;
76             }
77             else{
78              
79             # Create a new file, open for writing
80 0         0 $fh = FileHandle->new("> $olefile");
81              
82             # Workbook.pm also checks this but something may have happened since
83             # then.
84 0 0       0 if (not defined $fh) {
85 0         0 croak "Can't open $olefile. It may be in use or protected.\n";
86             }
87              
88             # binmode file whether platform requires it or not
89 0         0 binmode($fh);
90              
91 0         0 $self->{_internal_fh} = 1;
92             }
93              
94             # Store filehandle
95 60         158 $self->{_filehandle} = $fh;
96             }
97              
98              
99             ###############################################################################
100             #
101             # set_size($biffsize)
102             #
103             # Set the size of the data to be written to the OLE stream
104             #
105             # $big_blocks = (109 depot block x (128 -1 marker word)
106             # - (1 x end words)) = 13842
107             # $maxsize = $big_blocks * 512 bytes = 7087104
108             #
109             sub set_size {
110              
111 60     60 0 147 my $self = shift;
112 60         1072 my $maxsize = 7_087_104; # Use Spreadsheet::WriteExcel::Big to exceed this
113              
114 60 50       245 if ($_[0] > $maxsize) {
115 0         0 return $self->{_size_allowed} = 0;
116             }
117              
118 60         133 $self->{_biffsize} = $_[0];
119              
120             # Set the min file size to 4k to avoid having to use small blocks
121 60 100       229 if ($_[0] > 4096) {
122 1         8 $self->{_booksize} = $_[0];
123             }
124             else {
125 59         121 $self->{_booksize} = 4096;
126             }
127              
128 60         186 return $self->{_size_allowed} = 1;
129              
130             }
131              
132              
133             ###############################################################################
134             #
135             # _calculate_sizes()
136             #
137             # Calculate various sizes needed for the OLE stream
138             #
139             sub _calculate_sizes {
140              
141 59     59   431 my $self = shift;
142 59         129 my $datasize = $self->{_booksize};
143              
144 59 100       1285 if ($datasize % 512 == 0) {
145 58         180 $self->{_big_blocks} = $datasize/512;
146             }
147             else {
148 1         5 $self->{_big_blocks} = int($datasize/512) +1;
149             }
150             # There are 127 list blocks and 1 marker blocks for each big block
151             # depot + 1 end of chain block
152 59         211 $self->{_list_blocks} = int(($self->{_big_blocks})/127) +1;
153 59         150 $self->{_root_start} = $self->{_big_blocks};
154             }
155              
156              
157             ###############################################################################
158             #
159             # close()
160             #
161             # Write root entry, big block list and close the filehandle.
162             # This routine is used to explicitly close the open filehandle without
163             # having to wait for DESTROY.
164             #
165             sub close {
166              
167 60     60 0 205 my $self = shift;
168              
169 60 50       1267 return if not $self->{_size_allowed};
170              
171 60 100       1469 $self->_write_padding() if not $self->{_biff_only};
172 60 100       425 $self->_write_property_storage() if not $self->{_biff_only};
173 60 100       526 $self->_write_big_block_depot() if not $self->{_biff_only};
174              
175 60         111 my $close = 1; # Default to no error for external filehandles.
176              
177             # Close the filehandle if it was created internally.
178 60 50       26042 $close = CORE::close($self->{_filehandle}) if $self->{_internal_fh};
179              
180 60         242 $self->{_fileclosed} = 1;
181              
182 60         474 return $close;
183             }
184              
185              
186             ###############################################################################
187             #
188             # DESTROY()
189             #
190             # Close the filehandle if it hasn't already been explicitly closed.
191             #
192             sub DESTROY {
193              
194 60     60   120 my $self = shift;
195              
196 60         633 local ($@, $!, $^E, $?);
197              
198 60 50       1386 $self->close() unless $self->{_fileclosed};
199             }
200              
201              
202             ###############################################################################
203             #
204             # write($data)
205             #
206             # Write BIFF data to OLE file.
207             #
208             sub write {
209              
210 368     368 0 464 my $self = shift;
211              
212             # Protect print() from -l on the command line.
213 368         1065 local $\ = undef;
214 368         418 print {$self->{_filehandle}} $_[0];
  368         2508  
215             }
216              
217              
218             ###############################################################################
219             #
220             # write_header()
221             #
222             # Write OLE header block.
223             #
224             sub write_header {
225              
226 60     60 0 134 my $self = shift;
227              
228 60 100       215 return if $self->{_biff_only};
229 59         236 $self->_calculate_sizes();
230              
231 59         129 my $root_start = $self->{_root_start};
232 59         121 my $num_lists = $self->{_list_blocks};
233              
234 59         136 my $id = pack("NN", 0xD0CF11E0, 0xA1B11AE1);
235 59         127 my $unknown1 = pack("VVVV", 0x00, 0x00, 0x00, 0x00);
236 59         345 my $unknown2 = pack("vv", 0x3E, 0x03);
237 59         125 my $unknown3 = pack("v", -2);
238 59         107 my $unknown4 = pack("v", 0x09);
239 59         134 my $unknown5 = pack("VVV", 0x06, 0x00, 0x00);
240 59         168 my $num_bbd_blocks = pack("V", $num_lists);
241 59         130 my $root_startblock = pack("V", $root_start);
242 59         137 my $unknown6 = pack("VV", 0x00, 0x1000);
243 59         113 my $sbd_startblock = pack("V", -2);
244 59         126 my $unknown7 = pack("VVV", 0x00, -2 ,0x00);
245 59         107 my $unused = pack("V", -1);
246              
247             # Protect print() from -l on the command line.
248 59         190 local $\ = undef;
249              
250 59         149 print {$self->{_filehandle}} $id;
  59         954  
251 59         115 print {$self->{_filehandle}} $unknown1;
  59         144  
252 59         104 print {$self->{_filehandle}} $unknown2;
  59         126  
253 59         88 print {$self->{_filehandle}} $unknown3;
  59         144  
254 59         93 print {$self->{_filehandle}} $unknown4;
  59         128  
255 59         127 print {$self->{_filehandle}} $unknown5;
  59         120  
256 59         98 print {$self->{_filehandle}} $num_bbd_blocks;
  59         160  
257 59         1211 print {$self->{_filehandle}} $root_startblock;
  59         142  
258 59         95 print {$self->{_filehandle}} $unknown6;
  59         116  
259 59         87 print {$self->{_filehandle}} $sbd_startblock;
  59         128  
260 59         91 print {$self->{_filehandle}} $unknown7;
  59         146  
261              
262 59         190 for (1..$num_lists) {
263 59         111 $root_start++;
264 59         90 print {$self->{_filehandle}} pack("V", $root_start);
  59         280  
265             }
266              
267 59         158 for ($num_lists..108) {
268 6372         5788 print {$self->{_filehandle}} $unused;
  6372         10239  
269             }
270             }
271              
272              
273             ###############################################################################
274             #
275             # _write_big_block_depot()
276             #
277             # Write big block depot.
278             #
279             sub _write_big_block_depot {
280              
281 59     59   110 my $self = shift;
282 59         137 my $num_blocks = $self->{_big_blocks};
283 59         113 my $num_lists = $self->{_list_blocks};
284 59         120 my $total_blocks = $num_lists *128;
285 59         120 my $used_blocks = $num_blocks + $num_lists +2;
286              
287 59         105 my $marker = pack("V", -3);
288 59         99 my $end_of_chain = pack("V", -2);
289 59         130 my $unused = pack("V", -1);
290              
291              
292             # Protect print() from -l on the command line.
293 59         169 local $\ = undef;
294              
295 59         193 for my $i (1..$num_blocks-1) {
296 426         425 print {$self->{_filehandle}} pack("V",$i);
  426         1114  
297             }
298              
299 59         125 print {$self->{_filehandle}} $end_of_chain;
  59         153  
300 59         97 print {$self->{_filehandle}} $end_of_chain;
  59         125  
301              
302 59         149 for (1..$num_lists) {
303 59         100 print {$self->{_filehandle}} $marker;
  59         216  
304             }
305              
306 59         158 for ($used_blocks..$total_blocks) {
307 6949         6401 print {$self->{_filehandle}} $unused;
  6949         11149  
308             }
309             }
310              
311              
312             ###############################################################################
313             #
314             # _write_property_storage()
315             #
316             # Write property storage. TODO: add summary sheets
317             #
318             sub _write_property_storage {
319              
320 59     59   110 my $self = shift;
321              
322 59         105 my $rootsize = -2;
323 59         130 my $booksize = $self->{_booksize};
324              
325             ################# name type dir start size
326 59         919 $self->_write_pps('Root Entry', 0x05, 1, -2, 0x00);
327 59         225 $self->_write_pps('Workbook', 0x02, -1, 0x00, $booksize);
328 59         211 $self->_write_pps('', 0x00, -1, 0x00, 0x0000);
329 59         216 $self->_write_pps('', 0x00, -1, 0x00, 0x0000);
330             }
331              
332              
333             ###############################################################################
334             #
335             # _write_pps()
336             #
337             # Write property sheet in property storage
338             #
339             sub _write_pps {
340              
341 236     236   333 my $self = shift;
342              
343 236         336 my $name = $_[0];
344 236         361 my @name = ();
345 236         303 my $length = 0;
346              
347 236 100       791 if ($name ne '') {
348 118         257 $name = $_[0] . "\0";
349             # Simulate a Unicode string
350 118         1193 @name = map(ord, split('', $name));
351 118         360 $length = length($name) * 2;
352             }
353              
354 236         575 my $rawname = pack("v*", @name);
355 236         428 my $zero = pack("C", 0);
356              
357 236         1266 my $pps_sizeofname = pack("v", $length); #0x40
358 236         398 my $pps_type = pack("v", $_[1]); #0x42
359 236         326 my $pps_prev = pack("V", -1); #0x44
360 236         335 my $pps_next = pack("V", -1); #0x48
361 236         406 my $pps_dir = pack("V", $_[2]); #0x4c
362              
363 236         554 my $unknown1 = pack("V", 0);
364              
365 236         303 my $pps_ts1s = pack("V", 0); #0x64
366 236         282 my $pps_ts1d = pack("V", 0); #0x68
367 236         272 my $pps_ts2s = pack("V", 0); #0x6c
368 236         251 my $pps_ts2d = pack("V", 0); #0x70
369 236         388 my $pps_sb = pack("V", $_[3]); #0x74
370 236         350 my $pps_size = pack("V", $_[4]); #0x78
371              
372              
373             # Protect print() from -l on the command line.
374 236         525 local $\ = undef;
375              
376 236         264 print {$self->{_filehandle}} $rawname;
  236         510  
377 236         288 print {$self->{_filehandle}} $zero x (64 -$length);
  236         659  
378 236         297 print {$self->{_filehandle}} $pps_sizeofname;
  236         780  
379 236         289 print {$self->{_filehandle}} $pps_type;
  236         387  
380 236         305 print {$self->{_filehandle}} $pps_prev;
  236         852  
381 236         361 print {$self->{_filehandle}} $pps_next;
  236         411  
382 236         325 print {$self->{_filehandle}} $pps_dir;
  236         362  
383 236         290 print {$self->{_filehandle}} $unknown1 x 5;
  236         458  
384 236         281 print {$self->{_filehandle}} $pps_ts1s;
  236         379  
385 236         275 print {$self->{_filehandle}} $pps_ts1d;
  236         387  
386 236         266 print {$self->{_filehandle}} $pps_ts2d;
  236         357  
387 236         1367 print {$self->{_filehandle}} $pps_ts2d;
  236         392  
388 236         649 print {$self->{_filehandle}} $pps_sb;
  236         389  
389 236         273 print {$self->{_filehandle}} $pps_size;
  236         369  
390 236         259 print {$self->{_filehandle}} $unknown1;
  236         831  
391             }
392              
393              
394             ###############################################################################
395             #
396             # _write_padding()
397             #
398             # Pad the end of the file
399             #
400             sub _write_padding {
401              
402 59     59   117 my $self = shift;
403 59         154 my $biffsize = $self->{_biffsize};
404 59         252 my $min_size;
405              
406 59 100       234 if ($biffsize < 4096) {
407 58         121 $min_size = 4096;
408             }
409             else {
410 1         2 $min_size = 512;
411             }
412              
413             # Protect print() from -l on the command line.
414 59         160 local $\ = undef;
415              
416 59 50       276 if ($biffsize % $min_size != 0) {
417 59         320 my $padding = $min_size - ($biffsize % $min_size);
418 59         120 print {$self->{_filehandle}} "\0" x $padding;
  59         625  
419             }
420             }
421              
422              
423             1;
424              
425              
426             __END__