Completed
Pull Request — master (#715)
by
unknown
04:32
created

StyleManager::getMasterStylesSectionContent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 6
cts 6
cp 1
rs 9.6333
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
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
     * @param StyleRegistry $styleRegistry
26
     */
27 44
    public function __construct(StyleRegistry $styleRegistry, OptionsManagerInterface $optionsManager)
28
    {
29 44
        parent::__construct($styleRegistry);
30 44
        $this->setDefaultColumnWidth($optionsManager->getOption(Options::DEFAULT_COLUMN_WIDTH));
31 44
        $this->setDefaultRowHeight($optionsManager->getOption(Options::DEFAULT_ROW_HEIGHT));
32 44
        $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 44
    }
34
35
    /**
36
     * Returns the content of the "styles.xml" file, given a list of styles.
37
     *
38
     * @param int $numWorksheets Number of worksheets created
39
     * @return string
40
     */
41 41
    public function getStylesXMLFileContent($numWorksheets)
42
    {
43
        $content = <<<'EOD'
44 41
<?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 41
        $content .= $this->getFontFaceSectionContent();
49 41
        $content .= $this->getStylesSectionContent();
50 41
        $content .= $this->getAutomaticStylesSectionContent($numWorksheets);
51 41
        $content .= $this->getMasterStylesSectionContent($numWorksheets);
52
53
        $content .= <<<'EOD'
54 41
</office:document-styles>
55
EOD;
56
57 41
        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 41
    protected function getFontFaceSectionContent()
66
    {
67 41
        $content = '<office:font-face-decls>';
68 41
        foreach ($this->styleRegistry->getUsedFonts() as $fontName) {
69 41
            $content .= '<style:font-face style:name="' . $fontName . '" svg:font-family="' . $fontName . '"/>';
70
        }
71 41
        $content .= '</office:font-face-decls>';
72
73 41
        return $content;
74
    }
75
76
    /**
77
     * Returns the content of the "<office:styles>" section, inside "styles.xml" file.
78
     *
79
     * @return string
80
     */
81 41
    protected function getStylesSectionContent()
82
    {
83 41
        $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
    <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 41
        <style:text-properties fo:color="#{$defaultStyle->getFontColor()}"
93 41
                               fo:font-size="{$defaultStyle->getFontSize()}pt" style:font-size-asian="{$defaultStyle->getFontSize()}pt" style:font-size-complex="{$defaultStyle->getFontSize()}pt"
94 41
                               style:font-name="{$defaultStyle->getFontName()}" style:font-name-asian="{$defaultStyle->getFontName()}" style:font-name-complex="{$defaultStyle->getFontName()}"/>
95
    </style:style>
96
</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
     * @return string
105
     */
106 41
    protected function getAutomaticStylesSectionContent($numWorksheets)
107
    {
108 41
        $content = '<office:automatic-styles>';
109
110 41
        for ($i = 1; $i <= $numWorksheets; $i++) {
111
            $content .= <<<EOD
112 41
<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
    <style:footer-style/>
116
</style:page-layout>
117
EOD;
118
        }
119
120 41
        $content .= '</office:automatic-styles>';
121
122 41
        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
     */
131 41
    protected function getMasterStylesSectionContent($numWorksheets)
132
    {
133 41
        $content = '<office:master-styles>';
134
135 41
        for ($i = 1; $i <= $numWorksheets; $i++) {
136
            $content .= <<<EOD
137 41
<style:master-page style:name="mp$i" style:page-layout-name="pm$i">
138
    <style:header/>
139
    <style:header-left style:display="false"/>
140
    <style:footer/>
141
    <style:footer-left style:display="false"/>
142
</style:master-page>
143
EOD;
144
        }
145
146 41
        $content .= '</office:master-styles>';
147
148 41
        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 41
    public function getContentXmlFontFaceSectionContent()
157
    {
158 41
        $content = '<office:font-face-decls>';
159 41
        foreach ($this->styleRegistry->getUsedFonts() as $fontName) {
160 41
            $content .= '<style:font-face style:name="' . $fontName . '" svg:font-family="' . $fontName . '"/>';
161
        }
162 41
        $content .= '</office:font-face-decls>';
163
164 41
        return $content;
165
    }
166
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 41
    public function getContentXmlAutomaticStylesSectionContent($worksheets)
174
    {
175 41
        $content = '<office:automatic-styles>';
176
177 41
        foreach ($this->styleRegistry->getRegisteredStyles() as $style) {
178 41
            $content .= $this->getStyleSectionContent($style);
179
        }
180
181 41
        $useOptimalRowHeight = empty($this->defaultRowHeight) ? 'true' : 'false';
182 41
        $defaultRowHeight = empty($this->defaultRowHeight) ? '15pt' : "{$this->defaultRowHeight}pt";
183 41
        $defaultColumnWidth = empty($this->defaultColumnWidth) ? '' : "style:column-width=\"{$this->defaultColumnWidth}pt\"";
184
185
        $content .= <<<EOD
186
<style:style style:family="table-column" style:name="default-column-style">
187 41
    <style:table-column-properties fo:break-before="auto" {$defaultColumnWidth}/>
188
</style:style>
189
<style:style style:family="table-row" style:name="ro1">
190 41
    <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 41
        foreach ($worksheets as $worksheet) {
195 41
            $worksheetId = $worksheet->getId();
196 41
            $isSheetVisible = $worksheet->getExternalSheet()->isVisible() ? 'true' : 'false';
197
198
            $content .= <<<EOD
199 41
<style:style style:family="table" style:master-page-name="mp$worksheetId" style:name="ta$worksheetId">
200 41
    <style:table-properties style:writing-mode="lr-tb" table:display="$isSheetVisible"/>
201
</style:style>
202
EOD;
203
        }
204
205
        // Sort column widths since ODS cares about order
206
        usort($this->columnWidths, function ($a, $b) {
207 1
            if ($a[0] === $b[0]) {
208
                return 0;
209
            }
210
211 1
            return ($a[0] < $b[0]) ? -1 : 1;
212 41
        });
213 41
        $content .= $this->getTableColumnStylesXMLContent();
214
215 41
        $content .= '</office:automatic-styles>';
216
217 41
        return $content;
218
    }
219
220
    /**
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
     * @return string
225
     */
226 41
    protected function getStyleSectionContent($style)
227
    {
228 41
        $styleIndex = $style->getId() + 1; // 1-based
229
230 41
        $content = '<style:style style:data-style-name="N0" style:family="table-cell" style:name="ce' . $styleIndex . '" style:parent-style-name="Default">';
231
232 41
        $content .= $this->getTextPropertiesSectionContent($style);
233 41
        $content .= $this->getParagraphPropertiesSectionContent($style);
234 41
        $content .= $this->getTableCellPropertiesSectionContent($style);
235
236 41
        $content .= '</style:style>';
237
238 41
        return $content;
239
    }
240
241
    /**
242
     * Returns the contents of the "<style:text-properties>" section, inside "<style:style>" section
243
     *
244
     * @param \Box\Spout\Common\Entity\Style\Style $style
245
     * @return string
246
     */
247 41
    private function getTextPropertiesSectionContent($style)
248
    {
249 41
        if (!$style->shouldApplyFont()) {
250 40
            return '';
251
        }
252
253
        return '<style:text-properties '
254 5
            . $this->getFontSectionContent($style)
255 5
            . '/>';
256
    }
257
258
    /**
259
     * Returns the contents of the fonts definition section, inside "<style:text-properties>" section
260
     *
261
     * @param \Box\Spout\Common\Entity\Style\Style $style
262
     *
263
     * @return string
264
     */
265 5
    private function getFontSectionContent($style)
266
    {
267 5
        $defaultStyle = $this->getDefaultStyle();
268 5
        $content = '';
269
270 5
        $fontColor = $style->getFontColor();
271 5
        if ($fontColor !== $defaultStyle->getFontColor()) {
272 1
            $content .= ' fo:color="#' . $fontColor . '"';
273
        }
274
275 5
        $fontName = $style->getFontName();
276 5
        if ($fontName !== $defaultStyle->getFontName()) {
277 1
            $content .= ' style:font-name="' . $fontName . '" style:font-name-asian="' . $fontName . '" style:font-name-complex="' . $fontName . '"';
278
        }
279
280 5
        $fontSize = $style->getFontSize();
281 5
        if ($fontSize !== $defaultStyle->getFontSize()) {
282 2
            $content .= ' fo:font-size="' . $fontSize . 'pt" style:font-size-asian="' . $fontSize . 'pt" style:font-size-complex="' . $fontSize . 'pt"';
283
        }
284
285 5
        if ($style->isFontBold()) {
286 4
            $content .= ' fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"';
287
        }
288 5
        if ($style->isFontItalic()) {
289 1
            $content .= ' fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic"';
290
        }
291 5
        if ($style->isFontUnderline()) {
292 2
            $content .= ' style:text-underline-style="solid" style:text-underline-type="single"';
293
        }
294 5
        if ($style->isFontStrikethrough()) {
295 1
            $content .= ' style:text-line-through-style="solid"';
296
        }
297
298 5
        return $content;
299
    }
300
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 41
    private function getParagraphPropertiesSectionContent($style)
309
    {
310 41
        if (!$style->shouldApplyCellAlignment()) {
311 41
            return '';
312
        }
313
314
        return '<style:paragraph-properties '
315 1
            . $this->getCellAlignmentSectionContent($style)
316 1
            . '/>';
317
    }
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 1
    private function getCellAlignmentSectionContent($style)
327
    {
328 1
        return \sprintf(
329 1
            ' fo:text-align="%s" ',
330 1
            $this->transformCellAlignment($style->getCellAlignment())
331
        );
332
    }
333
334
    /**
335
     * Even though "left" and "right" alignments are part of the spec, and interpreted
336
     * respectively as "start" and "end", using the recommended values increase compatibility
337
     * with software that will read the created ODS file.
338
     *
339
     * @param string $cellAlignment
340
     *
341
     * @return string
342
     */
343 1
    private function transformCellAlignment($cellAlignment)
344
    {
345 1
        switch ($cellAlignment) {
346
            case CellAlignment::LEFT:
347
                return 'start';
348
            case CellAlignment::RIGHT:
349 1
                return 'end';
350
            default:
351
                return $cellAlignment;
352
        }
353
    }
354
355
    /**
356
     * 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 41
    private function getTableCellPropertiesSectionContent($style)
362
    {
363 41
        $content = '<style:table-cell-properties ';
364
365 41
        if ($style->shouldWrapText()) {
366 3
            $content .= $this->getWrapTextXMLContent();
367
        }
368
369 41
        if ($style->shouldApplyBorder()) {
370 1
            $content .= $this->getBorderXMLContent($style);
371
        }
372
373 41
        if ($style->shouldApplyBackgroundColor()) {
374 2
            $content .= $this->getBackgroundColorXMLContent($style);
375
        }
376
377 41
        $content .= '/>';
378
379 41
        return $content;
380
    }
381
382
    /**
383
     * Returns the contents of the wrap text definition for the "<style:table-cell-properties>" section
384
     *
385
     * @return string
386
     */
387 3
    private function getWrapTextXMLContent()
388
    {
389 3
        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 1
    private function getBorderXMLContent($style)
399
    {
400
        $borders = \array_map(function (BorderPart $borderPart) {
401 1
            return BorderHelper::serializeBorderPart($borderPart);
402 1
        }, $style->getBorder()->getParts());
403
404 1
        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 2
    private function getBackgroundColorXMLContent($style)
414
    {
415 2
        return \sprintf(' fo:background-color="#%s" ', $style->getBackgroundColor());
416
    }
417
418 41
    public function getTableColumnStylesXMLContent() : string
419
    {
420 41
        if (empty($this->columnWidths)) {
421 37
            return '';
422
        }
423
424 4
        $content = '';
425 4
        foreach ($this->columnWidths as $styleIndex => $entry) {
426
            $content .= <<<EOD
427 4
<style:style style:family="table-column" style:name="co{$styleIndex}">
428 4
    <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 4
        return $content;
434
    }
435
436 41
    public function getStyledTableColumnXMLContent(int $maxNumColumns) : string
437
    {
438 41
        if (empty($this->columnWidths)) {
439 37
            return '';
440
        }
441
442 4
        $content = '';
443 4
        foreach ($this->columnWidths as $styleIndex => $entry) {
444 4
            $numCols = $entry[1] - $entry[0] + 1;
445
            $content .= <<<EOD
446 4
<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 4
        $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 4
        return $content;
454
    }
455
}
456