Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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