Passed
Pull Request — master (#4508)
by Owen
24:45 queued 17:17
created

Xlsx::processDrawing()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 46
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 9.0027

Importance

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