Passed
Pull Request — master (#4271)
by Owen
14:45
created

Xlsx::setForceFullCalc()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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