MDL-33399 files: Fixed up handling of empty sort in File API methods.
[moodle.git] / lib / excel / OLEwriter.php
blob8c017d2e1fabd7eb89f554ad26f7e234d237e43c
1 <?php
2 /*
3 * Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
5 * The majority of this is _NOT_ my code. I simply ported it from the
6 * PERL Spreadsheet::WriteExcel module.
8 * The author of the Spreadsheet::WriteExcel module is John McNamara
9 * <jmcnamara@cpan.org>
11 * I _DO_ maintain this code, and John McNamara has nothing to do with the
12 * porting of this code to PHP. Any questions directly related to this
13 * class library should be directed to me.
15 * License Information:
17 * Spreadsheet::WriteExcel: A library for generating Excel Spreadsheets
18 * Copyright (C) 2002 Xavier Noguer xnoguer@rezebra.com
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Lesser General Public
22 * License as published by the Free Software Foundation; either
23 * version 2.1 of the License, or (at your option) any later version.
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Lesser General Public License for more details.
30 * You should have received a copy of the GNU Lesser General Public
31 * License along with this library; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 /**
36 * Class for creating OLE streams for Excel Spreadsheets
38 * @author Xavier Noguer <xnoguer@rezebra.com>
39 * @package Spreadsheet_WriteExcel
41 class OLEwriter
43 /**
44 * Filename for the OLE stream
45 * @var string
46 * @see _initialize()
48 var $_OLEfilename;
50 /**
51 * Filehandle for the OLE stream
52 * @var resource
54 var $_filehandle;
56 /**
57 * Name of the temporal file in case OLE stream goes to stdout
58 * @var string
60 var $_tmp_filename;
62 /**
63 * Variable for preventing closing two times
64 * @var integer
66 var $_fileclosed;
68 /**
69 * Size of the data to be written to the OLE stream
70 * @var integer
72 var $_biffsize;
74 /**
75 * Real data size to be written to the OLE stream
76 * @var integer
78 var $_booksize;
80 /**
81 * Number of big blocks in the OLE stream
82 * @var integer
84 var $_big_blocks;
86 /**
87 * Number of list blocks in the OLE stream
88 * @var integer
90 var $_list_blocks;
92 /**
93 * Number of big blocks in the OLE stream
94 * @var integer
96 var $_root_start;
98 /**
99 * Class for creating an OLEwriter
101 * @param string $OLEfilename the name of the file for the OLE stream
103 function OLEwriter($OLEfilename)
105 $this->_OLEfilename = $OLEfilename;
106 $this->_filehandle = "";
107 $this->_tmp_filename = "";
108 $this->_fileclosed = 0;
109 //$this->_size_allowed = 0;
110 $this->_biffsize = 0;
111 $this->_booksize = 0;
112 $this->_big_blocks = 0;
113 $this->_list_blocks = 0;
114 $this->_root_start = 0;
115 //$this->_block_count = 4;
116 $this->_initialize();
120 * Check for a valid filename and store the filehandle.
121 * Filehandle "-" writes to STDOUT
123 function _initialize()
125 $OLEfile = $this->_OLEfilename;
127 if(($OLEfile == '-') or ($OLEfile == ''))
129 $this->_tmp_filename = tempnam("/tmp", "OLEwriter");
130 $fh = fopen($this->_tmp_filename,"wb");
131 if ($fh == false) {
132 die("Can't create temporary file.");
135 else
137 // Create a new file, open for writing (in binmode)
138 $fh = fopen($OLEfile,"wb");
139 if ($fh == false) {
140 die("Can't open $OLEfile. It may be in use or protected.");
144 // Store filehandle
145 $this->_filehandle = $fh;
150 * Set the size of the data to be written to the OLE stream.
151 * The maximun size comes from this:
152 * $big_blocks = (109 depot block x (128 -1 marker word)
153 * - (1 x end words)) = 13842
154 * $maxsize = $big_blocks * 512 bytes = 7087104
156 * @access public
157 * @see Workbook::store_OLE_file()
158 * @param integer $biffsize The size of the data to be written to the OLE stream
159 * @return integer 1 for success
161 function set_size($biffsize)
163 $maxsize = 7087104; // TODO: extend max size
165 if ($biffsize > $maxsize) {
166 die("Maximum file size, $maxsize, exceeded.");
169 $this->_biffsize = $biffsize;
170 // Set the min file size to 4k to avoid having to use small blocks
171 if ($biffsize > 4096) {
172 $this->_booksize = $biffsize;
174 else {
175 $this->_booksize = 4096;
177 //$this->_size_allowed = 1;
178 return(1);
183 * Calculate various sizes needed for the OLE stream
185 function _calculate_sizes()
187 $datasize = $this->_booksize;
188 if ($datasize % 512 == 0) {
189 $this->_big_blocks = $datasize/512;
191 else {
192 $this->_big_blocks = floor($datasize/512) + 1;
194 // There are 127 list blocks and 1 marker blocks for each big block
195 // depot + 1 end of chain block
196 $this->_list_blocks = floor(($this->_big_blocks)/127) + 1;
197 $this->_root_start = $this->_big_blocks;
201 * Write root entry, big block list and close the filehandle.
202 * This routine is used to explicitly close the open filehandle without
203 * having to wait for DESTROY.
205 * @access public
206 * @see Workbook::store_OLE_file()
208 function close()
210 //return if not $this->{_size_allowed};
211 $this->_write_padding();
212 $this->_write_property_storage();
213 $this->_write_big_block_depot();
214 // Close the filehandle
215 fclose($this->_filehandle);
216 if(($this->_OLEfilename == '-') or ($this->_OLEfilename == ''))
218 $fh = fopen($this->_tmp_filename, "rb");
219 if ($fh == false) {
220 die("Can't read temporary file.");
222 fpassthru($fh);
223 // Delete the temporary file.
224 @unlink($this->_tmp_filename);
226 $this->_fileclosed = 1;
231 * Write BIFF data to OLE file.
233 * @param string $data string of bytes to be written
235 function write($data) //por ahora s�lo a STDOUT
237 fwrite($this->_filehandle,$data,strlen($data));
242 * Write OLE header block.
244 function write_header()
246 $this->_calculate_sizes();
247 $root_start = $this->_root_start;
248 $num_lists = $this->_list_blocks;
249 $id = pack("nnnn", 0xD0CF, 0x11E0, 0xA1B1, 0x1AE1);
250 $unknown1 = pack("VVVV", 0x00, 0x00, 0x00, 0x00);
251 $unknown2 = pack("vv", 0x3E, 0x03);
252 $unknown3 = pack("v", -2);
253 $unknown4 = pack("v", 0x09);
254 $unknown5 = pack("VVV", 0x06, 0x00, 0x00);
255 $num_bbd_blocks = pack("V", $num_lists);
256 $root_startblock = pack("V", $root_start);
257 $unknown6 = pack("VV", 0x00, 0x1000);
258 $sbd_startblock = pack("V", -2);
259 $unknown7 = pack("VVV", 0x00, -2 ,0x00);
260 $unused = pack("V", -1);
262 fwrite($this->_filehandle,$id);
263 fwrite($this->_filehandle,$unknown1);
264 fwrite($this->_filehandle,$unknown2);
265 fwrite($this->_filehandle,$unknown3);
266 fwrite($this->_filehandle,$unknown4);
267 fwrite($this->_filehandle,$unknown5);
268 fwrite($this->_filehandle,$num_bbd_blocks);
269 fwrite($this->_filehandle,$root_startblock);
270 fwrite($this->_filehandle,$unknown6);
271 fwrite($this->_filehandle,$sbd_startblock);
272 fwrite($this->_filehandle,$unknown7);
274 for($i=1; $i <= $num_lists; $i++)
276 $root_start++;
277 fwrite($this->_filehandle,pack("V",$root_start));
279 for($i = $num_lists; $i <=108; $i++)
281 fwrite($this->_filehandle,$unused);
287 * Write big block depot.
289 function _write_big_block_depot()
291 $num_blocks = $this->_big_blocks;
292 $num_lists = $this->_list_blocks;
293 $total_blocks = $num_lists *128;
294 $used_blocks = $num_blocks + $num_lists +2;
296 $marker = pack("V", -3);
297 $end_of_chain = pack("V", -2);
298 $unused = pack("V", -1);
300 for($i=1; $i < $num_blocks; $i++)
302 fwrite($this->_filehandle,pack("V",$i));
304 fwrite($this->_filehandle,$end_of_chain);
305 fwrite($this->_filehandle,$end_of_chain);
306 for($i=0; $i < $num_lists; $i++)
308 fwrite($this->_filehandle,$marker);
310 for($i=$used_blocks; $i <= $total_blocks; $i++)
312 fwrite($this->_filehandle,$unused);
317 * Write property storage. TODO: add summary sheets
319 function _write_property_storage()
321 //$rootsize = -2;
322 /*************** name type dir start size */
323 $this->_write_pps("Root Entry", 0x05, 1, -2, 0x00);
324 $this->_write_pps("Book", 0x02, -1, 0x00, $this->_booksize);
325 $this->_write_pps('', 0x00, -1, 0x00, 0x0000);
326 $this->_write_pps('', 0x00, -1, 0x00, 0x0000);
330 * Write property sheet in property storage
332 * @param string $name name of the property storage.
333 * @param integer $type type of the property storage.
334 * @param integer $dir dir of the property storage.
335 * @param integer $start start of the property storage.
336 * @param integer $size size of the property storage.
337 * @access private
339 function _write_pps($name,$type,$dir,$start,$size)
341 $length = 0;
342 $rawname = '';
344 if ($name != '')
346 $name = $name . "\0";
347 for($i=0;$i<strlen($name);$i++)
349 // Simulate a Unicode string
350 $rawname .= pack("H*",dechex(ord($name{$i}))).pack("C",0);
352 $length = strlen($name) * 2;
355 $zero = pack("C", 0);
356 $pps_sizeofname = pack("v", $length); // 0x40
357 $pps_type = pack("v", $type); // 0x42
358 $pps_prev = pack("V", -1); // 0x44
359 $pps_next = pack("V", -1); // 0x48
360 $pps_dir = pack("V", $dir); // 0x4c
362 $unknown1 = pack("V", 0);
364 $pps_ts1s = pack("V", 0); // 0x64
365 $pps_ts1d = pack("V", 0); // 0x68
366 $pps_ts2s = pack("V", 0); // 0x6c
367 $pps_ts2d = pack("V", 0); // 0x70
368 $pps_sb = pack("V", $start); // 0x74
369 $pps_size = pack("V", $size); // 0x78
372 fwrite($this->_filehandle,$rawname);
373 for($i=0; $i < (64 -$length); $i++) {
374 fwrite($this->_filehandle,$zero);
376 fwrite($this->_filehandle,$pps_sizeofname);
377 fwrite($this->_filehandle,$pps_type);
378 fwrite($this->_filehandle,$pps_prev);
379 fwrite($this->_filehandle,$pps_next);
380 fwrite($this->_filehandle,$pps_dir);
381 for($i=0; $i < 5; $i++) {
382 fwrite($this->_filehandle,$unknown1);
384 fwrite($this->_filehandle,$pps_ts1s);
385 fwrite($this->_filehandle,$pps_ts1d);
386 fwrite($this->_filehandle,$pps_ts2d);
387 fwrite($this->_filehandle,$pps_ts2d);
388 fwrite($this->_filehandle,$pps_sb);
389 fwrite($this->_filehandle,$pps_size);
390 fwrite($this->_filehandle,$unknown1);
394 * Pad the end of the file
396 function _write_padding()
398 $biffsize = $this->_biffsize;
399 if ($biffsize < 4096) {
400 $min_size = 4096;
402 else {
403 $min_size = 512;
405 if ($biffsize % $min_size != 0)
407 $padding = $min_size - ($biffsize % $min_size);
408 for($i=0; $i < $padding; $i++) {
409 fwrite($this->_filehandle,"\0");