Passed
Pull Request — master (#4581)
by Owen
11:44
created

Xlsx::getWriterPartWorkbook()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 1
cts 1
cp 1
crap 1
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\RichText\RichText;
9
use PhpOffice\PhpSpreadsheet\Spreadsheet;
10
use PhpOffice\PhpSpreadsheet\Style\Borders;
11
use PhpOffice\PhpSpreadsheet\Style\Conditional;
12
use PhpOffice\PhpSpreadsheet\Style\Fill;
13
use PhpOffice\PhpSpreadsheet\Style\Font;
14
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
15
use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
16
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing as WorksheetDrawing;
17
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
18
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
19
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Chart;
20
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Comments;
21
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\ContentTypes;
22
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\DocProps;
23
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Drawing;
24
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Rels;
25
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsRibbon;
26
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsVBA;
27
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\StringTable;
28
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Style;
29
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Table;
30
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Theme;
31
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Workbook;
32
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet;
33
use ZipArchive;
34
use ZipStream\Exception\OverflowException;
35
use ZipStream\ZipStream;
36
37
class Xlsx extends BaseWriter
38
{
39
    /**
40
     * Office2003 compatibility.
41
     */
42
    private bool $office2003compatibility = false;
43
44
    /**
45
     * Private Spreadsheet.
46
     */
47
    private Spreadsheet $spreadSheet;
48
49
    /**
50
     * Private string table.
51
     *
52
     * @var string[]
53
     */
54
    private array $stringTable = [];
55
56
    /**
57
     * Private unique Conditional HashTable.
58
     *
59
     * @var HashTable<Conditional>
60
     */
61
    private HashTable $stylesConditionalHashTable;
62
63
    /**
64
     * Private unique Style HashTable.
65
     *
66
     * @var HashTable<\PhpOffice\PhpSpreadsheet\Style\Style>
67
     */
68
    private HashTable $styleHashTable;
69
70
    /**
71
     * Private unique Fill HashTable.
72
     *
73
     * @var HashTable<Fill>
74
     */
75
    private HashTable $fillHashTable;
76
77
    /**
78
     * Private unique \PhpOffice\PhpSpreadsheet\Style\Font HashTable.
79
     *
80
     * @var HashTable<Font>
81
     */
82
    private HashTable $fontHashTable;
83
84
    /**
85
     * Private unique Borders HashTable.
86
     *
87
     * @var HashTable<Borders>
88
     */
89
    private HashTable $bordersHashTable;
90
91
    /**
92
     * Private unique NumberFormat HashTable.
93
     *
94
     * @var HashTable<NumberFormat>
95
     */
96
    private HashTable $numFmtHashTable;
97
98
    /**
99
     * Private unique \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\BaseDrawing HashTable.
100
     *
101
     * @var HashTable<BaseDrawing>
102
     */
103
    private HashTable $drawingHashTable;
104
105
    /**
106
     * Private handle for zip stream.
107
     */
108
    private ZipStream $zip;
109
110
    private Chart $writerPartChart;
111
112
    private Comments $writerPartComments;
113
114
    private ContentTypes $writerPartContentTypes;
115
116
    private DocProps $writerPartDocProps;
117
118
    private Drawing $writerPartDrawing;
119
120
    private Rels $writerPartRels;
121
122
    private RelsRibbon $writerPartRelsRibbon;
123
124
    private RelsVBA $writerPartRelsVBA;
125
126
    private StringTable $writerPartStringTable;
127
128
    private Style $writerPartStyle;
129
130
    private Theme $writerPartTheme;
131
132
    private Table $writerPartTable;
133
134
    private Workbook $writerPartWorkbook;
135
136
    private Worksheet $writerPartWorksheet;
137
138
    private bool $explicitStyle0 = false;
139
140
    private bool $useCSEArrays = false;
141
142
    private bool $useDynamicArray = false;
143
144
    public const DEFAULT_FORCE_FULL_CALC = false;
145
146
    // Default changed from null in PhpSpreadsheet 4.0.0.
147
    private ?bool $forceFullCalc = self::DEFAULT_FORCE_FULL_CALC;
148
149
    protected bool $restrictMaxColumnWidth = false;
150
151
    /**
152 482
     * Create a new Xlsx Writer.
153
     */
154
    public function __construct(Spreadsheet $spreadsheet)
155 482
    {
156
        // Assign PhpSpreadsheet
157 482
        $this->setSpreadsheet($spreadsheet);
158 482
159 482
        $this->writerPartChart = new Chart($this);
160 482
        $this->writerPartComments = new Comments($this);
161 482
        $this->writerPartContentTypes = new ContentTypes($this);
162 482
        $this->writerPartDocProps = new DocProps($this);
163 482
        $this->writerPartDrawing = new Drawing($this);
164 482
        $this->writerPartRels = new Rels($this);
165 482
        $this->writerPartRelsRibbon = new RelsRibbon($this);
166 482
        $this->writerPartRelsVBA = new RelsVBA($this);
167 482
        $this->writerPartStringTable = new StringTable($this);
168 482
        $this->writerPartStyle = new Style($this);
169 482
        $this->writerPartTheme = new Theme($this);
170 482
        $this->writerPartTable = new Table($this);
171
        $this->writerPartWorkbook = new Workbook($this);
172
        $this->writerPartWorksheet = new Worksheet($this);
173 482
174 482
        // Set HashTable variables
175 482
        $this->bordersHashTable = new HashTable();
176 482
        $this->drawingHashTable = new HashTable();
177 482
        $this->fillHashTable = new HashTable();
178 482
        $this->fontHashTable = new HashTable();
179 482
        $this->numFmtHashTable = new HashTable();
180 482
        $this->styleHashTable = new HashTable();
181
        $this->stylesConditionalHashTable = new HashTable();
182
        $this->determineUseDynamicArrays();
183 78
    }
184
185 78
    public function getWriterPartChart(): Chart
186
    {
187
        return $this->writerPartChart;
188 26
    }
189
190 26
    public function getWriterPartComments(): Comments
191
    {
192
        return $this->writerPartComments;
193 396
    }
194
195 396
    public function getWriterPartContentTypes(): ContentTypes
196
    {
197
        return $this->writerPartContentTypes;
198 396
    }
199
200 396
    public function getWriterPartDocProps(): DocProps
201
    {
202
        return $this->writerPartDocProps;
203 396
    }
204
205 396
    public function getWriterPartDrawing(): Drawing
206
    {
207
        return $this->writerPartDrawing;
208 396
    }
209
210 396
    public function getWriterPartRels(): Rels
211
    {
212
        return $this->writerPartRels;
213
    }
214
215
    public function getWriterPartRelsRibbon(): RelsRibbon
216
    {
217
        return $this->writerPartRelsRibbon;
218 2
    }
219
220 2
    public function getWriterPartRelsVBA(): RelsVBA
221
    {
222
        return $this->writerPartRelsVBA;
223 477
    }
224
225 477
    public function getWriterPartStringTable(): StringTable
226
    {
227
        return $this->writerPartStringTable;
228 399
    }
229
230 399
    public function getWriterPartStyle(): Style
231
    {
232
        return $this->writerPartStyle;
233 396
    }
234
235 396
    public function getWriterPartTheme(): Theme
236
    {
237
        return $this->writerPartTheme;
238 8
    }
239
240 8
    public function getWriterPartTable(): Table
241
    {
242
        return $this->writerPartTable;
243 396
    }
244
245 396
    public function getWriterPartWorkbook(): Workbook
246
    {
247
        return $this->writerPartWorkbook;
248 396
    }
249
250 396
    public function getWriterPartWorksheet(): Worksheet
251
    {
252
        return $this->writerPartWorksheet;
253 398
    }
254
255 398
    public function createStyleDictionaries(): void
256 398
    {
257 398
        $this->styleHashTable->addFromSource(
258 398
            $this->getWriterPartStyle()->allStyles(
259 398
                $this->spreadSheet
260 398
            )
261 398
        );
262 398
        $this->stylesConditionalHashTable->addFromSource(
263 398
            $this->getWriterPartStyle()->allConditionalStyles(
264 398
                $this->spreadSheet
265 398
            )
266 398
        );
267 398
        $this->fillHashTable->addFromSource(
268 398
            $this->getWriterPartStyle()->allFills(
269 398
                $this->spreadSheet
270 398
            )
271 398
        );
272 398
        $this->fontHashTable->addFromSource(
273 398
            $this->getWriterPartStyle()->allFonts(
274 398
                $this->spreadSheet
275 398
            )
276 398
        );
277 398
        $this->bordersHashTable->addFromSource(
278 398
            $this->getWriterPartStyle()->allBorders(
279 398
                $this->spreadSheet
280 398
            )
281 398
        );
282 398
        $this->numFmtHashTable->addFromSource(
283 398
            $this->getWriterPartStyle()->allNumberFormats(
284 398
                $this->spreadSheet
285
            )
286
        );
287
    }
288
289
    /**
290 397
     * @return (RichText|string)[] $stringTable
291
     */
292 397
    public function createStringTable(): array
293 397
    {
294 397
        $this->stringTable = [];
295
        for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
296
            $this->stringTable = $this->getWriterPartStringTable()->createStringTable($this->spreadSheet->getSheet($i), $this->stringTable);
297 397
        }
298
299
        return $this->stringTable;
300
    }
301
302
    /**
303
     * Save PhpSpreadsheet to file.
304
     *
305 396
     * @param resource|string $filename
306
     */
307 396
    public function save($filename, int $flags = 0): void
308 396
    {
309
        $this->processFlags($flags);
310
        $this->determineUseDynamicArrays();
311 396
312 396
        // garbage collect
313
        $this->pathNames = [];
314 396
        $this->spreadSheet->garbageCollect();
315 396
316 396
        $saveDebugLog = Calculation::getInstance($this->spreadSheet)->getDebugLog()->getWriteDebugLog();
317 396
        Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog(false);
318
        $saveDateReturnType = Functions::getReturnDateType();
319
        Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
320 396
321
        // Create string lookup table
322
        $this->createStringTable();
323 396
324
        // Create styles dictionaries
325
        $this->createStyleDictionaries();
326 396
327
        // Create drawing dictionary
328
        $this->drawingHashTable->addFromSource($this->getWriterPartDrawing()->allDrawings($this->spreadSheet));
329 396
330
        /** @var string[] */
331 396
        $zipContent = [];
332 396
        // Add [Content_Types].xml to ZIP file
333 396
        $zipContent['[Content_Types].xml'] = $this->getWriterPartContentTypes()->writeContentTypes($this->spreadSheet, $this->includeCharts);
334 11
        $metadataData = (new Xlsx\Metadata($this))->writeMetadata();
335
        if ($metadataData !== '') {
336
            $zipContent['xl/metadata.xml'] = $metadataData;
337
        }
338 396
339 2
        //if hasMacros, add the vbaProject.bin file, Certificate file(if exists)
340 2
        if ($this->spreadSheet->hasMacros()) {
341
            $macrosCode = $this->spreadSheet->getMacrosCode();
342 2
            if ($macrosCode !== null) {
343 2
                // we have the code ?
344
                $zipContent['xl/vbaProject.bin'] = $macrosCode; //allways in 'xl', allways named vbaProject.bin
345
                if ($this->spreadSheet->hasMacrosCertificate()) {
346 2
                    //signed macros ?
347 2
                    // Yes : add the certificate file and the related rels file
348
                    $zipContent['xl/vbaProjectSignature.bin'] = $this->spreadSheet->getMacrosCertificate();
349
                    $zipContent['xl/_rels/vbaProject.bin.rels'] = $this->getWriterPartRelsVBA()->writeVBARelationships();
350
                }
351
            }
352 396
        }
353 2
        //a custom UI in this workbook ? add it ("base" xml and additional objects (pictures) and rels)
354 2
        if ($this->spreadSheet->hasRibbon()) {
355 2
            $tmpRibbonTarget = $this->spreadSheet->getRibbonXMLData('target');
356 2
            $tmpRibbonTarget = is_string($tmpRibbonTarget) ? $tmpRibbonTarget : '';
357
            $zipContent[$tmpRibbonTarget] = $this->spreadSheet->getRibbonXMLData('data');
358
            if ($this->spreadSheet->hasRibbonBinObjects()) {
359
                $tmpRootPath = dirname($tmpRibbonTarget) . '/';
360
                $ribbonBinObjects = $this->spreadSheet->getRibbonBinObjects('data'); //the files to write
361
                if (is_array($ribbonBinObjects)) {
362
                    foreach ($ribbonBinObjects as $aPath => $aContent) {
363
                        $zipContent[$tmpRootPath . $aPath] = $aContent;
364
                    }
365
                }
366
                //the rels for files
367
                $zipContent[$tmpRootPath . '_rels/' . basename($tmpRibbonTarget) . '.rels'] = $this->getWriterPartRelsRibbon()->writeRibbonRelationships($this->spreadSheet);
368
            }
369
        }
370 396
371 396
        // Add relationships to ZIP file
372
        $zipContent['_rels/.rels'] = $this->getWriterPartRels()->writeRelationships($this->spreadSheet);
373
        $zipContent['xl/_rels/workbook.xml.rels'] = $this->getWriterPartRels()->writeWorkbookRelationships($this->spreadSheet);
374 396
375 396
        // Add document properties to ZIP file
376 396
        $zipContent['docProps/app.xml'] = $this->getWriterPartDocProps()->writeDocPropsApp($this->spreadSheet);
377 396
        $zipContent['docProps/core.xml'] = $this->getWriterPartDocProps()->writeDocPropsCore($this->spreadSheet);
378 31
        $customPropertiesPart = $this->getWriterPartDocProps()->writeDocPropsCustom($this->spreadSheet);
379
        if ($customPropertiesPart !== null) {
380
            $zipContent['docProps/custom.xml'] = $customPropertiesPart;
381
        }
382 396
383
        // Add theme to ZIP file
384
        $zipContent['xl/theme/theme1.xml'] = $this->getWriterPartTheme()->writeTheme($this->spreadSheet);
385 396
386
        // Add string table to ZIP file
387
        $zipContent['xl/sharedStrings.xml'] = $this->getWriterPartStringTable()->writeStringTable($this->stringTable);
388 396
389
        // Add styles to ZIP file
390
        $zipContent['xl/styles.xml'] = $this->getWriterPartStyle()->writeStyles($this->spreadSheet);
391 396
392
        // Add workbook to ZIP file
393 396
        $zipContent['xl/workbook.xml'] = $this->getWriterPartWorkbook()->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas, $this->forceFullCalc);
394
395 396
        $chartCount = 0;
396 396
        // Add worksheets
397 395
        for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
398 79
            $zipContent['xl/worksheets/sheet' . ($i + 1) . '.xml'] = $this->getWriterPartWorksheet()->writeWorksheet($this->spreadSheet->getSheet($i), $this->stringTable, $this->includeCharts);
399 79
            if ($this->includeCharts) {
400 78
                $charts = $this->spreadSheet->getSheet($i)->getChartCollection();
401 78
                if (count($charts) > 0) {
402 78
                    foreach ($charts as $chart) {
403
                        $zipContent['xl/charts/chart' . ($chartCount + 1) . '.xml'] = $this->getWriterPartChart()->writeChart($chart, $this->preCalculateFormulas);
404
                        ++$chartCount;
405
                    }
406
                }
407
            }
408 395
        }
