Completed
Push — develop ( 962367...96f3f6 )
by Adrien
30:43
created

Content::writeCells()   C

Complexity

Conditions 14
Paths 67

Size

Total Lines 77
Code Lines 59

Duplication

Lines 17
Ratio 22.08 %

Code Coverage

Tests 50
CRAP Score 14.1472

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 14
eloc 59
c 3
b 0
f 0
nc 67
nop 2
dl 17
loc 77
ccs 50
cts 55
cp 0.9091
crap 14.1472
rs 5.2115

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Style\Fill;
11
use PhpOffice\PhpSpreadsheet\Style\Font;
12
use PhpOffice\PhpSpreadsheet\Worksheet\Row;
13
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
14
use PhpOffice\PhpSpreadsheet\Writer\Exception;
15
use PhpOffice\PhpSpreadsheet\Writer\Ods;
16
use PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Comment;
17
18
/**
19
 * @category   PhpSpreadsheet
20
 *
21
 * @method Ods getParentWriter
22
 *
23
 * @copyright  Copyright (c) 2006 - 2015 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
24
 * @author     Alexander Pervakov <[email protected]>
25
 */
26
class Content extends WriterPart
27
{
28
    const NUMBER_COLS_REPEATED_MAX = 1024;
29
    const NUMBER_ROWS_REPEATED_MAX = 1048576;
30
    const CELL_STYLE_PREFIX = 'ce';
31
32
    /**
33
     * Write content.xml to XML format.
34
     *
35
     * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
36
     *
37
     * @return string XML Output
38
     */
39 2
    public function write()
40
    {
41 2
        $objWriter = null;
42 2 View Code Duplication
        if ($this->getParentWriter()->getUseDiskCaching()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
43
            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
44
        } else {
45 2
            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
46
        }
47
48
        // XML header
49 2
        $objWriter->startDocument('1.0', 'UTF-8');
50
51
        // Content
52 2
        $objWriter->startElement('office:document-content');
53 2
        $objWriter->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0');
54 2
        $objWriter->writeAttribute('xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0');
55 2
        $objWriter->writeAttribute('xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0');
56 2
        $objWriter->writeAttribute('xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0');
57 2
        $objWriter->writeAttribute('xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0');
58 2
        $objWriter->writeAttribute('xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0');
59 2
        $objWriter->writeAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
60 2
        $objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
61 2
        $objWriter->writeAttribute('xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0');
62 2
        $objWriter->writeAttribute('xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0');
63 2
        $objWriter->writeAttribute('xmlns:presentation', 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0');
64 2
        $objWriter->writeAttribute('xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0');
65 2
        $objWriter->writeAttribute('xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0');
66 2
        $objWriter->writeAttribute('xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0');
67 2
        $objWriter->writeAttribute('xmlns:math', 'http://www.w3.org/1998/Math/MathML');
68 2
        $objWriter->writeAttribute('xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0');
69 2
        $objWriter->writeAttribute('xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0');
70 2
        $objWriter->writeAttribute('xmlns:ooo', 'http://openoffice.org/2004/office');
71 2
        $objWriter->writeAttribute('xmlns:ooow', 'http://openoffice.org/2004/writer');
72 2
        $objWriter->writeAttribute('xmlns:oooc', 'http://openoffice.org/2004/calc');
73 2
        $objWriter->writeAttribute('xmlns:dom', 'http://www.w3.org/2001/xml-events');
74 2
        $objWriter->writeAttribute('xmlns:xforms', 'http://www.w3.org/2002/xforms');
75 2
        $objWriter->writeAttribute('xmlns:xsd', 'http://www.w3.org/2001/XMLSchema');
76 2
        $objWriter->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
77 2
        $objWriter->writeAttribute('xmlns:rpt', 'http://openoffice.org/2005/report');
78 2
        $objWriter->writeAttribute('xmlns:of', 'urn:oasis:names:tc:opendocument:xmlns:of:1.2');
79 2
        $objWriter->writeAttribute('xmlns:xhtml', 'http://www.w3.org/1999/xhtml');
80 2
        $objWriter->writeAttribute('xmlns:grddl', 'http://www.w3.org/2003/g/data-view#');
81 2
        $objWriter->writeAttribute('xmlns:tableooo', 'http://openoffice.org/2009/table');
82 2
        $objWriter->writeAttribute('xmlns:field', 'urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0');
83 2
        $objWriter->writeAttribute('xmlns:formx', 'urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0');
84 2
        $objWriter->writeAttribute('xmlns:css3t', 'http://www.w3.org/TR/css3-text/');
85 2
        $objWriter->writeAttribute('office:version', '1.2');
86
87 2
        $objWriter->writeElement('office:scripts');
88 2
        $objWriter->writeElement('office:font-face-decls');
89
90
        // Styles XF
91 2
        $objWriter->startElement('office:automatic-styles');
92 2
        $this->writeXfStyles($objWriter, $this->getParentWriter()->getSpreadsheet());
93 2
        $objWriter->endElement();
94
95 2
        $objWriter->startElement('office:body');
96 2
        $objWriter->startElement('office:spreadsheet');
97 2
        $objWriter->writeElement('table:calculation-settings');
98
99 2
        $this->writeSheets($objWriter);
100
101 2
        $objWriter->writeElement('table:named-expressions');
102 2
        $objWriter->endElement();
103 2
        $objWriter->endElement();
104 2
        $objWriter->endElement();
105
106 2
        return $objWriter->getData();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $objWriter->getData() could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
107
    }
108
109
    /**
110
     * Write sheets.
111
     *
112
     * @param XMLWriter $objWriter
113
     */
114 2
    private function writeSheets(XMLWriter $objWriter)
115
    {
116 2
        $spreadsheet = $this->getParentWriter()->getSpreadsheet(); // @var $spreadsheet Spreadsheet
117
118 2
        $sheetCount = $spreadsheet->getSheetCount();
119 2
        for ($i = 0; $i < $sheetCount; ++$i) {
120 2
            $objWriter->startElement('table:table');
121 2
            $objWriter->writeAttribute('table:name', $spreadsheet->getSheet($i)->getTitle());
122 2
            $objWriter->writeElement('office:forms');
123 2
            $objWriter->startElement('table:table-column');
124 2
            $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX);
125 2
            $objWriter->endElement();
126 2
            $this->writeRows($objWriter, $spreadsheet->getSheet($i));
127 2
            $objWriter->endElement();
128
        }
129 2
    }
130
131
    /**
132
     * Write rows of the specified sheet.
133
     *
134
     * @param XMLWriter $objWriter
135
     * @param Worksheet $sheet
136
     */
137 2
    private function writeRows(XMLWriter $objWriter, Worksheet $sheet)
138
    {
139 2
        $numberRowsRepeated = self::NUMBER_ROWS_REPEATED_MAX;
140 2
        $span_row = 0;
141 2
        $rows = $sheet->getRowIterator();
142 2
        while ($rows->valid()) {
143 2
            --$numberRowsRepeated;
144 2
            $row = $rows->current();
145 2
            if ($row->getCellIterator()->valid()) {
146 2
                if ($span_row) {
147
                    $objWriter->startElement('table:table-row');
148
                    if ($span_row > 1) {
149
                        $objWriter->writeAttribute('table:number-rows-repeated', $span_row);
150
                    }
151
                    $objWriter->startElement('table:table-cell');
152
                    $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX);
153
                    $objWriter->endElement();
154
                    $objWriter->endElement();
155
                    $span_row = 0;
156
                }
157 2
                $objWriter->startElement('table:table-row');
158 2
                $this->writeCells($objWriter, $row);
159 2
                $objWriter->endElement();
160
            } else {
161
                ++$span_row;
162
            }
163 2
            $rows->next();
164
        }
165 2
    }
