Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Writer;
   4  
   5  use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
   6  use PhpOffice\PhpSpreadsheet\Calculation\Functions;
   7  use PhpOffice\PhpSpreadsheet\HashTable;
   8  use PhpOffice\PhpSpreadsheet\Spreadsheet;
   9  use PhpOffice\PhpSpreadsheet\Style\Borders;
  10  use PhpOffice\PhpSpreadsheet\Style\Conditional;
  11  use PhpOffice\PhpSpreadsheet\Style\Fill;
  12  use PhpOffice\PhpSpreadsheet\Style\Font;
  13  use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
  14  use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
  15  use PhpOffice\PhpSpreadsheet\Worksheet\Drawing as WorksheetDrawing;
  16  use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
  17  use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
  18  use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Chart;
  19  use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Comments;
  20  use PhpOffice\PhpSpreadsheet\Writer\Xlsx\ContentTypes;
  21  use PhpOffice\PhpSpreadsheet\Writer\Xlsx\DocProps;
  22  use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Drawing;
  23  use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Rels;
  24  use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsRibbon;
  25  use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsVBA;
  26  use PhpOffice\PhpSpreadsheet\Writer\Xlsx\StringTable;
  27  use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Style;
  28  use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Theme;
  29  use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Workbook;
  30  use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet;
  31  use ZipArchive;
  32  use ZipStream\Exception\OverflowException;
  33  use ZipStream\Option\Archive;
  34  use ZipStream\ZipStream;
  35  
  36  class Xlsx extends BaseWriter
  37  {
  38      /**
  39       * Office2003 compatibility.
  40       *
  41       * @var bool
  42       */
  43      private $office2003compatibility = false;
  44  
  45      /**
  46       * Private Spreadsheet.
  47       *
  48       * @var Spreadsheet
  49       */
  50      private $spreadSheet;
  51  
  52      /**
  53       * Private string table.
  54       *
  55       * @var string[]
  56       */
  57      private $stringTable = [];
  58  
  59      /**
  60       * Private unique Conditional HashTable.
  61       *
  62       * @var HashTable<Conditional>
  63       */
  64      private $stylesConditionalHashTable;
  65  
  66      /**
  67       * Private unique Style HashTable.
  68       *
  69       * @var HashTable<\PhpOffice\PhpSpreadsheet\Style\Style>
  70       */
  71      private $styleHashTable;
  72  
  73      /**
  74       * Private unique Fill HashTable.
  75       *
  76       * @var HashTable<Fill>
  77       */
  78      private $fillHashTable;
  79  
  80      /**
  81       * Private unique \PhpOffice\PhpSpreadsheet\Style\Font HashTable.
  82       *
  83       * @var HashTable<Font>
  84       */
  85      private $fontHashTable;
  86  
  87      /**
  88       * Private unique Borders HashTable.
  89       *
  90       * @var HashTable<Borders>
  91       */
  92      private $bordersHashTable;
  93  
  94      /**
  95       * Private unique NumberFormat HashTable.
  96       *
  97       * @var HashTable<NumberFormat>
  98       */
  99      private $numFmtHashTable;
 100  
 101      /**
 102       * Private unique \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\BaseDrawing HashTable.
 103       *
 104       * @var HashTable<BaseDrawing>
 105       */
 106      private $drawingHashTable;
 107  
 108      /**
 109       * Private handle for zip stream.
 110       *
 111       * @var ZipStream
 112       */
 113      private $zip;
 114  
 115      /**
 116       * @var Chart
 117       */
 118      private $writerPartChart;
 119  
 120      /**
 121       * @var Comments
 122       */
 123      private $writerPartComments;
 124  
 125      /**
 126       * @var ContentTypes
 127       */
 128      private $writerPartContentTypes;
 129  
 130      /**
 131       * @var DocProps
 132       */
 133      private $writerPartDocProps;
 134  
 135      /**
 136       * @var Drawing
 137       */
 138      private $writerPartDrawing;
 139  
 140      /**
 141       * @var Rels
 142       */
 143      private $writerPartRels;
 144  
 145      /**
 146       * @var RelsRibbon
 147       */
 148      private $writerPartRelsRibbon;
 149  
 150      /**
 151       * @var RelsVBA
 152       */
 153      private $writerPartRelsVBA;
 154  
 155      /**
 156       * @var StringTable
 157       */
 158      private $writerPartStringTable;
 159  
 160      /**
 161       * @var Style
 162       */
 163      private $writerPartStyle;
 164  
 165      /**
 166       * @var Theme
 167       */
 168      private $writerPartTheme;
 169  
 170      /**
 171       * @var Workbook
 172       */
 173      private $writerPartWorkbook;
 174  
 175      /**
 176       * @var Worksheet
 177       */
 178      private $writerPartWorksheet;
 179  
 180      /**
 181       * Create a new Xlsx Writer.
 182       */
 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      {
 226          return $this->writerPartComments;
 227      }
 228  
 229      public function getWriterPartContentTypes(): ContentTypes
 230      {
 231          return $this->writerPartContentTypes;
 232      }
 233  
 234      public function getWriterPartDocProps(): DocProps
 235      {
 236          return $this->writerPartDocProps;
 237      }
 238  
 239      public function getWriterPartDrawing(): Drawing
 240      {
 241          return $this->writerPartDrawing;
 242      }
 243  
 244      public function getWriterPartRels(): Rels
 245      {
 246          return $this->writerPartRels;
 247      }
 248  
 249      public function getWriterPartRelsRibbon(): RelsRibbon
 250      {
 251          return $this->writerPartRelsRibbon;
 252      }
 253  
 254      public function getWriterPartRelsVBA(): RelsVBA
 255      {
 256          return $this->writerPartRelsVBA;
 257      }
 258  
 259      public function getWriterPartStringTable(): StringTable
 260      {
 261          return $this->writerPartStringTable;
 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      {
 276          return $this->writerPartWorkbook;
 277      }
 278  
 279      public function getWriterPartWorksheet(): Worksheet
 280      {
 281          return $this->writerPartWorksheet;
 282      }
 283  
 284      /**
 285       * Save PhpSpreadsheet to file.
 286       *
 287       * @param resource|string $filename
 288       */
 289      public function save($filename, int $flags = 0): void
 290      {
 291          $this->processFlags($flags);
 292  
 293          // garbage collect
 294          $this->pathNames = [];
 295          $this->spreadSheet->garbageCollect();
 296  
 297          $saveDebugLog = Calculation::getInstance($this->spreadSheet)->getDebugLog()->getWriteDebugLog();
 298          Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog(false);
 299          $saveDateReturnType = Functions::getReturnDateType();
 300          Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
 301  
 302          // Create string lookup table
 303          $this->stringTable = [];
 304          for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
 305              $this->stringTable = $this->getWriterPartStringTable()->createStringTable($this->spreadSheet->getSheet($i), $this->stringTable);
 306          }
 307  
 308          // Create styles dictionaries
 309          $this->styleHashTable->addFromSource($this->getWriterPartStyle()->allStyles($this->spreadSheet));
 310          $this->stylesConditionalHashTable->addFromSource($this->getWriterPartStyle()->allConditionalStyles($this->spreadSheet));
 311          $this->fillHashTable->addFromSource($this->getWriterPartStyle()->allFills($this->spreadSheet));
 312          $this->fontHashTable->addFromSource($this->getWriterPartStyle()->allFonts($this->spreadSheet));
 313          $this->bordersHashTable->addFromSource($this->getWriterPartStyle()->allBorders($this->spreadSheet));
 314          $this->numFmtHashTable->addFromSource($this->getWriterPartStyle()->allNumberFormats($this->spreadSheet));
 315  
 316          // Create drawing dictionary
 317          $this->drawingHashTable->addFromSource($this->getWriterPartDrawing()->allDrawings($this->spreadSheet));
 318  
 319          $zipContent = [];
 320          // Add [Content_Types].xml to ZIP file
 321          $zipContent['[Content_Types].xml'] = $this->getWriterPartContentTypes()->writeContentTypes($this->spreadSheet, $this->includeCharts);
 322  
 323          //if hasMacros, add the vbaProject.bin file, Certificate file(if exists)
 324          if ($this->spreadSheet->hasMacros()) {
 325              $macrosCode = $this->spreadSheet->getMacrosCode();
 326              if ($macrosCode !== null) {
 327                  // we have the code ?
 328                  $zipContent['xl/vbaProject.bin'] = $macrosCode; //allways in 'xl', allways named vbaProject.bin
 329                  if ($this->spreadSheet->hasMacrosCertificate()) {
 330                      //signed macros ?
 331                      // Yes : add the certificate file and the related rels file
 332                      $zipContent['xl/vbaProjectSignature.bin'] = $this->spreadSheet->getMacrosCertificate();
 333                      $zipContent['xl/_rels/vbaProject.bin.rels'] = $this->getWriterPartRelsVBA()->writeVBARelationships($this->spreadSheet);
 334                  }
 335              }
 336          }
 337          //a custom UI in this workbook ? add it ("base" xml and additional objects (pictures) and rels)
 338          if ($this->spreadSheet->hasRibbon()) {
 339              $tmpRibbonTarget = $this->spreadSheet->getRibbonXMLData('target');
 340              $zipContent[$tmpRibbonTarget] = $this->spreadSheet->getRibbonXMLData('data');
 341              if ($this->spreadSheet->hasRibbonBinObjects()) {
 342                  $tmpRootPath = dirname($tmpRibbonTarget) . '/';
 343                  $ribbonBinObjects = $this->spreadSheet->getRibbonBinObjects('data'); //the files to write
 344                  foreach ($ribbonBinObjects as $aPath => $aContent) {
 345                      $zipContent[$tmpRootPath . $aPath] = $aContent;
 346                  }
 347                  //the rels for files
 348                  $zipContent[$tmpRootPath . '_rels/' . basename($tmpRibbonTarget) . '.rels'] = $this->getWriterPartRelsRibbon()->writeRibbonRelationships($this->spreadSheet);
 349              }
 350          }
 351  
 352          // Add relationships to ZIP file
 353          $zipContent['_rels/.rels'] = $this->getWriterPartRels()->writeRelationships($this->spreadSheet);
 354          $zipContent['xl/_rels/workbook.xml.rels'] = $this->getWriterPartRels()->writeWorkbookRelationships($this->spreadSheet);
 355  
 356          // Add document properties to ZIP file
 357          $zipContent['docProps/app.xml'] = $this->getWriterPartDocProps()->writeDocPropsApp($this->spreadSheet);
 358          $zipContent['docProps/core.xml'] = $this->getWriterPartDocProps()->writeDocPropsCore($this->spreadSheet);
 359          $customPropertiesPart = $this->getWriterPartDocProps()->writeDocPropsCustom($this->spreadSheet);
 360          if ($customPropertiesPart !== null) {
 361              $zipContent['docProps/custom.xml'] = $customPropertiesPart;
 362          }
 363  
 364          // Add theme to ZIP file
 365          $zipContent['xl/theme/theme1.xml'] = $this->getWriterPartTheme()->writeTheme($this->spreadSheet);
 366  
 367          // Add string table to ZIP file
 368          $zipContent['xl/sharedStrings.xml'] = $this->getWriterPartStringTable()->writeStringTable($this->stringTable);
 369  
 370          // Add styles to ZIP file
 371          $zipContent['xl/styles.xml'] = $this->getWriterPartStyle()->writeStyles($this->spreadSheet);
 372  
 373          // Add workbook to ZIP file
 374          $zipContent['xl/workbook.xml'] = $this->getWriterPartWorkbook()->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas);
 375  
 376          $chartCount = 0;
 377          // Add worksheets
 378          for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
 379              $zipContent['xl/worksheets/sheet' . ($i + 1) . '.xml'] = $this->getWriterPartWorksheet()->writeWorksheet($this->spreadSheet->getSheet($i), $this->stringTable, $this->includeCharts);
 380              if ($this->includeCharts) {
 381                  $charts = $this->spreadSheet->getSheet($i)->getChartCollection();
 382                  if (count($charts) > 0) {
 383                      foreach ($charts as $chart) {
 384                          $zipContent['xl/charts/chart' . ($chartCount + 1) . '.xml'] = $this->getWriterPartChart()->writeChart($chart, $this->preCalculateFormulas);
 385                          ++$chartCount;
 386                      }
 387                  }
 388              }
 389          }
 390  
 391          $chartRef1 = 0;
 392          // Add worksheet relationships (drawings, ...)
 393          for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
 394              // Add relationships
 395              $zipContent['xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels'] = $this->getWriterPartRels()->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts);
 396  
 397              // Add unparsedLoadedData
 398              $sheetCodeName = $this->spreadSheet->getSheet($i)->getCodeName();
 399              $unparsedLoadedData = $this->spreadSheet->getUnparsedLoadedData();
 400              if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'])) {
 401                  foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'] as $ctrlProp) {
 402                      $zipContent[$ctrlProp['filePath']] = $ctrlProp['content'];
 403                  }
 404              }
 405              if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'])) {
 406                  foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'] as $ctrlProp) {
 407                      $zipContent[$ctrlProp['filePath']] = $ctrlProp['content'];
 408                  }
 409              }
 410  
 411              $drawings = $this->spreadSheet->getSheet($i)->getDrawingCollection();
 412              $drawingCount = count($drawings);
 413              if ($this->includeCharts) {
 414                  $chartCount = $this->spreadSheet->getSheet($i)->getChartCount();
 415              }
 416  
 417              // Add drawing and image relationship parts
 418              if (($drawingCount > 0) || ($chartCount > 0)) {
 419                  // Drawing relationships
 420                  $zipContent['xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels'] = $this->getWriterPartRels()->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts);
 421  
 422                  // Drawings
 423                  $zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'] = $this->getWriterPartDrawing()->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts);
 424              } elseif (isset($unparsedLoadedData['sheets'][$sheetCodeName]['drawingAlternateContents'])) {
 425                  // Drawings
 426                  $zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'] = $this->getWriterPartDrawing()->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts);
 427              }
 428  
 429              // Add unparsed drawings
 430              if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'])) {
 431                  foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'] as $relId => $drawingXml) {
 432                      $drawingFile = array_search($relId, $unparsedLoadedData['sheets'][$sheetCodeName]['drawingOriginalIds']);
 433                      if ($drawingFile !== false) {
 434                          //$drawingFile = ltrim($drawingFile, '.');
 435                          //$zipContent['xl' . $drawingFile] = $drawingXml;
 436                          $zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'] = $drawingXml;
 437                      }
 438                  }
 439              }
 440  
 441              // Add comment relationship parts
 442              if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
 443                  // VML Comments relationships
 444                  $zipContent['xl/drawings/_rels/vmlDrawing' . ($i + 1) . '.vml.rels'] = $this->getWriterPartRels()->writeVMLDrawingRelationships($this->spreadSheet->getSheet($i));
 445  
 446                  // VML Comments
 447                  $zipContent['xl/drawings/vmlDrawing' . ($i + 1) . '.vml'] = $this->getWriterPartComments()->writeVMLComments($this->spreadSheet->getSheet($i));
 448  
 449                  // Comments
 450                  $zipContent['xl/comments' . ($i + 1) . '.xml'] = $this->getWriterPartComments()->writeComments($this->spreadSheet->getSheet($i));
 451  
 452                  // Media
 453                  foreach ($this->spreadSheet->getSheet($i)->getComments() as $comment) {
 454                      if ($comment->hasBackgroundImage()) {
 455                          $image = $comment->getBackgroundImage();
 456                          $zipContent['xl/media/' . $image->getMediaFilename()] = $this->processDrawing($image);
 457                      }
 458                  }
 459              }
 460  
 461              // Add unparsed relationship parts
 462              if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'])) {
 463                  foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'] as $vmlDrawing) {
 464                      $zipContent[$vmlDrawing['filePath']] = $vmlDrawing['content'];
 465                  }
 466              }
 467  
 468              // Add header/footer relationship parts
 469              if (count($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) {
 470                  // VML Drawings
 471                  $zipContent['xl/drawings/vmlDrawingHF' . ($i + 1) . '.vml'] = $this->getWriterPartDrawing()->writeVMLHeaderFooterImages($this->spreadSheet->getSheet($i));
 472  
 473                  // VML Drawing relationships
 474                  $zipContent['xl/drawings/_rels/vmlDrawingHF' . ($i + 1) . '.vml.rels'] = $this->getWriterPartRels()->writeHeaderFooterDrawingRelationships($this->spreadSheet->getSheet($i));
 475  
 476                  // Media
 477                  foreach ($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
 478                      $zipContent['xl/media/' . $image->getIndexedFilename()] = file_get_contents($image->getPath());
 479                  }
 480              }
 481          }
 482  
 483          // Add media
 484          for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) {
 485              if ($this->getDrawingHashTable()->getByIndex($i) instanceof WorksheetDrawing) {
 486                  $imageContents = null;
 487                  $imagePath = $this->getDrawingHashTable()->getByIndex($i)->getPath();
 488                  if (strpos($imagePath, 'zip://') !== false) {
 489                      $imagePath = substr($imagePath, 6);
 490                      $imagePathSplitted = explode('#', $imagePath);
 491  
 492                      $imageZip = new ZipArchive();
 493                      $imageZip->open($imagePathSplitted[0]);
 494                      $imageContents = $imageZip->getFromName($imagePathSplitted[1]);
 495                      $imageZip->close();
 496                      unset($imageZip);
 497                  } else {
 498                      $imageContents = file_get_contents($imagePath);
 499                  }
 500  
 501                  $zipContent['xl/media/' . $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()] = $imageContents;
 502              } elseif ($this->getDrawingHashTable()->getByIndex($i) instanceof MemoryDrawing) {
 503                  ob_start();
 504                  call_user_func(
 505                      $this->getDrawingHashTable()->getByIndex($i)->getRenderingFunction(),
 506                      $this->getDrawingHashTable()->getByIndex($i)->getImageResource()
 507                  );
 508                  $imageContents = ob_get_contents();
 509                  ob_end_clean();
 510  
 511                  $zipContent['xl/media/' . $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()] = $imageContents;
 512              }
 513          }
 514  
 515          Functions::setReturnDateType($saveDateReturnType);
 516          Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
 517  
 518          $this->openFileHandle($filename);
 519  
 520          $options = new Archive();
 521          $options->setEnableZip64(false);
 522          $options->setOutputStream($this->fileHandle);
 523  
 524          $this->zip = new ZipStream(null, $options);
 525  
 526          $this->addZipFiles($zipContent);
 527  
 528          // Close file
 529          try {
 530              $this->zip->finish();
 531          } catch (OverflowException $e) {
 532              throw new WriterException('Could not close resource.');
 533          }
 534  
 535          $this->maybeCloseFileHandle();
 536      }
 537  
 538      /**
 539       * Get Spreadsheet object.
 540       *
 541       * @return Spreadsheet
 542       */
 543      public function getSpreadsheet()
 544      {
 545          return $this->spreadSheet;
 546      }
 547  
 548      /**
 549       * Set Spreadsheet object.
 550       *
 551       * @param Spreadsheet $spreadsheet PhpSpreadsheet object
 552       *
 553       * @return $this
 554       */
 555      public function setSpreadsheet(Spreadsheet $spreadsheet)
 556      {
 557          $this->spreadSheet = $spreadsheet;
 558  
 559          return $this;
 560      }
 561  
 562      /**
 563       * Get string table.
 564       *
 565       * @return string[]
 566       */
 567      public function getStringTable()
 568      {
 569          return $this->stringTable;
 570      }
 571  
 572      /**
 573       * Get Style HashTable.
 574       *
 575       * @return HashTable<\PhpOffice\PhpSpreadsheet\Style\Style>
 576       */
 577      public function getStyleHashTable()
 578      {
 579          return $this->styleHashTable;
 580      }
 581  
 582      /**
 583       * Get Conditional HashTable.
 584       *
 585       * @return HashTable<Conditional>
 586       */
 587      public function getStylesConditionalHashTable()
 588      {
 589          return $this->stylesConditionalHashTable;
 590      }
 591  
 592      /**
 593       * Get Fill HashTable.
 594       *
 595       * @return HashTable<Fill>
 596       */
 597      public function getFillHashTable()
 598      {
 599          return $this->fillHashTable;
 600      }
 601  
 602      /**
 603       * Get \PhpOffice\PhpSpreadsheet\Style\Font HashTable.
 604       *
 605       * @return HashTable<Font>
 606       */
 607      public function getFontHashTable()
 608      {
 609          return $this->fontHashTable;
 610      }
 611  
 612      /**
 613       * Get Borders HashTable.
 614       *
 615       * @return HashTable<Borders>
 616       */
 617      public function getBordersHashTable()
 618      {
 619          return $this->bordersHashTable;
 620      }
 621  
 622      /**
 623       * Get NumberFormat HashTable.
 624       *
 625       * @return HashTable<NumberFormat>
 626       */
 627      public function getNumFmtHashTable()
 628      {
 629          return $this->numFmtHashTable;
 630      }
 631  
 632      /**
 633       * Get \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\BaseDrawing HashTable.
 634       *
 635       * @return HashTable<BaseDrawing>
 636       */
 637      public function getDrawingHashTable()
 638      {
 639          return $this->drawingHashTable;
 640      }
 641  
 642      /**
 643       * Get Office2003 compatibility.
 644       *
 645       * @return bool
 646       */
 647      public function getOffice2003Compatibility()
 648      {
 649          return $this->office2003compatibility;
 650      }
 651  
 652      /**
 653       * Set Office2003 compatibility.
 654       *
 655       * @param bool $office2003compatibility Office2003 compatibility?
 656       *
 657       * @return $this
 658       */
 659      public function setOffice2003Compatibility($office2003compatibility)
 660      {
 661          $this->office2003compatibility = $office2003compatibility;
 662  
 663          return $this;
 664      }
 665  
 666      private $pathNames = [];
 667  
 668      private function addZipFile(string $path, string $content): void
 669      {
 670          if (!in_array($path, $this->pathNames)) {
 671              $this->pathNames[] = $path;
 672              $this->zip->addFile($path, $content);
 673          }
 674      }
 675  
 676      private function addZipFiles(array $zipContent): void
 677      {
 678          foreach ($zipContent as $path => $content) {
 679              $this->addZipFile($path, $content);
 680          }
 681      }
 682  
 683      /**
 684       * @return mixed
 685       */
 686      private function processDrawing(WorksheetDrawing $drawing)
 687      {
 688          $data = null;
 689          $filename = $drawing->getPath();
 690          $imageData = getimagesize($filename);
 691  
 692          if (is_array($imageData)) {
 693              switch ($imageData[2]) {
 694                  case 1: // GIF, not supported by BIFF8, we convert to PNG
 695                      $image = imagecreatefromgif($filename);
 696                      if ($image !== false) {
 697                          ob_start();
 698                          imagepng($image);
 699                          $data = ob_get_contents();
 700                          ob_end_clean();
 701                      }
 702  
 703                      break;
 704  
 705                  case 2: // JPEG
 706                      $data = file_get_contents($filename);
 707  
 708                      break;
 709  
 710                  case 3: // PNG
 711                      $data = file_get_contents($filename);
 712  
 713                      break;
 714  
 715                  case 6: // Windows DIB (BMP), we convert to PNG
 716                      $image = imagecreatefrombmp($filename);
 717                      if ($image !== false) {
 718                          ob_start();
 719                          imagepng($image);
 720                          $data = ob_get_contents();
 721                          ob_end_clean();
 722                      }
 723  
 724                      break;
 725              }
 726          }
 727  
 728          return $data;
 729      }
 730  }