409 395
410
        $chartRef1 = 0;
411 395
        $tableRef1 = 1;
412
        // Add worksheet relationships (drawings, ...)
413
        for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
414 395
            // Add relationships
415
            /** @var string[] $zipContent */
416
            $zipContent['xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels'] = $this->getWriterPartRels()->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts, $tableRef1, $zipContent);
417 395
418
            // Add unparsedLoadedData
419 395
            $sheetCodeName = $this->spreadSheet->getSheet($i)->getCodeName();
420
            /** @var mixed[][][] */
421 395
            $unparsedLoadedData = $this->spreadSheet->getUnparsedLoadedData();
422 395
            /** @var mixed[][] */
423
            $unparsedSheet = $unparsedLoadedData['sheets'][$sheetCodeName] ?? [];
424 4
            foreach (($unparsedSheet['ctrlProps'] ?? []) as $ctrlProp) {
425
                /** @var string[] $ctrlProp */
426 395
                $zipContent[$ctrlProp['filePath']] = $ctrlProp['content'];
427
            }
428 36
            foreach (($unparsedSheet['printerSettings'] ?? []) as $ctrlProp) {
429
                /** @var string[] $ctrlProp */
430
                $zipContent[$ctrlProp['filePath']] = $ctrlProp['content'];
431 395
            }
