Excel5.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. <?php
  2. /**
  3. * PHPExcel
  4. *
  5. * Copyright (c) 2006 - 2011 PHPExcel
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * @category PHPExcel
  22. * @package PHPExcel_Writer_Excel5
  23. * @copyright Copyright (c) 2006 - 2011 PHPExcel (http://www.codeplex.com/PHPExcel)
  24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  25. * @version 1.7.6, 2011-02-27
  26. */
  27. /**
  28. * PHPExcel_Writer_Excel5
  29. *
  30. * @category PHPExcel
  31. * @package PHPExcel_Writer_Excel5
  32. * @copyright Copyright (c) 2006 - 2011 PHPExcel (http://www.codeplex.com/PHPExcel)
  33. */
  34. class PHPExcel_Writer_Excel5 implements PHPExcel_Writer_IWriter
  35. {
  36. /**
  37. * Pre-calculate formulas
  38. *
  39. * @var boolean
  40. */
  41. private $_preCalculateFormulas = true;
  42. /**
  43. * PHPExcel object
  44. *
  45. * @var PHPExcel
  46. */
  47. private $_phpExcel;
  48. /**
  49. * The BIFF version of the written Excel file, BIFF5 = 0x0500, BIFF8 = 0x0600
  50. *
  51. * @var integer
  52. */
  53. private $_BIFF_version = 0x0600;
  54. /**
  55. * Total number of shared strings in workbook
  56. *
  57. * @var int
  58. */
  59. private $_str_total = 0;
  60. /**
  61. * Number of unique shared strings in workbook
  62. *
  63. * @var int
  64. */
  65. private $_str_unique = 0;
  66. /**
  67. * Array of unique shared strings in workbook
  68. *
  69. * @var array
  70. */
  71. private $_str_table = array();
  72. /**
  73. * Color cache. Mapping between RGB value and color index.
  74. *
  75. * @var array
  76. */
  77. private $_colors;
  78. /**
  79. * Formula parser
  80. *
  81. * @var PHPExcel_Writer_Excel5_Parser
  82. */
  83. private $_parser;
  84. /**
  85. * Identifier clusters for drawings. Used in MSODRAWINGGROUP record.
  86. *
  87. * @var array
  88. */
  89. private $_IDCLs;
  90. /**
  91. * Create a new PHPExcel_Writer_Excel5
  92. *
  93. * @param PHPExcel $phpExcel PHPExcel object
  94. */
  95. public function __construct(PHPExcel $phpExcel) {
  96. $this->_phpExcel = $phpExcel;
  97. $this->_parser = new PHPExcel_Writer_Excel5_Parser($this->_BIFF_version);
  98. }
  99. /**
  100. * Save PHPExcel to file
  101. *
  102. * @param string $pFileName
  103. * @throws Exception
  104. */
  105. public function save($pFilename = null) {
  106. // garbage collect
  107. $this->_phpExcel->garbageCollect();
  108. $saveDebugLog = PHPExcel_Calculation::getInstance()->writeDebugLog;
  109. PHPExcel_Calculation::getInstance()->writeDebugLog = false;
  110. $saveDateReturnType = PHPExcel_Calculation_Functions::getReturnDateType();
  111. PHPExcel_Calculation_Functions::setReturnDateType(PHPExcel_Calculation_Functions::RETURNDATE_EXCEL);
  112. // initialize colors array
  113. $this->_colors = array();
  114. // Initialise workbook writer
  115. $this->_writerWorkbook = new PHPExcel_Writer_Excel5_Workbook($this->_phpExcel, $this->_BIFF_version,
  116. $this->_str_total, $this->_str_unique, $this->_str_table, $this->_colors, $this->_parser);
  117. // Initialise worksheet writers
  118. $countSheets = $this->_phpExcel->getSheetCount();
  119. for ($i = 0; $i < $countSheets; ++$i) {
  120. $this->_writerWorksheets[$i] = new PHPExcel_Writer_Excel5_Worksheet($this->_BIFF_version,
  121. $this->_str_total, $this->_str_unique,
  122. $this->_str_table, $this->_colors,
  123. $this->_parser,
  124. $this->_preCalculateFormulas,
  125. $this->_phpExcel->getSheet($i));
  126. }
  127. // build Escher objects. Escher objects for workbooks needs to be build before Escher object for workbook.
  128. $this->_buildWorksheetEschers();
  129. $this->_buildWorkbookEscher();
  130. // add 15 identical cell style Xfs
  131. // for now, we use the first cellXf instead of cellStyleXf
  132. $cellXfCollection = $this->_phpExcel->getCellXfCollection();
  133. for ($i = 0; $i < 15; ++$i) {
  134. $this->_writerWorkbook->addXfWriter($cellXfCollection[0], true);
  135. }
  136. // add all the cell Xfs
  137. foreach ($this->_phpExcel->getCellXfCollection() as $style) {
  138. $this->_writerWorkbook->addXfWriter($style, false);
  139. }
  140. // initialize OLE file
  141. $workbookStreamName = ($this->_BIFF_version == 0x0600) ? 'Workbook' : 'Book';
  142. $OLE = new PHPExcel_Shared_OLE_PPS_File(PHPExcel_Shared_OLE::Asc2Ucs($workbookStreamName));
  143. // Write the worksheet streams before the global workbook stream,
  144. // because the byte sizes of these are needed in the global workbook stream
  145. $worksheetSizes = array();
  146. for ($i = 0; $i < $countSheets; ++$i) {
  147. $this->_writerWorksheets[$i]->close();
  148. $worksheetSizes[] = $this->_writerWorksheets[$i]->_datasize;
  149. }
  150. // add binary data for global workbook stream
  151. $OLE->append( $this->_writerWorkbook->writeWorkbook($worksheetSizes) );
  152. // add binary data for sheet streams
  153. for ($i = 0; $i < $countSheets; ++$i) {
  154. $OLE->append($this->_writerWorksheets[$i]->getData());
  155. }
  156. $root = new PHPExcel_Shared_OLE_PPS_Root(time(), time(), array($OLE));
  157. // save the OLE file
  158. $res = $root->save($pFilename);
  159. PHPExcel_Calculation_Functions::setReturnDateType($saveDateReturnType);
  160. PHPExcel_Calculation::getInstance()->writeDebugLog = $saveDebugLog;
  161. }
  162. /**
  163. * Set temporary storage directory
  164. *
  165. * @deprecated
  166. * @param string $pValue Temporary storage directory
  167. * @throws Exception Exception when directory does not exist
  168. * @return PHPExcel_Writer_Excel5
  169. */
  170. public function setTempDir($pValue = '') {
  171. return $this;
  172. }
  173. /**
  174. * Get Pre-Calculate Formulas
  175. *
  176. * @return boolean
  177. */
  178. public function getPreCalculateFormulas() {
  179. return $this->_preCalculateFormulas;
  180. }
  181. /**
  182. * Set Pre-Calculate Formulas
  183. *
  184. * @param boolean $pValue Pre-Calculate Formulas?
  185. */
  186. public function setPreCalculateFormulas($pValue = true) {
  187. $this->_preCalculateFormulas = $pValue;
  188. }
  189. private function _buildWorksheetEschers()
  190. {
  191. // 1-based index to BstoreContainer
  192. $blipIndex = 0;
  193. foreach ($this->_phpExcel->getAllsheets() as $sheet) {
  194. // sheet index
  195. $sheetIndex = $sheet->getParent()->getIndex($sheet);
  196. $escher = null;
  197. // check if there are any shapes for this sheet
  198. if (count($sheet->getDrawingCollection()) == 0) {
  199. continue;
  200. }
  201. // create intermediate Escher object
  202. $escher = new PHPExcel_Shared_Escher();
  203. // dgContainer
  204. $dgContainer = new PHPExcel_Shared_Escher_DgContainer();
  205. // set the drawing index (we use sheet index + 1)
  206. $dgId = $sheet->getParent()->getIndex($sheet) + 1;
  207. $dgContainer->setDgId($dgId);
  208. $escher->setDgContainer($dgContainer);
  209. // spgrContainer
  210. $spgrContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer();
  211. $dgContainer->setSpgrContainer($spgrContainer);
  212. // add one shape which is the group shape
  213. $spContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer();
  214. $spContainer->setSpgr(true);
  215. $spContainer->setSpType(0);
  216. $spContainer->setSpId(($sheet->getParent()->getIndex($sheet) + 1) << 10);
  217. $spgrContainer->addChild($spContainer);
  218. // add the shapes
  219. $countShapes[$sheetIndex] = 0; // count number of shapes (minus group shape), in sheet
  220. foreach ($sheet->getDrawingCollection() as $drawing) {
  221. ++$blipIndex;
  222. ++$countShapes[$sheetIndex];
  223. // add the shape
  224. $spContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer();
  225. // set the shape type
  226. $spContainer->setSpType(0x004B);
  227. // set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index)
  228. $reducedSpId = $countShapes[$sheetIndex];
  229. $spId = $reducedSpId
  230. | ($sheet->getParent()->getIndex($sheet) + 1) << 10;
  231. $spContainer->setSpId($spId);
  232. // keep track of last reducedSpId
  233. $lastReducedSpId = $reducedSpId;
  234. // keep track of last spId
  235. $lastSpId = $spId;
  236. // set the BLIP index
  237. $spContainer->setOPT(0x4104, $blipIndex);
  238. // set coordinates and offsets, client anchor
  239. $coordinates = $drawing->getCoordinates();
  240. $offsetX = $drawing->getOffsetX();
  241. $offsetY = $drawing->getOffsetY();
  242. $width = $drawing->getWidth();
  243. $height = $drawing->getHeight();
  244. $twoAnchor = PHPExcel_Shared_Excel5::oneAnchor2twoAnchor($sheet, $coordinates, $offsetX, $offsetY, $width, $height);
  245. $spContainer->setStartCoordinates($twoAnchor['startCoordinates']);
  246. $spContainer->setStartOffsetX($twoAnchor['startOffsetX']);
  247. $spContainer->setStartOffsetY($twoAnchor['startOffsetY']);
  248. $spContainer->setEndCoordinates($twoAnchor['endCoordinates']);
  249. $spContainer->setEndOffsetX($twoAnchor['endOffsetX']);
  250. $spContainer->setEndOffsetY($twoAnchor['endOffsetY']);
  251. $spgrContainer->addChild($spContainer);
  252. }
  253. // identifier clusters, used for workbook Escher object
  254. $this->_IDCLs[$dgId] = $lastReducedSpId;
  255. // set last shape index
  256. $dgContainer->setLastSpId($lastSpId);
  257. // set the Escher object
  258. $this->_writerWorksheets[$sheetIndex]->setEscher($escher);
  259. }
  260. }
  261. /**
  262. * Build the Escher object corresponding to the MSODRAWINGGROUP record
  263. */
  264. private function _buildWorkbookEscher()
  265. {
  266. $escher = null;
  267. // any drawings in this workbook?
  268. $found = false;
  269. foreach ($this->_phpExcel->getAllSheets() as $sheet) {
  270. if (count($sheet->getDrawingCollection()) > 0) {
  271. $found = true;
  272. }
  273. }
  274. // nothing to do if there are no drawings
  275. if (!$found) {
  276. return;
  277. }
  278. // if we reach here, then there are drawings in the workbook
  279. $escher = new PHPExcel_Shared_Escher();
  280. // dggContainer
  281. $dggContainer = new PHPExcel_Shared_Escher_DggContainer();
  282. $escher->setDggContainer($dggContainer);
  283. // set IDCLs (identifier clusters)
  284. $dggContainer->setIDCLs($this->_IDCLs);
  285. // this loop is for determining maximum shape identifier of all drawing
  286. $spIdMax = 0;
  287. $totalCountShapes = 0;
  288. $countDrawings = 0;
  289. foreach ($this->_phpExcel->getAllsheets() as $sheet) {
  290. $sheetCountShapes = 0; // count number of shapes (minus group shape), in sheet
  291. if (count($sheet->getDrawingCollection()) > 0) {
  292. ++$countDrawings;
  293. foreach ($sheet->getDrawingCollection() as $drawing) {
  294. ++$sheetCountShapes;
  295. ++$totalCountShapes;
  296. $spId = $sheetCountShapes
  297. | ($this->_phpExcel->getIndex($sheet) + 1) << 10;
  298. $spIdMax = max($spId, $spIdMax);
  299. }
  300. }
  301. }
  302. $dggContainer->setSpIdMax($spIdMax + 1);
  303. $dggContainer->setCDgSaved($countDrawings);
  304. $dggContainer->setCSpSaved($totalCountShapes + $countDrawings); // total number of shapes incl. one group shapes per drawing
  305. // bstoreContainer
  306. $bstoreContainer = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer();
  307. $dggContainer->setBstoreContainer($bstoreContainer);
  308. // the BSE's (all the images)
  309. foreach ($this->_phpExcel->getAllsheets() as $sheet) {
  310. foreach ($sheet->getDrawingCollection() as $drawing) {
  311. if ($drawing instanceof PHPExcel_Worksheet_Drawing) {
  312. $filename = $drawing->getPath();
  313. list($imagesx, $imagesy, $imageFormat) = getimagesize($filename);
  314. switch ($imageFormat) {
  315. case 1: // GIF, not supported by BIFF8, we convert to PNG
  316. $blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
  317. ob_start();
  318. imagepng(imagecreatefromgif($filename));
  319. $blipData = ob_get_contents();
  320. ob_end_clean();
  321. break;
  322. case 2: // JPEG
  323. $blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG;
  324. $blipData = file_get_contents($filename);
  325. break;
  326. case 3: // PNG
  327. $blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
  328. $blipData = file_get_contents($filename);
  329. break;
  330. case 6: // Windows DIB (BMP), we convert to PNG
  331. $blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
  332. ob_start();
  333. imagepng(PHPExcel_Shared_Drawing::imagecreatefrombmp($filename));
  334. $blipData = ob_get_contents();
  335. ob_end_clean();
  336. break;
  337. default: continue 2;
  338. }
  339. $blip = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip();
  340. $blip->setData($blipData);
  341. $BSE = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE();
  342. $BSE->setBlipType($blipType);
  343. $BSE->setBlip($blip);
  344. $bstoreContainer->addBSE($BSE);
  345. } else if ($drawing instanceof PHPExcel_Worksheet_MemoryDrawing) {
  346. switch ($drawing->getRenderingFunction()) {
  347. case PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG:
  348. $blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG;
  349. $renderingFunction = 'imagejpeg';
  350. break;
  351. case PHPExcel_Worksheet_MemoryDrawing::RENDERING_GIF:
  352. case PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG:
  353. case PHPExcel_Worksheet_MemoryDrawing::RENDERING_DEFAULT:
  354. $blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
  355. $renderingFunction = 'imagepng';
  356. break;
  357. }
  358. ob_start();
  359. call_user_func($renderingFunction, $drawing->getImageResource());
  360. $blipData = ob_get_contents();
  361. ob_end_clean();
  362. $blip = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip();
  363. $blip->setData($blipData);
  364. $BSE = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE();
  365. $BSE->setBlipType($blipType);
  366. $BSE->setBlip($blip);
  367. $bstoreContainer->addBSE($BSE);
  368. }
  369. }
  370. }
  371. // Set the Escher object
  372. $this->_writerWorkbook->setEscher($escher);
  373. }
  374. }