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\Xlsx;
4
 
5
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
6
use PhpOffice\PhpSpreadsheet\Shared\File;
7
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
8
use PhpOffice\PhpSpreadsheet\Spreadsheet;
9
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing as WorksheetDrawing;
10
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
11
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
12
 
13
class ContentTypes extends WriterPart
14
{
15
    /**
16
     * Write content types to XML format.
17
     *
18
     * @param bool $includeCharts Flag indicating if we should include drawing details for charts
19
     *
20
     * @return string XML Output
21
     */
22
    public function writeContentTypes(Spreadsheet $spreadsheet, bool $includeCharts = false): string
23
    {
24
        // Create XML writer
25
        $objWriter = null;
26
        if ($this->getParentWriter()->getUseDiskCaching()) {
27
            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
28
        } else {
29
            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
30
        }
31
 
32
        // XML header
33
        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
34
 
35
        // Types
36
        $objWriter->startElement('Types');
37
        $objWriter->writeAttribute('xmlns', Namespaces::CONTENT_TYPES);
38
 
39
        // Theme
40
        $this->writeOverrideContentType($objWriter, '/xl/theme/theme1.xml', 'application/vnd.openxmlformats-officedocument.theme+xml');
41
 
42
        // Styles
43
        $this->writeOverrideContentType($objWriter, '/xl/styles.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml');
44
 
45
        // Rels
46
        $this->writeDefaultContentType($objWriter, 'rels', 'application/vnd.openxmlformats-package.relationships+xml');
47
 
48
        // XML
49
        $this->writeDefaultContentType($objWriter, 'xml', 'application/xml');
50
 
51
        // VML
52
        $this->writeDefaultContentType($objWriter, 'vml', 'application/vnd.openxmlformats-officedocument.vmlDrawing');
53
 
54
        // Workbook
55
        if ($spreadsheet->hasMacros()) { //Macros in workbook ?
56
            // Yes : not standard content but "macroEnabled"
57
            $this->writeOverrideContentType($objWriter, '/xl/workbook.xml', 'application/vnd.ms-excel.sheet.macroEnabled.main+xml');
58
            //... and define a new type for the VBA project
59
            // Better use Override, because we can use 'bin' also for xl\printerSettings\printerSettings1.bin
60
            $this->writeOverrideContentType($objWriter, '/xl/vbaProject.bin', 'application/vnd.ms-office.vbaProject');
61
            if ($spreadsheet->hasMacrosCertificate()) {
62
                // signed macros ?
63
                // Yes : add needed information
64
                $this->writeOverrideContentType($objWriter, '/xl/vbaProjectSignature.bin', 'application/vnd.ms-office.vbaProjectSignature');
65
            }
66
        } else {
67
            // no macros in workbook, so standard type
68
            $this->writeOverrideContentType($objWriter, '/xl/workbook.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml');
69
        }
70
 
71
        // DocProps
72
        $this->writeOverrideContentType($objWriter, '/docProps/app.xml', 'application/vnd.openxmlformats-officedocument.extended-properties+xml');
73
 
74
        $this->writeOverrideContentType($objWriter, '/docProps/core.xml', 'application/vnd.openxmlformats-package.core-properties+xml');
75
 
76
        $customPropertyList = $spreadsheet->getProperties()->getCustomProperties();
77
        if (!empty($customPropertyList)) {
78
            $this->writeOverrideContentType($objWriter, '/docProps/custom.xml', 'application/vnd.openxmlformats-officedocument.custom-properties+xml');
79
        }
80
 
81
        // Worksheets
82
        $sheetCount = $spreadsheet->getSheetCount();
83
        for ($i = 0; $i < $sheetCount; ++$i) {
84
            $this->writeOverrideContentType($objWriter, '/xl/worksheets/sheet' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml');
85
        }
86
 
87
        // Shared strings
88
        $this->writeOverrideContentType($objWriter, '/xl/sharedStrings.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml');
89
 
90
        // Table
91
        $table = 1;
92
        for ($i = 0; $i < $sheetCount; ++$i) {
93
            $tableCount = $spreadsheet->getSheet($i)->getTableCollection()->count();
94
 
95
            for ($t = 1; $t <= $tableCount; ++$t) {
96
                $this->writeOverrideContentType($objWriter, '/xl/tables/table' . $table++ . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml');
97
            }
98
        }
99
 
100
        // Add worksheet relationship content types
101
        $unparsedLoadedData = $spreadsheet->getUnparsedLoadedData();
102
        $chart = 1;
103
        for ($i = 0; $i < $sheetCount; ++$i) {
104
            $drawings = $spreadsheet->getSheet($i)->getDrawingCollection();
105
            $drawingCount = count($drawings);
106
            $chartCount = ($includeCharts) ? $spreadsheet->getSheet($i)->getChartCount() : 0;
107
            $hasUnparsedDrawing = isset($unparsedLoadedData['sheets'][$spreadsheet->getSheet($i)->getCodeName()]['drawingOriginalIds']);
108
 
109
            //    We need a drawing relationship for the worksheet if we have either drawings or charts
110
            if (($drawingCount > 0) || ($chartCount > 0) || $hasUnparsedDrawing) {
111
                $this->writeOverrideContentType($objWriter, '/xl/drawings/drawing' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.drawing+xml');
112
            }
113
 
114
            //    If we have charts, then we need a chart relationship for every individual chart
115
            if ($chartCount > 0) {
116
                for ($c = 0; $c < $chartCount; ++$c) {
117
                    $this->writeOverrideContentType($objWriter, '/xl/charts/chart' . $chart++ . '.xml', 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml');
118
                }
119
            }
120
        }
121
 
122
        // Comments
123
        for ($i = 0; $i < $sheetCount; ++$i) {
124
            if (count($spreadsheet->getSheet($i)->getComments()) > 0) {
125
                $this->writeOverrideContentType($objWriter, '/xl/comments' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml');
126
            }
127
        }
128
 
129
        // Add media content-types
130
        $aMediaContentTypes = [];
131
        $mediaCount = $this->getParentWriter()->getDrawingHashTable()->count();
132
        for ($i = 0; $i < $mediaCount; ++$i) {
133
            $extension = '';
134
            $mimeType = '';
135
 
136
            $drawing = $this->getParentWriter()->getDrawingHashTable()->getByIndex($i);
137
            if ($drawing instanceof WorksheetDrawing && $drawing->getPath() !== '') {
138
                $extension = strtolower($drawing->getExtension());
139
                if ($drawing->getIsUrl()) {
140
                    $mimeType = image_type_to_mime_type($drawing->getType());
141
                } else {
142
                    $mimeType = $this->getImageMimeType($drawing->getPath());
143
                }
144
            } elseif ($drawing instanceof MemoryDrawing) {
145
                $extension = strtolower($drawing->getMimeType());
146
                $extension = explode('/', $extension);
147
                $extension = $extension[1];
148
 
149
                $mimeType = $drawing->getMimeType();
150
            }
151
 
152
            if ($mimeType !== '' && !isset($aMediaContentTypes[$extension])) {
153
                $aMediaContentTypes[$extension] = $mimeType;
154
 
155
                $this->writeDefaultContentType($objWriter, $extension, $mimeType);
156
            }
157
        }
158
        if ($spreadsheet->hasRibbonBinObjects()) {
159
            // Some additional objects in the ribbon ?
160
            // we need to write "Extension" but not already write for media content
161
            $tabRibbonTypes = array_diff($spreadsheet->getRibbonBinObjects('types') ?? [], array_keys($aMediaContentTypes));
162
            foreach ($tabRibbonTypes as $aRibbonType) {
163
                $mimeType = 'image/.' . $aRibbonType; //we wrote $mimeType like customUI Editor
164
                $this->writeDefaultContentType($objWriter, $aRibbonType, $mimeType);
165
            }
166
        }
167
        $sheetCount = $spreadsheet->getSheetCount();
168
        for ($i = 0; $i < $sheetCount; ++$i) {
169
            if (count($spreadsheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) {
170
                foreach ($spreadsheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
171
                    if ($image->getPath() !== '' && !isset($aMediaContentTypes[strtolower($image->getExtension())])) {
172
                        $aMediaContentTypes[strtolower($image->getExtension())] = $this->getImageMimeType($image->getPath());
173
 
174
                        $this->writeDefaultContentType($objWriter, strtolower($image->getExtension()), $aMediaContentTypes[strtolower($image->getExtension())]);
175
                    }
176
                }
177
            }
178
 
179
            if (count($spreadsheet->getSheet($i)->getComments()) > 0) {
180
                foreach ($spreadsheet->getSheet($i)->getComments() as $comment) {
181
                    if (!$comment->hasBackgroundImage()) {
182
                        continue;
183
                    }
184
 
185
                    $bgImage = $comment->getBackgroundImage();
186
                    $bgImageExtentionKey = strtolower($bgImage->getImageFileExtensionForSave(false));
187
 
188
                    if (!isset($aMediaContentTypes[$bgImageExtentionKey])) {
189
                        $aMediaContentTypes[$bgImageExtentionKey] = $bgImage->getImageMimeType();
190
 
191
                        $this->writeDefaultContentType($objWriter, $bgImageExtentionKey, $aMediaContentTypes[$bgImageExtentionKey]);
192
                    }
193
                }
194
            }
195
 
196
            $bgImage = $spreadsheet->getSheet($i)->getBackgroundImage();
197
            $mimeType = $spreadsheet->getSheet($i)->getBackgroundMime();
198
            $extension = $spreadsheet->getSheet($i)->getBackgroundExtension();
199
            if ($bgImage !== '' && !isset($aMediaContentTypes[$extension])) {
200
                $this->writeDefaultContentType($objWriter, $extension, $mimeType);
201
            }
202
        }
203
 
204
        // unparsed defaults
205
        if (isset($unparsedLoadedData['default_content_types'])) {
206
            foreach ($unparsedLoadedData['default_content_types'] as $extName => $contentType) {
207
                $this->writeDefaultContentType($objWriter, $extName, $contentType);
208
            }
209
        }
210
 
211
        // unparsed overrides
212
        if (isset($unparsedLoadedData['override_content_types'])) {
213
            foreach ($unparsedLoadedData['override_content_types'] as $partName => $overrideType) {
214
                $this->writeOverrideContentType($objWriter, $partName, $overrideType);
215
            }
216
        }
217
 
218
        // Metadata needed for Dynamic Arrays
219
        if ($this->getParentWriter()->useDynamicArrays()) {
220
            $this->writeOverrideContentType($objWriter, '/xl/metadata.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml');
221
        }
222
 
223
        $objWriter->endElement();
224
 
225
        // Return
226
        return $objWriter->getData();
227
    }
228
 
229
    private static int $three = 3; // phpstan silliness
230
 
231
    /**
232
     * Get image mime type.
233
     *
234
     * @param string $filename Filename
235
     *
236
     * @return string Mime Type
237
     */
238
    private function getImageMimeType(string $filename): string
239
    {
240
        if (File::fileExists($filename)) {
241
            $image = getimagesize($filename);
242
 
243
            return image_type_to_mime_type((is_array($image) && count($image) >= self::$three) ? $image[2] : 0);
244
        }
245
 
246
        throw new WriterException("File $filename does not exist");
247
    }
248
 
249
    /**
250
     * Write Default content type.
251
     *
252
     * @param string $partName Part name
253
     * @param string $contentType Content type
254
     */
255
    private function writeDefaultContentType(XMLWriter $objWriter, string $partName, string $contentType): void
256
    {
257
        if ($partName != '' && $contentType != '') {
258
            // Write content type
259
            $objWriter->startElement('Default');
260
            $objWriter->writeAttribute('Extension', $partName);
261
            $objWriter->writeAttribute('ContentType', $contentType);
262
            $objWriter->endElement();
263
        } else {
264
            throw new WriterException('Invalid parameters passed.');
265
        }
266
    }
267
 
268
    /**
269
     * Write Override content type.
270
     *
271
     * @param string $partName Part name
272
     * @param string $contentType Content type
273
     */
274
    private function writeOverrideContentType(XMLWriter $objWriter, string $partName, string $contentType): void
275
    {
276
        if ($partName != '' && $contentType != '') {
277
            // Write content type
278
            $objWriter->startElement('Override');
279
            $objWriter->writeAttribute('PartName', $partName);
280
            $objWriter->writeAttribute('ContentType', $contentType);
281
            $objWriter->endElement();
282
        } else {
283
            throw new WriterException('Invalid parameters passed.');
284
        }
285
    }
286
}