For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

462 lines
17 KiB

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