Failed Conditions
Pull Request — develop_3.0 (#594)
by
unknown
04:29
created

StyleManager   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 303
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 92.86%

Importance

Changes 0
Metric Value
wmc 37
lcom 1
cbo 7
dl 0
loc 303
ccs 104
cts 112
cp 0.9286
rs 9.44
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A shouldApplyStyleOnEmptyCell() 0 10 4
A getStylesXMLFileContent() 0 20 1
A getNumberFormatSectionContent() 0 19 3
B getFontsSectionContent() 0 34 6
A getFillsSectionContent() 0 27 2
A getBordersSectionContent() 0 36 4
A getCellStyleXfsSectionContent() 0 8 1
B getCellXfsSectionContent() 0 34 6
A getFillIdForStyleId() 0 8 3
A getBorderIdForStyleId() 0 8 3
A getNumberFormatIdForStyleId() 0 8 3
A getCellStylesSectionContent() 0 8 1
1
<?php
2
3
namespace Box\Spout\Writer\XLSX\Manager\Style;
4
5
use Box\Spout\Common\Entity\Style\Color;
6
use Box\Spout\Common\Entity\Style\NumberFormat;
7
use Box\Spout\Common\Entity\Style\Style;
8
use Box\Spout\Writer\XLSX\Helper\BorderHelper;
9
10
/**
11
 * Class StyleManager
12
 * Manages styles to be applied to a cell
13
 */
14
class StyleManager extends \Box\Spout\Writer\Common\Manager\Style\StyleManager
15
{
16
    /** @var StyleRegistry */
17
    protected $styleRegistry;
18
19
    /**
20
     * For empty cells, we can specify a style or not. If no style are specified,
21
     * then the software default will be applied. But sometimes, it may be useful
22
     * to override this default style, for instance if the cell should have a
23
     * background color different than the default one or some borders
24
     * (fonts property don't really matter here).
25
     *
26
     * @param int $styleId
27
     * @return bool Whether the cell should define a custom style
28
     */
29 10
    public function shouldApplyStyleOnEmptyCell($styleId)
30
    {
31 10
        $associatedFillId = $this->styleRegistry->getFillIdForStyleId($styleId);
32 10
        $hasStyleCustomFill = ($associatedFillId !== null && $associatedFillId !== 0);
33
34 10
        $associatedBorderId = $this->styleRegistry->getBorderIdForStyleId($styleId);
35 10
        $hasStyleCustomBorders = ($associatedBorderId !== null && $associatedBorderId !== 0);
36
37 10
        return ($hasStyleCustomFill || $hasStyleCustomBorders);
38
    }
39
40
    /**
41
     * Returns the content of the "styles.xml" file, given a list of styles.
42
     *
43
     * @return string
44
     */
45 30
    public function getStylesXMLFileContent()
46
    {
47
        $content = <<<'EOD'
48 30
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
49
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
50
EOD;
51 30
        $content .= $this->getNumberFormatSectionContent();
52 30
        $content .= $this->getFontsSectionContent();
53 30
        $content .= $this->getFillsSectionContent();
54 30
        $content .= $this->getBordersSectionContent();
55 30
        $content .= $this->getCellStyleXfsSectionContent();
56 30
        $content .= $this->getCellXfsSectionContent();
57 30
        $content .= $this->getCellStylesSectionContent();
58
59
        $content .= <<<'EOD'
60 30
</styleSheet>
61
EOD;
62
63 30
        return $content;
64
    }
65
66 30
    protected function getNumberFormatSectionContent()
67
    {
68 30
        $registeredFormats = $this->styleRegistry->getRegisteredNumberFormats();
69 30
        if (empty($registeredFormats)) {
70 30
            return '<numFmts count="0"/>';
71
        }
72
73
        $content = '<numFmts count="'.count($registeredFormats).'">';
74
        /** @var NumberFormat $format */
75
        foreach ($registeredFormats as $styleId) {
76
            $style = $this->styleRegistry->getStyleFromStyleId($styleId);
77
            $format = $style->getNumberFormat();
78
            $formatId = $this->styleRegistry->getNumberFormatIdForStyleId($styleId);
79
            $content .= '<numFmt numFmtId="'.$formatId.'" formatCode="'.$format->getFormatCode().'"/>';
80
        }
81
82
        $content .= '</numFmts>';
83
        return $content;
84
    }
85
86
    /**
87
     * Returns the content of the "<fonts>" section.
88
     *
89
     * @return string
90
     */
91 30
    protected function getFontsSectionContent()
92
    {
93 30
        $registeredStyles = $this->styleRegistry->getRegisteredStyles();
94
95 30
        $content = '<fonts count="' . count($registeredStyles) . '">';
96
97
        /** @var Style $style */
98 30
        foreach ($registeredStyles as $style) {
99 30
            $content .= '<font>';
100
101 30
            $content .= '<sz val="' . $style->getFontSize() . '"/>';
102 30
            $content .= '<color rgb="' . Color::toARGB($style->getFontColor()) . '"/>';
103 30
            $content .= '<name val="' . $style->getFontName() . '"/>';
104
105 30
            if ($style->isFontBold()) {
106 4
                $content .= '<b/>';
107
            }
108 30
            if ($style->isFontItalic()) {
109 1
                $content .= '<i/>';
110
            }
111 30
            if ($style->isFontUnderline()) {
112 2
                $content .= '<u/>';
113
            }
114 30
            if ($style->isFontStrikethrough()) {
115 1
                $content .= '<strike/>';
116
            }
117
118 30
            $content .= '</font>';
119
        }
120
121 30
        $content .= '</fonts>';
122
123 30
        return $content;
124
    }
125
126
    /**
127
     * Returns the content of the "<fills>" section.
128
     *
129
     * @return string
130
     */
131 30
    protected function getFillsSectionContent()
132
    {
133 30
        $registeredFills = $this->styleRegistry->getRegisteredFills();
134
135
        // Excel reserves two default fills
136 30
        $fillsCount = count($registeredFills) + 2;
137 30
        $content = sprintf('<fills count="%d">', $fillsCount);
138
139 30
        $content .= '<fill><patternFill patternType="none"/></fill>';
140 30
        $content .= '<fill><patternFill patternType="gray125"/></fill>';
141
142
        // The other fills are actually registered by setting a background color
143 30
        foreach ($registeredFills as $styleId) {
144
            /** @var Style $style */
145 2
            $style = $this->styleRegistry->getStyleFromStyleId($styleId);
146
147 2
            $backgroundColor = $style->getBackgroundColor();
148 2
            $content .= sprintf(
149 2
                '<fill><patternFill patternType="solid"><fgColor rgb="%s"/></patternFill></fill>',
150 2
                $backgroundColor
151
            );
152
        }
153
154 30
        $content .= '</fills>';
155
156 30
        return $content;
157
    }
158
159
    /**
160
     * Returns the content of the "<borders>" section.
161
     *
162
     * @return string
163
     */
164 30
    protected function getBordersSectionContent()
165
    {
166 30
        $registeredBorders = $this->styleRegistry->getRegisteredBorders();
167
168
        // There is one default border with index 0
169 30
        $borderCount = count($registeredBorders) + 1;
170
171 30
        $content = '<borders count="' . $borderCount . '">';
172
173
        // Default border starting at index 0
174 30
        $content .= '<border><left/><right/><top/><bottom/></border>';
175
176 30
        foreach ($registeredBorders as $styleId) {
177
            /** @var \Box\Spout\Common\Entity\Style\Style $style */
178 1
            $style = $this->styleRegistry->getStyleFromStyleId($styleId);
179 1
            $border = $style->getBorder();
180 1
            $content .= '<border>';
181
182
            // @link https://github.com/box/spout/issues/271
183 1
            $sortOrder = ['left', 'right', 'top', 'bottom'];
184
185 1
            foreach ($sortOrder as $partName) {
186 1
                if ($border->hasPart($partName)) {
187
                    /** @var $part \Box\Spout\Common\Entity\Style\BorderPart */
188 1
                    $part = $border->getPart($partName);
189 1
                    $content .= BorderHelper::serializeBorderPart($part);
190
                }
191
            }
192
193 1
            $content .= '</border>';
194
        }
195
196 30
        $content .= '</borders>';
197
198 30
        return $content;
199
    }
200
201
    /**
202
     * Returns the content of the "<cellStyleXfs>" section.
203
     *
204
     * @return string
205
     */
206 30
    protected function getCellStyleXfsSectionContent()
207
    {
208
        return <<<'EOD'
209 30
<cellStyleXfs count="1">
210
    <xf borderId="0" fillId="0" fontId="0" numFmtId="0"/>
211
</cellStyleXfs>
212
EOD;
213
    }
214
215
    /**
216
     * Returns the content of the "<cellXfs>" section.
217
     *
218
     * @return string
219
     */
220 30
    protected function getCellXfsSectionContent()
221
    {
222 30
        $registeredStyles = $this->styleRegistry->getRegisteredStyles();
223
224 30
        $content = '<cellXfs count="' . count($registeredStyles) . '">';
225
226 30
        foreach ($registeredStyles as $style) {
227 30
            $styleId = $style->getId();
228 30
            $fillId = $this->getFillIdForStyleId($styleId);
229 30
            $borderId = $this->getBorderIdForStyleId($styleId);
230 30
            $numberFormatId = $this->getNumberFormatIdForStyleId($styleId);
231
232 30
            $content .= '<xf numFmtId="'.$numberFormatId.'" fontId="' . $styleId . '" fillId="' . $fillId . '" borderId="' . $borderId . '" xfId="0"';
233
234 30
            if ($style->shouldApplyFont()) {
235 30
                $content .= ' applyFont="1"';
236
            }
237
238 30
            $content .= sprintf(' applyBorder="%d"', $style->shouldApplyBorder() ? 1 : 0);
239 30
            $content .= sprintf(' applyNumberFormat="%d"', $style->shouldApplyNumberFormat() ? 1 : 0);
240
241 30
            if ($style->shouldWrapText()) {
242 2
                $content .= ' applyAlignment="1">';
243 2
                $content .= '<alignment wrapText="1"/>';
244 2
                $content .= '</xf>';
245
            } else {
246 30
                $content .= '/>';
247
            }
248
        }
249
250 30
        $content .= '</cellXfs>';
251
252 30
        return $content;
253
    }
254
255
    /**
256
     * Returns the fill ID associated to the given style ID.
257
     * For the default style, we don't a fill.
258
     *
259
     * @param int $styleId
260
     * @return int
261
     */
262 30
    private function getFillIdForStyleId($styleId)
263
    {
264
        // For the default style (ID = 0), we don't want to override the fill.
265
        // Otherwise all cells of the spreadsheet will have a background color.
266 30
        $isDefaultStyle = ($styleId === 0);
267
268 30
        return $isDefaultStyle ? 0 : ($this->styleRegistry->getFillIdForStyleId($styleId) ?: 0);
269
    }
270
271
    /**
272
     * Returns the fill ID associated to the given style ID.
273
     * For the default style, we don't a border.
274
     *
275
     * @param int $styleId
276
     * @return int
277
     */
278 30
    private function getBorderIdForStyleId($styleId)
279
    {
280
        // For the default style (ID = 0), we don't want to override the border.
281
        // Otherwise all cells of the spreadsheet will have a border.
282 30
        $isDefaultStyle = ($styleId === 0);
283
284 30
        return $isDefaultStyle ? 0 : ($this->styleRegistry->getBorderIdForStyleId($styleId) ?: 0);
285
    }
286
287
    /**
288
     * Returns the fill ID associated to the given style ID.
289
     * For the default style, we don't a border.
290
     *
291
     * @param int $styleId
292
     * @return int
293
     */
294 30
    private function getNumberFormatIdForStyleId($styleId)
295
    {
296
        // For the default style (ID = 0), we don't want to override the border.
297
        // Otherwise all cells of the spreadsheet will have a border.
298 30
        $isDefaultStyle = ($styleId === 0);
299
300 30
        return $isDefaultStyle ? 0 : ($this->styleRegistry->getNumberFormatIdForStyleId($styleId) ?: 0);
301
    }
302
303
    /**
304
     * Returns the content of the "<cellStyles>" section.
305
     *
306
     * @return string
307
     */
308 30
    protected function getCellStylesSectionContent()
309
    {
310
        return <<<'EOD'
311 30
<cellStyles count="1">
312
    <cellStyle builtinId="0" name="Normal" xfId="0"/>
313
</cellStyles>
314
EOD;
315
    }
316
}
317