432 395
433 395
            $drawings = $this->spreadSheet->getSheet($i)->getDrawingCollection();
434 79
            $drawingCount = count($drawings);
435
            if ($this->includeCharts) {
436
                $chartCount = $this->spreadSheet->getSheet($i)->getChartCount();
437
            }
438 395
439
            // Add drawing and image relationship parts
440 120
            if (($drawingCount > 0) || ($chartCount > 0)) {
441
                // Drawing relationships
442
                $zipContent['xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels'] = $this->getWriterPartRels()->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts);
443 120
444 291
                // Drawings
445
                $zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'] = $this->getWriterPartDrawing()->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts);
446 4
            } elseif (isset($unparsedSheet['drawingAlternateContents'])) {
447
                // Drawings
448
                $zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'] = $this->getWriterPartDrawing()->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts);
449
            }
450 395
451 5
            // Add unparsed drawings
452 5
            if (isset($unparsedSheet['Drawings']) && !isset($zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'])) {
453 5
                foreach ($unparsedSheet['Drawings'] as $relId => $drawingXml) {
454
                    $drawingFile = array_search($relId, $unparsedSheet['drawingOriginalIds']);
455
                    if ($drawingFile !== false) {
456 5
                        //$drawingFile = ltrim($drawingFile, '.');
457
                        //$zipContent['xl' . $drawingFile] = $drawingXml;
458
                        $zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'] = $drawingXml;
459
                    }
460 395
                }
461 1
            }
462
            if (isset($unparsedSheet['drawingOriginalIds']) && !isset($zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'])) {
463
                $zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'] = '<xml></xml>';
464
            }
465
466 395
            // Add comment relationship parts
467 395
            /** @var mixed[][] */
468 395
            $legacyTemp = $unparsedLoadedData['sheets'] ?? [];
469 395
            $legacyTemp = $legacyTemp[$this->spreadSheet->getSheet($i)->getCodeName()] ?? [];
470
            $legacy = $legacyTemp['legacyDrawing'] ?? null;
471 28
            if (count($this->spreadSheet->getSheet($i)->getComments()) > 0 || $legacy !== null) {
472
                // VML Comments relationships
473
                $zipContent['xl/drawings/_rels/vmlDrawing' . ($i + 1) . '.vml.rels'] = $this->getWriterPartRels()->writeVMLDrawingRelationships($this->spreadSheet->getSheet($i));
474 28
475
                // VML Comments
476
                $zipContent['xl/drawings/vmlDrawing' . ($i + 1) . '.vml'] = $legacy ?? $this->getWriterPartComments()->writeVMLComments($this->spreadSheet->getSheet($i));
477
            }
478 395
479 26
            // Comments
480
            if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
481
                $zipContent['xl/comments' . ($i + 1) . '.xml'] = $this->getWriterPartComments()->writeComments($this->spreadSheet->getSheet($i));
482 26
483 26
                // Media
484 3
                foreach ($this->spreadSheet->getSheet($i)->getComments() as $comment) {
485 3
                    if ($comment->hasBackgroundImage()) {
486
                        $image = $comment->getBackgroundImage();
487
                        $zipContent['xl/media/' . $image->getMediaFilename()] = $this->processDrawing($image);
488
                    }
489
                }
490
            }
491 395
492 5
            // Add unparsed relationship parts
493
            if (isset($unparsedSheet['vmlDrawings'])) {
494 5
                foreach ($unparsedSheet['vmlDrawings'] as $vmlDrawing) {
495 3
                    /** @var string[] $vmlDrawing */
496
                    if (!isset($zipContent[$vmlDrawing['filePath']])) {
497
                        $zipContent[$vmlDrawing['filePath']] = $vmlDrawing['content'];
498
                    }
499
                }
500
            }
501 395
502
            // Add header/footer relationship parts
503 4
            if (count($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) {
504
                // VML Drawings
505
                $zipContent['xl/drawings/vmlDrawingHF' . ($i + 1) . '.vml'] = $this->getWriterPartDrawing()->writeVMLHeaderFooterImages($this->spreadSheet->getSheet($i));
506 4
507
                // VML Drawing relationships
508
                $zipContent['xl/drawings/_rels/vmlDrawingHF' . ($i + 1) . '.vml.rels'] = $this->getWriterPartRels()->writeHeaderFooterDrawingRelationships($this->spreadSheet->getSheet($i));
509 4
510 4
                // Media
511 4
                foreach ($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
512
                    if ($image->getPath() !== '') {
513
                        $zipContent['xl/media/' . $image->getIndexedFilename()] = file_get_contents($image->getPath());
514
                    }
515
                }
516
            }
517 395
518 395
            // Add Table parts
519 8
            $tables = $this->spreadSheet->getSheet($i)->getTableCollection();
520
            foreach ($tables as $table) {
521
                $zipContent['xl/tables/table' . $tableRef1 . '.xml'] = $this->getWriterPartTable()->writeTable($table, $tableRef1++);
522
            }
523
        }
524 395
525 53
        // Add media
526 42
        for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) {
527 42
            if ($this->getDrawingHashTable()->getByIndex($i) instanceof WorksheetDrawing) {
528 42
                $imageContents = null;
529
                $imagePath = $this->getDrawingHashTable()->getByIndex($i)->getPath();
530
                if ($imagePath === '') {
531 42
                    continue;
532 24
                }
533 24
                if (str_contains($imagePath, 'zip://')) {
534
                    $imagePath = substr($imagePath, 6);
535 24
                    $imagePathSplitted = explode('#', $imagePath);
536 24
537 24
                    $imageZip = new ZipArchive();
538 24
                    $imageZip->open($imagePathSplitted[0]);
539 24
                    $imageContents = $imageZip->getFromName($imagePathSplitted[1]);
540
                    $imageZip->close();
541 20
                    unset($imageZip);
542
                } else {
543
                    $imageContents = file_get_contents($imagePath);
544 42
                }
545 11
546 11
                $zipContent['xl/media/' . $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()] = $imageContents;
547 11
            } elseif ($this->getDrawingHashTable()->getByIndex($i) instanceof MemoryDrawing) {
548 11
                ob_start();
549 11
                $callable = $this->getDrawingHashTable()->getByIndex($i)->getRenderingFunction();
550 11
                call_user_func(
551 11
                    $callable,
552 11
                    $this->getDrawingHashTable()->getByIndex($i)->getImageResource()
553 11
                );
554
                $imageContents = ob_get_contents();
555 11
                ob_end_clean();
556
557
                $zipContent['xl/media/' . $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()] = $imageContents;
558
            }
559 395
        }
