blob: 7cbb49eebe587863dea9deee2ce7785b10a81ecb [file] [log] [blame]
yu.dongc33b3072024-08-21 23:14:49 -07001package 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
15use Exporter;
16use strict;
17use Carp;
18use FileHandle;
19
20
21
22
23
24use vars qw($VERSION @ISA);
25@ISA = qw(Exporter);
26
27$VERSION = '2.37';
28
29###############################################################################
30#
31# new()
32#
33# Constructor
34#
35sub new {
36
37 my $class = shift;
38 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 bless $self, $class;
54 $self->_initialize();
55 return $self;
56}
57
58
59###############################################################################
60#
61# _initialize()
62#
63# Create a new filehandle or use the provided filehandle.
64#
65sub _initialize {
66
67 my $self = shift;
68 my $olefile = $self->{_olefilename};
69 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 if (ref($olefile)) {
75 $fh = $olefile;
76 }
77 else{
78
79 # Create a new file, open for writing
80 $fh = FileHandle->new("> $olefile");
81
82 # Workbook.pm also checks this but something may have happened since
83 # then.
84 if (not defined $fh) {
85 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 binmode($fh);
90
91 $self->{_internal_fh} = 1;
92 }
93
94 # Store filehandle
95 $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#
109sub set_size {
110
111 my $self = shift;
112 my $maxsize = 7_087_104; # Use Spreadsheet::WriteExcel::Big to exceed this
113
114 if ($_[0] > $maxsize) {
115 return $self->{_size_allowed} = 0;
116 }
117
118 $self->{_biffsize} = $_[0];
119
120 # Set the min file size to 4k to avoid having to use small blocks
121 if ($_[0] > 4096) {
122 $self->{_booksize} = $_[0];
123 }
124 else {
125 $self->{_booksize} = 4096;
126 }
127
128 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#
139sub _calculate_sizes {
140
141 my $self = shift;
142 my $datasize = $self->{_booksize};
143
144 if ($datasize % 512 == 0) {
145 $self->{_big_blocks} = $datasize/512;
146 }
147 else {
148 $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 $self->{_list_blocks} = int(($self->{_big_blocks})/127) +1;
153 $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#
165sub close {
166
167 my $self = shift;
168
169 return if not $self->{_size_allowed};
170
171 $self->_write_padding() if not $self->{_biff_only};
172 $self->_write_property_storage() if not $self->{_biff_only};
173 $self->_write_big_block_depot() if not $self->{_biff_only};
174
175 my $close = 1; # Default to no error for external filehandles.
176
177 # Close the filehandle if it was created internally.
178 $close = CORE::close($self->{_filehandle}) if $self->{_internal_fh};
179
180 $self->{_fileclosed} = 1;
181
182 return $close;
183}
184
185
186###############################################################################
187#
188# DESTROY()
189#
190# Close the filehandle if it hasn't already been explicitly closed.
191#
192sub DESTROY {
193
194 my $self = shift;
195
196 local ($@, $!, $^E, $?);
197
198 $self->close() unless $self->{_fileclosed};
199}
200
201
202###############################################################################
203#
204# write($data)
205#
206# Write BIFF data to OLE file.
207#
208sub write {
209
210 my $self = shift;
211
212 # Protect print() from -l on the command line.
213 local $\ = undef;
214 print {$self->{_filehandle}} $_[0];
215}
216
217
218###############################################################################
219#
220# write_header()
221#
222# Write OLE header block.
223#
224sub write_header {
225
226 my $self = shift;
227
228 return if $self->{_biff_only};
229 $self->_calculate_sizes();
230
231 my $root_start = $self->{_root_start};
232 my $num_lists = $self->{_list_blocks};
233
234 my $id = pack("NN", 0xD0CF11E0, 0xA1B11AE1);
235 my $unknown1 = pack("VVVV", 0x00, 0x00, 0x00, 0x00);
236 my $unknown2 = pack("vv", 0x3E, 0x03);
237 my $unknown3 = pack("v", -2);
238 my $unknown4 = pack("v", 0x09);
239 my $unknown5 = pack("VVV", 0x06, 0x00, 0x00);
240 my $num_bbd_blocks = pack("V", $num_lists);
241 my $root_startblock = pack("V", $root_start);
242 my $unknown6 = pack("VV", 0x00, 0x1000);
243 my $sbd_startblock = pack("V", -2);
244 my $unknown7 = pack("VVV", 0x00, -2 ,0x00);
245 my $unused = pack("V", -1);
246
247 # Protect print() from -l on the command line.
248 local $\ = undef;
249
250 print {$self->{_filehandle}} $id;
251 print {$self->{_filehandle}} $unknown1;
252 print {$self->{_filehandle}} $unknown2;
253 print {$self->{_filehandle}} $unknown3;
254 print {$self->{_filehandle}} $unknown4;
255 print {$self->{_filehandle}} $unknown5;
256 print {$self->{_filehandle}} $num_bbd_blocks;
257 print {$self->{_filehandle}} $root_startblock;
258 print {$self->{_filehandle}} $unknown6;
259 print {$self->{_filehandle}} $sbd_startblock;
260 print {$self->{_filehandle}} $unknown7;
261
262 for (1..$num_lists) {
263 $root_start++;
264 print {$self->{_filehandle}} pack("V", $root_start);
265 }
266
267 for ($num_lists..108) {
268 print {$self->{_filehandle}} $unused;
269 }
270}
271
272
273###############################################################################
274#
275# _write_big_block_depot()
276#
277# Write big block depot.
278#
279sub _write_big_block_depot {
280
281 my $self = shift;
282 my $num_blocks = $self->{_big_blocks};
283 my $num_lists = $self->{_list_blocks};
284 my $total_blocks = $num_lists *128;
285 my $used_blocks = $num_blocks + $num_lists +2;
286
287 my $marker = pack("V", -3);
288 my $end_of_chain = pack("V", -2);
289 my $unused = pack("V", -1);
290
291
292 # Protect print() from -l on the command line.
293 local $\ = undef;
294
295 for my $i (1..$num_blocks-1) {
296 print {$self->{_filehandle}} pack("V",$i);
297 }
298
299 print {$self->{_filehandle}} $end_of_chain;
300 print {$self->{_filehandle}} $end_of_chain;
301
302 for (1..$num_lists) {
303 print {$self->{_filehandle}} $marker;
304 }
305
306 for ($used_blocks..$total_blocks) {
307 print {$self->{_filehandle}} $unused;
308 }
309}
310
311
312###############################################################################
313#
314# _write_property_storage()
315#
316# Write property storage. TODO: add summary sheets
317#
318sub _write_property_storage {
319
320 my $self = shift;
321
322 my $rootsize = -2;
323 my $booksize = $self->{_booksize};
324
325 ################# name type dir start size
326 $self->_write_pps('Root Entry', 0x05, 1, -2, 0x00);
327 $self->_write_pps('Workbook', 0x02, -1, 0x00, $booksize);
328 $self->_write_pps('', 0x00, -1, 0x00, 0x0000);
329 $self->_write_pps('', 0x00, -1, 0x00, 0x0000);
330}
331
332
333###############################################################################
334#
335# _write_pps()
336#
337# Write property sheet in property storage
338#
339sub _write_pps {
340
341 my $self = shift;
342
343 my $name = $_[0];
344 my @name = ();
345 my $length = 0;
346
347 if ($name ne '') {
348 $name = $_[0] . "\0";
349 # Simulate a Unicode string
350 @name = map(ord, split('', $name));
351 $length = length($name) * 2;
352 }
353
354 my $rawname = pack("v*", @name);
355 my $zero = pack("C", 0);
356
357 my $pps_sizeofname = pack("v", $length); #0x40
358 my $pps_type = pack("v", $_[1]); #0x42
359 my $pps_prev = pack("V", -1); #0x44
360 my $pps_next = pack("V", -1); #0x48
361 my $pps_dir = pack("V", $_[2]); #0x4c
362
363 my $unknown1 = pack("V", 0);
364
365 my $pps_ts1s = pack("V", 0); #0x64
366 my $pps_ts1d = pack("V", 0); #0x68
367 my $pps_ts2s = pack("V", 0); #0x6c
368 my $pps_ts2d = pack("V", 0); #0x70
369 my $pps_sb = pack("V", $_[3]); #0x74
370 my $pps_size = pack("V", $_[4]); #0x78
371
372
373 # Protect print() from -l on the command line.
374 local $\ = undef;
375
376 print {$self->{_filehandle}} $rawname;
377 print {$self->{_filehandle}} $zero x (64 -$length);
378 print {$self->{_filehandle}} $pps_sizeofname;
379 print {$self->{_filehandle}} $pps_type;
380 print {$self->{_filehandle}} $pps_prev;
381 print {$self->{_filehandle}} $pps_next;
382 print {$self->{_filehandle}} $pps_dir;
383 print {$self->{_filehandle}} $unknown1 x 5;
384 print {$self->{_filehandle}} $pps_ts1s;
385 print {$self->{_filehandle}} $pps_ts1d;
386 print {$self->{_filehandle}} $pps_ts2d;
387 print {$self->{_filehandle}} $pps_ts2d;
388 print {$self->{_filehandle}} $pps_sb;
389 print {$self->{_filehandle}} $pps_size;
390 print {$self->{_filehandle}} $unknown1;
391}
392
393
394###############################################################################
395#
396# _write_padding()
397#
398# Pad the end of the file
399#
400sub _write_padding {
401
402 my $self = shift;
403 my $biffsize = $self->{_biffsize};
404 my $min_size;
405
406 if ($biffsize < 4096) {
407 $min_size = 4096;
408 }
409 else {
410 $min_size = 512;
411 }
412
413 # Protect print() from -l on the command line.
414 local $\ = undef;
415
416 if ($biffsize % $min_size != 0) {
417 my $padding = $min_size - ($biffsize % $min_size);
418 print {$self->{_filehandle}} "\0" x $padding;
419 }
420}
421
422
4231;
424
425
426__END__
427
428
429=head1 NAME
430
431OLEwriter - A writer class to store BIFF data in a OLE compound storage file.
432
433=head1 SYNOPSIS
434
435See the documentation for Spreadsheet::WriteExcel
436
437=head1 DESCRIPTION
438
439This module is used in conjunction with Spreadsheet::WriteExcel.
440
441=head1 AUTHOR
442
443John McNamara jmcnamara@cpan.org
444
445=head1 COPYRIGHT
446
447© MM-MMX, John McNamara.
448
449All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.