_OLEfilename = $filename; $this->_filehandle = false; $this->_fileclosed = 0; $this->_internal_fh = 0; $this->_biff_only = 0; $this->_size_allowed = 0; $this->_biffsize = 0; $this->_booksize = 0; $this->_big_blocks = 0; $this->_list_blocks = 0; $this->_root_start = 0; $this->_block_count = 4; $this->_initialize(); } /* * Check for a valid filename and store the filehandle. */ function _initialize() { $OLEfile = $this->_OLEfilename; /* Check for a filename. Workbook.pm will catch this first. */ if ($OLEfile == '') { trigger_error("Filename required", E_USER_ERROR); } /* * If the filename is a resource it is assumed that it is a valid * filehandle, if not we create a filehandle. */ if (is_resource($OLEfile)) { $fh = $OLEfile; } else { // Create a new file, open for writing $fh = fopen($OLEfile, "wb"); // The workbook class also checks this but something may have // happened since then. if (!$fh) { trigger_error("Can't open $OLEfile. It may be in use or ". "protected", E_USER_ERROR); } $this->_internal_fh = 1; } // Store filehandle $this->_filehandle = $fh; } /* * Set the size of the data to be written to the OLE stream * * $big_blocks = (109 depot block x (128 -1 marker word) * - (1 x end words)) = 13842 * $maxsize = $big_blocks * 512 bytes = 7087104 */ function set_size($size) { $maxsize = 7087104; if ($size > $maxsize) { trigger_error("Maximum file size, $maxsize, exceeded. To create ". "files bigger than this limit please use the ". "workbookbig class.", E_USER_ERROR); return ($this->_size_allowed = 0); } $this->_biffsize = $size; // Set the min file size to 4k to avoid having to use small blocks if ($size > 4096) { $this->_booksize = $size; } else { $this->_booksize = 4096; } return ($this->_size_allowed = 1); } /* * Calculate various sizes needed for the OLE stream */ function _calculate_sizes() { $datasize = $this->_booksize; if ($datasize % 512 == 0) { $this->_big_blocks = $datasize/512; } else { $this->_big_blocks = floor($datasize/512)+1; } // There are 127 list blocks and 1 marker blocks for each big block // depot + 1 end of chain block $this->_list_blocks = floor(($this->_big_blocks)/127)+1; $this->_root_start = $this->_big_blocks; //print $this->_biffsize. "\n"; //print $this->_big_blocks. "\n"; //print $this->_list_blocks. "\n"; } /* * Write root entry, big block list and close the filehandle. * This method must be called so that the file contents are * actually written. */ function close() { if (!$this->_size_allowed) { return; } if (!$this->_biff_only) { $this->_write_padding(); $this->_write_property_storage(); $this->_write_big_block_depot(); } // Close the filehandle if it was created internally. if ($this->_internal_fh) { fclose($this->_filehandle); } /* ABR */ if ($this->_OLEtmpfilename != '') { $fh = fopen($this->_OLEtmpfilename, "rb"); if ($fh == false) { trigger_error("Can't read temporary file.", E_USER_ERROR); } fpassthru($fh); fclose($fh); unlink($this->_OLEtmpfilename); }; $this->_fileclosed = 1; } /* * Write BIFF data to OLE file. */ function write($data) { fputs($this->_filehandle, $data); } /* * Write OLE header block. */ function write_header() { if ($this->_biff_only) { return; } $this->_calculate_sizes(); $root_start = $this->_root_start; $num_lists = $this->_list_blocks; $id = pack("C8", 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1); $unknown1 = pack("VVVV", 0x00, 0x00, 0x00, 0x00); $unknown2 = pack("vv", 0x3E, 0x03); $unknown3 = pack("v", -2); $unknown4 = pack("v", 0x09); $unknown5 = pack("VVV", 0x06, 0x00, 0x00); $num_bbd_blocks = pack("V", $num_lists); $root_startblock = pack("V", $root_start); $unknown6 = pack("VV", 0x00, 0x1000); $sbd_startblock = pack("V", -2); $unknown7 = pack("VVV", 0x00, -2 ,0x00); $unused = pack("V", -1); fputs($this->_filehandle, $id); fputs($this->_filehandle, $unknown1); fputs($this->_filehandle, $unknown2); fputs($this->_filehandle, $unknown3); fputs($this->_filehandle, $unknown4); fputs($this->_filehandle, $unknown5); fputs($this->_filehandle, $num_bbd_blocks); fputs($this->_filehandle, $root_startblock); fputs($this->_filehandle, $unknown6); fputs($this->_filehandle, $sbd_startblock); fputs($this->_filehandle, $unknown7); for ($c=1;$c<=$num_lists;$c++) { $root_start++; fputs($this->_filehandle, pack("V", $root_start)); } for ($c=$num_lists;$c<=108;$c++) { fputs($this->_filehandle, $unused); } } /* * Write big block depot. */ function _write_big_block_depot() { $num_blocks = $this->_big_blocks; $num_lists = $this->_list_blocks; $total_blocks = $num_lists * 128; $used_blocks = $num_blocks + $num_lists + 2; $marker = pack("V", -3); $end_of_chain = pack("V", -2); $unused = pack("V", -1); for ($i=1;$i<=($num_blocks-1);$i++) { fputs($this->_filehandle, pack("V", $i)); } fputs($this->_filehandle, $end_of_chain); fputs($this->_filehandle, $end_of_chain); for ($c=1;$c<=$num_lists;$c++) { fputs($this->_filehandle, $marker); } for ($c=$used_blocks;$c<=$total_blocks;$c++) { fputs($this->_filehandle, $unused); } } /* * Write property storage. TODO: add summary sheets */ function _write_property_storage() { $rootsize = -2; $booksize = $this->_booksize; // name type dir start size $this->_write_pps('Root Entry', 0x05, 1, -2, 0x00); $this->_write_pps('Book', 0x02, -1, 0x00, $booksize); $this->_write_pps('', 0x00, -1, 0x00, 0x0000); $this->_write_pps('', 0x00, -1, 0x00, 0x0000); } /* * Write property sheet in property storage */ function _write_pps($name, $type, $dir, $start, $size) { $names = array(); $length = 0; if ($name != '') { $name = $name . "\0"; // Simulate a Unicode string $chars=preg_split("''", $name, -1, PREG_SPLIT_NO_EMPTY); foreach ($chars as $char) { array_push($names, ord($char)); } $length = strlen($name) * 2; } $rawname = call_user_func_array('pack', array_merge(array("v*"), $names)); $zero = pack("C", 0); $pps_sizeofname = pack("v", $length); //0x40 $pps_type = pack("v", $type); //0x42 $pps_prev = pack("V", -1); //0x44 $pps_next = pack("V", -1); //0x48 $pps_dir = pack("V", $dir); //0x4c $unknown1 = pack("V", 0); $pps_ts1s = pack("V", 0); //0x64 $pps_ts1d = pack("V", 0); //0x68 $pps_ts2s = pack("V", 0); //0x6c $pps_ts2d = pack("V", 0); //0x70 $pps_sb = pack("V", $start); //0x74 $pps_size = pack("V", $size); //0x78 fputs($this->_filehandle, $rawname); fputs($this->_filehandle, str_repeat($zero, (64-$length))); fputs($this->_filehandle, $pps_sizeofname); fputs($this->_filehandle, $pps_type); fputs($this->_filehandle, $pps_prev); fputs($this->_filehandle, $pps_next); fputs($this->_filehandle, $pps_dir); fputs($this->_filehandle, str_repeat($unknown1, 5)); fputs($this->_filehandle, $pps_ts1s); fputs($this->_filehandle, $pps_ts1d); fputs($this->_filehandle, $pps_ts2d); fputs($this->_filehandle, $pps_ts2d); fputs($this->_filehandle, $pps_sb); fputs($this->_filehandle, $pps_size); fputs($this->_filehandle, $unknown1); } /* * Pad the end of the file */ function _write_padding() { $biffsize = $this->_biffsize; if ($biffsize < 4096) { $min_size = 4096; } else { $min_size = 512; } if ($biffsize % $min_size != 0) { $padding = $min_size - ($biffsize % $min_size); fputs($this->_filehandle, str_repeat("\0", $padding)); } } } ?>