diff --git a/CHANGELOG.md b/CHANGELOG.md index fa5df9e0f1..24a4abd6ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Changed +- Xlsx Color schemes read in will be written out (previously Excel 2007-2010 Color scheme was always written); manipulation of those schemes before write, including restoring prior behavior, is provided [PR #3476](https://github.com/PHPOffice/PhpSpreadsheet/pull/3476) - Memory and speed optimisations for Read Filters with Xlsx Files and Shared Formulae. [PR #3474](https://github.com/PHPOffice/PhpSpreadsheet/pull/3474) ### Deprecated diff --git a/samples/Chart/33_Chart_create_area_2.php b/samples/Chart/33_Chart_create_area_2.php new file mode 100644 index 0000000000..7761caa633 --- /dev/null +++ b/samples/Chart/33_Chart_create_area_2.php @@ -0,0 +1,107 @@ +getTheme()->setThemeColorName(SpreadsheetTheme::COLOR_SCHEME_2013_PLUS_NAME); +$worksheet = $spreadsheet->getActiveSheet(); +$worksheet->fromArray( + [ + ['', 2010, 2011, 2012], + ['Q1', 12, 15, 21], + ['Q2', 56, 73, 86], + ['Q3', 52, 61, 69], + ['Q4', 30, 32, 0], + ] +); + +// Set the Labels for each data series we want to plot +// Datatype +// Cell reference for data +// Format Code +// Number of datapoints in series +// Data values +// Data Marker +$dataSeriesLabels = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$B$1', null, 1), // 2010 + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // 2011 + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // 2012 +]; +// Set the X-Axis Labels +// Datatype +// Cell reference for data +// Format Code +// Number of datapoints in series +// Data values +// Data Marker +$xAxisTickValues = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4 +]; +// Set the Data values for each data series we want to plot +// Datatype +// Cell reference for data +// Format Code +// Number of datapoints in series +// Data values +// Data Marker +$dataSeriesValues = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$5', null, 4), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', null, 4), +]; + +// Build the dataseries +$series = new DataSeries( + DataSeries::TYPE_AREACHART, // plotType + DataSeries::GROUPING_PERCENT_STACKED, // plotGrouping + range(0, count($dataSeriesValues) - 1), // plotOrder + $dataSeriesLabels, // plotLabel + $xAxisTickValues, // plotCategory + $dataSeriesValues // plotValues +); + +// Set the series in the plot area +$plotArea = new PlotArea(null, [$series]); +// Set the chart legend +$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false); + +$title = new Title('Test %age-Stacked Area Chart'); +$yAxisLabel = new Title('Value ($k)'); + +// Create the chart +$chart = new Chart( + 'chart1', // name + $title, // title + $legend, // legend + $plotArea, // plotArea + true, // plotVisibleOnly + DataSeries::EMPTY_AS_GAP, // displayBlanksAs + null, // xAxisLabel + $yAxisLabel // yAxisLabel +); + +// Set the position where the chart should appear in the worksheet +$chart->setTopLeftPosition('A7'); +$chart->setBottomRightPosition('H20'); + +// Add the chart to the worksheet +$worksheet->addChart($chart); + +// Save Excel 2007 file +$filename = $helper->getFilename(__FILE__); +$writer = IOFactory::createWriter($spreadsheet, 'Xlsx'); +$writer->setIncludeCharts(true); +$callStartTime = microtime(true); +$writer->save($filename); +$helper->logWrite($writer, $filename, $callStartTime); diff --git a/samples/templates/32readwriteLineChart6.xlsx b/samples/templates/32readwriteLineChart6.xlsx new file mode 100644 index 0000000000..95d6dfd90e Binary files /dev/null and b/samples/templates/32readwriteLineChart6.xlsx differ diff --git a/src/PhpSpreadsheet/Chart/AxisText.php b/src/PhpSpreadsheet/Chart/AxisText.php index db8191cc8a..cd9ba2ce29 100644 --- a/src/PhpSpreadsheet/Chart/AxisText.php +++ b/src/PhpSpreadsheet/Chart/AxisText.php @@ -2,18 +2,21 @@ namespace PhpOffice\PhpSpreadsheet\Chart; +use PhpOffice\PhpSpreadsheet\Style\Font; + class AxisText extends Properties { /** @var ?int */ private $rotation; - /** @var ChartColor */ - private $fillColor; + /** @var Font */ + private $font; public function __construct() { parent::__construct(); - $this->fillColor = new ChartColor(); + $this->font = new Font(); + $this->font->setSize(null, true); } public function setRotation(?int $rotation): self @@ -30,6 +33,24 @@ public function getRotation(): ?int public function getFillColorObject(): ChartColor { - return $this->fillColor; + $fillColor = $this->font->getChartColor(); + if ($fillColor === null) { + $fillColor = new ChartColor(); + $this->font->setChartColorFromObject($fillColor); + } + + return $fillColor; + } + + public function getFont(): Font + { + return $this->font; + } + + public function setFont(Font $font): self + { + $this->font = $font; + + return $this; } } diff --git a/src/PhpSpreadsheet/Chart/Layout.php b/src/PhpSpreadsheet/Chart/Layout.php index 0018d79d72..ac36c25c9f 100644 --- a/src/PhpSpreadsheet/Chart/Layout.php +++ b/src/PhpSpreadsheet/Chart/Layout.php @@ -2,6 +2,8 @@ namespace PhpOffice\PhpSpreadsheet\Chart; +use PhpOffice\PhpSpreadsheet\Style\Font; + class Layout { /** @@ -127,8 +129,11 @@ class Layout /** @var ?ChartColor */ private $labelBorderColor; - /** @var ?ChartColor */ - private $labelFontColor; + /** @var ?Font */ + private $labelFont; + + /** @var Properties */ + private $labelEffects; /** * Create a new Layout. @@ -172,7 +177,18 @@ public function __construct(array $layout = []) $this->initBoolean($layout, 'numFmtLinked'); $this->initColor($layout, 'labelFillColor'); $this->initColor($layout, 'labelBorderColor'); - $this->initColor($layout, 'labelFontColor'); + $labelFont = $layout['labelFont'] ?? null; + if ($labelFont instanceof Font) { + $this->labelFont = $labelFont; + } + $labelFontColor = $layout['labelFontColor'] ?? null; + if ($labelFontColor instanceof ChartColor) { + $this->setLabelFontColor($labelFontColor); + } + $labelEffects = $layout['labelEffects'] ?? null; + if ($labelEffects instanceof Properties) { + $this->labelEffects = $labelEffects; + } } private function initBoolean(array $layout, string $name): void @@ -493,14 +509,32 @@ public function setLabelBorderColor(?ChartColor $chartColor): self return $this; } + public function getLabelFont(): ?Font + { + return $this->labelFont; + } + + public function getLabelEffects(): ?Properties + { + return $this->labelEffects; + } + public function getLabelFontColor(): ?ChartColor { - return $this->labelFontColor; + if ($this->labelFont === null) { + return null; + } + + return $this->labelFont->getChartColor(); } public function setLabelFontColor(?ChartColor $chartColor): self { - $this->labelFontColor = $chartColor; + if ($this->labelFont === null) { + $this->labelFont = new Font(); + $this->labelFont->setSize(null, true); + } + $this->labelFont->setChartColorFromObject($chartColor); return $this; } diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index 46b7cfabbd..50fe8181a1 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -455,6 +455,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet $colourScheme = self::getAttributes($xmlTheme->themeElements->clrScheme); $colourSchemeName = (string) $colourScheme['name']; + $excel->getTheme()->setThemeColorName($colourSchemeName); $colourScheme = $xmlTheme->themeElements->clrScheme->children($drawingNS); $themeColours = []; @@ -466,9 +467,11 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet if (isset($xmlColour->sysClr)) { $xmlColourData = self::getAttributes($xmlColour->sysClr); $themeColours[$themePos] = (string) $xmlColourData['lastClr']; + $excel->getTheme()->setThemeColor($k, (string) $xmlColourData['lastClr']); } elseif (isset($xmlColour->srgbClr)) { $xmlColourData = self::getAttributes($xmlColour->srgbClr); $themeColours[$themePos] = (string) $xmlColourData['val']; + $excel->getTheme()->setThemeColor($k, (string) $xmlColourData['val']); } } $theme = new Theme($themeName, $colourSchemeName, $themeColours); diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php index 5859ed1077..7d33c6eba4 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php @@ -1143,6 +1143,37 @@ private function parseRichText(SimpleXMLElement $titleDetailPart): RichText return $value; } + private function parseFont(SimpleXMLElement $titleDetailPart): ?Font + { + if (!isset($titleDetailPart->pPr->defRPr)) { + return null; + } + $fontArray = []; + $fontArray['size'] = self::getAttribute($titleDetailPart->pPr->defRPr, 'sz', 'integer'); + $fontArray['bold'] = self::getAttribute($titleDetailPart->pPr->defRPr, 'b', 'boolean'); + $fontArray['italic'] = self::getAttribute($titleDetailPart->pPr->defRPr, 'i', 'boolean'); + $fontArray['underscore'] = self::getAttribute($titleDetailPart->pPr->defRPr, 'u', 'string'); + $fontArray['strikethrough'] = self::getAttribute($titleDetailPart->pPr->defRPr, 'strike', 'string'); + + if (isset($titleDetailPart->pPr->defRPr->latin)) { + $fontArray['latin'] = self::getAttribute($titleDetailPart->pPr->defRPr->latin, 'typeface', 'string'); + } + if (isset($titleDetailPart->pPr->defRPr->ea)) { + $fontArray['eastAsian'] = self::getAttribute($titleDetailPart->pPr->defRPr->ea, 'typeface', 'string'); + } + if (isset($titleDetailPart->pPr->defRPr->cs)) { + $fontArray['complexScript'] = self::getAttribute($titleDetailPart->pPr->defRPr->cs, 'typeface', 'string'); + } + if (isset($titleDetailPart->pPr->defRPr->solidFill)) { + $fontArray['chartColor'] = new ChartColor($this->readColor($titleDetailPart->pPr->defRPr->solidFill)); + } + $font = new Font(); + $font->setSize(null, true); + $font->applyFromArray($fontArray); + + return $font; + } + /** * @param ?SimpleXMLElement $chartDetail */ @@ -1189,8 +1220,13 @@ private function readChartAttributes($chartDetail): array } if (isset($chartDetail->dLbls->txPr)) { $txpr = $chartDetail->dLbls->txPr->children($this->aNamespace); - if (isset($txpr->p->pPr->defRPr->solidFill)) { - $plotAttributes['labelFontColor'] = new ChartColor($this->readColor($txpr->p->pPr->defRPr->solidFill)); + if (isset($txpr->p)) { + $plotAttributes['labelFont'] = $this->parseFont($txpr->p); + if (isset($txpr->p->pPr->defRPr->effectLst)) { + $labelEffects = new GridLines(); + $this->readEffects($txpr->p->pPr->defRPr, $labelEffects, false); + $plotAttributes['labelEffects'] = $labelEffects; + } } } } @@ -1489,10 +1525,12 @@ private function setAxisProperties(SimpleXMLElement $chartDetail, ?Axis $whichAx $addAxisText = true; } } - if (isset($children->p->pPr->defRPr->solidFill)) { - $colorArray = $this->readColor($children->p->pPr->defRPr->solidFill); - $axisText->getFillColorObject()->setColorPropertiesArray($colorArray); - $addAxisText = true; + if (isset($children->p->pPr->defRPr)) { + $font = $this->parseFont($children->p); + if ($font !== null) { + $axisText->setFont($font); + $addAxisText = true; + } } if (isset($children->p->pPr->defRPr->effectLst)) { $this->readEffects($children->p->pPr->defRPr, $axisText, false); diff --git a/src/PhpSpreadsheet/Spreadsheet.php b/src/PhpSpreadsheet/Spreadsheet.php index f0744cd27f..3c5ecacd89 100644 --- a/src/PhpSpreadsheet/Spreadsheet.php +++ b/src/PhpSpreadsheet/Spreadsheet.php @@ -203,6 +203,14 @@ class Spreadsheet implements JsonSerializable */ private $tabRatio = 600; + /** @var Theme */ + private $theme; + + public function getTheme(): Theme + { + return $this->theme; + } + /** * The workbook has macros ? * @@ -476,6 +484,7 @@ public function __construct() { $this->uniqueID = uniqid('', true); $this->calculationEngine = new Calculation($this); + $this->theme = new Theme(); // Initialise worksheet collection and add one worksheet $this->workSheetCollection = []; diff --git a/src/PhpSpreadsheet/Style/Font.php b/src/PhpSpreadsheet/Style/Font.php index 3d7bc1bce7..a4d6e99a56 100644 --- a/src/PhpSpreadsheet/Style/Font.php +++ b/src/PhpSpreadsheet/Style/Font.php @@ -231,6 +231,9 @@ public function applyFromArray(array $styleArray) if (isset($styleArray['size'])) { $this->setSize($styleArray['size']); } + if (isset($styleArray['chartColor'])) { + $this->chartColor = $styleArray['chartColor']; + } } return $this; @@ -634,6 +637,13 @@ public function setChartColor(array $colorArray): self return $this; } + public function setChartColorFromObject(?ChartColor $chartColor): self + { + $this->chartColor = $chartColor; + + return $this; + } + /** * Get Underline. * diff --git a/src/PhpSpreadsheet/Theme.php b/src/PhpSpreadsheet/Theme.php new file mode 100644 index 0000000000..217b155c6d --- /dev/null +++ b/src/PhpSpreadsheet/Theme.php @@ -0,0 +1,76 @@ + '000000', + 'lt1' => 'FFFFFF', + 'dk2' => '44546A', + 'lt2' => 'E7E6E6', + 'accent1' => '4472C4', + 'accent2' => 'ED7D31', + 'accent3' => 'A5A5A5', + 'accent4' => 'FFC000', + 'accent5' => '5B9BD5', + 'accent6' => '70AD47', + 'hlink' => '0563C1', + 'folHlink' => '954F72', + ]; + + public const COLOR_SCHEME_2007_2010_NAME = 'Office 2007-2010'; + public const COLOR_SCHEME_2007_2010 = [ + 'dk1' => '000000', + 'lt1' => 'FFFFFF', + 'dk2' => '1F497D', + 'lt2' => 'EEECE1', + 'accent1' => '4F81BD', + 'accent2' => 'C0504D', + 'accent3' => '9BBB59', + 'accent4' => '8064A2', + 'accent5' => '4BACC6', + 'accent6' => 'F79646', + 'hlink' => '0000FF', + 'folHlink' => '800080', + ]; + + /** @var string[] */ + private $themeColors = self::COLOR_SCHEME_2007_2010; + + public function getThemeColors(): array + { + return $this->themeColors; + } + + public function setThemeColor(string $key, string $value): self + { + $this->themeColors[$key] = $value; + + return $this; + } + + public function getThemeColorName(): string + { + return $this->themeColorName; + } + + public function setThemeColorName(string $name, ?array $themeColors = null): self + { + $this->themeColorName = $name; + if ($name === self::COLOR_SCHEME_2007_2010_NAME) { + $themeColors = $themeColors ?? self::COLOR_SCHEME_2007_2010; + } elseif ($name === self::COLOR_SCHEME_2013_PLUS_NAME) { + $themeColors = $themeColors ?? self::COLOR_SCHEME_2013_PLUS; + } + if ($themeColors !== null) { + $this->themeColors = $themeColors; + } + + return $this; + } +} diff --git a/src/PhpSpreadsheet/Writer/Xlsx.php b/src/PhpSpreadsheet/Writer/Xlsx.php index 07b790440a..3f677b0563 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx.php +++ b/src/PhpSpreadsheet/Writer/Xlsx.php @@ -377,7 +377,7 @@ public function save($filename, int $flags = 0): void } // Add theme to ZIP file - $zipContent['xl/theme/theme1.xml'] = $this->getWriterPartTheme()->writeTheme(); + $zipContent['xl/theme/theme1.xml'] = $this->getWriterPartTheme()->writeTheme($this->spreadSheet); // Add string table to ZIP file $zipContent['xl/sharedStrings.xml'] = $this->getWriterPartStringTable()->writeStringTable($this->stringTable); diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index bea6f49167..6200159a7f 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -14,6 +14,7 @@ use PhpOffice\PhpSpreadsheet\Chart\TrendLine; use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces; use PhpOffice\PhpSpreadsheet\Shared\XMLWriter; +use PhpOffice\PhpSpreadsheet\Style\Font; use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException; class Chart extends WriterPart @@ -451,8 +452,8 @@ private function writeDataLabels(XMLWriter $objWriter, ?Layout $chartLayout = nu } $objWriter->endElement(); // c:spPr } - $fontColor = $chartLayout->getLabelFontColor(); - if ($fontColor && $fontColor->isUsable()) { + $labelFont = $chartLayout->getLabelFont(); + if ($labelFont !== null) { $objWriter->startElement('c:txPr'); $objWriter->startElement('a:bodyPr'); @@ -468,14 +469,7 @@ private function writeDataLabels(XMLWriter $objWriter, ?Layout $chartLayout = nu $objWriter->startElement('a:lstStyle'); $objWriter->endElement(); // a:lstStyle - - $objWriter->startElement('a:p'); - $objWriter->startElement('a:pPr'); - $objWriter->startElement('a:defRPr'); - $this->writeColor($objWriter, $fontColor); - $objWriter->endElement(); // a:defRPr - $objWriter->endElement(); // a:pPr - $objWriter->endElement(); // a:p + $this->writeLabelFont($objWriter, $labelFont, $chartLayout->getLabelEffects()); $objWriter->endElement(); // c:txPr } @@ -642,16 +636,7 @@ private function writeCategoryAxis(XMLWriter $objWriter, ?Title $xAxisLabel, $id $objWriter->endElement(); // a:bodyPr $objWriter->startElement('a:lstStyle'); $objWriter->endElement(); // a:lstStyle - $objWriter->startElement('a:p'); - $objWriter->startElement('a:pPr'); - $objWriter->startElement('a:defRPr'); - if ($axisText !== null) { - $this->writeColor($objWriter, $axisText->getFillColorObject()); - $this->writeEffects($objWriter, $axisText); - } - $objWriter->endElement(); // a:defRPr - $objWriter->endElement(); // a:pPr - $objWriter->endElement(); // a:p + $this->writeLabelFont($objWriter, ($axisText === null) ? null : $axisText->getFont(), $axisText); $objWriter->endElement(); // c:txPr } @@ -868,16 +853,9 @@ private function writeValueAxis(XMLWriter $objWriter, ?Title $yAxisLabel, $group $objWriter->endElement(); // a:bodyPr $objWriter->startElement('a:lstStyle'); $objWriter->endElement(); // a:lstStyle - $objWriter->startElement('a:p'); - $objWriter->startElement('a:pPr'); - $objWriter->startElement('a:defRPr'); - if ($axisText !== null) { - $this->writeColor($objWriter, $axisText->getFillColorObject()); - $this->writeEffects($objWriter, $axisText); - } - $objWriter->endElement(); // a:defRPr - $objWriter->endElement(); // a:pPr - $objWriter->endElement(); // a:p + + $this->writeLabelFont($objWriter, ($axisText === null) ? null : $axisText->getFont(), $axisText); + $objWriter->endElement(); // c:txPr } @@ -1807,4 +1785,51 @@ private function writeColor(XMLWriter $objWriter, ChartColor $chartColor, bool $ } } } + + private function writeLabelFont(XMLWriter $objWriter, ?Font $labelFont, ?Properties $axisText): void + { + $objWriter->startElement('a:p'); + $objWriter->startElement('a:pPr'); + $objWriter->startElement('a:defRPr'); + if ($labelFont !== null) { + $fontSize = $labelFont->getSize(); + if (is_numeric($fontSize)) { + $fontSize *= (($fontSize < 100) ? 100 : 1); + $objWriter->writeAttribute('sz', (string) $fontSize); + } + if ($labelFont->getBold() === true) { + $objWriter->writeAttribute('b', '1'); + } + if ($labelFont->getItalic() === true) { + $objWriter->writeAttribute('i', '1'); + } + $fontColor = $labelFont->getChartColor(); + if ($fontColor !== null) { + $this->writeColor($objWriter, $fontColor); + } + } + if ($axisText !== null) { + $this->writeEffects($objWriter, $axisText); + } + if ($labelFont !== null) { + if (!empty($labelFont->getLatin())) { + $objWriter->startElement('a:latin'); + $objWriter->writeAttribute('typeface', $labelFont->getLatin()); + $objWriter->endElement(); + } + if (!empty($labelFont->getEastAsian())) { + $objWriter->startElement('a:eastAsian'); + $objWriter->writeAttribute('typeface', $labelFont->getEastAsian()); + $objWriter->endElement(); + } + if (!empty($labelFont->getComplexScript())) { + $objWriter->startElement('a:complexScript'); + $objWriter->writeAttribute('typeface', $labelFont->getComplexScript()); + $objWriter->endElement(); + } + } + $objWriter->endElement(); // a:defRPr + $objWriter->endElement(); // a:pPr + $objWriter->endElement(); // a:p + } } diff --git a/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php b/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php index 7f623933cb..92af57dda1 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php @@ -226,9 +226,10 @@ public function writeRichTextForCharts(XMLWriter $objWriter, $richText = null, $ if ($element->getFont() !== null) { // rPr $objWriter->startElement($prefix . 'rPr'); - $size = $element->getFont()->getSize(); - if (is_numeric($size)) { - $objWriter->writeAttribute('sz', (string) (int) ($size * 100)); + $fontSize = $element->getFont()->getSize(); + if (is_numeric($fontSize)) { + $fontSize *= (($fontSize < 100) ? 100 : 1); + $objWriter->writeAttribute('sz', (string) $fontSize); } // Bold diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Theme.php b/src/PhpSpreadsheet/Writer/Xlsx/Theme.php index 9ff29d45d7..0e0251dce0 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Theme.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Theme.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces; use PhpOffice\PhpSpreadsheet\Shared\XMLWriter; +use PhpOffice\PhpSpreadsheet\Spreadsheet; class Theme extends WriterPart { @@ -83,30 +84,12 @@ class Theme extends WriterPart 'Geor' => 'Sylfaen', ]; - /** - * Map of core colours. - * - * @var string[] - */ - private static $colourScheme = [ - 'dk2' => '1F497D', - 'lt2' => 'EEECE1', - 'accent1' => '4F81BD', - 'accent2' => 'C0504D', - 'accent3' => '9BBB59', - 'accent4' => '8064A2', - 'accent5' => '4BACC6', - 'accent6' => 'F79646', - 'hlink' => '0000FF', - 'folHlink' => '800080', - ]; - /** * Write theme to XML format. * * @return string XML Output */ - public function writeTheme() + public function writeTheme(Spreadsheet $spreadsheet) { // Create XML writer $objWriter = null; @@ -129,32 +112,9 @@ public function writeTheme() // a:clrScheme $objWriter->startElement('a:clrScheme'); - $objWriter->writeAttribute('name', 'Office'); - - // a:dk1 - $objWriter->startElement('a:dk1'); - - // a:sysClr - $objWriter->startElement('a:sysClr'); - $objWriter->writeAttribute('val', 'windowText'); - $objWriter->writeAttribute('lastClr', '000000'); - $objWriter->endElement(); - - $objWriter->endElement(); - - // a:lt1 - $objWriter->startElement('a:lt1'); + $objWriter->writeAttribute('name', $spreadsheet->getTheme()->getThemeColorName()); - // a:sysClr - $objWriter->startElement('a:sysClr'); - $objWriter->writeAttribute('val', 'window'); - $objWriter->writeAttribute('lastClr', 'FFFFFF'); - $objWriter->endElement(); - - $objWriter->endElement(); - - // a:dk2 - $this->writeColourScheme($objWriter); + $this->writeColourScheme($objWriter, $spreadsheet); $objWriter->endElement(); @@ -814,16 +774,33 @@ private function writeFonts(XMLWriter $objWriter, string $latinFont, array $font /** * Write colour scheme to XML format. */ - private function writeColourScheme(XMLWriter $objWriter): void + private function writeColourScheme(XMLWriter $objWriter, Spreadsheet $spreadsheet): void { - foreach (self::$colourScheme as $colourName => $colourValue) { - $objWriter->startElement('a:' . $colourName); - - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $colourValue); - $objWriter->endElement(); + $themeArray = $spreadsheet->getTheme()->getThemeColors(); + // a:dk1 + $objWriter->startElement('a:dk1'); + $objWriter->startElement('a:sysClr'); + $objWriter->writeAttribute('val', 'windowText'); + $objWriter->writeAttribute('lastClr', $themeArray['dk1'] ?? '000000'); + $objWriter->endElement(); // a:sysClr + $objWriter->endElement(); // a:dk1 - $objWriter->endElement(); + // a:lt1 + $objWriter->startElement('a:lt1'); + $objWriter->startElement('a:sysClr'); + $objWriter->writeAttribute('val', 'window'); + $objWriter->writeAttribute('lastClr', $themeArray['lt1'] ?? 'FFFFFF'); + $objWriter->endElement(); // a:sysClr + $objWriter->endElement(); // a:lt1 + + foreach ($themeArray as $colourName => $colourValue) { + if ($colourName !== 'dk1' && $colourName !== 'lt1') { + $objWriter->startElement('a:' . $colourName); + $objWriter->startElement('a:srgbClr'); + $objWriter->writeAttribute('val', $colourValue); + $objWriter->endElement(); // a:srgbClr + $objWriter->endElement(); // a:$colourName + } } } } diff --git a/tests/PhpSpreadsheetTests/Chart/LayoutEffectsTest.php b/tests/PhpSpreadsheetTests/Chart/LayoutEffectsTest.php new file mode 100644 index 0000000000..4c1bb9e5dd --- /dev/null +++ b/tests/PhpSpreadsheetTests/Chart/LayoutEffectsTest.php @@ -0,0 +1,63 @@ +setIncludeCharts(true); + } + + public function writeCharts(XlsxWriter $writer): void + { + $writer->setIncludeCharts(true); + } + + public function testLegend(): void + { + $reader = new XlsxReader(); + $this->readCharts($reader); + $spreadsheet = $reader->load(self::FILENAME); + + /** @var callable */ + $callableReader = [$this, 'readCharts']; + /** @var callable */ + $callableWriter = [$this, 'writeCharts']; + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $sheet = $reloadedSpreadsheet->getActiveSheet(); + $charts2 = $sheet->getChartCollection(); + self::assertCount(1, $charts2); + $chart2 = $charts2[0]; + self::assertNotNull($chart2); + $yAxis = $chart2->getChartAxisY(); + $yAxisText = $yAxis->getAxisText(); + self::assertNotNull($yAxisText); + self::assertSame(['value' => 'accent4', 'type' => 'schemeClr', 'alpha' => 60], $yAxisText->getGlowProperty('color')); + $plotArea2 = $chart2->getPlotArea(); + self::assertNotNull($plotArea2); + $plotGroup2 = $plotArea2->getPlotGroup()[0]; + $plotIndex2 = $plotGroup2->getPlotLabelByIndex(0); + if ($plotIndex2 === false) { + self::fail('Unexpected false for getPlotLabelByIndex'); + } else { + $layout2 = $plotIndex2->getLabelLayout(); + self::assertNotNull($layout2); + $effects2 = $layout2->getLabelEffects(); + self::assertNotNull($effects2); + $shadows2 = $effects2->getShadowArray(); + self::assertSame('outerShdw', $shadows2['effect']); + self::assertSame(['value' => 'FF0000', 'type' => 'srgbClr', 'alpha' => 70], $shadows2['color']); + } + + $reloadedSpreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/ThemeColorsTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/ThemeColorsTest.php new file mode 100644 index 0000000000..7eae3e5170 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/ThemeColorsTest.php @@ -0,0 +1,54 @@ +getTheme()->setThemeColorName(SpreadsheetTheme::COLOR_SCHEME_2013_PLUS_NAME); + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx'); + $spreadsheet->disconnectWorksheets(); + self::assertSame('Office 2013+', $reloadedSpreadsheet->getTheme()->getThemeColorName()); + self::assertSame('FFC000', $reloadedSpreadsheet->getTheme()->getThemeColors()['accent4']); + $reloadedSpreadsheet->disconnectWorksheets(); + } + + public function testOffice2007Theme(): void + { + $spreadsheet = new Spreadsheet(); + $spreadsheet->getTheme()->setThemeColorName(SpreadsheetTheme::COLOR_SCHEME_2007_2010_NAME); + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx'); + $spreadsheet->disconnectWorksheets(); + self::assertSame('Office 2007-2010', $reloadedSpreadsheet->getTheme()->getThemeColorName()); + self::assertSame('8064A2', $reloadedSpreadsheet->getTheme()->getThemeColors()['accent4']); + $reloadedSpreadsheet->disconnectWorksheets(); + } + + public function testDefaultTheme(): void + { + $spreadsheet = new Spreadsheet(); + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx'); + $spreadsheet->disconnectWorksheets(); + self::assertSame('Office', $reloadedSpreadsheet->getTheme()->getThemeColorName()); + self::assertSame('8064A2', $reloadedSpreadsheet->getTheme()->getThemeColors()['accent4']); + $reloadedSpreadsheet->disconnectWorksheets(); + } + + public function testGalleryTheme(): void + { + $reader = new XlsxReader(); + $spreadsheet = $reader->load('tests/data/Writer/XLSX/gallerytheme.xlsx'); + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx'); + $spreadsheet->disconnectWorksheets(); + self::assertSame('Gallery', $reloadedSpreadsheet->getTheme()->getThemeColorName()); + self::assertSame('795FAF', $reloadedSpreadsheet->getTheme()->getThemeColors()['accent4']); + $reloadedSpreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/data/Writer/XLSX/gallerytheme.xlsx b/tests/data/Writer/XLSX/gallerytheme.xlsx new file mode 100644 index 0000000000..649ab958ce Binary files /dev/null and b/tests/data/Writer/XLSX/gallerytheme.xlsx differ