Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
4
 
5
use PhpOffice\PhpSpreadsheet\Cell\Cell;
6
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
7
use PhpOffice\PhpSpreadsheet\Cell\DataType;
8
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
9
use PhpOffice\PhpSpreadsheet\Spreadsheet;
10
use PhpOffice\PhpSpreadsheet\Worksheet\Row;
11
use PhpOffice\PhpSpreadsheet\Worksheet\RowCellIterator;
12
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
13
use PhpOffice\PhpSpreadsheet\Writer\Exception;
14
use PhpOffice\PhpSpreadsheet\Writer\Ods;
15
use PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Comment;
16
use PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Style;
17
 
18
/**
19
 * @author     Alexander Pervakov <frost-nzcr4@jagmort.com>
20
 */
21
class Content extends WriterPart
22
{
23
    const NUMBER_COLS_REPEATED_MAX = 1024;
24
    const NUMBER_ROWS_REPEATED_MAX = 1048576;
25
 
26
    /** @var Formula */
27
    private $formulaConvertor;
28
 
29
    /**
30
     * Set parent Ods writer.
31
     */
32
    public function __construct(Ods $writer)
33
    {
34
        parent::__construct($writer);
35
 
36
        $this->formulaConvertor = new Formula($this->getParentWriter()->getSpreadsheet()->getDefinedNames());
37
    }
38
 
39
    /**
40
     * Write content.xml to XML format.
41
     *
42
     * @return string XML Output
43
     */
44
    public function write(): string
45
    {
46
        $objWriter = null;
47
        if ($this->getParentWriter()->getUseDiskCaching()) {
48
            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
49
        } else {
50
            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
51
        }
52
 
53
        // XML header
54
        $objWriter->startDocument('1.0', 'UTF-8');
55
 
56
        // Content
57
        $objWriter->startElement('office:document-content');
58
        $objWriter->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0');
59
        $objWriter->writeAttribute('xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0');
60
        $objWriter->writeAttribute('xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0');
61
        $objWriter->writeAttribute('xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0');
62
        $objWriter->writeAttribute('xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0');
63
        $objWriter->writeAttribute('xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0');
64
        $objWriter->writeAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
65
        $objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
66
        $objWriter->writeAttribute('xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0');
67
        $objWriter->writeAttribute('xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0');
68
        $objWriter->writeAttribute('xmlns:presentation', 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0');
69
        $objWriter->writeAttribute('xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0');
70
        $objWriter->writeAttribute('xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0');
71
        $objWriter->writeAttribute('xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0');
72
        $objWriter->writeAttribute('xmlns:math', 'http://www.w3.org/1998/Math/MathML');
73
        $objWriter->writeAttribute('xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0');
74
        $objWriter->writeAttribute('xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0');
75
        $objWriter->writeAttribute('xmlns:ooo', 'http://openoffice.org/2004/office');
76
        $objWriter->writeAttribute('xmlns:ooow', 'http://openoffice.org/2004/writer');
77
        $objWriter->writeAttribute('xmlns:oooc', 'http://openoffice.org/2004/calc');
78
        $objWriter->writeAttribute('xmlns:dom', 'http://www.w3.org/2001/xml-events');
79
        $objWriter->writeAttribute('xmlns:xforms', 'http://www.w3.org/2002/xforms');
80
        $objWriter->writeAttribute('xmlns:xsd', 'http://www.w3.org/2001/XMLSchema');
81
        $objWriter->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
82
        $objWriter->writeAttribute('xmlns:rpt', 'http://openoffice.org/2005/report');
83
        $objWriter->writeAttribute('xmlns:of', 'urn:oasis:names:tc:opendocument:xmlns:of:1.2');
84
        $objWriter->writeAttribute('xmlns:xhtml', 'http://www.w3.org/1999/xhtml');
85
        $objWriter->writeAttribute('xmlns:grddl', 'http://www.w3.org/2003/g/data-view#');
86
        $objWriter->writeAttribute('xmlns:tableooo', 'http://openoffice.org/2009/table');
87
        $objWriter->writeAttribute('xmlns:field', 'urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0');
88
        $objWriter->writeAttribute('xmlns:formx', 'urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0');
89
        $objWriter->writeAttribute('xmlns:css3t', 'http://www.w3.org/TR/css3-text/');
90
        $objWriter->writeAttribute('office:version', '1.2');
91
 
92
        $objWriter->writeElement('office:scripts');
93
        $objWriter->writeElement('office:font-face-decls');
94
 
95
        // Styles XF
96
        $objWriter->startElement('office:automatic-styles');
97
        $this->writeXfStyles($objWriter, $this->getParentWriter()->getSpreadsheet());
98
        $objWriter->endElement();
99
 
100
        $objWriter->startElement('office:body');
101
        $objWriter->startElement('office:spreadsheet');
102
        $objWriter->writeElement('table:calculation-settings');
103
 
104
        $this->writeSheets($objWriter);
105
 
106
        (new AutoFilters($objWriter, $this->getParentWriter()->getSpreadsheet()))->write();
107
        // Defined names (ranges and formulae)
108
        (new NamedExpressions($objWriter, $this->getParentWriter()->getSpreadsheet(), $this->formulaConvertor))->write();
109
 
110
        $objWriter->endElement();
111
        $objWriter->endElement();
112
        $objWriter->endElement();
113
 
114
        return $objWriter->getData();
115
    }
116
 
117
    /**
118
     * Write sheets.
119
     */
120
    private function writeSheets(XMLWriter $objWriter): void
121
    {
122
        $spreadsheet = $this->getParentWriter()->getSpreadsheet(); /** @var Spreadsheet $spreadsheet */
123
        $sheetCount = $spreadsheet->getSheetCount();
124
        for ($sheetIndex = 0; $sheetIndex < $sheetCount; ++$sheetIndex) {
125
            $objWriter->startElement('table:table');
126
            $objWriter->writeAttribute('table:name', $spreadsheet->getSheet($sheetIndex)->getTitle());
127
            $objWriter->writeAttribute('table:style-name', Style::TABLE_STYLE_PREFIX . (string) ($sheetIndex + 1));
128
            $objWriter->writeElement('office:forms');
129
            $lastColumn = 0;
130
            foreach ($spreadsheet->getSheet($sheetIndex)->getColumnDimensions() as $columnDimension) {
131
                $thisColumn = $columnDimension->getColumnNumeric();
132
                $emptyColumns = $thisColumn - $lastColumn - 1;
133
                if ($emptyColumns > 0) {
134
                    $objWriter->startElement('table:table-column');
135
                    $objWriter->writeAttribute('table:number-columns-repeated', (string) $emptyColumns);
136
                    $objWriter->endElement();
137
                }
138
                $lastColumn = $thisColumn;
139
                $objWriter->startElement('table:table-column');
140
                $objWriter->writeAttribute(
141
                    'table:style-name',
142
                    sprintf('%s_%d_%d', Style::COLUMN_STYLE_PREFIX, $sheetIndex, $columnDimension->getColumnNumeric())
143
                );
144
                $objWriter->writeAttribute('table:default-cell-style-name', 'ce0');
145
//                $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX);
146
                $objWriter->endElement();
147
            }
148
            $this->writeRows($objWriter, $spreadsheet->getSheet($sheetIndex), $sheetIndex);
149
            $objWriter->endElement();
150
        }
151
    }
152
 
153
    /**
154
     * Write rows of the specified sheet.
155
     */
156
    private function writeRows(XMLWriter $objWriter, Worksheet $sheet, int $sheetIndex): void
157
    {
158
        $numberRowsRepeated = self::NUMBER_ROWS_REPEATED_MAX;
159
        $span_row = 0;
160
        $rows = $sheet->getRowIterator();
161
        foreach ($rows as $row) {
162
            $cellIterator = $row->getCellIterator();
163
            --$numberRowsRepeated;
164
            if ($cellIterator->valid()) {
165
                $objWriter->startElement('table:table-row');
166
                if ($span_row) {
167
                    if ($span_row > 1) {
168
                        $objWriter->writeAttribute('table:number-rows-repeated', (string) $span_row);
169
                    }
170
                    $objWriter->startElement('table:table-cell');
171
                    $objWriter->writeAttribute('table:number-columns-repeated', (string) self::NUMBER_COLS_REPEATED_MAX);
172
                    $objWriter->endElement();
173
                    $span_row = 0;
174
                } else {
175
                    if ($sheet->getRowDimension($row->getRowIndex())->getRowHeight() > 0) {
176
                        $objWriter->writeAttribute(
177
                            'table:style-name',
178
                            sprintf('%s_%d_%d', Style::ROW_STYLE_PREFIX, $sheetIndex, $row->getRowIndex())
179
                        );
180
                    }
181
                    $this->writeCells($objWriter, $cellIterator);
182
                }
183
                $objWriter->endElement();
184
            } else {
185
                ++$span_row;
186
            }
187
        }
188
    }
189
 
190
    /**
191
     * Write cells of the specified row.
192
     */
193
    private function writeCells(XMLWriter $objWriter, RowCellIterator $cells): void
194
    {
195
        $numberColsRepeated = self::NUMBER_COLS_REPEATED_MAX;
196
        $prevColumn = -1;
197
        foreach ($cells as $cell) {
198
            /** @var \PhpOffice\PhpSpreadsheet\Cell\Cell $cell */
199
            $column = Coordinate::columnIndexFromString($cell->getColumn()) - 1;
200
 
201
            $this->writeCellSpan($objWriter, $column, $prevColumn);
202
            $objWriter->startElement('table:table-cell');
203
            $this->writeCellMerge($objWriter, $cell);
204
 
205
            // Style XF
206
            $style = $cell->getXfIndex();
207
            if ($style !== null) {
208
                $objWriter->writeAttribute('table:style-name', Style::CELL_STYLE_PREFIX . $style);
209
            }
210
 
211
            switch ($cell->getDataType()) {
212
                case DataType::TYPE_BOOL:
213
                    $objWriter->writeAttribute('office:value-type', 'boolean');
214
                    $objWriter->writeAttribute('office:value', $cell->getValue());
215
                    $objWriter->writeElement('text:p', $cell->getValue());
216
 
217
                    break;
218
                case DataType::TYPE_ERROR:
219
                    $objWriter->writeAttribute('table:formula', 'of:=#NULL!');
220
                    $objWriter->writeAttribute('office:value-type', 'string');
221
                    $objWriter->writeAttribute('office:string-value', '');
222
                    $objWriter->writeElement('text:p', '#NULL!');
223
 
224
                    break;
225
                case DataType::TYPE_FORMULA:
226
                    $formulaValue = $cell->getValue();
227
                    if ($this->getParentWriter()->getPreCalculateFormulas()) {
228
                        try {
229
                            $formulaValue = $cell->getCalculatedValue();
230
                        } catch (Exception $e) {
231
                            // don't do anything
232
                        }
233
                    }
234
                    $objWriter->writeAttribute('table:formula', $this->formulaConvertor->convertFormula($cell->getValue()));
235
                    if (is_numeric($formulaValue)) {
236
                        $objWriter->writeAttribute('office:value-type', 'float');
237
                    } else {
238
                        $objWriter->writeAttribute('office:value-type', 'string');
239
                    }
240
                    $objWriter->writeAttribute('office:value', $formulaValue);
241
                    $objWriter->writeElement('text:p', $formulaValue);
242
 
243
                    break;
244
                case DataType::TYPE_NUMERIC:
245
                    $objWriter->writeAttribute('office:value-type', 'float');
246
                    $objWriter->writeAttribute('office:value', $cell->getValue());
247
                    $objWriter->writeElement('text:p', $cell->getValue());
248
 
249
                    break;
250
                case DataType::TYPE_INLINE:
251
                    // break intentionally omitted
252
                case DataType::TYPE_STRING:
253
                    $objWriter->writeAttribute('office:value-type', 'string');
254
                    $objWriter->writeElement('text:p', $cell->getValue());
255
 
256
                    break;
257
            }
258
            Comment::write($objWriter, $cell);
259
            $objWriter->endElement();
260
            $prevColumn = $column;
261
        }
262
 
263
        $numberColsRepeated = $numberColsRepeated - $prevColumn - 1;
264
        if ($numberColsRepeated > 0) {
265
            if ($numberColsRepeated > 1) {
266
                $objWriter->startElement('table:table-cell');
267
                $objWriter->writeAttribute('table:number-columns-repeated', (string) $numberColsRepeated);
268
                $objWriter->endElement();
269
            } else {
270
                $objWriter->writeElement('table:table-cell');
271
            }
272
        }
273
    }
274
 
275
    /**
276
     * Write span.
277
     *
278
     * @param int $curColumn
279
     * @param int $prevColumn
280
     */
281
    private function writeCellSpan(XMLWriter $objWriter, $curColumn, $prevColumn): void
282
    {
283
        $diff = $curColumn - $prevColumn - 1;
284
        if (1 === $diff) {
285
            $objWriter->writeElement('table:table-cell');
286
        } elseif ($diff > 1) {
287
            $objWriter->startElement('table:table-cell');
288
            $objWriter->writeAttribute('table:number-columns-repeated', (string) $diff);
289
            $objWriter->endElement();
290
        }
291
    }
292
 
293
    /**
294
     * Write XF cell styles.
295
     */
296
    private function writeXfStyles(XMLWriter $writer, Spreadsheet $spreadsheet): void
297
    {
298
        $styleWriter = new Style($writer);
299
 
300
        $sheetCount = $spreadsheet->getSheetCount();
301
        for ($i = 0; $i < $sheetCount; ++$i) {
302
            $worksheet = $spreadsheet->getSheet($i);
303
            $styleWriter->writeTableStyle($worksheet, $i + 1);
304
 
305
            $worksheet->calculateColumnWidths();
306
            foreach ($worksheet->getColumnDimensions() as $columnDimension) {
307
                if ($columnDimension->getWidth() !== -1.0) {
308
                    $styleWriter->writeColumnStyles($columnDimension, $i);
309
                }
310
            }
311
        }
312
        for ($i = 0; $i < $sheetCount; ++$i) {
313
            $worksheet = $spreadsheet->getSheet($i);
314
            foreach ($worksheet->getRowDimensions() as $rowDimension) {
315
                if ($rowDimension->getRowHeight() > 0.0) {
316
                    $styleWriter->writeRowStyles($rowDimension, $i);
317
                }
318
            }
319
        }
320
 
321
        foreach ($spreadsheet->getCellXfCollection() as $style) {
322
            $styleWriter->write($style);
323
        }
324
    }
325
 
326
    /**
327
     * Write attributes for merged cell.
328
     */
329
    private function writeCellMerge(XMLWriter $objWriter, Cell $cell): void
330
    {
331
        if (!$cell->isMergeRangeValueCell()) {
332
            return;
333
        }
334
 
335
        $mergeRange = Coordinate::splitRange((string) $cell->getMergeRange());
336
        [$startCell, $endCell] = $mergeRange[0];
337
        $start = Coordinate::coordinateFromString($startCell);
338
        $end = Coordinate::coordinateFromString($endCell);
339
        $columnSpan = Coordinate::columnIndexFromString($end[0]) - Coordinate::columnIndexFromString($start[0]) + 1;
340
        $rowSpan = ((int) $end[1]) - ((int) $start[1]) + 1;
341
 
342
        $objWriter->writeAttribute('table:number-columns-spanned', (string) $columnSpan);
343
        $objWriter->writeAttribute('table:number-rows-spanned', (string) $rowSpan);
344
    }
345
}