Completed
Pull Request — master (#808)
by
unknown
11:17
created

StyleManager   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 439
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 98.26%

Importance

Changes 0
Metric Value
wmc 50
lcom 1
cbo 9
dl 0
loc 439
ccs 113
cts 115
cp 0.9826
rs 8.4
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A getStylesXMLFileContent() 0 18 1
A getFontFaceSectionContent() 0 10 2
A getStylesSectionContent() 0 18 1
A getAutomaticStylesSectionContent() 0 18 2
A getMasterStylesSectionContent() 0 19 2
A getContentXmlFontFaceSectionContent() 0 10 2
B getContentXmlAutomaticStylesSectionContent() 0 46 9
A getStyleSectionContent() 0 14 1
A getTextPropertiesSectionContent() 0 10 2
B getFontSectionContent() 0 35 8
A getCellAlignmentSectionContent() 0 7 1
A transformCellAlignment() 0 11 3
A getTableCellPropertiesSectionContent() 0 20 4
A getWrapTextXMLContent() 0 4 1
A getBorderXMLContent() 0 8 1
A getBackgroundColorXMLContent() 0 4 1
A getTableColumnStylesXMLContent() 0 17 3
A getStyledTableColumnXMLContent() 0 19 3
A getParagraphPropertiesSectionContent() 0 10 2

How to fix   Complexity   

Complex Class

Complex classes like StyleManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use StyleManager, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Box\Spout\Writer\ODS\Manager\Style;
4
5
use Box\Spout\Common\Entity\Style\BorderPart;
6
use Box\Spout\Common\Entity\Style\CellAlignment;
7
use Box\Spout\Common\Manager\OptionsManagerInterface;
8
use Box\Spout\Writer\Common\Entity\Options;
9
use Box\Spout\Writer\Common\Entity\Worksheet;
10
use Box\Spout\Writer\Common\Manager\ManagesCellSize;
11
use Box\Spout\Writer\ODS\Helper\BorderHelper;
12
13
/**
14
 * Class StyleManager
15
 * Manages styles to be applied to a cell
16
 */
17
class StyleManager extends \Box\Spout\Writer\Common\Manager\Style\StyleManager
18
{
19
    use ManagesCellSize;
20
21
    /** @var StyleRegistry */
22
    protected $styleRegistry;
23
24
    /**
25 36
     * @param StyleRegistry $styleRegistry
26
     */
27
    public function __construct(StyleRegistry $styleRegistry, OptionsManagerInterface $optionsManager)
28 36
    {
29
        parent::__construct($styleRegistry);
30
        $this->setDefaultColumnWidth($optionsManager->getOption(Options::DEFAULT_COLUMN_WIDTH));
31
        $this->setDefaultRowHeight($optionsManager->getOption(Options::DEFAULT_ROW_HEIGHT));
32 36
        $this->columnWidths = $optionsManager->getOption(Options::COLUMN_WIDTHS) ?? [];
0 ignored issues
show
Documentation Bug introduced by
It seems like $optionsManager->getOpti...LUMN_WIDTHS) ?? array() of type * is incompatible with the declared type array of property $columnWidths.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
33 36
    }
34 36
35 36
    /**
36
     * Returns the content of the "styles.xml" file, given a list of styles.
37
     *
38 36
     * @param int $numWorksheets Number of worksheets created
39
     * @return string
40
     */
41 36
    public function getStylesXMLFileContent($numWorksheets)
42
    {
43
        $content = <<<'EOD'
44
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
45
<office:document-styles office:version="1.2" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:msoxl="http://schemas.microsoft.com/office/excel/formula" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:xlink="http://www.w3.org/1999/xlink">
46
EOD;
47
48
        $content .= $this->getFontFaceSectionContent();
49 36
        $content .= $this->getStylesSectionContent();
50
        $content .= $this->getAutomaticStylesSectionContent($numWorksheets);
51 36
        $content .= $this->getMasterStylesSectionContent($numWorksheets);
52 36
53 36
        $content .= <<<'EOD'
54
</office:document-styles>
55 36
EOD;
56
57 36
        return $content;
58
    }
59
60
    /**
61
     * Returns the content of the "<office:font-face-decls>" section, inside "styles.xml" file.
62
     *
63
     * @return string
64
     */
65 36
    protected function getFontFaceSectionContent()
66
    {
67 36
        $content = '<office:font-face-decls>';
68
        foreach ($this->styleRegistry->getUsedFonts() as $fontName) {
69
            $content .= '<style:font-face style:name="' . $fontName . '" svg:font-family="' . $fontName . '"/>';
70
        }
71
        $content .= '</office:font-face-decls>';
72
73
        return $content;
74
    }
75
76 36
    /**
77 36
     * Returns the content of the "<office:styles>" section, inside "styles.xml" file.
78 36
     *
79
     * @return string
80
     */
81
    protected function getStylesSectionContent()
82
    {
83
        $defaultStyle = $this->getDefaultStyle();
84
85
        return <<<EOD
86
<office:styles>
87
    <number:number-style style:name="N0">
88
        <number:number number:min-integer-digits="1"/>
89
    </number:number-style>
90 36
    <style:style style:data-style-name="N0" style:family="table-cell" style:name="Default">
91
        <style:table-cell-properties fo:background-color="transparent" style:vertical-align="automatic"/>
92 36
        <style:text-properties fo:color="#{$defaultStyle->getFontColor()}"
93
                               fo:font-size="{$defaultStyle->getFontSize()}pt" style:font-size-asian="{$defaultStyle->getFontSize()}pt" style:font-size-complex="{$defaultStyle->getFontSize()}pt"
94 36
                               style:font-name="{$defaultStyle->getFontName()}" style:font-name-asian="{$defaultStyle->getFontName()}" style:font-name-complex="{$defaultStyle->getFontName()}"/>
95
    </style:style>
96 36
</office:styles>
97
EOD;
98
    }
99
100
    /**
101
     * Returns the content of the "<office:automatic-styles>" section, inside "styles.xml" file.
102
     *
103
     * @param int $numWorksheets Number of worksheets created
104 36
     * @return string
105
     */
106 36
    protected function getAutomaticStylesSectionContent($numWorksheets)
107
    {
108
        $content = '<office:automatic-styles>';
109
110
        for ($i = 1; $i <= $numWorksheets; $i++) {
111
            $content .= <<<EOD
112
<style:page-layout style:name="pm$i">
113
    <style:page-layout-properties style:first-page-number="continue" style:print="objects charts drawings" style:table-centering="none"/>
114
    <style:header-style/>
115 36
    <style:footer-style/>
116
</style:page-layout>
117 36
EOD;
118
        }
119 36
120
        $content .= '</office:automatic-styles>';
121 36
122
        return $content;
123
    }
124
125
    /**
126
     * Returns the content of the "<office:master-styles>" section, inside "styles.xml" file.
127
     *
128
     * @param int $numWorksheets Number of worksheets created
129
     * @return string
130 36
     */
131
    protected function getMasterStylesSectionContent($numWorksheets)
132 36
    {
133
        $content = '<office:master-styles>';
134
135
        for ($i = 1; $i <= $numWorksheets; $i++) {
136
            $content .= <<<EOD
137
<style:master-page style:name="mp$i" style:page-layout-name="pm$i">
138
    <style:header/>
139
    <style:header-left style:display="false"/>
140 36
    <style:footer/>
141
    <style:footer-left style:display="false"/>
142 36
</style:master-page>
143 36
EOD;
144 36
        }
145
146 36
        $content .= '</office:master-styles>';
147
148 36
        return $content;
149
    }
150
151
    /**
152
     * Returns the contents of the "<office:font-face-decls>" section, inside "content.xml" file.
153
     *
154
     * @return string
155
     */
156
    public function getContentXmlFontFaceSectionContent()
157 36
    {
158
        $content = '<office:font-face-decls>';
159 36
        foreach ($this->styleRegistry->getUsedFonts() as $fontName) {
160
            $content .= '<style:font-face style:name="' . $fontName . '" svg:font-family="' . $fontName . '"/>';
161 36
        }
162 36
        $content .= '</office:font-face-decls>';
163
164
        return $content;
165
    }
166 36
167
    /**
168
     * Returns the contents of the "<office:automatic-styles>" section, inside "content.xml" file.
169
     *
170
     * @param Worksheet[] $worksheets
171
     * @return string
172
     */
173
    public function getContentXmlAutomaticStylesSectionContent($worksheets)
174 36
    {
175 36
        $content = '<office:automatic-styles>';
176 36
177
        foreach ($this->styleRegistry->getRegisteredStyles() as $style) {
178
            $content .= $this->getStyleSectionContent($style);
179 36
        }
180 36
181
        $useOptimalRowHeight = empty($this->defaultRowHeight) ? 'true' : 'false';
182
        $defaultRowHeight = empty($this->defaultRowHeight) ? '15pt' : "{$this->defaultRowHeight}pt";
183
        $defaultColumnWidth = empty($this->defaultColumnWidth) ? '' : "style:column-width=\"{$this->defaultColumnWidth}pt\"";
184
185 36
        $content .= <<<EOD
186
<style:style style:family="table-column" style:name="default-column-style">
187 36
    <style:table-column-properties fo:break-before="auto" {$defaultColumnWidth}/>
188
</style:style>
189
<style:style style:family="table-row" style:name="ro1">
190
    <style:table-row-properties fo:break-before="auto" style:row-height="{$defaultRowHeight}" style:use-optimal-row-height="{$useOptimalRowHeight}"/>
191
</style:style>
192
EOD;
193
194
        foreach ($worksheets as $worksheet) {
195
            $worksheetId = $worksheet->getId();
196 36
            $isSheetVisible = $worksheet->getExternalSheet()->isVisible() ? 'true' : 'false';
197
198 36
            $content .= <<<EOD
199
<style:style style:family="table" style:master-page-name="mp$worksheetId" style:name="ta$worksheetId">
200 36
    <style:table-properties style:writing-mode="lr-tb" table:display="$isSheetVisible"/>
201
</style:style>
202 36
EOD;
203 36
        }
204 36
205
        // Sort column widths since ODS cares about order
206 36
        usort($this->columnWidths, function ($a, $b) {
207
            if ($a[0] === $b[0]) {
208 36
                return 0;
209
            }
210
211
            return ($a[0] < $b[0]) ? -1 : 1;
212
        });
213
        $content .= $this->getTableColumnStylesXMLContent();
214
215
        $content .= '</office:automatic-styles>';
216
217 36
        return $content;
218
    }
219 36
220 35
    /**
221
     * Returns the contents of the "<style:style>" section, inside "<office:automatic-styles>" section
222
     *
223
     * @param \Box\Spout\Common\Entity\Style\Style $style
224 5
     * @return string
225 5
     */
226
    protected function getStyleSectionContent($style)
227
    {
228
        $styleIndex = $style->getId() + 1; // 1-based
229
230
        $content = '<style:style style:data-style-name="N0" style:family="table-cell" style:name="ce' . $styleIndex . '" style:parent-style-name="Default">';
231
232
        $content .= $this->getTextPropertiesSectionContent($style);
233
        $content .= $this->getParagraphPropertiesSectionContent($style);
234
        $content .= $this->getTableCellPropertiesSectionContent($style);
235 5
236
        $content .= '</style:style>';
237 5
238 5
        return $content;
239
    }
240 5
241 5
    /**
242 1
     * Returns the contents of the "<style:text-properties>" section, inside "<style:style>" section
243
     *
244
     * @param \Box\Spout\Common\Entity\Style\Style $style
245 5
     * @return string
246 5
     */
247 1
    private function getTextPropertiesSectionContent($style)
248
    {
249
        if (!$style->shouldApplyFont()) {
250 5
            return '';
251 5
        }
252 2
253
        return '<style:text-properties '
254
            . $this->getFontSectionContent($style)
255 5
            . '/>';
256 4
    }
257
258 5
    /**
259 1
     * Returns the contents of the fonts definition section, inside "<style:text-properties>" section
260
     *
261 5
     * @param \Box\Spout\Common\Entity\Style\Style $style
262 2
     *
263
     * @return string
264 5
     */
265 1
    private function getFontSectionContent($style)
266
    {
267
        $defaultStyle = $this->getDefaultStyle();
268 5
        $content = '';
269
270
        $fontColor = $style->getFontColor();
271
        if ($fontColor !== $defaultStyle->getFontColor()) {
272
            $content .= ' fo:color="#' . $fontColor . '"';
273
        }
274
275
        $fontName = $style->getFontName();
276
        if ($fontName !== $defaultStyle->getFontName()) {
277
            $content .= ' style:font-name="' . $fontName . '" style:font-name-asian="' . $fontName . '" style:font-name-complex="' . $fontName . '"';
278 36
        }
279
280 36
        $fontSize = $style->getFontSize();
281 36
        if ($fontSize !== $defaultStyle->getFontSize()) {
282
            $content .= ' fo:font-size="' . $fontSize . 'pt" style:font-size-asian="' . $fontSize . 'pt" style:font-size-complex="' . $fontSize . 'pt"';
283
        }
284
285 1
        if ($style->isFontBold()) {
286 1
            $content .= ' fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"';
287
        }
288
        if ($style->isFontItalic()) {
289
            $content .= ' fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic"';
290
        }
291
        if ($style->isFontUnderline()) {
292
            $content .= ' style:text-underline-style="solid" style:text-underline-type="single"';
293
        }
294
        if ($style->isFontStrikethrough()) {
295
            $content .= ' style:text-line-through-style="solid"';
296 1
        }
297
298 1
        return $content;
299 1
    }
300 1
301
    /**
302
     * Returns the contents of the "<style:paragraph-properties>" section, inside "<style:style>" section
303
     *
304
     * @param \Box\Spout\Common\Entity\Style\Style $style
305
     *
306
     * @return string
307
     */
308
    private function getParagraphPropertiesSectionContent($style)
309
    {
310
        if (!$style->shouldApplyCellAlignment()) {
311
            return '';
312
        }
313 1
314
        return '<style:paragraph-properties '
315 1
            . $this->getCellAlignmentSectionContent($style)
316
            . '/>';
317 1
    }
318
319
    /**
320
     * Returns the contents of the cell alignment definition for the "<style:paragraph-properties>" section
321
     *
322
     * @param \Box\Spout\Common\Entity\Style\Style $style
323
     *
324
     * @return string
325
     */
326
    private function getCellAlignmentSectionContent($style)
327
    {
328 36
        return \sprintf(
329
            ' fo:text-align="%s" ',
330 36
            $this->transformCellAlignment($style->getCellAlignment())
331
        );
332 36
    }
333 3
334
    /**
335
     * Even though "left" and "right" alignments are part of the spec, and interpreted
336 36
     * respectively as "start" and "end", using the recommended values increase compatibility
337 1
     * with software that will read the created ODS file.
338
     *
339
     * @param string $cellAlignment
340 36
     *
341 2
     * @return string
342
     */
343
    private function transformCellAlignment($cellAlignment)
344 36
    {
345
        switch ($cellAlignment) {
346 36
            case CellAlignment::LEFT:
347
                return 'start';
348
            case CellAlignment::RIGHT:
349
                return 'end';
350
            default:
351
                return $cellAlignment;
352
        }
353
    }
354 3
355
    /**
356 3
     * Returns the contents of the "<style:table-cell-properties>" section, inside "<style:style>" section
357
     *
358
     * @param \Box\Spout\Common\Entity\Style\Style $style
359
     * @return string
360
     */
361
    private function getTableCellPropertiesSectionContent($style)
362
    {
363
        $content = '<style:table-cell-properties ';
364
365 1
        if ($style->shouldWrapText()) {
366
            $content .= $this->getWrapTextXMLContent();
367
        }
368 1
369 1
        if ($style->shouldApplyBorder()) {
370
            $content .= $this->getBorderXMLContent($style);
371 1
        }
372
373
        if ($style->shouldApplyBackgroundColor()) {
374
            $content .= $this->getBackgroundColorXMLContent($style);
375
        }
376
377
        $content .= '/>';
378
379
        return $content;
380 2
    }
381
382 2
    /**
383
     * Returns the contents of the wrap text definition for the "<style:table-cell-properties>" section
384
     *
385
     * @return string
386
     */
387
    private function getWrapTextXMLContent()
388
    {
389
        return ' fo:wrap-option="wrap" style:vertical-align="automatic" ';
390
    }
391
392
    /**
393
     * Returns the contents of the borders definition for the "<style:table-cell-properties>" section
394
     *
395
     * @param \Box\Spout\Common\Entity\Style\Style $style
396
     * @return string
397
     */
398
    private function getBorderXMLContent($style)
399
    {
400
        $borders = \array_map(function (BorderPart $borderPart) {
401
            return BorderHelper::serializeBorderPart($borderPart);
402
        }, $style->getBorder()->getParts());
403
404
        return \sprintf(' %s ', \implode(' ', $borders));
405
    }
406
407
    /**
408
     * Returns the contents of the background color definition for the "<style:table-cell-properties>" section
409
     *
410
     * @param \Box\Spout\Common\Entity\Style\Style $style
411
     * @return string
412
     */
413
    private function getBackgroundColorXMLContent($style)
414
    {
415
        return \sprintf(' fo:background-color="#%s" ', $style->getBackgroundColor());
416
    }
417
418
    public function getTableColumnStylesXMLContent() : string
419
    {
420
        if (empty($this->columnWidths)) {
421
            return '';
422
        }
423
424
        $content = '';
425
        foreach ($this->columnWidths as $styleIndex => $entry) {
426
            $content .= <<<EOD
427
<style:style style:family="table-column" style:name="co{$styleIndex}">
428
    <style:table-column-properties fo:break-before="auto" style:use-optimal-column-width="false" style:column-width="{$entry[2]}pt"/>
429
</style:style>
430
EOD;
431
        }
432
433
        return $content;
434
    }
435
436
    public function getStyledTableColumnXMLContent(int $maxNumColumns) : string
437
    {
438
        if (empty($this->columnWidths)) {
439
            return '';
440
        }
441
442
        $content = '';
443
        foreach ($this->columnWidths as $styleIndex => $entry) {
444
            $numCols = $entry[1] - $entry[0] + 1;
445
            $content .= <<<EOD
446
<table:table-column table:default-cell-style-name='Default' table:style-name="co{$styleIndex}" table:number-columns-repeated="{$numCols}"/>
447
EOD;
448
        }
449
        // Note: This assumes the column widths are contiguous and default width is
450
        // only applied to columns after the last custom column with a custom width
451
        $content .= '<table:table-column table:default-cell-style-name="ce1" table:style-name="default-column-style" table:number-columns-repeated="' . ($maxNumColumns - $entry[1]) . '"/>';
0 ignored issues
show
Bug introduced by
The variable $entry seems to be defined by a foreach iteration on line 443. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
452
453
        return $content;
454
    }
455
}
456