560 395
561
        Functions::setReturnDateType($saveDateReturnType);
562 395
        Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
563
564 395
        $this->openFileHandle($filename);
565
566
        $this->zip = ZipStream0::newZipStream($this->fileHandle);
567 395
568
        /** @var string[] $zipContent */
569
        $this->addZipFiles($zipContent);
570
571 395
        // Close file
572
        try {
573
            $this->zip->finish();
574
        } catch (OverflowException) {
575
            throw new WriterException('Could not close resource.');
576 395
        }
577
578
        $this->maybeCloseFileHandle();
579
    }
580
581
    /**
582 477
     * Get Spreadsheet object.
583
     */
584 477
    public function getSpreadsheet(): Spreadsheet
585
    {
586
        return $this->spreadSheet;
587
    }
588
589
    /**
590
     * Set Spreadsheet object.
591
     *
592
     * @param Spreadsheet $spreadsheet PhpSpreadsheet object
593
     *
594 482
     * @return $this
595
     */
596 482
    public function setSpreadsheet(Spreadsheet $spreadsheet): static
597
    {
598 482
        $this->spreadSheet = $spreadsheet;
599
600
        return $this;
601
    }
602
603
    /**
604
     * Get string table.
605
     *
606
     * @return string[]
607
     */
608
    public function getStringTable(): array
