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.

533 lines
22 KiB

  1. <?php
  2. /**
  3. * PHPExcel_Writer_Excel2007
  4. *
  5. * Copyright (c) 2006 - 2015 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_Excel2007
  23. * @copyright Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel)
  24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  25. * @version ##VERSION##, ##DATE##
  26. */
  27. class PHPExcel_Writer_Excel2007 extends PHPExcel_Writer_Abstract implements PHPExcel_Writer_IWriter
  28. {
  29. /**
  30. * Pre-calculate formulas
  31. * Forces PHPExcel to recalculate all formulae in a workbook when saving, so that the pre-calculated values are
  32. * immediately available to MS Excel or other office spreadsheet viewer when opening the file
  33. *
  34. * Overrides the default TRUE for this specific writer for performance reasons
  35. *
  36. * @var boolean
  37. */
  38. protected $preCalculateFormulas = false;
  39. /**
  40. * Office2003 compatibility
  41. *
  42. * @var boolean
  43. */
  44. private $office2003compatibility = false;
  45. /**
  46. * Private writer parts
  47. *
  48. * @var PHPExcel_Writer_Excel2007_WriterPart[]
  49. */
  50. private $writerParts = array();
  51. /**
  52. * Private PHPExcel
  53. *
  54. * @var PHPExcel
  55. */
  56. private $spreadSheet;
  57. /**
  58. * Private string table
  59. *
  60. * @var string[]
  61. */
  62. private $stringTable = array();
  63. /**
  64. * Private unique PHPExcel_Style_Conditional HashTable
  65. *
  66. * @var PHPExcel_HashTable
  67. */
  68. private $stylesConditionalHashTable;
  69. /**
  70. * Private unique PHPExcel_Style HashTable
  71. *
  72. * @var PHPExcel_HashTable
  73. */
  74. private $styleHashTable;
  75. /**
  76. * Private unique PHPExcel_Style_Fill HashTable
  77. *
  78. * @var PHPExcel_HashTable
  79. */
  80. private $fillHashTable;
  81. /**
  82. * Private unique PHPExcel_Style_Font HashTable
  83. *
  84. * @var PHPExcel_HashTable
  85. */
  86. private $fontHashTable;
  87. /**
  88. * Private unique PHPExcel_Style_Borders HashTable
  89. *
  90. * @var PHPExcel_HashTable
  91. */
  92. private $bordersHashTable ;
  93. /**
  94. * Private unique PHPExcel_Style_NumberFormat HashTable
  95. *
  96. * @var PHPExcel_HashTable
  97. */
  98. private $numFmtHashTable;
  99. /**
  100. * Private unique PHPExcel_Worksheet_BaseDrawing HashTable
  101. *
  102. * @var PHPExcel_HashTable
  103. */
  104. private $drawingHashTable;
  105. /**
  106. * Create a new PHPExcel_Writer_Excel2007
  107. *
  108. * @param PHPExcel $pPHPExcel
  109. */
  110. public function __construct(PHPExcel $pPHPExcel = null)
  111. {
  112. // Assign PHPExcel
  113. $this->setPHPExcel($pPHPExcel);
  114. $writerPartsArray = array( 'stringtable' => 'PHPExcel_Writer_Excel2007_StringTable',
  115. 'contenttypes' => 'PHPExcel_Writer_Excel2007_ContentTypes',
  116. 'docprops' => 'PHPExcel_Writer_Excel2007_DocProps',
  117. 'rels' => 'PHPExcel_Writer_Excel2007_Rels',
  118. 'theme' => 'PHPExcel_Writer_Excel2007_Theme',
  119. 'style' => 'PHPExcel_Writer_Excel2007_Style',
  120. 'workbook' => 'PHPExcel_Writer_Excel2007_Workbook',
  121. 'worksheet' => 'PHPExcel_Writer_Excel2007_Worksheet',
  122. 'drawing' => 'PHPExcel_Writer_Excel2007_Drawing',
  123. 'comments' => 'PHPExcel_Writer_Excel2007_Comments',
  124. 'chart' => 'PHPExcel_Writer_Excel2007_Chart',
  125. 'relsvba' => 'PHPExcel_Writer_Excel2007_RelsVBA',
  126. 'relsribbonobjects' => 'PHPExcel_Writer_Excel2007_RelsRibbon'
  127. );
  128. // Initialise writer parts
  129. // and Assign their parent IWriters
  130. foreach ($writerPartsArray as $writer => $class) {
  131. $this->writerParts[$writer] = new $class($this);
  132. }
  133. $hashTablesArray = array( 'stylesConditionalHashTable', 'fillHashTable', 'fontHashTable',
  134. 'bordersHashTable', 'numFmtHashTable', 'drawingHashTable',
  135. 'styleHashTable'
  136. );
  137. // Set HashTable variables
  138. foreach ($hashTablesArray as $tableName) {
  139. $this->$tableName = new PHPExcel_HashTable();
  140. }
  141. }
  142. /**
  143. * Get writer part
  144. *
  145. * @param string $pPartName Writer part name
  146. * @return PHPExcel_Writer_Excel2007_WriterPart
  147. */
  148. public function getWriterPart($pPartName = '')
  149. {
  150. if ($pPartName != '' && isset($this->writerParts[strtolower($pPartName)])) {
  151. return $this->writerParts[strtolower($pPartName)];
  152. } else {
  153. return null;
  154. }
  155. }
  156. /**
  157. * Save PHPExcel to file
  158. *
  159. * @param string $pFilename
  160. * @throws PHPExcel_Writer_Exception
  161. */
  162. public function save($pFilename = null)
  163. {
  164. if ($this->spreadSheet !== null) {
  165. // garbage collect
  166. $this->spreadSheet->garbageCollect();
  167. // If $pFilename is php://output or php://stdout, make it a temporary file...
  168. $originalFilename = $pFilename;
  169. if (strtolower($pFilename) == 'php://output' || strtolower($pFilename) == 'php://stdout') {
  170. $pFilename = @tempnam(PHPExcel_Shared_File::sys_get_temp_dir(), 'phpxltmp');
  171. if ($pFilename == '') {
  172. $pFilename = $originalFilename;
  173. }
  174. }
  175. $saveDebugLog = PHPExcel_Calculation::getInstance($this->spreadSheet)->getDebugLog()->getWriteDebugLog();
  176. PHPExcel_Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog(false);
  177. $saveDateReturnType = PHPExcel_Calculation_Functions::getReturnDateType();
  178. PHPExcel_Calculation_Functions::setReturnDateType(PHPExcel_Calculation_Functions::RETURNDATE_EXCEL);
  179. // Create string lookup table
  180. $this->stringTable = array();
  181. for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
  182. $this->stringTable = $this->getWriterPart('StringTable')->createStringTable($this->spreadSheet->getSheet($i), $this->stringTable);
  183. }
  184. // Create styles dictionaries
  185. $this->styleHashTable->addFromSource($this->getWriterPart('Style')->allStyles($this->spreadSheet));
  186. $this->stylesConditionalHashTable->addFromSource($this->getWriterPart('Style')->allConditionalStyles($this->spreadSheet));
  187. $this->fillHashTable->addFromSource($this->getWriterPart('Style')->allFills($this->spreadSheet));
  188. $this->fontHashTable->addFromSource($this->getWriterPart('Style')->allFonts($this->spreadSheet));
  189. $this->bordersHashTable->addFromSource($this->getWriterPart('Style')->allBorders($this->spreadSheet));
  190. $this->numFmtHashTable->addFromSource($this->getWriterPart('Style')->allNumberFormats($this->spreadSheet));
  191. // Create drawing dictionary
  192. $this->drawingHashTable->addFromSource($this->getWriterPart('Drawing')->allDrawings($this->spreadSheet));
  193. // Create new ZIP file and open it for writing
  194. $zipClass = PHPExcel_Settings::getZipClass();
  195. $objZip = new $zipClass();
  196. // Retrieve OVERWRITE and CREATE constants from the instantiated zip class
  197. // This method of accessing constant values from a dynamic class should work with all appropriate versions of PHP
  198. $ro = new ReflectionObject($objZip);
  199. $zipOverWrite = $ro->getConstant('OVERWRITE');
  200. $zipCreate = $ro->getConstant('CREATE');
  201. if (file_exists($pFilename)) {
  202. unlink($pFilename);
  203. }
  204. // Try opening the ZIP file
  205. if ($objZip->open($pFilename, $zipOverWrite) !== true) {
  206. if ($objZip->open($pFilename, $zipCreate) !== true) {
  207. throw new PHPExcel_Writer_Exception("Could not open " . $pFilename . " for writing.");
  208. }
  209. }
  210. // Add [Content_Types].xml to ZIP file
  211. $objZip->addFromString('[Content_Types].xml', $this->getWriterPart('ContentTypes')->writeContentTypes($this->spreadSheet, $this->includeCharts));
  212. //if hasMacros, add the vbaProject.bin file, Certificate file(if exists)
  213. if ($this->spreadSheet->hasMacros()) {
  214. $macrosCode=$this->spreadSheet->getMacrosCode();
  215. if (!is_null($macrosCode)) {// we have the code ?
  216. $objZip->addFromString('xl/vbaProject.bin', $macrosCode);//allways in 'xl', allways named vbaProject.bin
  217. if ($this->spreadSheet->hasMacrosCertificate()) {//signed macros ?
  218. // Yes : add the certificate file and the related rels file
  219. $objZip->addFromString('xl/vbaProjectSignature.bin', $this->spreadSheet->getMacrosCertificate());
  220. $objZip->addFromString('xl/_rels/vbaProject.bin.rels', $this->getWriterPart('RelsVBA')->writeVBARelationships($this->spreadSheet));
  221. }
  222. }
  223. }
  224. //a custom UI in this workbook ? add it ("base" xml and additional objects (pictures) and rels)
  225. if ($this->spreadSheet->hasRibbon()) {
  226. $tmpRibbonTarget=$this->spreadSheet->getRibbonXMLData('target');
  227. $objZip->addFromString($tmpRibbonTarget, $this->spreadSheet->getRibbonXMLData('data'));
  228. if ($this->spreadSheet->hasRibbonBinObjects()) {
  229. $tmpRootPath=dirname($tmpRibbonTarget).'/';
  230. $ribbonBinObjects=$this->spreadSheet->getRibbonBinObjects('data');//the files to write
  231. foreach ($ribbonBinObjects as $aPath => $aContent) {
  232. $objZip->addFromString($tmpRootPath.$aPath, $aContent);
  233. }
  234. //the rels for files
  235. $objZip->addFromString($tmpRootPath.'_rels/'.basename($tmpRibbonTarget).'.rels', $this->getWriterPart('RelsRibbonObjects')->writeRibbonRelationships($this->spreadSheet));
  236. }
  237. }
  238. // Add relationships to ZIP file
  239. $objZip->addFromString('_rels/.rels', $this->getWriterPart('Rels')->writeRelationships($this->spreadSheet));
  240. $objZip->addFromString('xl/_rels/workbook.xml.rels', $this->getWriterPart('Rels')->writeWorkbookRelationships($this->spreadSheet));
  241. // Add document properties to ZIP file
  242. $objZip->addFromString('docProps/app.xml', $this->getWriterPart('DocProps')->writeDocPropsApp($this->spreadSheet));
  243. $objZip->addFromString('docProps/core.xml', $this->getWriterPart('DocProps')->writeDocPropsCore($this->spreadSheet));
  244. $customPropertiesPart = $this->getWriterPart('DocProps')->writeDocPropsCustom($this->spreadSheet);
  245. if ($customPropertiesPart !== null) {
  246. $objZip->addFromString('docProps/custom.xml', $customPropertiesPart);
  247. }
  248. // Add theme to ZIP file
  249. $objZip->addFromString('xl/theme/theme1.xml', $this->getWriterPart('Theme')->writeTheme($this->spreadSheet));
  250. // Add string table to ZIP file
  251. $objZip->addFromString('xl/sharedStrings.xml', $this->getWriterPart('StringTable')->writeStringTable($this->stringTable));
  252. // Add styles to ZIP file
  253. $objZip->addFromString('xl/styles.xml', $this->getWriterPart('Style')->writeStyles($this->spreadSheet));
  254. // Add workbook to ZIP file
  255. $objZip->addFromString('xl/workbook.xml', $this->getWriterPart('Workbook')->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas));
  256. $chartCount = 0;
  257. // Add worksheets
  258. for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
  259. $objZip->addFromString('xl/worksheets/sheet' . ($i + 1) . '.xml', $this->getWriterPart('Worksheet')->writeWorksheet($this->spreadSheet->getSheet($i), $this->stringTable, $this->includeCharts));
  260. if ($this->includeCharts) {
  261. $charts = $this->spreadSheet->getSheet($i)->getChartCollection();
  262. if (count($charts) > 0) {
  263. foreach ($charts as $chart) {
  264. $objZip->addFromString('xl/charts/chart' . ($chartCount + 1) . '.xml', $this->getWriterPart('Chart')->writeChart($chart, $this->preCalculateFormulas));
  265. $chartCount++;
  266. }
  267. }
  268. }
  269. }
  270. $chartRef1 = $chartRef2 = 0;
  271. // Add worksheet relationships (drawings, ...)
  272. for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
  273. // Add relationships
  274. $objZip->addFromString('xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts));
  275. $drawings = $this->spreadSheet->getSheet($i)->getDrawingCollection();
  276. $drawingCount = count($drawings);
  277. if ($this->includeCharts) {
  278. $chartCount = $this->spreadSheet->getSheet($i)->getChartCount();
  279. }
  280. // Add drawing and image relationship parts
  281. if (($drawingCount > 0) || ($chartCount > 0)) {
  282. // Drawing relationships
  283. $objZip->addFromString('xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts));
  284. // Drawings
  285. $objZip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $chartRef2, $this->includeCharts));
  286. }
  287. // Add comment relationship parts
  288. if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
  289. // VML Comments
  290. $objZip->addFromString('xl/drawings/vmlDrawing' . ($i + 1) . '.vml', $this->getWriterPart('Comments')->writeVMLComments($this->spreadSheet->getSheet($i)));
  291. // Comments
  292. $objZip->addFromString('xl/comments' . ($i + 1) . '.xml', $this->getWriterPart('Comments')->writeComments($this->spreadSheet->getSheet($i)));
  293. }
  294. // Add header/footer relationship parts
  295. if (count($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) {
  296. // VML Drawings
  297. $objZip->addFromString('xl/drawings/vmlDrawingHF' . ($i + 1) . '.vml', $this->getWriterPart('Drawing')->writeVMLHeaderFooterImages($this->spreadSheet->getSheet($i)));
  298. // VML Drawing relationships
  299. $objZip->addFromString('xl/drawings/_rels/vmlDrawingHF' . ($i + 1) . '.vml.rels', $this->getWriterPart('Rels')->writeHeaderFooterDrawingRelationships($this->spreadSheet->getSheet($i)));
  300. // Media
  301. foreach ($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
  302. $objZip->addFromString('xl/media/' . $image->getIndexedFilename(), file_get_contents($image->getPath()));
  303. }
  304. }
  305. }
  306. // Add media
  307. for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) {
  308. if ($this->getDrawingHashTable()->getByIndex($i) instanceof PHPExcel_Worksheet_Drawing) {
  309. $imageContents = null;
  310. $imagePath = $this->getDrawingHashTable()->getByIndex($i)->getPath();
  311. if (strpos($imagePath, 'zip://') !== false) {
  312. $imagePath = substr($imagePath, 6);
  313. $imagePathSplitted = explode('#', $imagePath);
  314. $imageZip = new ZipArchive();
  315. $imageZip->open($imagePathSplitted[0]);
  316. $imageContents = $imageZip->getFromName($imagePathSplitted[1]);
  317. $imageZip->close();
  318. unset($imageZip);
  319. } else {
  320. $imageContents = file_get_contents($imagePath);
  321. }
  322. $objZip->addFromString('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents);
  323. } elseif ($this->getDrawingHashTable()->getByIndex($i) instanceof PHPExcel_Worksheet_MemoryDrawing) {
  324. ob_start();
  325. call_user_func(
  326. $this->getDrawingHashTable()->getByIndex($i)->getRenderingFunction(),
  327. $this->getDrawingHashTable()->getByIndex($i)->getImageResource()
  328. );
  329. $imageContents = ob_get_contents();
  330. ob_end_clean();
  331. $objZip->addFromString('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents);
  332. }
  333. }
  334. PHPExcel_Calculation_Functions::setReturnDateType($saveDateReturnType);
  335. PHPExcel_Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
  336. // Close file
  337. if ($objZip->close() === false) {
  338. throw new PHPExcel_Writer_Exception("Could not close zip file $pFilename.");
  339. }
  340. // If a temporary file was used, copy it to the correct file stream
  341. if ($originalFilename != $pFilename) {
  342. if (copy($pFilename, $originalFilename) === false) {
  343. throw new PHPExcel_Writer_Exception("Could not copy temporary zip file $pFilename to $originalFilename.");
  344. }
  345. @unlink($pFilename);
  346. }
  347. } else {
  348. throw new PHPExcel_Writer_Exception("PHPExcel object unassigned.");
  349. }
  350. }
  351. /**
  352. * Get PHPExcel object
  353. *
  354. * @return PHPExcel
  355. * @throws PHPExcel_Writer_Exception
  356. */
  357. public function getPHPExcel()
  358. {
  359. if ($this->spreadSheet !== null) {
  360. return $this->spreadSheet;
  361. } else {
  362. throw new PHPExcel_Writer_Exception("No PHPExcel object assigned.");
  363. }
  364. }
  365. /**
  366. * Set PHPExcel object
  367. *
  368. * @param PHPExcel $pPHPExcel PHPExcel object
  369. * @throws PHPExcel_Writer_Exception
  370. * @return PHPExcel_Writer_Excel2007
  371. */
  372. public function setPHPExcel(PHPExcel $pPHPExcel = null)
  373. {
  374. $this->spreadSheet = $pPHPExcel;
  375. return $this;
  376. }
  377. /**
  378. * Get string table
  379. *
  380. * @return string[]
  381. */
  382. public function getStringTable()
  383. {
  384. return $this->stringTable;
  385. }
  386. /**
  387. * Get PHPExcel_Style HashTable
  388. *
  389. * @return PHPExcel_HashTable
  390. */
  391. public function getStyleHashTable()
  392. {
  393. return $this->styleHashTable;
  394. }
  395. /**
  396. * Get PHPExcel_Style_Conditional HashTable
  397. *
  398. * @return PHPExcel_HashTable
  399. */
  400. public function getStylesConditionalHashTable()
  401. {
  402. return $this->stylesConditionalHashTable;
  403. }
  404. /**
  405. * Get PHPExcel_Style_Fill HashTable
  406. *
  407. * @return PHPExcel_HashTable
  408. */
  409. public function getFillHashTable()
  410. {
  411. return $this->fillHashTable;
  412. }
  413. /**
  414. * Get PHPExcel_Style_Font HashTable
  415. *
  416. * @return PHPExcel_HashTable
  417. */
  418. public function getFontHashTable()
  419. {
  420. return $this->fontHashTable;
  421. }
  422. /**
  423. * Get PHPExcel_Style_Borders HashTable
  424. *
  425. * @return PHPExcel_HashTable
  426. */
  427. public function getBordersHashTable()
  428. {
  429. return $this->bordersHashTable;
  430. }
  431. /**
  432. * Get PHPExcel_Style_NumberFormat HashTable
  433. *
  434. * @return PHPExcel_HashTable
  435. */
  436. public function getNumFmtHashTable()
  437. {
  438. return $this->numFmtHashTable;
  439. }
  440. /**
  441. * Get PHPExcel_Worksheet_BaseDrawing HashTable
  442. *
  443. * @return PHPExcel_HashTable
  444. */
  445. public function getDrawingHashTable()
  446. {
  447. return $this->drawingHashTable;
  448. }
  449. /**
  450. * Get Office2003 compatibility
  451. *
  452. * @return boolean
  453. */
  454. public function getOffice2003Compatibility()
  455. {
  456. return $this->office2003compatibility;
  457. }
  458. /**
  459. * Set Office2003 compatibility
  460. *
  461. * @param boolean $pValue Office2003 compatibility?
  462. * @return PHPExcel_Writer_Excel2007
  463. */
  464. public function setOffice2003Compatibility($pValue = false)
  465. {
  466. $this->office2003compatibility = $pValue;
  467. return $this;
  468. }
  469. }