1<?php 2/* vim: set expandtab tabstop=4 shiftwidth=4: */ 3// +----------------------------------------------------------------------+ 4// | PHP Version 4 | 5// +----------------------------------------------------------------------+ 6// | Copyright (c) 1997-2002 The PHP Group | 7// +----------------------------------------------------------------------+ 8// | This source file is subject to version 2.02 of the PHP license, | 9// | that is bundled with this package in the file LICENSE, and is | 10// | available at through the world-wide-web at | 11// | http://www.php.net/license/2_02.txt. | 12// | If you did not receive a copy of the PHP license and are unable to | 13// | obtain it through the world-wide-web, please send a note to | 14// | license@php.net so we can mail you a copy immediately. | 15// +----------------------------------------------------------------------+ 16// | Author: Xavier Noguer <xnoguer@php.net> | 17// | Based on OLE::Storage_Lite by Kawai, Takanori | 18// +----------------------------------------------------------------------+ 19// 20// $Id: Root.php,v 1.9 2005/04/23 21:53:49 dufuz Exp $ 21 22 23/** 24* Class for creating Root PPS's for OLE containers 25* 26* @author Xavier Noguer <xnoguer@php.net> 27* @category PHPExcel 28* @package PHPExcel_Shared_OLE 29*/ 30class PHPExcel_Shared_OLE_PPS_Root extends PHPExcel_Shared_OLE_PPS 31 { 32 33 /** 34 * Directory for temporary files 35 * @var string 36 */ 37 protected $_tmp_dir = NULL; 38 39 /** 40 * @param integer $time_1st A timestamp 41 * @param integer $time_2nd A timestamp 42 */ 43 public function __construct($time_1st, $time_2nd, $raChild) 44 { 45 $this->_tempDir = PHPExcel_Shared_File::sys_get_temp_dir(); 46 47 parent::__construct( 48 null, 49 PHPExcel_Shared_OLE::Asc2Ucs('Root Entry'), 50 PHPExcel_Shared_OLE::OLE_PPS_TYPE_ROOT, 51 null, 52 null, 53 null, 54 $time_1st, 55 $time_2nd, 56 null, 57 $raChild); 58 } 59 60 /** 61 * Method for saving the whole OLE container (including files). 62 * In fact, if called with an empty argument (or '-'), it saves to a 63 * temporary file and then outputs it's contents to stdout. 64 * If a resource pointer to a stream created by fopen() is passed 65 * it will be used, but you have to close such stream by yourself. 66 * 67 * @param string|resource $filename The name of the file or stream where to save the OLE container. 68 * @access public 69 * @return mixed true on success 70 */ 71 public function save($filename) 72 { 73 // Initial Setting for saving 74 $this->_BIG_BLOCK_SIZE = pow(2, 75 ((isset($this->_BIG_BLOCK_SIZE))? self::_adjust2($this->_BIG_BLOCK_SIZE) : 9)); 76 $this->_SMALL_BLOCK_SIZE= pow(2, 77 ((isset($this->_SMALL_BLOCK_SIZE))? self::_adjust2($this->_SMALL_BLOCK_SIZE): 6)); 78 79 if (is_resource($filename)) { 80 $this->_FILEH_ = $filename; 81 } else if ($filename == '-' || $filename == '') { 82 if ($this->_tmp_dir === NULL) 83 $this->_tmp_dir = PHPExcel_Shared_File::sys_get_temp_dir(); 84 $this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_Root"); 85 $this->_FILEH_ = fopen($this->_tmp_filename,"w+b"); 86 if ($this->_FILEH_ == false) { 87 throw new PHPExcel_Writer_Exception("Can't create temporary file."); 88 } 89 } else { 90 $this->_FILEH_ = fopen($filename, "wb"); 91 } 92 if ($this->_FILEH_ == false) { 93 throw new PHPExcel_Writer_Exception("Can't open $filename. It may be in use or protected."); 94 } 95 // Make an array of PPS's (for Save) 96 $aList = array(); 97 PHPExcel_Shared_OLE_PPS::_savePpsSetPnt($aList, array($this)); 98 // calculate values for header 99 list($iSBDcnt, $iBBcnt, $iPPScnt) = $this->_calcSize($aList); //, $rhInfo); 100 // Save Header 101 $this->_saveHeader($iSBDcnt, $iBBcnt, $iPPScnt); 102 103 // Make Small Data string (write SBD) 104 $this->_data = $this->_makeSmallData($aList); 105 106 // Write BB 107 $this->_saveBigData($iSBDcnt, $aList); 108 // Write PPS 109 $this->_savePps($aList); 110 // Write Big Block Depot and BDList and Adding Header informations 111 $this->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt); 112 113 if (!is_resource($filename)) { 114 fclose($this->_FILEH_); 115 } 116 117 return true; 118 } 119 120 /** 121 * Calculate some numbers 122 * 123 * @access public 124 * @param array $raList Reference to an array of PPS's 125 * @return array The array of numbers 126 */ 127 public function _calcSize(&$raList) 128 { 129 // Calculate Basic Setting 130 list($iSBDcnt, $iBBcnt, $iPPScnt) = array(0,0,0); 131 $iSmallLen = 0; 132 $iSBcnt = 0; 133 $iCount = count($raList); 134 for ($i = 0; $i < $iCount; ++$i) { 135 if ($raList[$i]->Type == PHPExcel_Shared_OLE::OLE_PPS_TYPE_FILE) { 136 $raList[$i]->Size = $raList[$i]->_DataLen(); 137 if ($raList[$i]->Size < PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL) { 138 $iSBcnt += floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE) 139 + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0); 140 } else { 141 $iBBcnt += (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) + 142 (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0)); 143 } 144 } 145 } 146 $iSmallLen = $iSBcnt * $this->_SMALL_BLOCK_SIZE; 147 $iSlCnt = floor($this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE); 148 $iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt)? 1:0); 149 $iBBcnt += (floor($iSmallLen / $this->_BIG_BLOCK_SIZE) + 150 (( $iSmallLen % $this->_BIG_BLOCK_SIZE)? 1: 0)); 151 $iCnt = count($raList); 152 $iBdCnt = $this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_PPS_SIZE; 153 $iPPScnt = (floor($iCnt/$iBdCnt) + (($iCnt % $iBdCnt)? 1: 0)); 154 155 return array($iSBDcnt, $iBBcnt, $iPPScnt); 156 } 157 158 /** 159 * Helper function for caculating a magic value for block sizes 160 * 161 * @access public 162 * @param integer $i2 The argument 163 * @see save() 164 * @return integer 165 */ 166 private static function _adjust2($i2) 167 { 168 $iWk = log($i2)/log(2); 169 return ($iWk > floor($iWk))? floor($iWk)+1:$iWk; 170 } 171 172 /** 173 * Save OLE header 174 * 175 * @access public 176 * @param integer $iSBDcnt 177 * @param integer $iBBcnt 178 * @param integer $iPPScnt 179 */ 180 public function _saveHeader($iSBDcnt, $iBBcnt, $iPPScnt) 181 { 182 $FILE = $this->_FILEH_; 183 184 // Calculate Basic Setting 185 $iBlCnt = $this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE; 186 $i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE; 187 188 $iBdExL = 0; 189 $iAll = $iBBcnt + $iPPScnt + $iSBDcnt; 190 $iAllW = $iAll; 191 $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0); 192 $iBdCnt = floor(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0); 193 194 // Calculate BD count 195 if ($iBdCnt > $i1stBdL) { 196 while (1) { 197 ++$iBdExL; 198 ++$iAllW; 199 $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0); 200 $iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0); 201 if ($iBdCnt <= ($iBdExL*$iBlCnt+ $i1stBdL)) { 202 break; 203 } 204 } 205 } 206 207 // Save Header 208 fwrite($FILE, 209 "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" 210 . "\x00\x00\x00\x00" 211 . "\x00\x00\x00\x00" 212 . "\x00\x00\x00\x00" 213 . "\x00\x00\x00\x00" 214 . pack("v", 0x3b) 215 . pack("v", 0x03) 216 . pack("v", -2) 217 . pack("v", 9) 218 . pack("v", 6) 219 . pack("v", 0) 220 . "\x00\x00\x00\x00" 221 . "\x00\x00\x00\x00" 222 . pack("V", $iBdCnt) 223 . pack("V", $iBBcnt+$iSBDcnt) //ROOT START 224 . pack("V", 0) 225 . pack("V", 0x1000) 226 . pack("V", $iSBDcnt ? 0 : -2) //Small Block Depot 227 . pack("V", $iSBDcnt) 228 ); 229 // Extra BDList Start, Count 230 if ($iBdCnt < $i1stBdL) { 231 fwrite($FILE, 232 pack("V", -2) // Extra BDList Start 233 . pack("V", 0) // Extra BDList Count 234 ); 235 } else { 236 fwrite($FILE, pack("V", $iAll+$iBdCnt) . pack("V", $iBdExL)); 237 } 238 239 // BDList 240 for ($i = 0; $i < $i1stBdL && $i < $iBdCnt; ++$i) { 241 fwrite($FILE, pack("V", $iAll+$i)); 242 } 243 if ($i < $i1stBdL) { 244 $jB = $i1stBdL - $i; 245 for ($j = 0; $j < $jB; ++$j) { 246 fwrite($FILE, (pack("V", -1))); 247 } 248 } 249 } 250 251 /** 252 * Saving big data (PPS's with data bigger than PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL) 253 * 254 * @access public 255 * @param integer $iStBlk 256 * @param array &$raList Reference to array of PPS's 257 */ 258 public function _saveBigData($iStBlk, &$raList) 259 { 260 $FILE = $this->_FILEH_; 261 262 // cycle through PPS's 263 $iCount = count($raList); 264 for ($i = 0; $i < $iCount; ++$i) { 265 if ($raList[$i]->Type != PHPExcel_Shared_OLE::OLE_PPS_TYPE_DIR) { 266 $raList[$i]->Size = $raList[$i]->_DataLen(); 267 if (($raList[$i]->Size >= PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL) || 268 (($raList[$i]->Type == PHPExcel_Shared_OLE::OLE_PPS_TYPE_ROOT) && isset($raList[$i]->_data))) 269 { 270 // Write Data 271 //if (isset($raList[$i]->_PPS_FILE)) { 272 // $iLen = 0; 273 // fseek($raList[$i]->_PPS_FILE, 0); // To The Top 274 // while($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) { 275 // $iLen += strlen($sBuff); 276 // fwrite($FILE, $sBuff); 277 // } 278 //} else { 279 fwrite($FILE, $raList[$i]->_data); 280 //} 281 282 if ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE) { 283 fwrite($FILE, str_repeat("\x00", $this->_BIG_BLOCK_SIZE - ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE))); 284 } 285 // Set For PPS 286 $raList[$i]->_StartBlock = $iStBlk; 287 $iStBlk += 288 (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) + 289 (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0)); 290 } 291 // Close file for each PPS, and unlink it 292 //if (isset($raList[$i]->_PPS_FILE)) { 293 // fclose($raList[$i]->_PPS_FILE); 294 // $raList[$i]->_PPS_FILE = null; 295 // unlink($raList[$i]->_tmp_filename); 296 //} 297 } 298 } 299 } 300 301 /** 302 * get small data (PPS's with data smaller than PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL) 303 * 304 * @access public 305 * @param array &$raList Reference to array of PPS's 306 */ 307 public function _makeSmallData(&$raList) 308 { 309 $sRes = ''; 310 $FILE = $this->_FILEH_; 311 $iSmBlk = 0; 312 313 $iCount = count($raList); 314 for ($i = 0; $i < $iCount; ++$i) { 315 // Make SBD, small data string 316 if ($raList[$i]->Type == PHPExcel_Shared_OLE::OLE_PPS_TYPE_FILE) { 317 if ($raList[$i]->Size <= 0) { 318 continue; 319 } 320 if ($raList[$i]->Size < PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL) { 321 $iSmbCnt = floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE) 322 + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0); 323 // Add to SBD 324 $jB = $iSmbCnt - 1; 325 for ($j = 0; $j < $jB; ++$j) { 326 fwrite($FILE, pack("V", $j+$iSmBlk+1)); 327 } 328 fwrite($FILE, pack("V", -2)); 329 330 //// Add to Data String(this will be written for RootEntry) 331 //if ($raList[$i]->_PPS_FILE) { 332 // fseek($raList[$i]->_PPS_FILE, 0); // To The Top 333 // while ($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) { 334 // $sRes .= $sBuff; 335 // } 336 //} else { 337 $sRes .= $raList[$i]->_data; 338 //} 339 if ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE) { 340 $sRes .= str_repeat("\x00",$this->_SMALL_BLOCK_SIZE - ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)); 341 } 342 // Set for PPS 343 $raList[$i]->_StartBlock = $iSmBlk; 344 $iSmBlk += $iSmbCnt; 345 } 346 } 347 } 348 $iSbCnt = floor($this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE); 349 if ($iSmBlk % $iSbCnt) { 350 $iB = $iSbCnt - ($iSmBlk % $iSbCnt); 351 for ($i = 0; $i < $iB; ++$i) { 352 fwrite($FILE, pack("V", -1)); 353 } 354 } 355 return $sRes; 356 } 357 358 /** 359 * Saves all the PPS's WKs 360 * 361 * @access public 362 * @param array $raList Reference to an array with all PPS's 363 */ 364 public function _savePps(&$raList) 365 { 366 // Save each PPS WK 367 $iC = count($raList); 368 for ($i = 0; $i < $iC; ++$i) { 369 fwrite($this->_FILEH_, $raList[$i]->_getPpsWk()); 370 } 371 // Adjust for Block 372 $iCnt = count($raList); 373 $iBCnt = $this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_PPS_SIZE; 374 if ($iCnt % $iBCnt) { 375 fwrite($this->_FILEH_, str_repeat("\x00",($iBCnt - ($iCnt % $iBCnt)) * PHPExcel_Shared_OLE::OLE_PPS_SIZE)); 376 } 377 } 378 379 /** 380 * Saving Big Block Depot 381 * 382 * @access public 383 * @param integer $iSbdSize 384 * @param integer $iBsize 385 * @param integer $iPpsCnt 386 */ 387 public function _saveBbd($iSbdSize, $iBsize, $iPpsCnt) 388 { 389 $FILE = $this->_FILEH_; 390 // Calculate Basic Setting 391 $iBbCnt = $this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE; 392 $i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE; 393 394 $iBdExL = 0; 395 $iAll = $iBsize + $iPpsCnt + $iSbdSize; 396 $iAllW = $iAll; 397 $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0); 398 $iBdCnt = floor(($iAll + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0); 399 // Calculate BD count 400 if ($iBdCnt >$i1stBdL) { 401 while (1) { 402 ++$iBdExL; 403 ++$iAllW; 404 $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0); 405 $iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0); 406 if ($iBdCnt <= ($iBdExL*$iBbCnt+ $i1stBdL)) { 407 break; 408 } 409 } 410 } 411 412 // Making BD 413 // Set for SBD 414 if ($iSbdSize > 0) { 415 for ($i = 0; $i < ($iSbdSize - 1); ++$i) { 416 fwrite($FILE, pack("V", $i+1)); 417 } 418 fwrite($FILE, pack("V", -2)); 419 } 420 // Set for B 421 for ($i = 0; $i < ($iBsize - 1); ++$i) { 422 fwrite($FILE, pack("V", $i+$iSbdSize+1)); 423 } 424 fwrite($FILE, pack("V", -2)); 425 426 // Set for PPS 427 for ($i = 0; $i < ($iPpsCnt - 1); ++$i) { 428 fwrite($FILE, pack("V", $i+$iSbdSize+$iBsize+1)); 429 } 430 fwrite($FILE, pack("V", -2)); 431 // Set for BBD itself ( 0xFFFFFFFD : BBD) 432 for ($i = 0; $i < $iBdCnt; ++$i) { 433 fwrite($FILE, pack("V", 0xFFFFFFFD)); 434 } 435 // Set for ExtraBDList 436 for ($i = 0; $i < $iBdExL; ++$i) { 437 fwrite($FILE, pack("V", 0xFFFFFFFC)); 438 } 439 // Adjust for Block 440 if (($iAllW + $iBdCnt) % $iBbCnt) { 441 $iBlock = ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt)); 442 for ($i = 0; $i < $iBlock; ++$i) { 443 fwrite($FILE, pack("V", -1)); 444 } 445 } 446 // Extra BDList 447 if ($iBdCnt > $i1stBdL) { 448 $iN=0; 449 $iNb=0; 450 for ($i = $i1stBdL;$i < $iBdCnt; $i++, ++$iN) { 451 if ($iN >= ($iBbCnt - 1)) { 452 $iN = 0; 453 ++$iNb; 454 fwrite($FILE, pack("V", $iAll+$iBdCnt+$iNb)); 455 } 456 fwrite($FILE, pack("V", $iBsize+$iSbdSize+$iPpsCnt+$i)); 457 } 458 if (($iBdCnt-$i1stBdL) % ($iBbCnt-1)) { 459 $iB = ($iBbCnt - 1) - (($iBdCnt - $i1stBdL) % ($iBbCnt - 1)); 460 for ($i = 0; $i < $iB; ++$i) { 461 fwrite($FILE, pack("V", -1)); 462 } 463 } 464 fwrite($FILE, pack("V", -2)); 465 } 466 } 467} 468