166
167
    /**
168
     * Write cells of the specified row.
169
     *
170
     * @param XMLWriter $objWriter
171
     * @param Row $row
172
     *
173
     * @throws Exception
174
     */
175 2
    private function writeCells(XMLWriter $objWriter, Row $row)
176
    {
177 2
        $numberColsRepeated = self::NUMBER_COLS_REPEATED_MAX;
178 2
        $prevColumn = -1;
179 2
        $cells = $row->getCellIterator();
180 2
        while ($cells->valid()) {
181
            /** @var \PhpOffice\PhpSpreadsheet\Cell\Cell $cell */
182 2
            $cell = $cells->current();
183 2
            $column = Coordinate::columnIndexFromString($cell->getColumn()) - 1;
184
185 2
            $this->writeCellSpan($objWriter, $column, $prevColumn);
186 2
            $objWriter->startElement('table:table-cell');
187 2
            $this->writeCellMerge($objWriter, $cell);
188
189
            // Style XF
190 2
            $style = $cell->getXfIndex();
191 2
            if ($style !== null) {
192 2
                $objWriter->writeAttribute('table:style-name', self::CELL_STYLE_PREFIX . $style);
193
            }
194
195 2
            switch ($cell->getDataType()) {
196 2 View Code Duplication
                case DataType::TYPE_BOOL:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
197 1
                    $objWriter->writeAttribute('office:value-type', 'boolean');
198 1
                    $objWriter->writeAttribute('office:value', $cell->getValue());
199 1
                    $objWriter->writeElement('text:p', $cell->getValue());
200
201 1
                    break;
202 2
                case DataType::TYPE_ERROR:
203
                    throw new Exception('Writing of error not implemented yet.');
204
                    break;
205 2
                case DataType::TYPE_FORMULA:
206 1
                    $formulaValue = $cell->getValue();
207 1
                    if ($this->getParentWriter()->getPreCalculateFormulas()) {
208
                        try {
209 1
                            $formulaValue = $cell->getCalculatedValue();
210
                        } catch (Exception $e) {
211
                            // don't do anything
212
                        }
213
                    }
214 1
                    $objWriter->writeAttribute('table:formula', 'of:' . $cell->getValue());
215 1
                    if (is_numeric($formulaValue)) {
216
                        $objWriter->writeAttribute('office:value-type', 'float');
217
                    } else {
218 1
                        $objWriter->writeAttribute('office:value-type', 'string');
219
                    }
220 1
                    $objWriter->writeAttribute('office:value', $formulaValue);
221 1
                    $objWriter->writeElement('text:p', $formulaValue);
222
223 1
                    break;
224 2
                case DataType::TYPE_INLINE:
225
                    throw new Exception('Writing of inline not implemented yet.');
226
                    break;
227 2 View Code Duplication
                case DataType::TYPE_NUMERIC:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
228 1
                    $objWriter->writeAttribute('office:value-type', 'float');
229 1
                    $objWriter->writeAttribute('office:value', $cell->getValue());
230 1
                    $objWriter->writeElement('text:p', $cell->getValue());
231
232 1
                    break;
233 2
                case DataType::TYPE_STRING:
234 1
                    $objWriter->writeAttribute('office:value-type', 'string');
235 1
                    $objWriter->writeElement('text:p', $cell->getValue());
236
237 1
                    break;
238
            }
239 2
            Comment::write($objWriter, $cell);
240 2
            $objWriter->endElement();
241 2
            $prevColumn = $column;
242 2
            $cells->next();
243
        }
244 2
        $numberColsRepeated = $numberColsRepeated - $prevColumn - 1;
245 2 View Code Duplication
        if ($numberColsRepeated > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
246 2
            if ($numberColsRepeated > 1) {
247 2
                $objWriter->startElement('table:table-cell');
248 2
                $objWriter->writeAttribute('table:number-columns-repeated', $numberColsRepeated);
249 2
                $objWriter->endElement();
250
            } else {
251
                $objWriter->writeElement('table:table-cell');
252
            }
253
        }
254 2
    }
