PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.9.4
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.9.4
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
visualizer / vendor / phpoffice / phpspreadsheet / src / PhpSpreadsheet / Writer / Xlsx.php

Xlsx.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.9.4, at vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php

559 lines 21.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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\Shared\File;
9 use PhpOffice\PhpSpreadsheet\Spreadsheet;
10 use PhpOffice\PhpSpreadsheet\Worksheet\Drawing as WorksheetDrawing;
11 use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
12 use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
13 use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Chart;
14 use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Comments;
15 use PhpOffice\PhpSpreadsheet\Writer\Xlsx\ContentTypes;
16 use PhpOffice\PhpSpreadsheet\Writer\Xlsx\DocProps;
17 use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Drawing;
18 use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Rels;
19 use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsRibbon;
20 use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsVBA;
21 use PhpOffice\PhpSpreadsheet\Writer\Xlsx\StringTable;
22 use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Style;
23 use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Theme;
24 use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Workbook;
25 use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet;
26 use ZipArchive;
27
28 class Xlsx extends BaseWriter
29 {
30 /**
31 * Office2003 compatibility.
32 *
33 * @var bool
34 */
35 private $office2003compatibility = false;
36
37 /**
38 * Private writer parts.
39 *
40 * @var Xlsx\WriterPart[]
41 */
42 private $writerParts = [];
43
44 /**
45 * Private Spreadsheet.
46 *
47 * @var Spreadsheet
48 */
49 private $spreadSheet;
50
51 /**
52 * Private string table.
53 *
54 * @var string[]
55 */
56 private $stringTable = [];
57
58 /**
59 * Private unique Conditional HashTable.
60 *
61 * @var HashTable
62 */
63 private $stylesConditionalHashTable;
64
65 /**
66 * Private unique Style HashTable.
67 *
68 * @var HashTable
69 */
70 private $styleHashTable;
71
72 /**
73 * Private unique Fill HashTable.
74 *
75 * @var HashTable
76 */
77 private $fillHashTable;
78
79 /**
80 * Private unique \PhpOffice\PhpSpreadsheet\Style\Font HashTable.
81 *
82 * @var HashTable
83 */
84 private $fontHashTable;
85
86 /**
87 * Private unique Borders HashTable.
88 *
89 * @var HashTable
90 */
91 private $bordersHashTable;
92
93 /**
94 * Private unique NumberFormat HashTable.
95 *
96 * @var HashTable
97 */
98 private $numFmtHashTable;
99
100 /**
101 * Private unique \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\BaseDrawing HashTable.
102 *
103 * @var HashTable
104 */
105 private $drawingHashTable;
106
107 /**
108 * Create a new Xlsx Writer.
109 *
110 * @param Spreadsheet $spreadsheet
111 */
112 public function __construct(Spreadsheet $spreadsheet)
113 {
114 // Assign PhpSpreadsheet
115 $this->setSpreadsheet($spreadsheet);
116
117 $writerPartsArray = [
118 'stringtable' => StringTable::class,
119 'contenttypes' => ContentTypes::class,
120 'docprops' => DocProps::class,
121 'rels' => Rels::class,
122 'theme' => Theme::class,
123 'style' => Style::class,
124 'workbook' => Workbook::class,
125 'worksheet' => Worksheet::class,
126 'drawing' => Drawing::class,
127 'comments' => Comments::class,
128 'chart' => Chart::class,
129 'relsvba' => RelsVBA::class,
130 'relsribbonobjects' => RelsRibbon::class,
131 ];
132
133 // Initialise writer parts
134 // and Assign their parent IWriters
135 foreach ($writerPartsArray as $writer => $class) {
136 $this->writerParts[$writer] = new $class($this);
137 }
138
139 $hashTablesArray = ['stylesConditionalHashTable', 'fillHashTable', 'fontHashTable',
140 'bordersHashTable', 'numFmtHashTable', 'drawingHashTable',
141 'styleHashTable',
142 ];
143
144 // Set HashTable variables
145 foreach ($hashTablesArray as $tableName) {
146 $this->$tableName = new HashTable();
147 }
148 }
149
150 /**
151 * Get writer part.
152 *
153 * @param string $pPartName Writer part name
154 *
155 * @return \PhpOffice\PhpSpreadsheet\Writer\Xlsx\WriterPart
156 */
157 public function getWriterPart($pPartName)
158 {
159 if ($pPartName != '' && isset($this->writerParts[strtolower($pPartName)])) {
160 return $this->writerParts[strtolower($pPartName)];
161 }
162
163 return null;
164 }
165
166 /**
167 * Save PhpSpreadsheet to file.
168 *
169 * @param string $pFilename
170 *
171 * @throws WriterException
172 */
173 public function save($pFilename)
174 {
175 if ($this->spreadSheet !== null) {
176 // garbage collect
177 $this->spreadSheet->garbageCollect();
178
179 // If $pFilename is php://output or php://stdout, make it a temporary file...
180 $originalFilename = $pFilename;
181 if (strtolower($pFilename) == 'php://output' || strtolower($pFilename) == 'php://stdout') {
182 $pFilename = @tempnam(File::sysGetTempDir(), 'phpxltmp');
183 if ($pFilename == '') {
184 $pFilename = $originalFilename;
185 }
186 }
187
188 $saveDebugLog = Calculation::getInstance($this->spreadSheet)->getDebugLog()->getWriteDebugLog();
189 Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog(false);
190 $saveDateReturnType = Functions::getReturnDateType();
191 Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
192
193 // Create string lookup table
194 $this->stringTable = [];
195 for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
196 $this->stringTable = $this->getWriterPart('StringTable')->createStringTable($this->spreadSheet->getSheet($i), $this->stringTable);
197 }
198
199 // Create styles dictionaries
200 $this->styleHashTable->addFromSource($this->getWriterPart('Style')->allStyles($this->spreadSheet));
201 $this->stylesConditionalHashTable->addFromSource($this->getWriterPart('Style')->allConditionalStyles($this->spreadSheet));
202 $this->fillHashTable->addFromSource($this->getWriterPart('Style')->allFills($this->spreadSheet));
203 $this->fontHashTable->addFromSource($this->getWriterPart('Style')->allFonts($this->spreadSheet));
204 $this->bordersHashTable->addFromSource($this->getWriterPart('Style')->allBorders($this->spreadSheet));
205 $this->numFmtHashTable->addFromSource($this->getWriterPart('Style')->allNumberFormats($this->spreadSheet));
206
207 // Create drawing dictionary
208 $this->drawingHashTable->addFromSource($this->getWriterPart('Drawing')->allDrawings($this->spreadSheet));
209
210 $zip = new ZipArchive();
211
212 if (file_exists($pFilename)) {
213 unlink($pFilename);
214 }
215 // Try opening the ZIP file
216 if ($zip->open($pFilename, ZipArchive::OVERWRITE) !== true) {
217 if ($zip->open($pFilename, ZipArchive::CREATE) !== true) {
218 throw new WriterException('Could not open ' . $pFilename . ' for writing.');
219 }
220 }
221
222 // Add [Content_Types].xml to ZIP file
223 $zip->addFromString('[Content_Types].xml', $this->getWriterPart('ContentTypes')->writeContentTypes($this->spreadSheet, $this->includeCharts));
224
225 //if hasMacros, add the vbaProject.bin file, Certificate file(if exists)
226 if ($this->spreadSheet->hasMacros()) {
227 $macrosCode = $this->spreadSheet->getMacrosCode();
228 if ($macrosCode !== null) {
229 // we have the code ?
230 $zip->addFromString('xl/vbaProject.bin', $macrosCode); //allways in 'xl', allways named vbaProject.bin
231 if ($this->spreadSheet->hasMacrosCertificate()) {
232 //signed macros ?
233 // Yes : add the certificate file and the related rels file
234 $zip->addFromString('xl/vbaProjectSignature.bin', $this->spreadSheet->getMacrosCertificate());
235 $zip->addFromString('xl/_rels/vbaProject.bin.rels', $this->getWriterPart('RelsVBA')->writeVBARelationships($this->spreadSheet));
236 }
237 }
238 }
239 //a custom UI in this workbook ? add it ("base" xml and additional objects (pictures) and rels)
240 if ($this->spreadSheet->hasRibbon()) {
241 $tmpRibbonTarget = $this->spreadSheet->getRibbonXMLData('target');
242 $zip->addFromString($tmpRibbonTarget, $this->spreadSheet->getRibbonXMLData('data'));
243 if ($this->spreadSheet->hasRibbonBinObjects()) {
244 $tmpRootPath = dirname($tmpRibbonTarget) . '/';
245 $ribbonBinObjects = $this->spreadSheet->getRibbonBinObjects('data'); //the files to write
246 foreach ($ribbonBinObjects as $aPath => $aContent) {
247 $zip->addFromString($tmpRootPath . $aPath, $aContent);
248 }
249 //the rels for files
250 $zip->addFromString($tmpRootPath . '_rels/' . basename($tmpRibbonTarget) . '.rels', $this->getWriterPart('RelsRibbonObjects')->writeRibbonRelationships($this->spreadSheet));
251 }
252 }
253
254 // Add relationships to ZIP file
255 $zip->addFromString('_rels/.rels', $this->getWriterPart('Rels')->writeRelationships($this->spreadSheet));
256 $zip->addFromString('xl/_rels/workbook.xml.rels', $this->getWriterPart('Rels')->writeWorkbookRelationships($this->spreadSheet));
257
258 // Add document properties to ZIP file
259 $zip->addFromString('docProps/app.xml', $this->getWriterPart('DocProps')->writeDocPropsApp($this->spreadSheet));
260 $zip->addFromString('docProps/core.xml', $this->getWriterPart('DocProps')->writeDocPropsCore($this->spreadSheet));
261 $customPropertiesPart = $this->getWriterPart('DocProps')->writeDocPropsCustom($this->spreadSheet);
262 if ($customPropertiesPart !== null) {
263 $zip->addFromString('docProps/custom.xml', $customPropertiesPart);
264 }
265
266 // Add theme to ZIP file
267 $zip->addFromString('xl/theme/theme1.xml', $this->getWriterPart('Theme')->writeTheme($this->spreadSheet));
268
269 // Add string table to ZIP file
270 $zip->addFromString('xl/sharedStrings.xml', $this->getWriterPart('StringTable')->writeStringTable($this->stringTable));
271
272 // Add styles to ZIP file
273 $zip->addFromString('xl/styles.xml', $this->getWriterPart('Style')->writeStyles($this->spreadSheet));
274
275 // Add workbook to ZIP file
276 $zip->addFromString('xl/workbook.xml', $this->getWriterPart('Workbook')->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas));
277
278 $chartCount = 0;
279 // Add worksheets
280 for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
281 $zip->addFromString('xl/worksheets/sheet' . ($i + 1) . '.xml', $this->getWriterPart('Worksheet')->writeWorksheet($this->spreadSheet->getSheet($i), $this->stringTable, $this->includeCharts));
282 if ($this->includeCharts) {
283 $charts = $this->spreadSheet->getSheet($i)->getChartCollection();
284 if (count($charts) > 0) {
285 foreach ($charts as $chart) {
286 $zip->addFromString('xl/charts/chart' . ($chartCount + 1) . '.xml', $this->getWriterPart('Chart')->writeChart($chart, $this->preCalculateFormulas));
287 ++$chartCount;
288 }
289 }
290 }
291 }
292
293 $chartRef1 = 0;
294 // Add worksheet relationships (drawings, ...)
295 for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
296 // Add relationships
297 $zip->addFromString('xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts));
298
299 // Add unparsedLoadedData
300 $sheetCodeName = $this->spreadSheet->getSheet($i)->getCodeName();
301 $unparsedLoadedData = $this->spreadSheet->getUnparsedLoadedData();
302 if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'])) {
303 foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'] as $ctrlProp) {
304 $zip->addFromString($ctrlProp['filePath'], $ctrlProp['content']);
305 }
306 }
307 if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'])) {
308 foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'] as $ctrlProp) {
309 $zip->addFromString($ctrlProp['filePath'], $ctrlProp['content']);
310 }
311 }
312
313 $drawings = $this->spreadSheet->getSheet($i)->getDrawingCollection();
314 $drawingCount = count($drawings);
315 if ($this->includeCharts) {
316 $chartCount = $this->spreadSheet->getSheet($i)->getChartCount();
317 }
318
319 // Add drawing and image relationship parts
320 if (($drawingCount > 0) || ($chartCount > 0)) {
321 // Drawing relationships
322 $zip->addFromString('xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts));
323
324 // Drawings
325 $zip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts));
326 } elseif (isset($unparsedLoadedData['sheets'][$sheetCodeName]['drawingAlternateContents'])) {
327 // Drawings
328 $zip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts));
329 }
330
331 // Add unparsed drawings
332 if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'])) {
333 foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'] as $relId => $drawingXml) {
334 $drawingFile = array_search($relId, $unparsedLoadedData['sheets'][$sheetCodeName]['drawingOriginalIds']);
335 if ($drawingFile !== false) {
336 $drawingFile = ltrim($drawingFile, '.');
337 $zip->addFromString('xl' . $drawingFile, $drawingXml);
338 }
339 }
340 }
341
342 // Add comment relationship parts
343 if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
344 // VML Comments
345 $zip->addFromString('xl/drawings/vmlDrawing' . ($i + 1) . '.vml', $this->getWriterPart('Comments')->writeVMLComments($this->spreadSheet->getSheet($i)));
346
347 // Comments
348 $zip->addFromString('xl/comments' . ($i + 1) . '.xml', $this->getWriterPart('Comments')->writeComments($this->spreadSheet->getSheet($i)));
349 }
350
351 // Add unparsed relationship parts
352 if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'])) {
353 foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'] as $vmlDrawing) {
354 $zip->addFromString($vmlDrawing['filePath'], $vmlDrawing['content']);
355 }
356 }
357
358 // Add header/footer relationship parts
359 if (count($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) {
360 // VML Drawings
361 $zip->addFromString('xl/drawings/vmlDrawingHF' . ($i + 1) . '.vml', $this->getWriterPart('Drawing')->writeVMLHeaderFooterImages($this->spreadSheet->getSheet($i)));
362
363 // VML Drawing relationships
364 $zip->addFromString('xl/drawings/_rels/vmlDrawingHF' . ($i + 1) . '.vml.rels', $this->getWriterPart('Rels')->writeHeaderFooterDrawingRelationships($this->spreadSheet->getSheet($i)));
365
366 // Media
367 foreach ($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
368 $zip->addFromString('xl/media/' . $image->getIndexedFilename(), file_get_contents($image->getPath()));
369 }
370 }
371 }
372
373 // Add media
374 for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) {
375 if ($this->getDrawingHashTable()->getByIndex($i) instanceof WorksheetDrawing) {
376 $imageContents = null;
377 $imagePath = $this->getDrawingHashTable()->getByIndex($i)->getPath();
378 if (strpos($imagePath, 'zip://') !== false) {
379 $imagePath = substr($imagePath, 6);
380 $imagePathSplitted = explode('#', $imagePath);
381
382 $imageZip = new ZipArchive();
383 $imageZip->open($imagePathSplitted[0]);
384 $imageContents = $imageZip->getFromName($imagePathSplitted[1]);
385 $imageZip->close();
386 unset($imageZip);
387 } else {
388 $imageContents = file_get_contents($imagePath);
389 }
390
391 $zip->addFromString('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents);
392 } elseif ($this->getDrawingHashTable()->getByIndex($i) instanceof MemoryDrawing) {
393 ob_start();
394 call_user_func(
395 $this->getDrawingHashTable()->getByIndex($i)->getRenderingFunction(),
396 $this->getDrawingHashTable()->getByIndex($i)->getImageResource()
397 );
398 $imageContents = ob_get_contents();
399 ob_end_clean();
400
401 $zip->addFromString('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents);
402 }
403 }
404
405 Functions::setReturnDateType($saveDateReturnType);
406 Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
407
408 // Close file
409 if ($zip->close() === false) {
410 throw new WriterException("Could not close zip file $pFilename.");
411 }
412
413 // If a temporary file was used, copy it to the correct file stream
414 if ($originalFilename != $pFilename) {
415 if (copy($pFilename, $originalFilename) === false) {
416 throw new WriterException("Could not copy temporary zip file $pFilename to $originalFilename.");
417 }
418 @unlink($pFilename);
419 }
420 } else {
421 throw new WriterException('PhpSpreadsheet object unassigned.');
422 }
423 }
424
425 /**
426 * Get Spreadsheet object.
427 *
428 * @throws WriterException
429 *
430 * @return Spreadsheet
431 */
432 public function getSpreadsheet()
433 {
434 if ($this->spreadSheet !== null) {
435 return $this->spreadSheet;
436 }
437
438 throw new WriterException('No Spreadsheet object assigned.');
439 }
440
441 /**
442 * Set Spreadsheet object.
443 *
444 * @param Spreadsheet $spreadsheet PhpSpreadsheet object
445 *
446 * @return Xlsx
447 */
448 public function setSpreadsheet(Spreadsheet $spreadsheet)
449 {
450 $this->spreadSheet = $spreadsheet;
451
452 return $this;
453 }
454
455 /**
456 * Get string table.
457 *
458 * @return string[]
459 */
460 public function getStringTable()
461 {
462 return $this->stringTable;
463 }
464
465 /**
466 * Get Style HashTable.
467 *
468 * @return HashTable
469 */
470 public function getStyleHashTable()
471 {
472 return $this->styleHashTable;
473 }
474
475 /**
476 * Get Conditional HashTable.
477 *
478 * @return HashTable
479 */
480 public function getStylesConditionalHashTable()
481 {
482 return $this->stylesConditionalHashTable;
483 }
484
485 /**
486 * Get Fill HashTable.
487 *
488 * @return HashTable
489 */
490 public function getFillHashTable()
491 {
492 return $this->fillHashTable;
493 }
494
495 /**
496 * Get \PhpOffice\PhpSpreadsheet\Style\Font HashTable.
497 *
498 * @return HashTable
499 */
500 public function getFontHashTable()
501 {
502 return $this->fontHashTable;
503 }
504
505 /**
506 * Get Borders HashTable.
507 *
508 * @return HashTable
509 */
510 public function getBordersHashTable()
511 {
512 return $this->bordersHashTable;
513 }
514
515 /**
516 * Get NumberFormat HashTable.
517 *
518 * @return HashTable
519 */
520 public function getNumFmtHashTable()
521 {
522 return $this->numFmtHashTable;
523 }
524
525 /**
526 * Get \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\BaseDrawing HashTable.
527 *
528 * @return HashTable
529 */
530 public function getDrawingHashTable()
531 {
532 return $this->drawingHashTable;
533 }
534
535 /**
536 * Get Office2003 compatibility.
537 *
538 * @return bool
539 */
540 public function getOffice2003Compatibility()
541 {
542 return $this->office2003compatibility;
543 }
544
545 /**
546 * Set Office2003 compatibility.
547 *
548 * @param bool $pValue Office2003 compatibility?
549 *
550 * @return Xlsx
551 */
552 public function setOffice2003Compatibility($pValue)
553 {
554 $this->office2003compatibility = $pValue;
555
556 return $this;
557 }
558 }
559