ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Xlsx.php
Go to the documentation of this file.
1 <?php
2 
4 
31 use ZipArchive;
35 
36 class Xlsx extends BaseWriter
37 {
43  private $office2003compatibility = false;
44 
50  private $spreadSheet;
51 
57  private $stringTable = [];
58 
65 
71  private $styleHashTable;
72 
78  private $fillHashTable;
79 
85  private $fontHashTable;
86 
93 
100 
107 
113  private $zip;
114 
119 
124 
129 
134 
139 
144 
149 
154 
159 
164 
169 
174 
179 
183  public function __construct(Spreadsheet $spreadsheet)
184  {
185  // Assign PhpSpreadsheet
186  $this->setSpreadsheet($spreadsheet);
187 
188  $this->writerPartChart = new Chart($this);
189  $this->writerPartComments = new Comments($this);
190  $this->writerPartContentTypes = new ContentTypes($this);
191  $this->writerPartDocProps = new DocProps($this);
192  $this->writerPartDrawing = new Drawing($this);
193  $this->writerPartRels = new Rels($this);
194  $this->writerPartRelsRibbon = new RelsRibbon($this);
195  $this->writerPartRelsVBA = new RelsVBA($this);
196  $this->writerPartStringTable = new StringTable($this);
197  $this->writerPartStyle = new Style($this);
198  $this->writerPartTheme = new Theme($this);
199  $this->writerPartWorkbook = new Workbook($this);
200  $this->writerPartWorksheet = new Worksheet($this);
201 
202  // Set HashTable variables
203  // @phpstan-ignore-next-line
204  $this->bordersHashTable = new HashTable();
205  // @phpstan-ignore-next-line
206  $this->drawingHashTable = new HashTable();
207  // @phpstan-ignore-next-line
208  $this->fillHashTable = new HashTable();
209  // @phpstan-ignore-next-line
210  $this->fontHashTable = new HashTable();
211  // @phpstan-ignore-next-line
212  $this->numFmtHashTable = new HashTable();
213  // @phpstan-ignore-next-line
214  $this->styleHashTable = new HashTable();
215  // @phpstan-ignore-next-line
216  $this->stylesConditionalHashTable = new HashTable();
217  }
218 
219  public function getWriterPartChart(): Chart
220  {
221  return $this->writerPartChart;
222  }
223 
224  public function getWriterPartComments(): Comments
225  {
227  }
228 
230  {
232  }
233 
234  public function getWriterPartDocProps(): DocProps
235  {
237  }
238 
239  public function getWriterPartDrawing(): Drawing
240  {
242  }
243 
244  public function getWriterPartRels(): Rels
245  {
246  return $this->writerPartRels;
247  }
248 
250  {
252  }
253 
254  public function getWriterPartRelsVBA(): RelsVBA
255  {
257  }
258 
260  {
262  }
263 
264  public function getWriterPartStyle(): Style
265  {
266  return $this->writerPartStyle;
267  }
268 
269  public function getWriterPartTheme(): Theme
270  {
271  return $this->writerPartTheme;
272  }
273 
274  public function getWriterPartWorkbook(): Workbook
275  {
277  }
278 
280  {
282  }
283 
289  public function save($pFilename): void
290  {
291  // garbage collect
292  $this->pathNames = [];
293  $this->spreadSheet->garbageCollect();
294 
295  $saveDebugLog = Calculation::getInstance($this->spreadSheet)->getDebugLog()->getWriteDebugLog();
296  Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog(false);
297  $saveDateReturnType = Functions::getReturnDateType();
299 
300  // Create string lookup table
301  $this->stringTable = [];
302  for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
303  $this->stringTable = $this->getWriterPartStringTable()->createStringTable($this->spreadSheet->getSheet($i), $this->stringTable);
304  }
305 
306  // Create styles dictionaries
307  $this->styleHashTable->addFromSource($this->getWriterPartStyle()->allStyles($this->spreadSheet));
308  $this->stylesConditionalHashTable->addFromSource($this->getWriterPartStyle()->allConditionalStyles($this->spreadSheet));
309  $this->fillHashTable->addFromSource($this->getWriterPartStyle()->allFills($this->spreadSheet));
310  $this->fontHashTable->addFromSource($this->getWriterPartStyle()->allFonts($this->spreadSheet));
311  $this->bordersHashTable->addFromSource($this->getWriterPartStyle()->allBorders($this->spreadSheet));
312  $this->numFmtHashTable->addFromSource($this->getWriterPartStyle()->allNumberFormats($this->spreadSheet));
313 
314  // Create drawing dictionary
315  $this->drawingHashTable->addFromSource($this->getWriterPartDrawing()->allDrawings($this->spreadSheet));
316 
317  $zipContent = [];
318  // Add [Content_Types].xml to ZIP file
319  $zipContent['[Content_Types].xml'] = $this->getWriterPartContentTypes()->writeContentTypes($this->spreadSheet, $this->includeCharts);
320 
321  //if hasMacros, add the vbaProject.bin file, Certificate file(if exists)
322  if ($this->spreadSheet->hasMacros()) {
323  $macrosCode = $this->spreadSheet->getMacrosCode();
324  if ($macrosCode !== null) {
325  // we have the code ?
326  $zipContent['xl/vbaProject.bin'] = $macrosCode; //allways in 'xl', allways named vbaProject.bin
327  if ($this->spreadSheet->hasMacrosCertificate()) {
328  //signed macros ?
329  // Yes : add the certificate file and the related rels file
330  $zipContent['xl/vbaProjectSignature.bin'] = $this->spreadSheet->getMacrosCertificate();
331  $zipContent['xl/_rels/vbaProject.bin.rels'] = $this->getWriterPartRelsVBA()->writeVBARelationships($this->spreadSheet);
332  }
333  }
334  }
335  //a custom UI in this workbook ? add it ("base" xml and additional objects (pictures) and rels)
336  if ($this->spreadSheet->hasRibbon()) {
337  $tmpRibbonTarget = $this->spreadSheet->getRibbonXMLData('target');
338  $zipContent[$tmpRibbonTarget] = $this->spreadSheet->getRibbonXMLData('data');
339  if ($this->spreadSheet->hasRibbonBinObjects()) {
340  $tmpRootPath = dirname($tmpRibbonTarget) . '/';
341  $ribbonBinObjects = $this->spreadSheet->getRibbonBinObjects('data'); //the files to write
342  foreach ($ribbonBinObjects as $aPath => $aContent) {
343  $zipContent[$tmpRootPath . $aPath] = $aContent;
344  }
345  //the rels for files
346  $zipContent[$tmpRootPath . '_rels/' . basename($tmpRibbonTarget) . '.rels'] = $this->getWriterPartRelsRibbon()->writeRibbonRelationships($this->spreadSheet);
347  }
348  }
349 
350  // Add relationships to ZIP file
351  $zipContent['_rels/.rels'] = $this->getWriterPartRels()->writeRelationships($this->spreadSheet);
352  $zipContent['xl/_rels/workbook.xml.rels'] = $this->getWriterPartRels()->writeWorkbookRelationships($this->spreadSheet);
353 
354  // Add document properties to ZIP file
355  $zipContent['docProps/app.xml'] = $this->getWriterPartDocProps()->writeDocPropsApp($this->spreadSheet);
356  $zipContent['docProps/core.xml'] = $this->getWriterPartDocProps()->writeDocPropsCore($this->spreadSheet);
357  $customPropertiesPart = $this->getWriterPartDocProps()->writeDocPropsCustom($this->spreadSheet);
358  if ($customPropertiesPart !== null) {
359  $zipContent['docProps/custom.xml'] = $customPropertiesPart;
360  }
361 
362  // Add theme to ZIP file
363  $zipContent['xl/theme/theme1.xml'] = $this->getWriterPartTheme()->writeTheme($this->spreadSheet);
364 
365  // Add string table to ZIP file
366  $zipContent['xl/sharedStrings.xml'] = $this->getWriterPartStringTable()->writeStringTable($this->stringTable);
367 
368  // Add styles to ZIP file
369  $zipContent['xl/styles.xml'] = $this->getWriterPartStyle()->writeStyles($this->spreadSheet);
370 
371  // Add workbook to ZIP file
372  $zipContent['xl/workbook.xml'] = $this->getWriterPartWorkbook()->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas);
373 
374  $chartCount = 0;
375  // Add worksheets
376  for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
377  $zipContent['xl/worksheets/sheet' . ($i + 1) . '.xml'] = $this->getWriterPartWorksheet()->writeWorksheet($this->spreadSheet->getSheet($i), $this->stringTable, $this->includeCharts);
378  if ($this->includeCharts) {
379  $charts = $this->spreadSheet->getSheet($i)->getChartCollection();
380  if (count($charts) > 0) {
381  foreach ($charts as $chart) {
382  $zipContent['xl/charts/chart' . ($chartCount + 1) . '.xml'] = $this->getWriterPartChart()->writeChart($chart, $this->preCalculateFormulas);
383  ++$chartCount;
384  }
385  }
386  }
387  }
388 
389  $chartRef1 = 0;
390  // Add worksheet relationships (drawings, ...)
391  for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
392  // Add relationships
393  $zipContent['xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels'] = $this->getWriterPartRels()->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts);
394 
395  // Add unparsedLoadedData
396  $sheetCodeName = $this->spreadSheet->getSheet($i)->getCodeName();
397  $unparsedLoadedData = $this->spreadSheet->getUnparsedLoadedData();
398  if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'])) {
399  foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'] as $ctrlProp) {
400  $zipContent[$ctrlProp['filePath']] = $ctrlProp['content'];
401  }
402  }
403  if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'])) {
404  foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'] as $ctrlProp) {
405  $zipContent[$ctrlProp['filePath']] = $ctrlProp['content'];
406  }
407  }
408 
409  $drawings = $this->spreadSheet->getSheet($i)->getDrawingCollection();
410  $drawingCount = count($drawings);
411  if ($this->includeCharts) {
412  $chartCount = $this->spreadSheet->getSheet($i)->getChartCount();
413  }
414 
415  // Add drawing and image relationship parts
416  if (($drawingCount > 0) || ($chartCount > 0)) {
417  // Drawing relationships
418  $zipContent['xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels'] = $this->getWriterPartRels()->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts);
419 
420  // Drawings
421  $zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'] = $this->getWriterPartDrawing()->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts);
422  } elseif (isset($unparsedLoadedData['sheets'][$sheetCodeName]['drawingAlternateContents'])) {
423  // Drawings
424  $zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'] = $this->getWriterPartDrawing()->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts);
425  }
426 
427  // Add unparsed drawings
428  if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'])) {
429  foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'] as $relId => $drawingXml) {
430  $drawingFile = array_search($relId, $unparsedLoadedData['sheets'][$sheetCodeName]['drawingOriginalIds']);
431  if ($drawingFile !== false) {
432  $drawingFile = ltrim($drawingFile, '.');
433  $zipContent['xl' . $drawingFile] = $drawingXml;
434  }
435  }
436  }
437 
438  // Add comment relationship parts
439  if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
440  // VML Comments
441  $zipContent['xl/drawings/vmlDrawing' . ($i + 1) . '.vml'] = $this->getWriterPartComments()->writeVMLComments($this->spreadSheet->getSheet($i));
442 
443  // Comments
444  $zipContent['xl/comments' . ($i + 1) . '.xml'] = $this->getWriterPartComments()->writeComments($this->spreadSheet->getSheet($i));
445  }
446 
447  // Add unparsed relationship parts
448  if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'])) {
449  foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'] as $vmlDrawing) {
450  $zipContent[$vmlDrawing['filePath']] = $vmlDrawing['content'];
451  }
452  }
453 
454  // Add header/footer relationship parts
455  if (count($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) {
456  // VML Drawings
457  $zipContent['xl/drawings/vmlDrawingHF' . ($i + 1) . '.vml'] = $this->getWriterPartDrawing()->writeVMLHeaderFooterImages($this->spreadSheet->getSheet($i));
458 
459  // VML Drawing relationships
460  $zipContent['xl/drawings/_rels/vmlDrawingHF' . ($i + 1) . '.vml.rels'] = $this->getWriterPartRels()->writeHeaderFooterDrawingRelationships($this->spreadSheet->getSheet($i));
461 
462  // Media
463  foreach ($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
464  $zipContent['xl/media/' . $image->getIndexedFilename()] = file_get_contents($image->getPath());
465  }
466  }
467  }
468 
469  // Add media
470  for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) {
471  if ($this->getDrawingHashTable()->getByIndex($i) instanceof WorksheetDrawing) {
472  $imageContents = null;
473  $imagePath = $this->getDrawingHashTable()->getByIndex($i)->getPath();
474  if (strpos($imagePath, 'zip://') !== false) {
475  $imagePath = substr($imagePath, 6);
476  $imagePathSplitted = explode('#', $imagePath);
477 
478  $imageZip = new ZipArchive();
479  $imageZip->open($imagePathSplitted[0]);
480  $imageContents = $imageZip->getFromName($imagePathSplitted[1]);
481  $imageZip->close();
482  unset($imageZip);
483  } else {
484  $imageContents = file_get_contents($imagePath);
485  }
486 
487  $zipContent['xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename())] = $imageContents;
488  } elseif ($this->getDrawingHashTable()->getByIndex($i) instanceof MemoryDrawing) {
489  ob_start();
490  call_user_func(
491  $this->getDrawingHashTable()->getByIndex($i)->getRenderingFunction(),
492  $this->getDrawingHashTable()->getByIndex($i)->getImageResource()
493  );
494  $imageContents = ob_get_contents();
495  ob_end_clean();
496 
497  $zipContent['xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename())] = $imageContents;
498  }
499  }
500 
501  Functions::setReturnDateType($saveDateReturnType);
502  Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
503 
504  $this->openFileHandle($pFilename);
505 
506  $options = new Archive();
507  $options->setEnableZip64(false);
508  $options->setOutputStream($this->fileHandle);
509 
510  $this->zip = new ZipStream(null, $options);
511 
512  $this->addZipFiles($zipContent);
513 
514  // Close file
515  try {
516  $this->zip->finish();
517  } catch (OverflowException $e) {
518  throw new WriterException('Could not close resource.');
519  }
520 
521  $this->maybeCloseFileHandle();
522  }
523 
529  public function getSpreadsheet()
530  {
531  return $this->spreadSheet;
532  }
533 
541  public function setSpreadsheet(Spreadsheet $spreadsheet)
542  {
543  $this->spreadSheet = $spreadsheet;
544 
545  return $this;
546  }
547 
553  public function getStringTable()
554  {
555  return $this->stringTable;
556  }
557 
563  public function getStyleHashTable()
564  {
565  return $this->styleHashTable;
566  }
567 
574  {
576  }
577 
583  public function getFillHashTable()
584  {
585  return $this->fillHashTable;
586  }
587 
593  public function getFontHashTable()
594  {
595  return $this->fontHashTable;
596  }
597 
603  public function getBordersHashTable()
604  {
606  }
607 
613  public function getNumFmtHashTable()
614  {
615  return $this->numFmtHashTable;
616  }
617 
623  public function getDrawingHashTable()
624  {
626  }
627 
633  public function getOffice2003Compatibility()
634  {
636  }
637 
645  public function setOffice2003Compatibility($pValue)
646  {
647  $this->office2003compatibility = $pValue;
648 
649  return $this;
650  }
651 
652  private $pathNames = [];
653 
654  private function addZipFile(string $path, string $content): void
655  {
656  if (!in_array($path, $this->pathNames)) {
657  $this->pathNames[] = $path;
658  $this->zip->addFile($path, $content);
659  }
660  }
661 
662  private function addZipFiles(array $zipContent): void
663  {
664  foreach ($zipContent as $path => $content) {
665  $this->addZipFile($path, $content);
666  }
667  }
668 }
getStyleHashTable()
Get Style HashTable.
Definition: Xlsx.php:563
Class Version .
Definition: Bigint.php:4
$path
Definition: aliased.php:25
static getReturnDateType()
Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object).
Definition: Functions.php:133
setOffice2003Compatibility($pValue)
Set Office2003 compatibility.
Definition: Xlsx.php:645
getNumFmtHashTable()
Get NumberFormat HashTable.
Definition: Xlsx.php:613
getOffice2003Compatibility()
Get Office2003 compatibility.
Definition: Xlsx.php:633
getFillHashTable()
Get Fill HashTable.
Definition: Xlsx.php:583
static setReturnDateType($returnDateType)
Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric o...
Definition: Functions.php:109
getSpreadsheet()
Get Spreadsheet object.
Definition: Xlsx.php:529
addZipFiles(array $zipContent)
Definition: Xlsx.php:662
setSpreadsheet(Spreadsheet $spreadsheet)
Set Spreadsheet object.
Definition: Xlsx.php:541
addZipFile(string $path, string $content)
Definition: Xlsx.php:654
getStringTable()
Get string table.
Definition: Xlsx.php:553
getFontHashTable()
Get HashTable.
Definition: Xlsx.php:593
static getInstance(?Spreadsheet $spreadsheet=null)
Get an instance of this class.
getBordersHashTable()
Get Borders HashTable.
Definition: Xlsx.php:603
save($pFilename)
Save PhpSpreadsheet to file.
Definition: Xlsx.php:289
$i
Definition: disco.tpl.php:19
getStylesConditionalHashTable()
Get Conditional HashTable.
Definition: Xlsx.php:573
openFileHandle($filename)
Open file handle.
Definition: BaseWriter.php:102
getDrawingHashTable()
Get HashTable.
Definition: Xlsx.php:623
maybeCloseFileHandle()
Close file handle only if we opened it ourselves.
Definition: BaseWriter.php:123
__construct(Spreadsheet $spreadsheet)
Create a new Xlsx Writer.
Definition: Xlsx.php:183