1 <?php
3 /*
4 * Copyleft 2002 Johann Hanne
5 *
6 * This is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This software is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this software; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place,
19 * Suite 330, Boston, MA 02111-1307 USA
20 */
22 /*
23 * This is the Spreadsheet::WriteExcel Perl package ported to PHP
24 * Spreadsheet::WriteExcel was written by John McNamara, jmcnamara@cpan.org
25 */
27 class writeexcel_olewriter {
28 var $_OLEfilename;
29 var $_OLEtmpfilename; /* ABR */
30 var $_filehandle;
31 var $_fileclosed;
32 var $_internal_fh;
33 var $_biff_only;
34 var $_size_allowed;
35 var $_biffsize;
36 var $_booksize;
37 var $_big_blocks;
38 var $_list_blocks;
39 var $_root_start;
40 var $_block_count;
42 /*
43 * Constructor
44 */
45 function writeexcel_olewriter($filename) {
47 $this->_OLEfilename = $filename;
48 $this->_filehandle = false;
49 $this->_fileclosed = 0;
50 $this->_internal_fh = 0;
51 $this->_biff_only = 0;
52 $this->_size_allowed = 0;
53 $this->_biffsize = 0;
54 $this->_booksize = 0;
55 $this->_big_blocks = 0;
56 $this->_list_blocks = 0;
57 $this->_root_start = 0;
58 $this->_block_count = 4;
60 $this->_initialize();
61 }
63 /*
64 * Check for a valid filename and store the filehandle.
65 */
66 function _initialize() {
67 $OLEfile = $this->_OLEfilename;
69 /* Check for a filename. Workbook.pm will catch this first. */
70 if ($OLEfile == '') {
71 trigger_error("Filename required", E_USER_ERROR);
72 }
74 /*
75 * If the filename is a resource it is assumed that it is a valid
76 * filehandle, if not we create a filehandle.
77 */
78 if (is_resource($OLEfile)) {
79 $fh = $OLEfile;
80 } else {
81 // Create a new file, open for writing
82 $fh = fopen($OLEfile, "wb");
83 // The workbook class also checks this but something may have
84 // happened since then.
85 if (!$fh) {
86 trigger_error("Cannot open $OLEfile! It may be in use or ".
87 "protected.", E_USER_ERROR);
88 }
90 $this->_internal_fh = 1;
91 }
93 // Store filehandle
94 $this->_filehandle = $fh;
95 }
97 /*
98 * Set the size of the data to be written to the OLE stream
99 *
100 * $big_blocks = (109 depot block x (128 -1 marker word)
101 * - (1 x end words)) = 13842
102 * $maxsize = $big_blocks * 512 bytes = 7087104
103 */
104 function set_size($size) {
105 $maxsize = 7087104;
107 if ($size > $maxsize) {
108 trigger_error("Maximum file size, $maxsize, exceeded. To create ".
109 "files bigger than this limit please use the ".
110 "workbookbig class.", E_USER_ERROR);
111 return ($this->_size_allowed = 0);
112 }
114 $this->_biffsize = $size;
116 // Set the min file size to 4k to avoid having to use small blocks
117 if ($size > 4096) {
118 $this->_booksize = $size;
119 } else {
120 $this->_booksize = 4096;
121 }
123 return ($this->_size_allowed = 1);
124 }
126 /*
127 * Calculate various sizes needed for the OLE stream
128 */
129 function _calculate_sizes() {
130 $datasize = $this->_booksize;
132 if ($datasize % 512 == 0) {
133 $this->_big_blocks = $datasize/512;
134 } else {
135 $this->_big_blocks = floor($datasize/512)+1;
136 }
137 // There are 127 list blocks and 1 marker blocks for each big block
138 // depot + 1 end of chain block
139 $this->_list_blocks = floor(($this->_big_blocks)/127)+1;
140 $this->_root_start = $this->_big_blocks;
142 //print $this->_biffsize. "\n";
143 //print $this->_big_blocks. "\n";
144 //print $this->_list_blocks. "\n";
145 }
147 /*
148 * Write root entry, big block list and close the filehandle.
149 * This method must be called so that the file contents are
150 * actually written.
151 */
152 function close() {
154 if (!$this->_size_allowed) {
155 return;
156 }
158 if (!$this->_biff_only) {
159 $this->_write_padding();
160 $this->_write_property_storage();
161 $this->_write_big_block_depot();
162 }
164 // Close the filehandle if it was created internally.
165 if ($this->_internal_fh) {
166 fclose($this->_filehandle);
167 }
168 /* ABR */
169 if ($this->_OLEtmpfilename != '') {
170 $fh = fopen($this->_OLEtmpfilename, "rb");
171 if ($fh == false) {
172 trigger_error("Cannot read temporary file!", E_USER_ERROR);
173 }
174 fpassthru($fh);
175 fclose($fh);
176 unlink($this->_OLEtmpfilename);
177 };
179 $this->_fileclosed = 1;
180 }
182 /*
183 * Write BIFF data to OLE file.
184 */
185 function write($data) {
186 fputs($this->_filehandle, $data);
187 }
189 /*
190 * Write OLE header block.
191 */
192 function write_header() {
193 if ($this->_biff_only) {
194 return;
195 }
197 $this->_calculate_sizes();
199 $root_start = $this->_root_start;
200 $num_lists = $this->_list_blocks;
202 $id = pack("C8", 0xD0, 0xCF, 0x11, 0xE0,
203 0xA1, 0xB1, 0x1A, 0xE1);
204 $unknown1 = pack("VVVV", 0x00, 0x00, 0x00, 0x00);
205 $unknown2 = pack("vv", 0x3E, 0x03);
206 $unknown3 = pack("v", -2);
207 $unknown4 = pack("v", 0x09);
208 $unknown5 = pack("VVV", 0x06, 0x00, 0x00);
209 $num_bbd_blocks = pack("V", $num_lists);
210 $root_startblock = pack("V", $root_start);
211 $unknown6 = pack("VV", 0x00, 0x1000);
212 $sbd_startblock = pack("V", -2);
213 $unknown7 = pack("VVV", 0x00, -2 ,0x00);
214 $unused = pack("V", -1);
216 fputs($this->_filehandle, $id);
217 fputs($this->_filehandle, $unknown1);
218 fputs($this->_filehandle, $unknown2);
219 fputs($this->_filehandle, $unknown3);
220 fputs($this->_filehandle, $unknown4);
221 fputs($this->_filehandle, $unknown5);
222 fputs($this->_filehandle, $num_bbd_blocks);
223 fputs($this->_filehandle, $root_startblock);
224 fputs($this->_filehandle, $unknown6);
225 fputs($this->_filehandle, $sbd_startblock);
226 fputs($this->_filehandle, $unknown7);
228 for ($c=1;$c<=$num_lists;$c++) {
229 $root_start++;
230 fputs($this->_filehandle, pack("V", $root_start));
231 }
233 for ($c=$num_lists;$c<=108;$c++) {
234 fputs($this->_filehandle, $unused);
235 }
236 }
238 /*
239 * Write big block depot.
240 */
241 function _write_big_block_depot() {
242 $num_blocks = $this->_big_blocks;
243 $num_lists = $this->_list_blocks;
244 $total_blocks = $num_lists * 128;
245 $used_blocks = $num_blocks + $num_lists + 2;
247 $marker = pack("V", -3);
248 $end_of_chain = pack("V", -2);
249 $unused = pack("V", -1);
251 for ($i=1;$i<=($num_blocks-1);$i++) {
252 fputs($this->_filehandle, pack("V", $i));
253 }
255 fputs($this->_filehandle, $end_of_chain);
256 fputs($this->_filehandle, $end_of_chain);
258 for ($c=1;$c<=$num_lists;$c++) {
259 fputs($this->_filehandle, $marker);
260 }
262 for ($c=$used_blocks;$c<=$total_blocks;$c++) {
263 fputs($this->_filehandle, $unused);
264 }
265 }
267 /*
268 * Write property storage. TODO: add summary sheets
269 */
270 function _write_property_storage() {
271 $rootsize = -2;
272 $booksize = $this->_booksize;
274 // name type dir start size
275 $this->_write_pps('Root Entry', 0x05, 1, -2, 0x00);
276 $this->_write_pps('Book', 0x02, -1, 0x00, $booksize);
277 $this->_write_pps('', 0x00, -1, 0x00, 0x0000);
278 $this->_write_pps('', 0x00, -1, 0x00, 0x0000);
279 }
281 /*
282 * Write property sheet in property storage
283 */
284 function _write_pps($name, $type, $dir, $start, $size) {
285 $names = array();
286 $length = 0;
288 if ($name != '') {
289 $name = $name . "\0";
290 // Simulate a Unicode string
291 $chars=preg_split("''", $name, -1, PREG_SPLIT_NO_EMPTY);
292 foreach ($chars as $char) {
293 array_push($names, ord($char));
294 }
295 $length = strlen($name) * 2;
296 }
298 $rawname = call_user_func_array('pack', array_merge(array("v*"), $names));
299 $zero = pack("C", 0);
301 $pps_sizeofname = pack("v", $length); //0x40
302 $pps_type = pack("v", $type); //0x42
303 $pps_prev = pack("V", -1); //0x44
304 $pps_next = pack("V", -1); //0x48
305 $pps_dir = pack("V", $dir); //0x4c
307 $unknown1 = pack("V", 0);
309 $pps_ts1s = pack("V", 0); //0x64
310 $pps_ts1d = pack("V", 0); //0x68
311 $pps_ts2s = pack("V", 0); //0x6c
312 $pps_ts2d = pack("V", 0); //0x70
313 $pps_sb = pack("V", $start); //0x74
314 $pps_size = pack("V", $size); //0x78
316 fputs($this->_filehandle, $rawname);
317 fputs($this->_filehandle, str_repeat($zero, (64-$length)));
318 fputs($this->_filehandle, $pps_sizeofname);
319 fputs($this->_filehandle, $pps_type);
320 fputs($this->_filehandle, $pps_prev);
321 fputs($this->_filehandle, $pps_next);
322 fputs($this->_filehandle, $pps_dir);
323 fputs($this->_filehandle, str_repeat($unknown1, 5));
324 fputs($this->_filehandle, $pps_ts1s);
325 fputs($this->_filehandle, $pps_ts1d);
326 fputs($this->_filehandle, $pps_ts2d);
327 fputs($this->_filehandle, $pps_ts2d);
328 fputs($this->_filehandle, $pps_sb);
329 fputs($this->_filehandle, $pps_size);
330 fputs($this->_filehandle, $unknown1);
331 }
333 /*
334 * Pad the end of the file
335 */
336 function _write_padding() {
337 $biffsize = $this->_biffsize;
339 if ($biffsize < 4096) {
340 $min_size = 4096;
341 } else {
342 $min_size = 512;
343 }
345 if ($biffsize % $min_size != 0) {
346 $padding = $min_size - ($biffsize % $min_size);
347 fputs($this->_filehandle, str_repeat("\0", $padding));
348 }
349 }
351 }
353 ?>