609
    {
610
        return $this->stringTable;
611
    }
612
613
    /**
614
     * Get Style HashTable.
615
     *
616
     * @return HashTable<\PhpOffice\PhpSpreadsheet\Style\Style>
617
     */
618
    public function getStyleHashTable(): HashTable
619
    {
620
        return $this->styleHashTable;
621
    }
622
623
    /**
624
     * Get Conditional HashTable.
625
     *
626 457
     * @return HashTable<Conditional>
627
     */
628 457
    public function getStylesConditionalHashTable(): HashTable
629
    {
630
        return $this->stylesConditionalHashTable;
631
    }
632
633
    /**
634
     * Get Fill HashTable.
635
     *
636 399
     * @return HashTable<Fill>
637
     */
638 399
    public function getFillHashTable(): HashTable
639
    {
640
        return $this->fillHashTable;
641
    }
642
643
    /**
644
     * Get \PhpOffice\PhpSpreadsheet\Style\Font HashTable.
645
     *
646 399
     * @return HashTable<Font>
647
     */
648 399
    public function getFontHashTable(): HashTable
649
    {
650
        return $this->fontHashTable;
651
    }
652
653
    /**
654
     * Get Borders HashTable.
655
     *
656 399
     * @return HashTable<Borders>
657
     */
658 399
    public function getBordersHashTable(): HashTable
