OLEwriter.pm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. # DISCLAIMER OF WARRANTY
  2. # Because this software is licensed free of charge, there is no warranty for the software,
  3. # to the extent permitted by applicable law. Except when otherwise stated in writing
  4. # the copyright holders and/or other parties provide the software "as is" without
  5. # warranty of any kind, either expressed or implied, including, but not limited to,
  6. # the implied warranties of merchantability and fitness for a particular purpose.
  7. # The entire risk as to the quality and performance of the software is with you.
  8. # Should the software prove defective, you assume the cost of all necessary
  9. # servicing, repair, or correction.
  10. # In no event unless required by applicable law or agreed to in writing will any
  11. # copyright holder, or any other party who may modify and/or redistribute the software
  12. # as permitted by the above licence, be liable to you for damages, including any general,
  13. # special, incidental, or consequential damages arising out of the use or inability
  14. # to use the software (including but not limited to loss of data or data being rendered
  15. # inaccurate or losses sustained by you or third parties or a failure of the software
  16. # to operate with any other software), even if such holder or other party
  17. # has been advised of the possibility of such damages.
  18. # AUTHOR
  19. # John McNamara jmcnamara@cpan.org
  20. # COPYRIGHT
  21. # Copyright MM-MMX, John McNamara.
  22. # All Rights Reserved. This module is free software. It may be used,
  23. # redistributed and/or modified under the terms of
  24. # the Artistic License(full text of the Artistic License http://dev.perl.org/licenses/artistic.html).
  25. package Spreadsheet::WriteExcel::OLEwriter;
  26. ###############################################################################
  27. #
  28. # OLEwriter - A writer class to store BIFF data in a OLE compound storage file.
  29. #
  30. #
  31. # Used in conjunction with Spreadsheet::WriteExcel
  32. #
  33. # Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
  34. #
  35. # Documentation after __END__
  36. #
  37. use Exporter;
  38. use strict;
  39. use Carp;
  40. use FileHandle;
  41. use vars qw($VERSION @ISA);
  42. @ISA = qw(Exporter);
  43. $VERSION = '2.37';
  44. ###############################################################################
  45. #
  46. # new()
  47. #
  48. # Constructor
  49. #
  50. sub new {
  51. my $class = shift;
  52. my $self = {
  53. _olefilename => $_[0],
  54. _filehandle => "",
  55. _fileclosed => 0,
  56. _internal_fh => 0,
  57. _biff_only => 0,
  58. _size_allowed => 0,
  59. _biffsize => 0,
  60. _booksize => 0,
  61. _big_blocks => 0,
  62. _list_blocks => 0,
  63. _root_start => 0,
  64. _block_count => 4,
  65. };
  66. bless $self, $class;
  67. $self->_initialize();
  68. return $self;
  69. }
  70. ###############################################################################
  71. #
  72. # _initialize()
  73. #
  74. # Create a new filehandle or use the provided filehandle.
  75. #
  76. sub _initialize {
  77. my $self = shift;
  78. my $olefile = $self->{_olefilename};
  79. my $fh;
  80. # If the filename is a reference it is assumed that it is a valid
  81. # filehandle, if not we create a filehandle.
  82. #
  83. if (ref($olefile)) {
  84. $fh = $olefile;
  85. }
  86. else{
  87. # Create a new file, open for writing
  88. $fh = FileHandle->new("> $olefile");
  89. # Workbook.pm also checks this but something may have happened since
  90. # then.
  91. if (not defined $fh) {
  92. croak "Can't open $olefile. It may be in use or protected.\n";
  93. }
  94. # binmode file whether platform requires it or not
  95. binmode($fh);
  96. $self->{_internal_fh} = 1;
  97. }
  98. # Store filehandle
  99. $self->{_filehandle} = $fh;
  100. }
  101. ###############################################################################
  102. #
  103. # set_size($biffsize)
  104. #
  105. # Set the size of the data to be written to the OLE stream
  106. #
  107. # $big_blocks = (109 depot block x (128 -1 marker word)
  108. # - (1 x end words)) = 13842
  109. # $maxsize = $big_blocks * 512 bytes = 7087104
  110. #
  111. sub set_size {
  112. my $self = shift;
  113. my $maxsize = 7_087_104; # Use Spreadsheet::WriteExcel::Big to exceed this
  114. if ($_[0] > $maxsize) {
  115. return $self->{_size_allowed} = 0;
  116. }
  117. $self->{_biffsize} = $_[0];
  118. # Set the min file size to 4k to avoid having to use small blocks
  119. if ($_[0] > 4096) {
  120. $self->{_booksize} = $_[0];
  121. }
  122. else {
  123. $self->{_booksize} = 4096;
  124. }
  125. return $self->{_size_allowed} = 1;
  126. }
  127. ###############################################################################
  128. #
  129. # _calculate_sizes()
  130. #
  131. # Calculate various sizes needed for the OLE stream
  132. #
  133. sub _calculate_sizes {
  134. my $self = shift;
  135. my $datasize = $self->{_booksize};
  136. if ($datasize % 512 == 0) {
  137. $self->{_big_blocks} = $datasize/512;
  138. }
  139. else {
  140. $self->{_big_blocks} = int($datasize/512) +1;
  141. }
  142. # There are 127 list blocks and 1 marker blocks for each big block
  143. # depot + 1 end of chain block
  144. $self->{_list_blocks} = int(($self->{_big_blocks})/127) +1;
  145. $self->{_root_start} = $self->{_big_blocks};
  146. }
  147. ###############################################################################
  148. #
  149. # close()
  150. #
  151. # Write root entry, big block list and close the filehandle.
  152. # This routine is used to explicitly close the open filehandle without
  153. # having to wait for DESTROY.
  154. #
  155. sub close {
  156. my $self = shift;
  157. return if not $self->{_size_allowed};
  158. $self->_write_padding() if not $self->{_biff_only};
  159. $self->_write_property_storage() if not $self->{_biff_only};
  160. $self->_write_big_block_depot() if not $self->{_biff_only};
  161. my $close = 1; # Default to no error for external filehandles.
  162. # Close the filehandle if it was created internally.
  163. $close = CORE::close($self->{_filehandle}) if $self->{_internal_fh};
  164. $self->{_fileclosed} = 1;
  165. return $close;
  166. }
  167. ###############################################################################
  168. #
  169. # DESTROY()
  170. #
  171. # Close the filehandle if it hasn't already been explicitly closed.
  172. #
  173. sub DESTROY {
  174. my $self = shift;
  175. local ($@, $!, $^E, $?);
  176. $self->close() unless $self->{_fileclosed};
  177. }
  178. ###############################################################################
  179. #
  180. # write($data)
  181. #
  182. # Write BIFF data to OLE file.
  183. #
  184. sub write {
  185. my $self = shift;
  186. # Protect print() from -l on the command line.
  187. local $\ = undef;
  188. print {$self->{_filehandle}} $_[0];
  189. }
  190. ###############################################################################
  191. #
  192. # write_header()
  193. #
  194. # Write OLE header block.
  195. #
  196. sub write_header {
  197. my $self = shift;
  198. return if $self->{_biff_only};
  199. $self->_calculate_sizes();
  200. my $root_start = $self->{_root_start};
  201. my $num_lists = $self->{_list_blocks};
  202. my $id = pack("NN", 0xD0CF11E0, 0xA1B11AE1);
  203. my $unknown1 = pack("VVVV", 0x00, 0x00, 0x00, 0x00);
  204. my $unknown2 = pack("vv", 0x3E, 0x03);
  205. my $unknown3 = pack("v", -2);
  206. my $unknown4 = pack("v", 0x09);
  207. my $unknown5 = pack("VVV", 0x06, 0x00, 0x00);
  208. my $num_bbd_blocks = pack("V", $num_lists);
  209. my $root_startblock = pack("V", $root_start);
  210. my $unknown6 = pack("VV", 0x00, 0x1000);
  211. my $sbd_startblock = pack("V", -2);
  212. my $unknown7 = pack("VVV", 0x00, -2 ,0x00);
  213. my $unused = pack("V", -1);
  214. # Protect print() from -l on the command line.
  215. local $\ = undef;
  216. print {$self->{_filehandle}} $id;
  217. print {$self->{_filehandle}} $unknown1;
  218. print {$self->{_filehandle}} $unknown2;
  219. print {$self->{_filehandle}} $unknown3;
  220. print {$self->{_filehandle}} $unknown4;
  221. print {$self->{_filehandle}} $unknown5;
  222. print {$self->{_filehandle}} $num_bbd_blocks;
  223. print {$self->{_filehandle}} $root_startblock;
  224. print {$self->{_filehandle}} $unknown6;
  225. print {$self->{_filehandle}} $sbd_startblock;
  226. print {$self->{_filehandle}} $unknown7;
  227. for (1..$num_lists) {
  228. $root_start++;
  229. print {$self->{_filehandle}} pack("V", $root_start);
  230. }
  231. for ($num_lists..108) {
  232. print {$self->{_filehandle}} $unused;
  233. }
  234. }
  235. ###############################################################################
  236. #
  237. # _write_big_block_depot()
  238. #
  239. # Write big block depot.
  240. #
  241. sub _write_big_block_depot {
  242. my $self = shift;
  243. my $num_blocks = $self->{_big_blocks};
  244. my $num_lists = $self->{_list_blocks};
  245. my $total_blocks = $num_lists *128;
  246. my $used_blocks = $num_blocks + $num_lists +2;
  247. my $marker = pack("V", -3);
  248. my $end_of_chain = pack("V", -2);
  249. my $unused = pack("V", -1);
  250. # Protect print() from -l on the command line.
  251. local $\ = undef;
  252. for my $i (1..$num_blocks-1) {
  253. print {$self->{_filehandle}} pack("V",$i);
  254. }
  255. print {$self->{_filehandle}} $end_of_chain;
  256. print {$self->{_filehandle}} $end_of_chain;
  257. for (1..$num_lists) {
  258. print {$self->{_filehandle}} $marker;
  259. }
  260. for ($used_blocks..$total_blocks) {
  261. print {$self->{_filehandle}} $unused;
  262. }
  263. }
  264. ###############################################################################
  265. #
  266. # _write_property_storage()
  267. #
  268. # Write property storage. TODO: add summary sheets
  269. #
  270. sub _write_property_storage {
  271. my $self = shift;
  272. my $rootsize = -2;
  273. my $booksize = $self->{_booksize};
  274. ################# name type dir start size
  275. $self->_write_pps('Root Entry', 0x05, 1, -2, 0x00);
  276. $self->_write_pps('Workbook', 0x02, -1, 0x00, $booksize);
  277. $self->_write_pps('', 0x00, -1, 0x00, 0x0000);
  278. $self->_write_pps('', 0x00, -1, 0x00, 0x0000);
  279. }
  280. ###############################################################################
  281. #
  282. # _write_pps()
  283. #
  284. # Write property sheet in property storage
  285. #
  286. sub _write_pps {
  287. my $self = shift;
  288. my $name = $_[0];
  289. my @name = ();
  290. my $length = 0;
  291. if ($name ne '') {
  292. $name = $_[0] . "\0";
  293. # Simulate a Unicode string
  294. @name = map(ord, split('', $name));
  295. $length = length($name) * 2;
  296. }
  297. my $rawname = pack("v*", @name);
  298. my $zero = pack("C", 0);
  299. my $pps_sizeofname = pack("v", $length); #0x40
  300. my $pps_type = pack("v", $_[1]); #0x42
  301. my $pps_prev = pack("V", -1); #0x44
  302. my $pps_next = pack("V", -1); #0x48
  303. my $pps_dir = pack("V", $_[2]); #0x4c
  304. my $unknown1 = pack("V", 0);
  305. my $pps_ts1s = pack("V", 0); #0x64
  306. my $pps_ts1d = pack("V", 0); #0x68
  307. my $pps_ts2s = pack("V", 0); #0x6c
  308. my $pps_ts2d = pack("V", 0); #0x70
  309. my $pps_sb = pack("V", $_[3]); #0x74
  310. my $pps_size = pack("V", $_[4]); #0x78
  311. # Protect print() from -l on the command line.
  312. local $\ = undef;
  313. print {$self->{_filehandle}} $rawname;
  314. print {$self->{_filehandle}} $zero x (64 -$length);
  315. print {$self->{_filehandle}} $pps_sizeofname;
  316. print {$self->{_filehandle}} $pps_type;
  317. print {$self->{_filehandle}} $pps_prev;
  318. print {$self->{_filehandle}} $pps_next;
  319. print {$self->{_filehandle}} $pps_dir;
  320. print {$self->{_filehandle}} $unknown1 x 5;
  321. print {$self->{_filehandle}} $pps_ts1s;
  322. print {$self->{_filehandle}} $pps_ts1d;
  323. print {$self->{_filehandle}} $pps_ts2d;
  324. print {$self->{_filehandle}} $pps_ts2d;
  325. print {$self->{_filehandle}} $pps_sb;
  326. print {$self->{_filehandle}} $pps_size;
  327. print {$self->{_filehandle}} $unknown1;
  328. }
  329. ###############################################################################
  330. #
  331. # _write_padding()
  332. #
  333. # Pad the end of the file
  334. #
  335. sub _write_padding {
  336. my $self = shift;
  337. my $biffsize = $self->{_biffsize};
  338. my $min_size;
  339. if ($biffsize < 4096) {
  340. $min_size = 4096;
  341. }
  342. else {
  343. $min_size = 512;
  344. }
  345. # Protect print() from -l on the command line.
  346. local $\ = undef;
  347. if ($biffsize % $min_size != 0) {
  348. my $padding = $min_size - ($biffsize % $min_size);
  349. print {$self->{_filehandle}} "\0" x $padding;
  350. }
  351. }
  352. 1;
  353. __END__
  354. =head1 NAME
  355. OLEwriter - A writer class to store BIFF data in a OLE compound storage file.
  356. =head1 SYNOPSIS
  357. See the documentation for Spreadsheet::WriteExcel
  358. =head1 DESCRIPTION
  359. This module is used in conjunction with Spreadsheet::WriteExcel.
  360. =head1 AUTHOR
  361. John McNamara jmcnamara@cpan.org
  362. =head1 COPYRIGHT
  363. © MM-MMX, John McNamara.
  364. All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.