Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
99.81% |
537 / 538 |
|
95.45% |
21 / 22 |
CRAP | |
0.00% |
0 / 1 |
| ObjectsChart | |
99.81% |
537 / 538 |
|
95.45% |
21 / 22 |
117 | |
0.00% |
0 / 1 |
| render | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
| writeContentPart | |
100.00% |
92 / 92 |
|
100.00% |
1 / 1 |
8 | |||
| writeAxis | |
100.00% |
33 / 33 |
|
100.00% |
1 / 1 |
5 | |||
| writeGridline | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
| writeAxisStyle | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
| writeAxisMainStyle | |
100.00% |
36 / 36 |
|
100.00% |
1 / 1 |
6 | |||
| writeAxisTitleStyle | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
2 | |||
| writeGridlineStyle | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
2 | |||
| writeChartStyle | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
| writeFloor | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| writeFloorStyle | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
1 | |||
| writeLegend | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
7 | |||
| writeLegendStyle | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
2 | |||
| writePlotArea | |
100.00% |
33 / 33 |
|
100.00% |
1 / 1 |
7 | |||
| writePlotAreaStyle | |
100.00% |
42 / 42 |
|
100.00% |
1 / 1 |
14 | |||
| writeSeries | |
100.00% |
39 / 39 |
|
100.00% |
1 / 1 |
10 | |||
| writeSeriesStyle | |
98.72% |
77 / 78 |
|
0.00% |
0 / 1 |
24 | |||
| writeTable | |
100.00% |
50 / 50 |
|
100.00% |
1 / 1 |
12 | |||
| writeTitle | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
2 | |||
| writeTitleStyle | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
3 | |||
| writeWall | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| writeWallStyle | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
3 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * This file is part of PHPPresentation - A pure PHP library for reading and writing |
| 4 | * presentations documents. |
| 5 | * |
| 6 | * PHPPresentation is free software distributed under the terms of the GNU Lesser |
| 7 | * General Public License version 3 as published by the Free Software Foundation. |
| 8 | * |
| 9 | * For the full copyright and license information, please read the LICENSE |
| 10 | * file that was distributed with this source code. For the full list of |
| 11 | * contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors. |
| 12 | * |
| 13 | * @see https://github.com/PHPOffice/PHPPresentation |
| 14 | * |
| 15 | * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 |
| 16 | */ |
| 17 | |
| 18 | declare(strict_types=1); |
| 19 | |
| 20 | namespace PhpOffice\PhpPresentation\Writer\ODPresentation; |
| 21 | |
| 22 | use PhpOffice\Common\Adapter\Zip\ZipInterface; |
| 23 | use PhpOffice\Common\Drawing as CommonDrawing; |
| 24 | use PhpOffice\Common\Text; |
| 25 | use PhpOffice\Common\XMLWriter; |
| 26 | use PhpOffice\PhpPresentation\Shape\Chart; |
| 27 | use PhpOffice\PhpPresentation\Shape\Chart\Axis; |
| 28 | use PhpOffice\PhpPresentation\Shape\Chart\Title; |
| 29 | use PhpOffice\PhpPresentation\Shape\Chart\Type\AbstractType; |
| 30 | use PhpOffice\PhpPresentation\Shape\Chart\Type\AbstractTypeBar; |
| 31 | use PhpOffice\PhpPresentation\Shape\Chart\Type\AbstractTypeLine; |
| 32 | use PhpOffice\PhpPresentation\Shape\Chart\Type\AbstractTypePie; |
| 33 | use PhpOffice\PhpPresentation\Shape\Chart\Type\Area; |
| 34 | use PhpOffice\PhpPresentation\Shape\Chart\Type\Bar; |
| 35 | use PhpOffice\PhpPresentation\Shape\Chart\Type\Bar3D; |
| 36 | use PhpOffice\PhpPresentation\Shape\Chart\Type\Doughnut; |
| 37 | use PhpOffice\PhpPresentation\Shape\Chart\Type\Line; |
| 38 | use PhpOffice\PhpPresentation\Shape\Chart\Type\Pie3D; |
| 39 | use PhpOffice\PhpPresentation\Shape\Chart\Type\Radar; |
| 40 | use PhpOffice\PhpPresentation\Shape\Chart\Type\Scatter; |
| 41 | use PhpOffice\PhpPresentation\Style\Fill; |
| 42 | use PhpOffice\PhpPresentation\Style\Outline; |
| 43 | |
| 44 | class ObjectsChart extends AbstractDecoratorWriter |
| 45 | { |
| 46 | /** |
| 47 | * @var XMLWriter |
| 48 | */ |
| 49 | protected $xmlContent; |
| 50 | |
| 51 | /** |
| 52 | * @var mixed |
| 53 | */ |
| 54 | protected $arrayData; |
| 55 | |
| 56 | /** |
| 57 | * @var mixed |
| 58 | */ |
| 59 | protected $arrayTitle; |
| 60 | |
| 61 | /** |
| 62 | * @var int |
| 63 | */ |
| 64 | protected $numData; |
| 65 | |
| 66 | /** |
| 67 | * @var int |
| 68 | */ |
| 69 | protected $numSeries; |
| 70 | |
| 71 | /** |
| 72 | * @var string |
| 73 | */ |
| 74 | protected $rangeCol; |
| 75 | |
| 76 | public function render(): ZipInterface |
| 77 | { |
| 78 | foreach ($this->getArrayChart() as $keyChart => $shapeChart) { |
| 79 | $content = $this->writeContentPart($shapeChart); |
| 80 | |
| 81 | if (!empty($content)) { |
| 82 | $this->getZip()->addFromString('Object ' . $keyChart . '/content.xml', $content); |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | return $this->getZip(); |
| 87 | } |
| 88 | |
| 89 | protected function writeContentPart(Chart $chart): string |
| 90 | { |
| 91 | $this->xmlContent = new XMLWriter(XMLWriter::STORAGE_MEMORY); |
| 92 | |
| 93 | $chartType = $chart->getPlotArea()->getType(); |
| 94 | |
| 95 | // Data |
| 96 | $this->arrayData = []; |
| 97 | $this->arrayTitle = []; |
| 98 | $this->numData = 0; |
| 99 | foreach ($chartType->getSeries() as $series) { |
| 100 | $inc = 0; |
| 101 | $this->arrayTitle[] = $series->getTitle(); |
| 102 | foreach ($series->getValues() as $key => $value) { |
| 103 | if (!isset($this->arrayData[$inc])) { |
| 104 | $this->arrayData[$inc] = []; |
| 105 | } |
| 106 | if (empty($this->arrayData[$inc])) { |
| 107 | $this->arrayData[$inc][] = $key; |
| 108 | } |
| 109 | $this->arrayData[$inc][] = $value; |
| 110 | ++$inc; |
| 111 | } |
| 112 | if ($inc > $this->numData) { |
| 113 | $this->numData = $inc; |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | // office:document-content |
| 118 | $this->xmlContent->startElement('office:document-content'); |
| 119 | $this->xmlContent->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'); |
| 120 | $this->xmlContent->writeAttribute('xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'); |
| 121 | $this->xmlContent->writeAttribute('xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'); |
| 122 | $this->xmlContent->writeAttribute('xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'); |
| 123 | $this->xmlContent->writeAttribute('xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'); |
| 124 | $this->xmlContent->writeAttribute('xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'); |
| 125 | $this->xmlContent->writeAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink'); |
| 126 | $this->xmlContent->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/'); |
| 127 | $this->xmlContent->writeAttribute('xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'); |
| 128 | $this->xmlContent->writeAttribute('xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'); |
| 129 | $this->xmlContent->writeAttribute('xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'); |
| 130 | $this->xmlContent->writeAttribute('xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'); |
| 131 | $this->xmlContent->writeAttribute('xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'); |
| 132 | $this->xmlContent->writeAttribute('xmlns:math', 'http://www.w3.org/1998/Math/MathML'); |
| 133 | $this->xmlContent->writeAttribute('xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'); |
| 134 | $this->xmlContent->writeAttribute('xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'); |
| 135 | $this->xmlContent->writeAttribute('xmlns:ooo', 'http://openoffice.org/2004/office'); |
| 136 | $this->xmlContent->writeAttribute('xmlns:ooow', 'http://openoffice.org/2004/writer'); |
| 137 | $this->xmlContent->writeAttribute('xmlns:oooc', 'http://openoffice.org/2004/calc'); |
| 138 | $this->xmlContent->writeAttribute('xmlns:dom', 'http://www.w3.org/2001/xml-events'); |
| 139 | $this->xmlContent->writeAttribute('xmlns:xforms', 'http://www.w3.org/2002/xforms'); |
| 140 | $this->xmlContent->writeAttribute('xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'); |
| 141 | $this->xmlContent->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); |
| 142 | $this->xmlContent->writeAttribute('xmlns:rpt', 'http://openoffice.org/2005/report'); |
| 143 | $this->xmlContent->writeAttribute('xmlns:of', 'urn:oasis:names:tc:opendocument:xmlns:of:1.2'); |
| 144 | $this->xmlContent->writeAttribute('xmlns:xhtml', 'http://www.w3.org/1999/xhtml'); |
| 145 | $this->xmlContent->writeAttribute('xmlns:grddl', 'http://www.w3.org/2003/g/data-view#'); |
| 146 | $this->xmlContent->writeAttribute('xmlns:tableooo', 'http://openoffice.org/2009/table'); |
| 147 | $this->xmlContent->writeAttribute('xmlns:chartooo', 'http://openoffice.org/2010/chart'); |
| 148 | $this->xmlContent->writeAttribute('xmlns:drawooo', 'http://openoffice.org/2010/draw'); |
| 149 | $this->xmlContent->writeAttribute('xmlns:calcext', 'urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0'); |
| 150 | $this->xmlContent->writeAttribute('xmlns:loext', 'urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0'); |
| 151 | $this->xmlContent->writeAttribute('xmlns:field', 'urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0'); |
| 152 | $this->xmlContent->writeAttribute('xmlns:formx', 'urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0'); |
| 153 | $this->xmlContent->writeAttribute('xmlns:css3t', 'http://www.w3.org/TR/css3-text/'); |
| 154 | $this->xmlContent->writeAttribute('office:version', '1.2'); |
| 155 | |
| 156 | // office:automatic-styles |
| 157 | $this->xmlContent->startElement('office:automatic-styles'); |
| 158 | |
| 159 | // Styles |
| 160 | $this->writeChartStyle($chart); |
| 161 | $this->writeAxisStyle($chart); |
| 162 | $this->numSeries = 0; |
| 163 | foreach ($chartType->getSeries() as $series) { |
| 164 | $this->writeSeriesStyle($chart, $series); |
| 165 | ++$this->numSeries; |
| 166 | } |
| 167 | $this->writeFloorStyle(); |
| 168 | $this->writeLegendStyle($chart); |
| 169 | $this->writePlotAreaStyle($chart); |
| 170 | $this->writeTitleStyle($chart->getTitle()); |
| 171 | $this->writeWallStyle($chart); |
| 172 | |
| 173 | // > office:automatic-styles |
| 174 | $this->xmlContent->endElement(); |
| 175 | |
| 176 | // office:body |
| 177 | $this->xmlContent->startElement('office:body'); |
| 178 | // office:chart |
| 179 | $this->xmlContent->startElement('office:chart'); |
| 180 | // office:chart |
| 181 | $this->xmlContent->startElement('chart:chart'); |
| 182 | $this->xmlContent->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $chart->getWidth()), 3) . 'cm'); |
| 183 | $this->xmlContent->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $chart->getHeight()), 3) . 'cm'); |
| 184 | $this->xmlContent->writeAttribute('xlink:href', '.'); |
| 185 | $this->xmlContent->writeAttribute('xlink:type', 'simple'); |
| 186 | $this->xmlContent->writeAttribute('chart:style-name', 'styleChart'); |
| 187 | $this->xmlContent->writeAttributeIf($chartType instanceof Area, 'chart:class', 'chart:area'); |
| 188 | $this->xmlContent->writeAttributeIf($chartType instanceof AbstractTypeBar, 'chart:class', 'chart:bar'); |
| 189 | if (!($chartType instanceof Doughnut)) { |
| 190 | $this->xmlContent->writeAttributeIf($chartType instanceof AbstractTypePie, 'chart:class', 'chart:circle'); |
| 191 | } |
| 192 | $this->xmlContent->writeAttributeIf($chartType instanceof Doughnut, 'chart:class', 'chart:ring'); |
| 193 | $this->xmlContent->writeAttributeIf($chartType instanceof Line, 'chart:class', 'chart:line'); |
| 194 | $this->xmlContent->writeAttributeIf($chartType instanceof Radar, 'chart:class', 'chart:radar'); |
| 195 | $this->xmlContent->writeAttributeIf($chartType instanceof Scatter, 'chart:class', 'chart:scatter'); |
| 196 | |
| 197 | $this->writeTitle($chart->getTitle()); |
| 198 | $this->writeLegend($chart); |
| 199 | $this->writePlotArea($chart); |
| 200 | $this->writeTable(); |
| 201 | |
| 202 | // > chart:chart |
| 203 | $this->xmlContent->endElement(); |
| 204 | // > office:chart |
| 205 | $this->xmlContent->endElement(); |
| 206 | // > office:body |
| 207 | $this->xmlContent->endElement(); |
| 208 | // > office:document-content |
| 209 | $this->xmlContent->endElement(); |
| 210 | |
| 211 | return $this->xmlContent->getData(); |
| 212 | } |
| 213 | |
| 214 | protected function writeAxis(Chart $chart): void |
| 215 | { |
| 216 | $chartType = $chart->getPlotArea()->getType(); |
| 217 | |
| 218 | // chart:axis |
| 219 | $this->xmlContent->startElement('chart:axis'); |
| 220 | $this->xmlContent->writeAttribute('chart:dimension', 'x'); |
| 221 | $this->xmlContent->writeAttribute('chart:name', 'primary-x'); |
| 222 | $this->xmlContent->writeAttribute('chart:style-name', 'styleAxisX'); |
| 223 | // chart:axis > chart:title |
| 224 | if ($chart->getPlotArea()->getAxisX()->isVisible()) { |
| 225 | $this->xmlContent->startElement('chart:title'); |
| 226 | $this->xmlContent->writeAttribute('chart:style-name', 'styleAxisXTitle'); |
| 227 | $this->xmlContent->writeElement('text:p', $chart->getPlotArea()->getAxisX()->getTitle()); |
| 228 | $this->xmlContent->endElement(); |
| 229 | } |
| 230 | // chart:axis > chart:categories |
| 231 | $this->xmlContent->startElement('chart:categories'); |
| 232 | $this->xmlContent->writeAttribute('table:cell-range-address', 'table-local.$A$2:.$A$' . ($this->numData + 1)); |
| 233 | $this->xmlContent->endElement(); |
| 234 | // chart:axis > chart:grid |
| 235 | $this->writeGridline($chart->getPlotArea()->getAxisX()->getMajorGridlines(), 'styleAxisXGridlinesMajor', 'major'); |
| 236 | // chart:axis > chart:grid |
| 237 | $this->writeGridline($chart->getPlotArea()->getAxisX()->getMinorGridlines(), 'styleAxisXGridlinesMinor', 'minor'); |
| 238 | // ##chart:axis |
| 239 | $this->xmlContent->endElement(); |
| 240 | |
| 241 | // chart:axis |
| 242 | $this->xmlContent->startElement('chart:axis'); |
| 243 | $this->xmlContent->writeAttribute('chart:dimension', 'y'); |
| 244 | $this->xmlContent->writeAttribute('chart:name', 'primary-y'); |
| 245 | $this->xmlContent->writeAttribute('chart:style-name', 'styleAxisY'); |
| 246 | // chart:axis > chart:title |
| 247 | if ($chart->getPlotArea()->getAxisY()->isVisible()) { |
| 248 | $this->xmlContent->startElement('chart:title'); |
| 249 | $this->xmlContent->writeAttribute('chart:style-name', 'styleAxisYTitle'); |
| 250 | $this->xmlContent->writeElement('text:p', $chart->getPlotArea()->getAxisY()->getTitle()); |
| 251 | $this->xmlContent->endElement(); |
| 252 | } |
| 253 | // chart:axis > chart:grid |
| 254 | $this->writeGridline($chart->getPlotArea()->getAxisY()->getMajorGridlines(), 'styleAxisYGridlinesMajor', 'major'); |
| 255 | // chart:axis > chart:grid |
| 256 | $this->writeGridline($chart->getPlotArea()->getAxisY()->getMinorGridlines(), 'styleAxisYGridlinesMinor', 'minor'); |
| 257 | // ##chart:axis |
| 258 | $this->xmlContent->endElement(); |
| 259 | |
| 260 | if ($chartType instanceof Bar3D || $chartType instanceof Pie3D) { |
| 261 | // chart:axis |
| 262 | $this->xmlContent->startElement('chart:axis'); |
| 263 | $this->xmlContent->writeAttribute('chart:dimension', 'z'); |
| 264 | $this->xmlContent->writeAttribute('chart:name', 'primary-z'); |
| 265 | // > chart:axis |
| 266 | $this->xmlContent->endElement(); |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | protected function writeGridline(?Chart\Gridlines $oGridlines, string $styleName, string $chartClass): void |
| 271 | { |
| 272 | if (!$oGridlines) { |
| 273 | return; |
| 274 | } |
| 275 | |
| 276 | $this->xmlContent->startElement('chart:grid'); |
| 277 | $this->xmlContent->writeAttribute('chart:style-name', $styleName); |
| 278 | $this->xmlContent->writeAttribute('chart:class', $chartClass); |
| 279 | $this->xmlContent->endElement(); |
| 280 | } |
| 281 | |
| 282 | /** |
| 283 | * @todo Set function in \PhpPresentation\Shape\Chart\Axis for defining width and color of the axis |
| 284 | */ |
| 285 | protected function writeAxisStyle(Chart $chart): void |
| 286 | { |
| 287 | $chartType = $chart->getPlotArea()->getType(); |
| 288 | |
| 289 | // AxisX |
| 290 | $this->writeAxisMainStyle($chart->getPlotArea()->getAxisX(), 'styleAxisX', $chartType); |
| 291 | |
| 292 | // AxisX Title |
| 293 | $this->writeAxisTitleStyle($chart->getPlotArea()->getAxisX(), 'styleAxisXTitle'); |
| 294 | |
| 295 | // AxisX GridLines Major |
| 296 | $this->writeGridlineStyle($chart->getPlotArea()->getAxisX()->getMajorGridlines(), 'styleAxisXGridlinesMajor'); |
| 297 | |
| 298 | // AxisX GridLines Minor |
| 299 | $this->writeGridlineStyle($chart->getPlotArea()->getAxisX()->getMinorGridlines(), 'styleAxisXGridlinesMinor'); |
| 300 | |
| 301 | // AxisY |
| 302 | $this->writeAxisMainStyle($chart->getPlotArea()->getAxisY(), 'styleAxisY', $chartType); |
| 303 | |
| 304 | // AxisY Title |
| 305 | $this->writeAxisTitleStyle($chart->getPlotArea()->getAxisY(), 'styleAxisYTitle'); |
| 306 | |
| 307 | // AxisY GridLines Major |
| 308 | $this->writeGridlineStyle($chart->getPlotArea()->getAxisY()->getMajorGridlines(), 'styleAxisYGridlinesMajor'); |
| 309 | |
| 310 | // AxisY GridLines Minor |
| 311 | $this->writeGridlineStyle($chart->getPlotArea()->getAxisY()->getMinorGridlines(), 'styleAxisYGridlinesMinor'); |
| 312 | } |
| 313 | |
| 314 | protected function writeAxisMainStyle(Axis $axis, string $styleName, AbstractType $chartType): void |
| 315 | { |
| 316 | // style:style |
| 317 | $this->xmlContent->startElement('style:style'); |
| 318 | $this->xmlContent->writeAttribute('style:name', $styleName); |
| 319 | $this->xmlContent->writeAttribute('style:family', 'chart'); |
| 320 | // style:style > style:chart-properties |
| 321 | $this->xmlContent->startElement('style:chart-properties'); |
| 322 | $this->xmlContent->writeAttribute('chart:display-label', 'true'); |
| 323 | $this->xmlContent->writeAttribute('chart:tick-marks-major-inner', 'false'); |
| 324 | $this->xmlContent->writeAttribute('chart:tick-marks-major-outer', 'false'); |
| 325 | $this->xmlContent->writeAttributeIf($chartType instanceof AbstractTypePie, 'chart:reverse-direction', 'true'); |
| 326 | $this->xmlContent->writeAttributeIf(null !== $axis->getMinBounds(), 'chart:minimum', $axis->getMinBounds()); |
| 327 | $this->xmlContent->writeAttributeIf(null !== $axis->getMaxBounds(), 'chart:maximum', $axis->getMaxBounds()); |
| 328 | $this->xmlContent->writeAttributeIf(null !== $axis->getMajorUnit(), 'chart:interval-major', $axis->getMajorUnit()); |
| 329 | $this->xmlContent->writeAttributeIf(null !== $axis->getMinorUnit(), 'chart:interval-minor-divisor', $axis->getMinorUnit()); |
| 330 | switch ($axis->getTickLabelPosition()) { |
| 331 | case Axis::TICK_LABEL_POSITION_NEXT_TO: |
| 332 | $this->xmlContent->writeAttribute('chart:axis-label-position', 'near-axis'); |
| 333 | |
| 334 | break; |
| 335 | case Axis::TICK_LABEL_POSITION_HIGH: |
| 336 | $this->xmlContent->writeAttribute('chart:axis-position', '0'); |
| 337 | $this->xmlContent->writeAttribute('chart:axis-label-position', 'outside-end'); |
| 338 | |
| 339 | break; |
| 340 | case Axis::TICK_LABEL_POSITION_LOW: |
| 341 | $this->xmlContent->writeAttribute('chart:axis-position', '0'); |
| 342 | $this->xmlContent->writeAttribute('chart:axis-label-position', 'outside-start'); |
| 343 | $this->xmlContent->writeAttribute('chart:tick-mark-position', 'at-axis'); |
| 344 | |
| 345 | break; |
| 346 | } |
| 347 | $this->xmlContent->writeAttributeIf($chartType instanceof Radar && $styleName == 'styleAxisX', 'chart:reverse-direction', 'true'); |
| 348 | $this->xmlContent->endElement(); |
| 349 | // style:graphic-properties |
| 350 | $this->xmlContent->startElement('style:graphic-properties'); |
| 351 | $this->xmlContent->writeAttribute('draw:stroke', $axis->getOutline()->getFill()->getFillType()); |
| 352 | $this->xmlContent->writeAttribute('svg:stroke-width', number_format(CommonDrawing::pointsToCentimeters($axis->getOutline()->getWidth()), 3, '.', '') . 'cm'); |
| 353 | $this->xmlContent->writeAttribute('svg:stroke-color', '#' . $axis->getOutline()->getFill()->getStartColor()->getRGB()); |
| 354 | $this->xmlContent->endElement(); |
| 355 | // style:style > style:text-properties |
| 356 | $this->xmlContent->startElement('style:text-properties'); |
| 357 | $this->xmlContent->writeAttribute('fo:color', '#' . $axis->getFont()->getColor()->getRGB()); |
| 358 | $this->xmlContent->writeAttribute('fo:font-family', $axis->getFont()->getName()); |
| 359 | $this->xmlContent->writeAttribute('fo:font-size', $axis->getFont()->getSize() . 'pt'); |
| 360 | $this->xmlContent->writeAttribute('fo:font-style', $axis->getFont()->isItalic() ? 'italic' : 'normal'); |
| 361 | $this->xmlContent->endElement(); |
| 362 | // ## style:style |
| 363 | $this->xmlContent->endElement(); |
| 364 | } |
| 365 | |
| 366 | protected function writeAxisTitleStyle(Axis $axis, string $styleName): void |
| 367 | { |
| 368 | // style:style |
| 369 | $this->xmlContent->startElement('style:style'); |
| 370 | $this->xmlContent->writeAttribute('style:name', $styleName); |
| 371 | $this->xmlContent->writeAttribute('style:family', 'chart'); |
| 372 | // style:chart-properties |
| 373 | $this->xmlContent->startElement('style:chart-properties'); |
| 374 | $this->xmlContent->writeAttribute('chart:auto-position', 'true'); |
| 375 | $this->xmlContent->writeAttributeIf($axis->getTitleRotation() != 0, 'style:rotation-angle', '-' . $axis->getTitleRotation()); |
| 376 | // > style:chart-properties |
| 377 | $this->xmlContent->endElement(); |
| 378 | // style:text-properties |
| 379 | $this->xmlContent->startElement('style:text-properties'); |
| 380 | $this->xmlContent->writeAttribute('fo:color', '#' . $axis->getFont()->getColor()->getRGB()); |
| 381 | $this->xmlContent->writeAttribute('fo:font-family', $axis->getFont()->getName()); |
| 382 | $this->xmlContent->writeAttribute('fo:font-size', $axis->getFont()->getSize() . 'pt'); |
| 383 | $this->xmlContent->writeAttribute('fo:font-style', $axis->getFont()->isItalic() ? 'italic' : 'normal'); |
| 384 | // > style:text-properties |
| 385 | $this->xmlContent->endElement(); |
| 386 | // > style:style |
| 387 | $this->xmlContent->endElement(); |
| 388 | } |
| 389 | |
| 390 | protected function writeGridlineStyle(?Chart\Gridlines $oGridlines, string $styleName): void |
| 391 | { |
| 392 | if (!$oGridlines) { |
| 393 | return; |
| 394 | } |
| 395 | // style:style |
| 396 | $this->xmlContent->startElement('style:style'); |
| 397 | $this->xmlContent->writeAttribute('style:name', $styleName); |
| 398 | $this->xmlContent->writeAttribute('style:family', 'chart'); |
| 399 | // style:style > style:graphic-properties |
| 400 | $this->xmlContent->startElement('style:graphic-properties'); |
| 401 | $this->xmlContent->writeAttribute('svg:stroke-width', number_format(CommonDrawing::pointsToCentimeters($oGridlines->getOutline()->getWidth()), 2, '.', '') . 'cm'); |
| 402 | $this->xmlContent->writeAttribute('svg:stroke-color', '#' . $oGridlines->getOutline()->getFill()->getStartColor()->getRGB()); |
| 403 | $this->xmlContent->endElement(); |
| 404 | // ##style:style |
| 405 | $this->xmlContent->endElement(); |
| 406 | } |
| 407 | |
| 408 | protected function writeChartStyle(Chart $chart): void |
| 409 | { |
| 410 | // style:style |
| 411 | $this->xmlContent->startElement('style:style'); |
| 412 | $this->xmlContent->writeAttribute('style:name', 'styleChart'); |
| 413 | $this->xmlContent->writeAttribute('style:family', 'chart'); |
| 414 | // style:graphic-properties |
| 415 | $this->xmlContent->startElement('style:graphic-properties'); |
| 416 | $this->xmlContent->writeAttribute('draw:stroke', $chart->getFill()->getFillType()); |
| 417 | $this->xmlContent->writeAttribute('draw:fill-color', '#' . $chart->getFill()->getStartColor()->getRGB()); |
| 418 | // > style:graphic-properties |
| 419 | $this->xmlContent->endElement(); |
| 420 | // > style:style |
| 421 | $this->xmlContent->endElement(); |
| 422 | } |
| 423 | |
| 424 | protected function writeFloor(): void |
| 425 | { |
| 426 | // chart:floor |
| 427 | $this->xmlContent->startElement('chart:floor'); |
| 428 | $this->xmlContent->writeAttribute('chart:style-name', 'styleFloor'); |
| 429 | // > chart:floor |
| 430 | $this->xmlContent->endElement(); |
| 431 | } |
| 432 | |
| 433 | protected function writeFloorStyle(): void |
| 434 | { |
| 435 | // style:style |
| 436 | $this->xmlContent->startElement('style:style'); |
| 437 | $this->xmlContent->writeAttribute('style:name', 'styleFloor'); |
| 438 | $this->xmlContent->writeAttribute('style:family', 'chart'); |
| 439 | // style:chart-properties |
| 440 | $this->xmlContent->startElement('style:graphic-properties'); |
| 441 | $this->xmlContent->writeAttribute('draw:fill', 'none'); |
| 442 | //@todo : Permit edit color and size border of floor |
| 443 | $this->xmlContent->writeAttribute('draw:stroke', 'solid'); |
| 444 | $this->xmlContent->writeAttribute('svg:stroke-width', '0.026cm'); |
| 445 | $this->xmlContent->writeAttribute('svg:stroke-color', '#878787'); |
| 446 | // > style:chart-properties |
| 447 | $this->xmlContent->endElement(); |
| 448 | // > style:style |
| 449 | $this->xmlContent->endElement(); |
| 450 | } |
| 451 | |
| 452 | protected function writeLegend(Chart $chart): void |
| 453 | { |
| 454 | // chart:legend |
| 455 | $this->xmlContent->startElement('chart:legend'); |
| 456 | switch ($chart->getLegend()->getPosition()) { |
| 457 | case Chart\Legend::POSITION_BOTTOM: |
| 458 | $position = 'bottom'; |
| 459 | |
| 460 | break; |
| 461 | case Chart\Legend::POSITION_LEFT: |
| 462 | $position = 'start'; |
| 463 | |
| 464 | break; |
| 465 | case Chart\Legend::POSITION_TOP: |
| 466 | $position = 'top'; |
| 467 | |
| 468 | break; |
| 469 | case Chart\Legend::POSITION_TOPRIGHT: |
| 470 | $position = 'top-end'; |
| 471 | |
| 472 | break; |
| 473 | case Chart\Legend::POSITION_RIGHT: |
| 474 | default: |
| 475 | $position = 'end'; |
| 476 | |
| 477 | break; |
| 478 | } |
| 479 | $this->xmlContent->writeAttribute('chart:legend-position', $position); |
| 480 | $this->xmlContent->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $chart->getLegend()->getOffsetX()), 3) . 'cm'); |
| 481 | $this->xmlContent->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $chart->getLegend()->getOffsetY()), 3) . 'cm'); |
| 482 | $this->xmlContent->writeAttribute('style:legend-expansion', 'high'); |
| 483 | $this->xmlContent->writeAttribute('chart:style-name', 'styleLegend'); |
| 484 | // > chart:legend |
| 485 | $this->xmlContent->endElement(); |
| 486 | } |
| 487 | |
| 488 | protected function writeLegendStyle(Chart $chart): void |
| 489 | { |
| 490 | // style:style |
| 491 | $this->xmlContent->startElement('style:style'); |
| 492 | $this->xmlContent->writeAttribute('style:name', 'styleLegend'); |
| 493 | $this->xmlContent->writeAttribute('style:family', 'chart'); |
| 494 | // style:chart-properties |
| 495 | $this->xmlContent->startElement('style:chart-properties'); |
| 496 | $this->xmlContent->writeAttribute('chart:auto-position', 'true'); |
| 497 | // > style:chart-properties |
| 498 | $this->xmlContent->endElement(); |
| 499 | // style:text-properties |
| 500 | $this->xmlContent->startElement('style:text-properties'); |
| 501 | $this->xmlContent->writeAttribute('fo:color', '#' . $chart->getLegend()->getFont()->getColor()->getRGB()); |
| 502 | $this->xmlContent->writeAttribute('fo:font-family', $chart->getLegend()->getFont()->getName()); |
| 503 | $this->xmlContent->writeAttribute('fo:font-size', $chart->getLegend()->getFont()->getSize() . 'pt'); |
| 504 | $this->xmlContent->writeAttribute('fo:font-style', $chart->getLegend()->getFont()->isItalic() ? 'italic' : 'normal'); |
| 505 | // > style:text-properties |
| 506 | $this->xmlContent->endElement(); |
| 507 | // > style:style |
| 508 | $this->xmlContent->endElement(); |
| 509 | } |
| 510 | |
| 511 | protected function writePlotArea(Chart $chart): void |
| 512 | { |
| 513 | $chartType = $chart->getPlotArea()->getType(); |
| 514 | |
| 515 | // chart:plot-area |
| 516 | $this->xmlContent->startElement('chart:plot-area'); |
| 517 | $this->xmlContent->writeAttribute('chart:style-name', 'stylePlotArea'); |
| 518 | if ($chartType instanceof Bar3D || $chartType instanceof Pie3D) { |
| 519 | $this->xmlContent->writeAttribute('dr3d:ambient-color', '#cccccc'); |
| 520 | $this->xmlContent->writeAttribute('dr3d:lighting-mode', 'true'); |
| 521 | } |
| 522 | if ($chartType instanceof Bar3D || $chartType instanceof Pie3D) { |
| 523 | // dr3d:light |
| 524 | $arrayLight = [ |
| 525 | ['#808080', '(0 0 1)', 'false', 'true'], |
| 526 | ['#666666', '(0.2 0.4 1)', 'true', 'false'], |
| 527 | ['#808080', '(0 0 1)', 'false', 'false'], |
| 528 | ['#808080', '(0 0 1)', 'false', 'false'], |
| 529 | ['#808080', '(0 0 1)', 'false', 'false'], |
| 530 | ['#808080', '(0 0 1)', 'false', 'false'], |
| 531 | ['#808080', '(0 0 1)', 'false', 'false'], |
| 532 | ]; |
| 533 | foreach ($arrayLight as $light) { |
| 534 | $this->xmlContent->startElement('dr3d:light'); |
| 535 | $this->xmlContent->writeAttribute('dr3d:diffuse-color', $light[0]); |
| 536 | $this->xmlContent->writeAttribute('dr3d:direction', $light[1]); |
| 537 | $this->xmlContent->writeAttribute('dr3d:enabled', $light[2]); |
| 538 | $this->xmlContent->writeAttribute('dr3d:specular', $light[3]); |
| 539 | $this->xmlContent->endElement(); |
| 540 | } |
| 541 | } |
| 542 | |
| 543 | //**** Axis **** |
| 544 | $this->writeAxis($chart); |
| 545 | |
| 546 | //**** Series **** |
| 547 | $this->rangeCol = 'B'; |
| 548 | $this->numSeries = 0; |
| 549 | foreach ($chartType->getSeries() as $series) { |
| 550 | $this->writeSeries($chart, $series); |
| 551 | ++$this->rangeCol; |
| 552 | ++$this->numSeries; |
| 553 | } |
| 554 | |
| 555 | //**** Wall **** |
| 556 | $this->writeWall(); |
| 557 | //**** Floor **** |
| 558 | $this->writeFloor(); |
| 559 | // > chart:plot-area |
| 560 | $this->xmlContent->endElement(); |
| 561 | } |
| 562 | |
| 563 | /** |
| 564 | * @see : http://books.evc-cit.info/odbook/ch08.html#chart-plot-area-section |
| 565 | */ |
| 566 | protected function writePlotAreaStyle(Chart $chart): void |
| 567 | { |
| 568 | $chartType = $chart->getPlotArea()->getType(); |
| 569 | |
| 570 | // style:style |
| 571 | $this->xmlContent->startElement('style:style'); |
| 572 | $this->xmlContent->writeAttribute('style:name', 'stylePlotArea'); |
| 573 | $this->xmlContent->writeAttribute('style:family', 'chart'); |
| 574 | // style:text-properties |
| 575 | $this->xmlContent->startElement('style:chart-properties'); |
| 576 | if ($chartType instanceof Bar3D) { |
| 577 | $this->xmlContent->writeAttribute('chart:three-dimensional', 'true'); |
| 578 | $this->xmlContent->writeAttribute('chart:right-angled-axes', 'true'); |
| 579 | } elseif ($chartType instanceof Pie3D) { |
| 580 | $this->xmlContent->writeAttribute('chart:three-dimensional', 'true'); |
| 581 | $this->xmlContent->writeAttribute('chart:right-angled-axes', 'true'); |
| 582 | } elseif ($chartType instanceof AbstractTypeLine) { |
| 583 | $this->xmlContent->writeAttributeIf($chartType->isSmooth(), 'chart:interpolation', 'cubic-spline'); |
| 584 | } |
| 585 | switch ($chart->getDisplayBlankAs()) { |
| 586 | case Chart::BLANKAS_ZERO: |
| 587 | $this->xmlContent->writeAttribute('chart:treat-empty-cells', 'use-zero'); |
| 588 | |
| 589 | break; |
| 590 | case Chart::BLANKAS_GAP: |
| 591 | $this->xmlContent->writeAttribute('chart:treat-empty-cells', 'leave-gap'); |
| 592 | |
| 593 | break; |
| 594 | case Chart::BLANKAS_SPAN: |
| 595 | $this->xmlContent->writeAttribute('chart:treat-empty-cells', 'ignore'); |
| 596 | |
| 597 | break; |
| 598 | } |
| 599 | if ($chartType instanceof AbstractTypeBar) { |
| 600 | $chartVertical = 'false'; |
| 601 | if (AbstractTypeBar::DIRECTION_HORIZONTAL == $chartType->getBarDirection()) { |
| 602 | $chartVertical = 'true'; |
| 603 | } |
| 604 | $this->xmlContent->writeAttribute('chart:vertical', $chartVertical); |
| 605 | if (Bar::GROUPING_CLUSTERED == $chartType->getBarGrouping()) { |
| 606 | $this->xmlContent->writeAttribute('chart:stacked', 'false'); |
| 607 | $this->xmlContent->writeAttribute('chart:overlap', '0'); |
| 608 | } elseif (Bar::GROUPING_STACKED == $chartType->getBarGrouping()) { |
| 609 | $this->xmlContent->writeAttribute('chart:stacked', 'true'); |
| 610 | $this->xmlContent->writeAttribute('chart:overlap', '100'); |
| 611 | } elseif (Bar::GROUPING_PERCENTSTACKED == $chartType->getBarGrouping()) { |
| 612 | $this->xmlContent->writeAttribute('chart:stacked', 'true'); |
| 613 | $this->xmlContent->writeAttribute('chart:overlap', '100'); |
| 614 | $this->xmlContent->writeAttribute('chart:percentage', 'true'); |
| 615 | } |
| 616 | } |
| 617 | $labelFormat = 'value'; |
| 618 | if ($chartType instanceof AbstractTypeBar) { |
| 619 | if (Bar::GROUPING_PERCENTSTACKED == $chartType->getBarGrouping()) { |
| 620 | $labelFormat = 'percentage'; |
| 621 | } |
| 622 | } |
| 623 | $this->xmlContent->writeAttribute('chart:data-label-number', $labelFormat); |
| 624 | |
| 625 | // > style:text-properties |
| 626 | $this->xmlContent->endElement(); |
| 627 | // > style:style |
| 628 | $this->xmlContent->endElement(); |
| 629 | } |
| 630 | |
| 631 | protected function writeSeries(Chart $chart, Chart\Series $series): void |
| 632 | { |
| 633 | $chartType = $chart->getPlotArea()->getType(); |
| 634 | |
| 635 | $numRange = count($series->getValues()); |
| 636 | // chart:series |
| 637 | $this->xmlContent->startElement('chart:series'); |
| 638 | $this->xmlContent->writeAttribute('chart:values-cell-range-address', 'table-local.$' . $this->rangeCol . '$2:.$' . $this->rangeCol . '$' . ($numRange + 1)); |
| 639 | $this->xmlContent->writeAttribute('chart:label-cell-address', 'table-local.$' . $this->rangeCol . '$1'); |
| 640 | $this->xmlContent->writeAttribute('chart:style-name', 'styleSeries' . $this->numSeries); |
| 641 | if ($chartType instanceof Area |
| 642 | || $chartType instanceof AbstractTypeBar |
| 643 | || $chartType instanceof Line |
| 644 | || $chartType instanceof Radar |
| 645 | || $chartType instanceof Scatter |
| 646 | ) { |
| 647 | $dataPointFills = $series->getDataPointFills(); |
| 648 | |
| 649 | $incRepeat = $numRange; |
| 650 | if (!empty($dataPointFills)) { |
| 651 | $inc = 0; |
| 652 | $incRepeat = 1; |
| 653 | $newFill = new Fill(); |
| 654 | do { |
| 655 | if ($series->getDataPointFill($inc)->getHashCode() !== $newFill->getHashCode()) { |
| 656 | // chart:data-point |
| 657 | $this->xmlContent->startElement('chart:data-point'); |
| 658 | $this->xmlContent->writeAttribute('chart:repeated', $incRepeat); |
| 659 | // > chart:data-point |
| 660 | $this->xmlContent->endElement(); |
| 661 | $incRepeat = 1; |
| 662 | |
| 663 | // chart:data-point |
| 664 | $this->xmlContent->startElement('chart:data-point'); |
| 665 | $this->xmlContent->writeAttribute('chart:style-name', 'styleSeries' . $this->numSeries . '_' . $inc); |
| 666 | // > chart:data-point |
| 667 | $this->xmlContent->endElement(); |
| 668 | } |
| 669 | ++$inc; |
| 670 | ++$incRepeat; |
| 671 | } while ($inc < $numRange); |
| 672 | --$incRepeat; |
| 673 | } |
| 674 | // chart:data-point |
| 675 | $this->xmlContent->startElement('chart:data-point'); |
| 676 | $this->xmlContent->writeAttribute('chart:repeated', $incRepeat); |
| 677 | // > chart:data-point |
| 678 | $this->xmlContent->endElement(); |
| 679 | } elseif ($chartType instanceof AbstractTypePie) { |
| 680 | $count = count($series->getDataPointFills()); |
| 681 | for ($inc = 0; $inc < $count; ++$inc) { |
| 682 | // chart:data-point |
| 683 | $this->xmlContent->startElement('chart:data-point'); |
| 684 | $this->xmlContent->writeAttribute('chart:style-name', 'styleSeries' . $this->numSeries . '_' . $inc); |
| 685 | // > chart:data-point |
| 686 | $this->xmlContent->endElement(); |
| 687 | } |
| 688 | } |
| 689 | |
| 690 | // > chart:series |
| 691 | $this->xmlContent->endElement(); |
| 692 | } |
| 693 | |
| 694 | protected function writeSeriesStyle(Chart $chart, Chart\Series $series): void |
| 695 | { |
| 696 | $chartType = $chart->getPlotArea()->getType(); |
| 697 | |
| 698 | // style:style |
| 699 | $this->xmlContent->startElement('style:style'); |
| 700 | $this->xmlContent->writeAttribute('style:name', 'styleSeries' . $this->numSeries); |
| 701 | $this->xmlContent->writeAttribute('style:family', 'chart'); |
| 702 | // style:chart-properties |
| 703 | $this->xmlContent->startElement('style:chart-properties'); |
| 704 | if ($series->hasShowValue()) { |
| 705 | if ($series->hasShowPercentage()) { |
| 706 | $this->xmlContent->writeAttribute('chart:data-label-number', 'value-and-percentage'); |
| 707 | } else { |
| 708 | $this->xmlContent->writeAttribute('chart:data-label-number', 'value'); |
| 709 | } |
| 710 | } else { |
| 711 | if ($series->hasShowPercentage()) { |
| 712 | $this->xmlContent->writeAttribute('chart:data-label-number', 'percentage'); |
| 713 | } |
| 714 | } |
| 715 | if ($series->hasShowCategoryName()) { |
| 716 | $this->xmlContent->writeAttribute('chart:data-label-text', 'true'); |
| 717 | } |
| 718 | $this->xmlContent->writeAttribute('chart:label-position', 'center'); |
| 719 | if ($chartType instanceof AbstractTypePie) { |
| 720 | $this->xmlContent->writeAttribute('chart:pie-offset', $chartType->getExplosion()); |
| 721 | } |
| 722 | if ($chartType instanceof Line || $chartType instanceof Scatter) { |
| 723 | $oMarker = $series->getMarker(); |
| 724 | // @link : http://www.datypic.com/sc/odf/a-chart_symbol-type.html |
| 725 | $this->xmlContent->writeAttributeIf(Chart\Marker::SYMBOL_NONE == $oMarker->getSymbol(), 'chart:symbol-type', 'none'); |
| 726 | // @link : http://www.datypic.com/sc/odf/a-chart_symbol-name.html |
| 727 | $this->xmlContent->writeAttributeIf(Chart\Marker::SYMBOL_NONE != $oMarker->getSymbol(), 'chart:symbol-type', 'named-symbol'); |
| 728 | if (Chart\Marker::SYMBOL_NONE != $oMarker->getSymbol()) { |
| 729 | switch ($oMarker->getSymbol()) { |
| 730 | case Chart\Marker::SYMBOL_DASH: |
| 731 | $symbolName = 'horizontal-bar'; |
| 732 | |
| 733 | break; |
| 734 | case Chart\Marker::SYMBOL_DOT: |
| 735 | $symbolName = 'circle'; |
| 736 | |
| 737 | break; |
| 738 | case Chart\Marker::SYMBOL_TRIANGLE: |
| 739 | $symbolName = 'arrow-up'; |
| 740 | |
| 741 | break; |
| 742 | default: |
| 743 | $symbolName = $oMarker->getSymbol(); |
| 744 | |
| 745 | break; |
| 746 | } |
| 747 | $this->xmlContent->writeAttribute('chart:symbol-name', $symbolName); |
| 748 | $symbolSize = number_format(CommonDrawing::pointsToCentimeters($oMarker->getSize()), 2, '.', ''); |
| 749 | $this->xmlContent->writeAttribute('chart:symbol-width', $symbolSize . 'cm'); |
| 750 | $this->xmlContent->writeAttribute('chart:symbol-height', $symbolSize . 'cm'); |
| 751 | } |
| 752 | } |
| 753 | |
| 754 | $separator = $series->getSeparator(); |
| 755 | if (!empty($separator)) { |
| 756 | // style:chart-properties/chart:label-separator |
| 757 | $this->xmlContent->startElement('chart:label-separator'); |
| 758 | if (PHP_EOL == $separator) { |
| 759 | $this->xmlContent->writeRaw('<text:p><text:line-break /></text:p>'); |
| 760 | } else { |
| 761 | $this->xmlContent->writeElement('text:p', $separator); |
| 762 | } |
| 763 | $this->xmlContent->endElement(); |
| 764 | } |
| 765 | |
| 766 | // > style:chart-properties |
| 767 | $this->xmlContent->endElement(); |
| 768 | // style:graphic-properties |
| 769 | $this->xmlContent->startElement('style:graphic-properties'); |
| 770 | if ($chartType instanceof Line || $chartType instanceof Radar || $chartType instanceof Scatter) { |
| 771 | $outlineWidth = ''; |
| 772 | $outlineColor = ''; |
| 773 | |
| 774 | $oOutline = $series->getOutline(); |
| 775 | if ($oOutline instanceof Outline) { |
| 776 | $outlineWidth = $oOutline->getWidth(); |
| 777 | if (!empty($outlineWidth)) { |
| 778 | $outlineWidth = number_format(CommonDrawing::pixelsToCentimeters($outlineWidth), 3, '.', ''); |
| 779 | } |
| 780 | $outlineColor = $oOutline->getFill()->getStartColor()->getRGB(); |
| 781 | } |
| 782 | if (empty($outlineWidth)) { |
| 783 | $outlineWidth = '0.079'; |
| 784 | } |
| 785 | if (empty($outlineColor)) { |
| 786 | $outlineColor = '4a7ebb'; |
| 787 | } |
| 788 | $this->xmlContent->writeAttribute('svg:stroke-width', $outlineWidth . 'cm'); |
| 789 | $this->xmlContent->writeAttribute('svg:stroke-color', '#' . $outlineColor); |
| 790 | } else { |
| 791 | $this->xmlContent->writeAttribute('draw:stroke', 'none'); |
| 792 | if (!($chartType instanceof Area)) { |
| 793 | $this->xmlContent->writeAttribute('draw:fill', $series->getFill()->getFillType()); |
| 794 | } |
| 795 | } |
| 796 | $this->xmlContent->writeAttribute('draw:fill-color', '#' . $series->getFill()->getStartColor()->getRGB()); |
| 797 | // > style:graphic-properties |
| 798 | $this->xmlContent->endElement(); |
| 799 | // style:text-properties |
| 800 | $this->xmlContent->startElement('style:text-properties'); |
| 801 | $this->xmlContent->writeAttribute('fo:color', '#' . $series->getFont()->getColor()->getRGB()); |
| 802 | $this->xmlContent->writeAttribute('fo:font-family', $series->getFont()->getName()); |
| 803 | $this->xmlContent->writeAttribute('fo:font-size', $series->getFont()->getSize() . 'pt'); |
| 804 | // > style:text-properties |
| 805 | $this->xmlContent->endElement(); |
| 806 | |
| 807 | // > style:style |
| 808 | $this->xmlContent->endElement(); |
| 809 | |
| 810 | foreach ($series->getDataPointFills() as $idx => $oFill) { |
| 811 | // style:style |
| 812 | $this->xmlContent->startElement('style:style'); |
| 813 | $this->xmlContent->writeAttribute('style:name', 'styleSeries' . $this->numSeries . '_' . $idx); |
| 814 | $this->xmlContent->writeAttribute('style:family', 'chart'); |
| 815 | // style:graphic-properties |
| 816 | $this->xmlContent->startElement('style:graphic-properties'); |
| 817 | $this->xmlContent->writeAttribute('draw:fill', $oFill->getFillType()); |
| 818 | $this->xmlContent->writeAttribute('draw:fill-color', '#' . $oFill->getStartColor()->getRGB()); |
| 819 | // > style:graphic-properties |
| 820 | $this->xmlContent->endElement(); |
| 821 | // > style:style |
| 822 | $this->xmlContent->endElement(); |
| 823 | } |
| 824 | } |
| 825 | |
| 826 | protected function writeTable(): void |
| 827 | { |
| 828 | // table:table |
| 829 | $this->xmlContent->startElement('table:table'); |
| 830 | $this->xmlContent->writeAttribute('table:name', 'table-local'); |
| 831 | |
| 832 | // table:table-columns |
| 833 | $this->xmlContent->startElement('table:table-columns'); |
| 834 | // table:table-column |
| 835 | $this->xmlContent->startElement('table:table-column'); |
| 836 | if (!empty($this->arrayData)) { |
| 837 | $rowFirst = reset($this->arrayData); |
| 838 | $this->xmlContent->writeAttribute('table:number-columns-repeated', count($rowFirst) - 1); |
| 839 | } |
| 840 | // > table:table-column |
| 841 | $this->xmlContent->endElement(); |
| 842 | // > table:table-columns |
| 843 | $this->xmlContent->endElement(); |
| 844 | |
| 845 | // table:table-header-columns |
| 846 | $this->xmlContent->startElement('table:table-header-columns'); |
| 847 | // table:table-column |
| 848 | $this->xmlContent->writeElement('table:table-column'); |
| 849 | // > table:table-header-columns |
| 850 | $this->xmlContent->endElement(); |
| 851 | |
| 852 | // table:table-header-rows |
| 853 | $this->xmlContent->startElement('table:table-header-rows'); |
| 854 | // table:table-row |
| 855 | $this->xmlContent->startElement('table:table-row'); |
| 856 | if (empty($this->arrayData)) { |
| 857 | $this->xmlContent->startElement('table:table-cell'); |
| 858 | $this->xmlContent->endElement(); |
| 859 | } else { |
| 860 | $rowFirst = reset($this->arrayData); |
| 861 | foreach ($rowFirst as $key => $cell) { |
| 862 | // table:table-cell |
| 863 | $this->xmlContent->startElement('table:table-cell'); |
| 864 | if (isset($this->arrayTitle[$key - 1])) { |
| 865 | $this->xmlContent->writeAttribute('office:value-type', 'string'); |
| 866 | } |
| 867 | // text:p |
| 868 | $this->xmlContent->startElement('text:p'); |
| 869 | if (isset($this->arrayTitle[$key - 1])) { |
| 870 | $this->xmlContent->text($this->arrayTitle[$key - 1]); |
| 871 | } |
| 872 | // > text:p |
| 873 | $this->xmlContent->endElement(); |
| 874 | // > table:table-cell |
| 875 | $this->xmlContent->endElement(); |
| 876 | } |
| 877 | } |
| 878 | // > table:table-row |
| 879 | $this->xmlContent->endElement(); |
| 880 | // > table:table-header-rows |
| 881 | $this->xmlContent->endElement(); |
| 882 | |
| 883 | // table:table-rows |
| 884 | $this->xmlContent->startElement('table:table-rows'); |
| 885 | if (empty($this->arrayData)) { |
| 886 | $this->xmlContent->startElement('table:table-row'); |
| 887 | $this->xmlContent->startElement('table:table-cell'); |
| 888 | $this->xmlContent->endElement(); |
| 889 | $this->xmlContent->endElement(); |
| 890 | } else { |
| 891 | foreach ($this->arrayData as $row) { |
| 892 | // table:table-row |
| 893 | $this->xmlContent->startElement('table:table-row'); |
| 894 | foreach ($row as $cell) { |
| 895 | // table:table-cell |
| 896 | $this->xmlContent->startElement('table:table-cell'); |
| 897 | |
| 898 | $cellValueTypeFloat = null === $cell ? true : is_numeric($cell); |
| 899 | $this->xmlContent->writeAttributeIf(!$cellValueTypeFloat, 'office:value-type', 'string'); |
| 900 | $this->xmlContent->writeAttributeIf($cellValueTypeFloat, 'office:value-type', 'float'); |
| 901 | $this->xmlContent->writeAttributeIf($cellValueTypeFloat, 'office:value', null === $cell ? 'NaN' : $cell); |
| 902 | // text:p |
| 903 | $this->xmlContent->startElement('text:p'); |
| 904 | $this->xmlContent->text(null === $cell ? 'NaN' : (string) $cell); |
| 905 | $this->xmlContent->endElement(); |
| 906 | // > table:table-cell |
| 907 | $this->xmlContent->endElement(); |
| 908 | } |
| 909 | // > table:table-row |
| 910 | $this->xmlContent->endElement(); |
| 911 | } |
| 912 | } |
| 913 | // > table:table-rows |
| 914 | $this->xmlContent->endElement(); |
| 915 | |
| 916 | // > table:table |
| 917 | $this->xmlContent->endElement(); |
| 918 | } |
| 919 | |
| 920 | protected function writeTitle(Title $oTitle): void |
| 921 | { |
| 922 | if (!$oTitle->isVisible()) { |
| 923 | return; |
| 924 | } |
| 925 | // chart:title |
| 926 | $this->xmlContent->startElement('chart:title'); |
| 927 | $this->xmlContent->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $oTitle->getOffsetX()), 3) . 'cm'); |
| 928 | $this->xmlContent->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $oTitle->getOffsetY()), 3) . 'cm'); |
| 929 | $this->xmlContent->writeAttribute('chart:style-name', 'styleTitle'); |
| 930 | // > text:p |
| 931 | $this->xmlContent->startElement('text:p'); |
| 932 | $this->xmlContent->text($oTitle->getText()); |
| 933 | $this->xmlContent->endElement(); |
| 934 | // > chart:title |
| 935 | $this->xmlContent->endElement(); |
| 936 | } |
| 937 | |
| 938 | protected function writeTitleStyle(Title $oTitle): void |
| 939 | { |
| 940 | if (!$oTitle->isVisible()) { |
| 941 | return; |
| 942 | } |
| 943 | // style:style |
| 944 | $this->xmlContent->startElement('style:style'); |
| 945 | $this->xmlContent->writeAttribute('style:name', 'styleTitle'); |
| 946 | $this->xmlContent->writeAttribute('style:family', 'chart'); |
| 947 | // style:text-properties |
| 948 | $this->xmlContent->startElement('style:text-properties'); |
| 949 | $this->xmlContent->writeAttribute('fo:color', '#' . $oTitle->getFont()->getColor()->getRGB()); |
| 950 | $this->xmlContent->writeAttribute('fo:font-family', $oTitle->getFont()->getName()); |
| 951 | $this->xmlContent->writeAttribute('fo:font-size', $oTitle->getFont()->getSize() . 'pt'); |
| 952 | $this->xmlContent->writeAttribute('fo:font-style', $oTitle->getFont()->isItalic() ? 'italic' : 'normal'); |
| 953 | // > style:text-properties |
| 954 | $this->xmlContent->endElement(); |
| 955 | // > style:style |
| 956 | $this->xmlContent->endElement(); |
| 957 | } |
| 958 | |
| 959 | protected function writeWall(): void |
| 960 | { |
| 961 | // chart:wall |
| 962 | $this->xmlContent->startElement('chart:wall'); |
| 963 | $this->xmlContent->writeAttribute('chart:style-name', 'styleWall'); |
| 964 | $this->xmlContent->endElement(); |
| 965 | } |
| 966 | |
| 967 | protected function writeWallStyle(Chart $chart): void |
| 968 | { |
| 969 | $chartType = $chart->getPlotArea()->getType(); |
| 970 | |
| 971 | // style:style |
| 972 | $this->xmlContent->startElement('style:style'); |
| 973 | $this->xmlContent->writeAttribute('style:name', 'styleWall'); |
| 974 | $this->xmlContent->writeAttribute('style:family', 'chart'); |
| 975 | // style:chart-properties |
| 976 | $this->xmlContent->startElement('style:graphic-properties'); |
| 977 | //@todo : Permit edit color and size border of wall |
| 978 | if ($chartType instanceof Line || $chartType instanceof Scatter) { |
| 979 | $this->xmlContent->writeAttribute('draw:fill', 'solid'); |
| 980 | $this->xmlContent->writeAttribute('draw:fill-color', '#FFFFFF'); |
| 981 | } else { |
| 982 | $this->xmlContent->writeAttribute('draw:fill', 'none'); |
| 983 | $this->xmlContent->writeAttribute('draw:stroke', 'solid'); |
| 984 | $this->xmlContent->writeAttribute('svg:stroke-width', '0.026cm'); |
| 985 | $this->xmlContent->writeAttribute('svg:stroke-color', '#878787'); |
| 986 | } |
| 987 | // > style:chart-properties |
| 988 | $this->xmlContent->endElement(); |
| 989 | // > style:style |
| 990 | $this->xmlContent->endElement(); |
| 991 | } |
| 992 | } |