659
    {
660
        return $this->bordersHashTable;
661
    }
662
663
    /**
664
     * Get NumberFormat HashTable.
665
     *
666 399
     * @return HashTable<NumberFormat>
667
     */
668 399
    public function getNumFmtHashTable(): HashTable
669
    {
670
        return $this->numFmtHashTable;
671
    }
672
673
    /**
674
     * Get \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\BaseDrawing HashTable.
675
     *
676 396
     * @return HashTable<BaseDrawing>
677
     */
678 396
    public function getDrawingHashTable(): HashTable
679
    {
680
        return $this->drawingHashTable;
681
    }
682
683
    /**
684 399
     * Get Office2003 compatibility.
685
     */
686 399
    public function getOffice2003Compatibility(): bool
687
    {
688
        return $this->office2003compatibility;
689
    }
690
691
    /**
692
     * Set Office2003 compatibility.
693
     *
694
     * @param bool $office2003compatibility Office2003 compatibility?
695
     *
696
     * @return $this
697
     */
698
    public function setOffice2003Compatibility(bool $office2003compatibility): static
699
    {
700
        $this->office2003compatibility = $office2003compatibility;
701
702
        return $this;
703
    }
704
705
    /** @var string[] */
706 395
    private array $pathNames = [];
707
708 395
    private function addZipFile(string $path, string $content): void