255
256
    /**
257
     * Write span.
258
     *
259
     * @param XMLWriter $objWriter
260
     * @param int $curColumn
261
     * @param int $prevColumn
262
     */
263 2
    private function writeCellSpan(XMLWriter $objWriter, $curColumn, $prevColumn)
264
    {
265 2
        $diff = $curColumn - $prevColumn - 1;
266 2 View Code Duplication
        if (1 === $diff) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
267
            $objWriter->writeElement('table:table-cell');
268 2
        } elseif ($diff > 1) {
269
            $objWriter->startElement('table:table-cell');
270
            $objWriter->writeAttribute('table:number-columns-repeated', $diff);
271
            $objWriter->endElement();
272
        }
273 2
    }
274
275
    /**
276
     * Write XF cell styles.
277
     *
278
     * @param XMLWriter $writer
279
     * @param Spreadsheet $spreadsheet
280
     */
281 2
    private function writeXfStyles(XMLWriter $writer, Spreadsheet $spreadsheet)
282
    {
283 2
        foreach ($spreadsheet->getCellXfCollection() as $style) {
284 2
            $writer->startElement('style:style');
285 2
            $writer->writeAttribute('style:name', self::CELL_STYLE_PREFIX . $style->getIndex());
286 2
            $writer->writeAttribute('style:family', 'table-cell');
287 2
            $writer->writeAttribute('style:parent-style-name', 'Default');
288
289
            // style:text-properties
290
291
            // Font
292 2
            $writer->startElement('style:text-properties');
293
294 2
            $font = $style->getFont();
295
296 2
            if ($font->getBold()) {
297 1
                $writer->writeAttribute('fo:font-weight', 'bold');
298 1
                $writer->writeAttribute('style:font-weight-complex', 'bold');
299 1
                $writer->writeAttribute('style:font-weight-asian', 'bold');
300
            }
301
302 2
            if ($font->getItalic()) {
303 1
                $writer->writeAttribute('fo:font-style', 'italic');
304
            }
305
306 2
            if ($color = $font->getColor()) {
307 2
                $writer->writeAttribute('fo:color', sprintf('#%s', $color->getRGB()));
308
            }
309
310 2
            if ($family = $font->getName()) {
311 2
                $writer->writeAttribute('fo:font-family', $family);
312
            }
313
314 2
            if ($size = $font->getSize()) {
315 2
                $writer->writeAttribute('fo:font-size', sprintf('%.1fpt', $size));
316
            }
317
318 2
            if ($font->getUnderline() && $font->getUnderline() != Font::UNDERLINE_NONE) {
319 1
                $writer->writeAttribute('style:text-underline-style', 'solid');
320 1
                $writer->writeAttribute('style:text-underline-width', 'auto');
321 1
                $writer->writeAttribute('style:text-underline-color', 'font-color');
322
323 1
                switch ($font->getUnderline()) {
324 1
                    case Font::UNDERLINE_DOUBLE:
325 1
                        $writer->writeAttribute('style:text-underline-type', 'double');
326
327 1
                        break;
328 1
                    case Font::UNDERLINE_SINGLE:
329 1
                        $writer->writeAttribute('style:text-underline-type', 'single');
330
331 1
                        break;
332
                }
333
            }
334
335 2
            $writer->endElement(); // Close style:text-properties
336
337
            // style:table-cell-properties
338
339 2
            $writer->startElement('style:table-cell-properties');
340 2
            $writer->writeAttribute('style:rotation-align', 'none');
341
342
            // Fill
343 2
            if ($fill = $style->getFill()) {
344 2
                switch ($fill->getFillType()) {
345 2
                    case Fill::FILL_SOLID:
346 1
                        $writer->writeAttribute('fo:background-color', sprintf(
347 1
                            '#%s',
348 1
                            strtolower($fill->getStartColor()->getRGB())
349
                        ));
350
351 1
                        break;
352 2
                    case Fill::FILL_GRADIENT_LINEAR:
353 2
                    case Fill::FILL_GRADIENT_PATH:
354
                        /// TODO :: To be implemented
355
                        break;
356 2
                    case Fill::FILL_NONE:
357
                    default:
358
                }
359
            }
360
361 2
            $writer->endElement(); // Close style:table-cell-properties
362
363
            // End
364
365 2
            $writer->endElement(); // Close style:style
366
        }
367 2
    }
368
369
    /**
370
     * Write attributes for merged cell.
371
     *
372
     * @param XMLWriter $objWriter
373
     * @param Cell $cell
374
     *
375
     * @throws \PhpOffice\PhpSpreadsheet\Exception
376
     */
377 2
    private function writeCellMerge(XMLWriter $objWriter, Cell $cell)
378
    {
379 2
        if (!$cell->isMergeRangeValueCell()) {
380 2
            return;
381
        }
382
383
        $mergeRange = Coordinate::splitRange($cell->getMergeRange());
384
        list($startCell, $endCell) = $mergeRange[0];
385
        $start = Coordinate::coordinateFromString($startCell);
386
        $end = Coordinate::coordinateFromString($endCell);
387
        $columnSpan = Coordinate::columnIndexFromString($end[0]) - Coordinate::columnIndexFromString($start[0]) + 1;
388
        $rowSpan = $end[1] - $start[1] + 1;
389
390
        $objWriter->writeAttribute('table:number-columns-spanned', $columnSpan);
391
        $objWriter->writeAttribute('table:number-rows-spanned', $rowSpan);
392
    }
393
}
394