Skip to content

Commit 04f4667

Browse files
authored
Expand Chart Support for schemeClr and prstClr (#2879)
* Expand Chart Support for schemeClr and prstClr Fix #2219. Address some, but not all, issues mentioned in #2863. For Pie Charts, fill color is stored in XML as part of dPt node, which had been ignored by Reader/Xlsx/Chart. Add support for it, including when specified as schemeClr or prstClr rather than srgbClr. Add support for prstClr in other cases where schemeClr is supported. * Update Change Log Add this PR.
1 parent 90bdc7c commit 04f4667

File tree

8 files changed

+325
-86
lines changed

8 files changed

+325
-86
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
4848
- Time interval formatting [Issue #2768](https://github.com/PHPOffice/PhpSpreadsheet/issues/2768) [PR #2772](https://github.com/PHPOffice/PhpSpreadsheet/pull/2772)
4949
- Copy from Xls(x) to Html/Pdf loses drawings [PR #2788](https://github.com/PHPOffice/PhpSpreadsheet/pull/2788)
5050
- Html Reader converting cell containing 0 to null string [Issue #2810](https://github.com/PHPOffice/PhpSpreadsheet/issues/2810) [PR #2813](https://github.com/PHPOffice/PhpSpreadsheet/pull/2813)
51-
- Many fixes for Charts, especially, but not limited to, Scatter, Bubble, and Surface charts. [Issue #2762](https://github.com/PHPOffice/PhpSpreadsheet/issues/2762) [Issue #2299](https://github.com/PHPOffice/PhpSpreadsheet/issues/2299) [Issue #2700](https://github.com/PHPOffice/PhpSpreadsheet/issues/2700) [Issue #2817](https://github.com/PHPOffice/PhpSpreadsheet/issues/2817) [Issue #2763](https://github.com/PHPOffice/PhpSpreadsheet/issues/2763) [PR #2828](https://github.com/PHPOffice/PhpSpreadsheet/pull/2828) [PR #2841](https://github.com/PHPOffice/PhpSpreadsheet/pull/2841) [PR #2846](https://github.com/PHPOffice/PhpSpreadsheet/pull/2846) [PR #2852](https://github.com/PHPOffice/PhpSpreadsheet/pull/2852)
51+
- Many fixes for Charts, especially, but not limited to, Scatter, Bubble, and Surface charts. [Issue #2762](https://github.com/PHPOffice/PhpSpreadsheet/issues/2762) [Issue #2299](https://github.com/PHPOffice/PhpSpreadsheet/issues/2299) [Issue #2700](https://github.com/PHPOffice/PhpSpreadsheet/issues/2700) [Issue #2817](https://github.com/PHPOffice/PhpSpreadsheet/issues/2817) [Issue #2763](https://github.com/PHPOffice/PhpSpreadsheet/issues/2763) [Issue #2219](https://github.com/PHPOffice/PhpSpreadsheet/issues/2219) [PR #2828](https://github.com/PHPOffice/PhpSpreadsheet/pull/2828) [PR #2841](https://github.com/PHPOffice/PhpSpreadsheet/pull/2841) [PR #2846](https://github.com/PHPOffice/PhpSpreadsheet/pull/2846) [PR #2852](https://github.com/PHPOffice/PhpSpreadsheet/pull/2852) [PR #2856](https://github.com/PHPOffice/PhpSpreadsheet/pull/2856) [PR #2865](https://github.com/PHPOffice/PhpSpreadsheet/pull/2865) [PR #2872](https://github.com/PHPOffice/PhpSpreadsheet/pull/2872) [PR #2879](https://github.com/PHPOffice/PhpSpreadsheet/pull/2879)
5252

5353
## 1.23.0 - 2022-04-24
5454

12.2 KB
Binary file not shown.

src/PhpSpreadsheet/Chart/DataSeriesValues.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ class DataSeriesValues
7676
/** @var string */
7777
private $schemeClr = '';
7878

79+
/** @var string */
80+
private $prstClr = '';
81+
7982
/**
8083
* Line Width.
8184
*
@@ -262,18 +265,22 @@ public function getFillColor()
262265
/**
263266
* Set fill color for series.
264267
*
265-
* @param null|string|string[] $color HEX color or array with HEX colors
268+
* @param string|string[] $color HEX color or array with HEX colors
266269
*
267270
* @return DataSeriesValues
268271
*/
269272
public function setFillColor($color)
270273
{
271274
if (is_array($color)) {
272275
foreach ($color as $colorValue) {
273-
$this->validateColor($colorValue);
276+
if (substr($colorValue, 0, 1) !== '*' && substr($colorValue, 0, 1) !== '/') {
277+
$this->validateColor($colorValue);
278+
}
274279
}
275280
} else {
276-
$this->validateColor("$color");
281+
if (substr($color, 0, 1) !== '*' && substr($color, 0, 1) !== '/') {
282+
$this->validateColor("$color");
283+
}
277284
}
278285
$this->fillColor = $color;
279286

@@ -470,4 +477,16 @@ public function setSchemeClr(string $schemeClr): self
470477

471478
return $this;
472479
}
480+
481+
public function getPrstClr(): string
482+
{
483+
return $this->prstClr;
484+
}
485+
486+
public function setPrstClr(string $prstClr): self
487+
{
488+
$this->prstClr = $prstClr;
489+
490+
return $this;
491+
}
473492
}

src/PhpSpreadsheet/Chart/Properties.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ abstract class Properties
1414
EXCEL_COLOR_TYPE_STANDARD = 'prstClr';
1515
const EXCEL_COLOR_TYPE_SCHEME = 'schemeClr';
1616
const EXCEL_COLOR_TYPE_ARGB = 'srgbClr';
17+
const EXCEL_COLOR_TYPES = [
18+
self::EXCEL_COLOR_TYPE_ARGB,
19+
self::EXCEL_COLOR_TYPE_SCHEME,
20+
self::EXCEL_COLOR_TYPE_STANDARD,
21+
];
1722

1823
const
1924
AXIS_LABELS_LOW = 'low';

src/PhpSpreadsheet/Reader/Xlsx/Chart.php

Lines changed: 61 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,9 @@ private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType
359359
$pointSize = null;
360360
$noFill = false;
361361
$schemeClr = '';
362+
$prstClr = '';
362363
$bubble3D = false;
364+
$dPtColors = [];
363365
foreach ($seriesDetails as $seriesKey => $seriesDetail) {
364366
switch ($seriesKey) {
365367
case 'idx':
@@ -383,7 +385,24 @@ private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType
383385
$noFill = true;
384386
}
385387
if (isset($children->solidFill)) {
386-
$this->readColor($children->solidFill, $srgbClr, $schemeClr);
388+
$this->readColor($children->solidFill, $srgbClr, $schemeClr, $prstClr);
389+
}
390+
391+
break;
392+
case 'dPt':
393+
$dptIdx = (int) self::getAttribute($seriesDetail->idx, 'val', 'string');
394+
if (isset($seriesDetail->spPr)) {
395+
$children = $seriesDetail->spPr->children($this->aNamespace);
396+
if (isset($children->solidFill)) {
397+
$arrayColors = $this->readColor($children->solidFill);
398+
if ($arrayColors['type'] === 'srgbClr') {
399+
$dptColors[$dptIdx] = $arrayColors['value'];
400+
} elseif ($arrayColors['type'] === 'prstClr') {
401+
$dptColors[$dptIdx] = '/' . $arrayColors['value'];
402+
} else {
403+
$dptColors[$dptIdx] = '*' . $arrayColors['value'];
404+
}
405+
}
387406
}
388407

389408
break;
@@ -394,7 +413,7 @@ private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType
394413
if (count($seriesDetail->spPr) === 1) {
395414
$ln = $seriesDetail->spPr->children($this->aNamespace);
396415
if (isset($ln->solidFill)) {
397-
$this->readColor($ln->solidFill, $srgbClr, $schemeClr);
416+
$this->readColor($ln->solidFill, $srgbClr, $schemeClr, $prstClr);
398417
}
399418
}
400419

@@ -461,6 +480,16 @@ private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType
461480
if (isset($seriesValues[$seriesIndex])) {
462481
$seriesValues[$seriesIndex]->setSchemeClr($schemeClr);
463482
}
483+
} elseif ($prstClr) {
484+
if (isset($seriesLabel[$seriesIndex])) {
485+
$seriesLabel[$seriesIndex]->setPrstClr($prstClr);
486+
}
487+
if (isset($seriesCategory[$seriesIndex])) {
488+
$seriesCategory[$seriesIndex]->setPrstClr($prstClr);
489+
}
490+
if (isset($seriesValues[$seriesIndex])) {
491+
$seriesValues[$seriesIndex]->setPrstClr($prstClr);
492+
}
464493
}
465494
if ($bubble3D) {
466495
if (isset($seriesLabel[$seriesIndex])) {
@@ -473,6 +502,17 @@ private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType
473502
$seriesValues[$seriesIndex]->setBubble3D($bubble3D);
474503
}
475504
}
505+
if (!empty($dptColors)) {
506+
if (isset($seriesLabel[$seriesIndex])) {
507+
$seriesLabel[$seriesIndex]->setFillColor($dptColors);
508+
}
509+
if (isset($seriesCategory[$seriesIndex])) {
510+
$seriesCategory[$seriesIndex]->setFillColor($dptColors);
511+
}
512+
if (isset($seriesValues[$seriesIndex])) {
513+
$seriesValues[$seriesIndex]->setFillColor($dptColors);
514+
}
515+
}
476516
}
477517
}
478518
/** @phpstan-ignore-next-line */
@@ -1001,39 +1041,31 @@ private function readEffects(SimpleXMLElement $chartDetail, $chartObject): void
10011041
'innerShdw',
10021042
];
10031043

1004-
private function readColor(SimpleXMLElement $colorXml, ?string &$srgbClr = null, ?string &$schemeClr = null): array
1044+
private function readColor(SimpleXMLElement $colorXml, ?string &$srgbClr = null, ?string &$schemeClr = null, ?string &$prstClr = null): array
10051045
{
10061046
$result = [
10071047
'type' => null,
10081048
'value' => null,
10091049
'alpha' => null,
10101050
];
1011-
if (isset($colorXml->srgbClr)) {
1012-
$result['type'] = Properties::EXCEL_COLOR_TYPE_ARGB;
1013-
$result['value'] = $srgbClr = self::getAttribute($colorXml->srgbClr, 'val', 'string');
1014-
if (isset($colorXml->srgbClr->alpha)) {
1015-
/** @var string */
1016-
$alpha = self::getAttribute($colorXml->srgbClr->alpha, 'val', 'string');
1017-
$alpha = Properties::alphaFromXml($alpha);
1018-
$result['alpha'] = $alpha;
1019-
}
1020-
} elseif (isset($colorXml->schemeClr)) {
1021-
$result['type'] = Properties::EXCEL_COLOR_TYPE_SCHEME;
1022-
$result['value'] = $schemeClr = self::getAttribute($colorXml->schemeClr, 'val', 'string');
1023-
if (isset($colorXml->schemeClr->alpha)) {
1024-
/** @var string */
1025-
$alpha = self::getAttribute($colorXml->schemeClr->alpha, 'val', 'string');
1026-
$alpha = Properties::alphaFromXml($alpha);
1027-
$result['alpha'] = $alpha;
1028-
}
1029-
} elseif (isset($colorXml->prstClr)) {
1030-
$result['type'] = Properties::EXCEL_COLOR_TYPE_STANDARD;
1031-
$result['value'] = self::getAttribute($colorXml->prstClr, 'val', 'string');
1032-
if (isset($colorXml->prstClr->alpha)) {
1033-
/** @var string */
1034-
$alpha = self::getAttribute($colorXml->prstClr->alpha, 'val', 'string');
1035-
$alpha = Properties::alphaFromXml($alpha);
1036-
$result['alpha'] = $alpha;
1051+
foreach (Properties::EXCEL_COLOR_TYPES as $type) {
1052+
if (isset($colorXml->$type)) {
1053+
$result['type'] = $type;
1054+
$result['value'] = self::getAttribute($colorXml->$type, 'val', 'string');
1055+
if ($type === Properties::EXCEL_COLOR_TYPE_ARGB) {
1056+
$srgbClr = $result['value'];
1057+
} elseif ($type === Properties::EXCEL_COLOR_TYPE_SCHEME) {
1058+
$schemeClr = $result['value'];
1059+
} elseif ($type === Properties::EXCEL_COLOR_TYPE_STANDARD) {
1060+
$prstClr = $result['value'];
1061+
}
1062+
if (isset($colorXml->$type->alpha)) {
1063+
$alpha = (int) self::getAttribute($colorXml->$type->alpha, 'val', 'string');
1064+
$alpha = 100 - (int) ($alpha / 1000);
1065+
$result['alpha'] = $alpha;
1066+
}
1067+
1068+
break;
10371069
}
10381070
}
10391071

src/PhpSpreadsheet/Writer/Xlsx/Chart.php

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,9 @@ private static function getChartType(PlotArea $plotArea): array
942942
*/
943943
private function writePlotSeriesValuesElement(XMLWriter $objWriter, $val = 3, $fillColor = 'FF9900'): void
944944
{
945+
if ($fillColor === '') {
946+
return;
947+
}
945948
$objWriter->startElement('c:dPt');
946949
$objWriter->startElement('c:idx');
947950
$objWriter->writeAttribute('val', $val);
@@ -953,8 +956,16 @@ private function writePlotSeriesValuesElement(XMLWriter $objWriter, $val = 3, $f
953956

954957
$objWriter->startElement('c:spPr');
955958
$objWriter->startElement('a:solidFill');
956-
$objWriter->startElement('a:srgbClr');
957-
$objWriter->writeAttribute('val', $fillColor);
959+
if (substr($fillColor, 0, 1) === '*') {
960+
$objWriter->startElement('a:schemeClr');
961+
$objWriter->writeAttribute('val', substr($fillColor, 1));
962+
} elseif (substr($fillColor, 0, 1) === '/') {
963+
$objWriter->startElement('a:prstClr');
964+
$objWriter->writeAttribute('val', substr($fillColor, 1));
965+
} else {
966+
$objWriter->startElement('a:srgbClr');
967+
$objWriter->writeAttribute('val', $fillColor);
968+
}
958969
$objWriter->endElement();
959970
$objWriter->endElement();
960971
$objWriter->endElement();
@@ -1039,7 +1050,7 @@ private function writePlotGroup(?DataSeries $plotGroup, string $groupType, XMLWr
10391050
$fillColorValues = $plotSeriesValues->getFillColor();
10401051
if ($fillColorValues !== null && is_array($fillColorValues)) {
10411052
foreach ($plotSeriesValues->getDataValues() as $dataKey => $dataValue) {
1042-
$this->writePlotSeriesValuesElement($objWriter, $dataKey, ($fillColorValues[$dataKey] ?? 'FF9900'));
1053+
$this->writePlotSeriesValuesElement($objWriter, $dataKey, $fillColorValues[$dataKey] ?? '');
10431054
}
10441055
} else {
10451056
$this->writePlotSeriesValuesElement($objWriter);
@@ -1061,18 +1072,29 @@ private function writePlotGroup(?DataSeries $plotGroup, string $groupType, XMLWr
10611072
$groupType == DataSeries::TYPE_LINECHART
10621073
|| $groupType == DataSeries::TYPE_STOCKCHART
10631074
|| ($groupType === DataSeries::TYPE_SCATTERCHART && $plotSeriesValues !== false && !$plotSeriesValues->getScatterLines())
1064-
|| ($plotSeriesValues !== false && $plotSeriesValues->getSchemeClr())
1075+
|| ($plotSeriesValues !== false && ($plotSeriesValues->getSchemeClr() || $plotSeriesValues->getPrstClr()))
10651076
) {
10661077
$plotLineWidth = 12700;
10671078
if ($plotSeriesValues) {
10681079
$plotLineWidth = $plotSeriesValues->getLineWidth();
10691080
}
10701081

10711082
$objWriter->startElement('c:spPr');
1072-
$schemeClr = $plotLabel ? $plotLabel->getSchemeClr() : null;
1083+
$schemeClr = $typeClr = '';
1084+
if ($plotLabel) {
1085+
$schemeClr = $plotLabel->getSchemeClr();
1086+
if ($schemeClr) {
1087+
$typeClr = 'schemeClr';
1088+
} else {
1089+
$schemeClr = $plotLabel->getPrstClr();
1090+
if ($schemeClr) {
1091+
$typeClr = 'prstClr';
1092+
}
1093+
}
1094+
}
10731095
if ($schemeClr) {
10741096
$objWriter->startElement('a:solidFill');
1075-
$objWriter->startElement('a:schemeClr');
1097+
$objWriter->startElement("a:$typeClr");
10761098
$objWriter->writeAttribute('val', $schemeClr);
10771099
$objWriter->endElement();
10781100
$objWriter->endElement();

0 commit comments

Comments
 (0)