709 395
    {
710 395
        if (!in_array($path, $this->pathNames)) {
711
            $this->pathNames[] = $path;
712
            $this->zip->addFile($path, $content);
713
        }
714
    }
715 395
716
    /** @param string[] $zipContent */
717 395
    private function addZipFiles(array $zipContent): void
718 395
    {
719
        foreach ($zipContent as $path => $content) {
720
            $this->addZipFile($path, $content);
721
        }
722 3
    }
723
724 3
    private function processDrawing(WorksheetDrawing $drawing): string|null|false
725 3
    {
726 3
        $data = null;
727
        $filename = $drawing->getPath();
728
        if ($filename === '') {
729 3
            return null;
730
        }
731 3
        $imageData = getimagesize($filename);
732 3
733 3
        if (!empty($imageData)) {
734 2
            switch ($imageData[2]) {
735 2
                case 1: // GIF, not supported by BIFF8, we convert to PNG
736 2
                    $image = imagecreatefromgif($filename);
737 2
                    if ($image !== false) {
738 2
                        ob_start();
739 2
                        imagepng($image);
740
                        $data = ob_get_contents();
741
                        ob_end_clean();
742 2
                    }
743
744 3
                    break;
745 2
746
                case 2: // JPEG
747 2
                    $data = file_get_contents($filename);
748
749 2
                    break;
750 1
751
                case 3: // PNG
752 1
                    $data = file_get_contents($filename);
753
754 2
                    break;
755 2
756 2
                case 6: // Windows DIB (BMP), we convert to PNG
757 2
                    $image = imagecreatefrombmp($filename);
758 2
                    if ($image !== false) {
759 2
                        ob_start();
760 2
                        imagepng($image);
761
                        $data = ob_get_contents();
762
                        ob_end_clean();
763 2
                    }
764
765
                    break;
766
            }
767 3
        }
768
769
        return $data;
770 464
    }
771
772 464
    public function getExplicitStyle0(): bool
773
    {
774
        return $this->explicitStyle0;
775
    }
776
777
    /**
778
     * This may be useful if non-default Alignment is part of default style
779
     * and you think you might want to open the spreadsheet
780 1
     * with LibreOffice or Gnumeric.
781
     */
782 1
    public function setExplicitStyle0(bool $explicitStyle0): self
783
    {
784 1
        $this->explicitStyle0 = $explicitStyle0;
785
786
        return $this;
787 3
    }
788
789 3
    public function setUseCSEArrays(?bool $useCSEArrays): void
790 3
    {
791
        if ($useCSEArrays !== null) {
792 3
            $this->useCSEArrays = $useCSEArrays;
793
        }
794
        $this->determineUseDynamicArrays();
795 465
    }
796
797 465
    public function useDynamicArrays(): bool
798
    {
799
        return $this->useDynamicArray;
800 482
    }
801
802 482
    private function determineUseDynamicArrays(): void
803
    {
804
        $this->useDynamicArray = $this->preCalculateFormulas && Calculation::getInstance($this->spreadSheet)->getInstanceArrayReturnType() === Calculation::RETURN_ARRAY_AS_ARRAY && !$this->useCSEArrays;
805
    }
806
807
    /**
808
     * If this is set when a spreadsheet is opened,
809
     * values may not be automatically re-calculated,
810
     * and a button will be available to force re-calculation.
811
     * This may apply to all spreadsheets open at that time.
812
     * If null, this will be set to the opposite of $preCalculateFormulas.
813
     * It is likely that false is the desired setting, although
814
     * cases have been reported where true is required (issue #456).
815 4
     * Nevertheless, default is set to false in PhpSpreadsheet 4.0.0.
816
     */
817 4
    public function setForceFullCalc(?bool $forceFullCalc): self
818
    {
819 4
        $this->forceFullCalc = $forceFullCalc;
820
821
        return $this;
822
    }
823
824
    /**
825
     * Excel has a nominal width limint of 255 for a column.
826
     * Surprisingly, Xlsx can read and write larger values,
827
     * and the file will appear as desired,
828
     * but the User Interface does not allow you to set the width beyond 255,
829
     * either directly or though auto-fit width.
830
     * Xls sets its own value when the width is beyond 255.
831
     * This method gets whether PhpSpreadsheet should restrict the
832
     * column widths which it writes to the Excel limit, for formats
833
     * which allow it to exceed 255.
834
     */
835
    public function setRestrictMaxColumnWidth(bool $restrictMaxColumnWidth): self
836
    {
837
        $this->restrictMaxColumnWidth = $restrictMaxColumnWidth;
838
839
        return $this;
840
    }
841
842
    public function getRestrictMaxColumnWidth(): bool
843
    {
844
        return $this->restrictMaxColumnWidth;
845
    }
846
}
847