From ef031e74e1cdb7489eef567c44c1757d9c08664c Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sat, 21 May 2022 07:01:18 -0700 Subject: [PATCH] More Chart Fixes (#2841) * More Chart Fixes Taking up where #2828 left off. Most of the following changes are demonstrated in 32readwriteChartWithImages1: - Adds support for "scheme" colors (because rgb, theme, and index colors just weren't enough for Excel) for DataSeriesValues. See issue #2299. - For chart titles (including axis labels), rather than a font name, Excel supplies a 3-fold series of font names for Latin, East Asian, and Complex Scripts. New properties `latin`, `eastAsian`, and `complexScript` are added to the Font class. I frankly have no idea how, or even if, you can set these in Excel; my test case (sample 32readwriteScatterChart7) is a result of manually editing the XML. - Add support for subscript/superscript to chart titles. This requires a new property `baseLine` in Font (positive=superscript negative=subscript baseline value says how high/low). - Support for underscore with different scheme color than its text, using a new string property `uSchemeClr` in Font. - Support for extra options for strikethrough, using a new string property `strikeType` in Font. - Support for extra options for underscore type, using the existing string property `underline` in Font. - I do not anticipate that any of the new Font properties will be used except for chart titles. - If no default font overrides are found for a Rich Text element in chart titles, and no explicit font overrides are found for a Run under such an element, the font element of the Run is set to null. - PhpSpreadsheet will always write a tag `a:pPr` and, underneath that, an empty tag `a:defRPr`, for default font settings for chart titles and axis labels. Combined with the previous bullet item, this will prevent PhpSpreadsheet from inadvertently overriding the Excel defaults (18 point bold Calibri for chart title, 10 point bold Calibri for axis labels). - Axis labels will now be written to XML in the same manner as chart titles. Among other considerations, this means that they can now have colors. Fix #2700. Supersedes PR #2701. Demonstrated in sample 32readwriteStockChart5. * Fix Some Chart Corruption Fix #2817, where @bridgeplayr gives an excellent description of the problem and how it should be solved. * Fix Bubble Charts Sample produced corrupt output - see issue #2763. After a lot of research, solution was just re-ordering of parameters in a single function call. Bubble 3D had not been supported at all. It is now. Surface Charts remain corrupted. --- phpstan-baseline.neon | 4 +- samples/Chart/33_Chart_create_scatter2.php | 121 ++++++++++++ .../templates/32readwriteScatterChart7.xlsx | Bin 0 -> 31611 bytes samples/templates/32readwriteStockChart5.xlsx | Bin 0 -> 31410 bytes src/PhpSpreadsheet/Chart/DataSeriesValues.php | 30 +++ src/PhpSpreadsheet/Reader/Xlsx/Chart.php | 123 +++++++++++- src/PhpSpreadsheet/Style/Font.php | 181 ++++++++++++++++++ src/PhpSpreadsheet/Writer/Xlsx/Chart.php | 71 +++++-- .../Writer/Xlsx/StringTable.php | 38 +++- .../Xlsx/Charts32ColoredAxisLabelTest.php | 80 ++++++++ .../Writer/Xlsx/Charts32ScatterTest.php | 82 +++++++- 11 files changed, 691 insertions(+), 39 deletions(-) create mode 100644 samples/Chart/33_Chart_create_scatter2.php create mode 100644 samples/templates/32readwriteScatterChart7.xlsx create mode 100644 samples/templates/32readwriteStockChart5.xlsx create mode 100644 tests/PhpSpreadsheetTests/Writer/Xlsx/Charts32ColoredAxisLabelTest.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index de63f72f..eabedcc8 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -4792,7 +4792,7 @@ parameters: - message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, int given\\.$#" - count: 44 + count: 43 path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - @@ -4982,7 +4982,7 @@ parameters: - message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, string\\|null given\\.$#" - count: 5 + count: 4 path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php - diff --git a/samples/Chart/33_Chart_create_scatter2.php b/samples/Chart/33_Chart_create_scatter2.php new file mode 100644 index 00000000..59d6bd3b --- /dev/null +++ b/samples/Chart/33_Chart_create_scatter2.php @@ -0,0 +1,121 @@ +getActiveSheet(); +// changed data to simulate a trend chart - Xaxis are dates; Yaxis are 3 meausurements from each date +$worksheet->fromArray( + [ + ['', 'metric1', 'metric2', 'metric3'], + ['=DATEVALUE("2021-01-01")', 12.1, 15.1, 21.1], + ['=DATEVALUE("2021-01-04")', 56.2, 73.2, 86.2], + ['=DATEVALUE("2021-01-07")', 52.2, 61.2, 69.2], + ['=DATEVALUE("2021-01-10")', 30.2, 32.2, 0.2], + ] +); +$worksheet->getStyle('A2:A5')->getNumberFormat()->setFormatCode('yyyy-mm-dd'); +$worksheet->getColumnDimension('A')->setAutoSize(true); +$worksheet->setSelectedCells('A1'); + +// 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), // was 2010 + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // was 2011 + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // was 2012 +]; +// Set the X-Axis Labels +// changed from STRING to NUMBER +// added 2 additional x-axis values associated with each of the 3 metrics +// added FORMATE_CODE_NUMBER +$xAxisTickValues = [ + //new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4 + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$A$2:$A$5', Properties::FORMAT_CODE_DATE, 4), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$A$2:$A$5', Properties::FORMAT_CODE_DATE, 4), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$A$2:$A$5', Properties::FORMAT_CODE_DATE, 4), +]; +// 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 +// added FORMAT_CODE_NUMBER +$dataSeriesValues = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$5', Properties::FORMAT_CODE_NUMBER, 4), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', Properties::FORMAT_CODE_NUMBER, 4), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', Properties::FORMAT_CODE_NUMBER, 4), +]; + // Added so that Xaxis shows dates instead of Excel-equivalent-year1900-numbers +$xAxis = new Axis(); +//$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE ); +$xAxis->setAxisNumberProperties('yyyy-mm-dd'); + +// Build the dataseries +$series = new DataSeries( + DataSeries::TYPE_SCATTERCHART, // plotType + null, // plotGrouping (Scatter charts don't have any grouping) + range(0, count($dataSeriesValues) - 1), // plotOrder + $dataSeriesLabels, // plotLabel + $xAxisTickValues, // plotCategory + $dataSeriesValues, // plotValues + null, // plotDirection + null, // smooth line + //DataSeries::STYLE_LINEMARKER // plotStyle + DataSeries::STYLE_MARKER // plotStyle +); + +// 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 Scatter Trend 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 + // added xAxis for correct date display + $xAxis, // xAxis +); + +// Set the position where the chart should appear in the worksheet +$chart->setTopLeftPosition('A7'); +$chart->setBottomRightPosition('N20'); +// 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); +$spreadsheet->disconnectWorksheets(); +$helper->logWrite($writer, $filename, $callStartTime); diff --git a/samples/templates/32readwriteScatterChart7.xlsx b/samples/templates/32readwriteScatterChart7.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..6c01bf400127cd8c3d4af3f260ba53de37ac070c GIT binary patch literal 31611 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 QTQ%T~5&Vh6`Tp$x0rdzZfdBvi literal 0 HcmV?d00001 diff --git a/samples/templates/32readwriteStockChart5.xlsx b/samples/templates/32readwriteStockChart5.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..364803f2eef1c9b64e92ab904e9ac0154d041f8e GIT binary patch literal 31410 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 literal 0 HcmV?d00001 diff --git a/src/PhpSpreadsheet/Chart/DataSeriesValues.php b/src/PhpSpreadsheet/Chart/DataSeriesValues.php index a4f8f61d..cf3e0853 100644 --- a/src/PhpSpreadsheet/Chart/DataSeriesValues.php +++ b/src/PhpSpreadsheet/Chart/DataSeriesValues.php @@ -73,6 +73,9 @@ class DataSeriesValues */ private $fillColor; + /** @var string */ + private $schemeClr = ''; + /** * Line Width. * @@ -83,6 +86,9 @@ class DataSeriesValues /** @var bool */ private $scatterLines = true; + /** @var bool */ + private $bubble3D = false; + /** * Create a new DataSeriesValues object. * @@ -440,4 +446,28 @@ class DataSeriesValues return $this; } + + public function getBubble3D(): bool + { + return $this->bubble3D; + } + + public function setBubble3D(bool $bubble3D): self + { + $this->bubble3D = $bubble3D; + + return $this; + } + + public function getSchemeClr(): string + { + return $this->schemeClr; + } + + public function setSchemeClr(string $schemeClr): self + { + $this->schemeClr = $schemeClr; + + return $this; + } } diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php index 4e3cd02d..4480c6ff 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php @@ -288,6 +288,8 @@ class Chart $lineWidth = null; $pointSize = null; $noFill = false; + $schemeClr = ''; + $bubble3D = false; foreach ($seriesDetails as $seriesKey => $seriesDetail) { switch ($seriesKey) { case 'idx': @@ -304,11 +306,16 @@ class Chart break; case 'spPr': - $ln = $seriesDetail->children($namespacesChartMeta['a'])->ln; + $children = $seriesDetail->children($namespacesChartMeta['a']); + $ln = $children->ln; $lineWidth = self::getAttribute($ln, 'w', 'string'); if (is_countable($ln->noFill) && count($ln->noFill) === 1) { $noFill = true; } + $sf = $children->solidFill->schemeClr; + if ($sf) { + $schemeClr = self::getAttribute($sf, 'val', 'string'); + } break; case 'marker': @@ -342,6 +349,10 @@ class Chart case 'yVal': $seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, "$marker", "$srgbClr", "$pointSize"); + break; + case 'bubble3D': + $bubble3D = self::getAttribute($seriesDetail, 'val', 'boolean'); + break; } } @@ -367,6 +378,28 @@ class Chart $seriesValues[$seriesIndex]->setLineWidth((int) $lineWidth); } } + if ($schemeClr) { + if (isset($seriesLabel[$seriesIndex])) { + $seriesLabel[$seriesIndex]->setSchemeClr($schemeClr); + } + if (isset($seriesCategory[$seriesIndex])) { + $seriesCategory[$seriesIndex]->setSchemeClr($schemeClr); + } + if (isset($seriesValues[$seriesIndex])) { + $seriesValues[$seriesIndex]->setSchemeClr($schemeClr); + } + } + if ($bubble3D) { + if (isset($seriesLabel[$seriesIndex])) { + $seriesLabel[$seriesIndex]->setBubble3D($bubble3D); + } + if (isset($seriesCategory[$seriesIndex])) { + $seriesCategory[$seriesIndex]->setBubble3D($bubble3D); + } + if (isset($seriesValues[$seriesIndex])) { + $seriesValues[$seriesIndex]->setBubble3D($bubble3D); + } + } } } @@ -514,6 +547,9 @@ class Chart $defaultStrikethrough = null; $defaultBaseline = null; $defaultFontName = null; + $defaultLatin = null; + $defaultEastAsian = null; + $defaultComplexScript = null; $defaultColor = null; if (isset($titleDetailPart->pPr->defRPr)) { /** @var ?int */ @@ -528,9 +564,20 @@ class Chart $defaultStrikethrough = self::getAttribute($titleDetailPart->pPr->defRPr, 'strike', 'string'); /** @var ?int */ $defaultBaseline = self::getAttribute($titleDetailPart->pPr->defRPr, 'baseline', 'integer'); + if (isset($titleDetailPart->defRPr->rFont['val'])) { + $defaultFontName = (string) $titleDetailPart->defRPr->rFont['val']; + } if (isset($titleDetailPart->pPr->defRPr->latin)) { /** @var ?string */ - $defaultFontName = self::getAttribute($titleDetailPart->pPr->defRPr->latin, 'typeface', 'string'); + $defaultLatin = self::getAttribute($titleDetailPart->pPr->defRPr->latin, 'typeface', 'string'); + } + if (isset($titleDetailPart->pPr->defRPr->ea)) { + /** @var ?string */ + $defaultEastAsian = self::getAttribute($titleDetailPart->pPr->defRPr->ea, 'typeface', 'string'); + } + if (isset($titleDetailPart->pPr->defRPr->cs)) { + /** @var ?string */ + $defaultComplexScript = self::getAttribute($titleDetailPart->pPr->defRPr->cs, 'typeface', 'string'); } if (isset($titleDetailPart->pPr->defRPr->solidFill->srgbClr)) { /** @var ?string */ @@ -538,12 +585,18 @@ class Chart } } foreach ($titleDetailPart as $titleDetailElementKey => $titleDetailElement) { - if (isset($titleDetailElement->t)) { - $objText = $value->createTextRun((string) $titleDetailElement->t); - } - if ($objText === null || $objText->getFont() === null) { + if ( + (string) $titleDetailElementKey !== 'r' + || !isset($titleDetailElement->t) + ) { continue; } + $objText = $value->createTextRun((string) $titleDetailElement->t); + if ($objText->getFont() === null) { + // @codeCoverageIgnoreStart + continue; + // @codeCoverageIgnoreEnd + } $fontSize = null; $bold = null; $italic = null; @@ -551,15 +604,29 @@ class Chart $strikethrough = null; $baseline = null; $fontName = null; + $latinName = null; + $eastAsian = null; + $complexScript = null; $fontColor = null; + $uSchemeClr = null; if (isset($titleDetailElement->rPr)) { // not used now, not sure it ever was, grandfathering if (isset($titleDetailElement->rPr->rFont['val'])) { + // @codeCoverageIgnoreStart $fontName = (string) $titleDetailElement->rPr->rFont['val']; + // @codeCoverageIgnoreEnd } if (isset($titleDetailElement->rPr->latin)) { /** @var ?string */ - $fontName = self::getAttribute($titleDetailElement->rPr->latin, 'typeface', 'string'); + $latinName = self::getAttribute($titleDetailElement->rPr->latin, 'typeface', 'string'); + } + if (isset($titleDetailElement->rPr->ea)) { + /** @var ?string */ + $eastAsian = self::getAttribute($titleDetailElement->rPr->ea, 'typeface', 'string'); + } + if (isset($titleDetailElement->rPr->cs)) { + /** @var ?string */ + $complexScript = self::getAttribute($titleDetailElement->rPr->cs, 'typeface', 'string'); } /** @var ?int */ $fontSize = self::getAttribute($titleDetailElement->rPr, 'sz', 'integer'); @@ -583,43 +650,72 @@ class Chart /** @var ?string */ $underscore = self::getAttribute($titleDetailElement->rPr, 'u', 'string'); + if (isset($titleDetailElement->rPr->uFill->solidFill->schemeClr)) { + /** @var ?string */ + $uSchemeClr = self::getAttribute($titleDetailElement->rPr->uFill->solidFill->schemeClr, 'val', 'string'); + } /** @var ?string */ - $strikethrough = self::getAttribute($titleDetailElement->rPr, 's', 'string'); + $strikethrough = self::getAttribute($titleDetailElement->rPr, 'strike', 'string'); } + $fontFound = false; + $latinName = $latinName ?? $defaultLatin; + if ($latinName !== null) { + $objText->getFont()->setLatin($latinName); + $fontFound = true; + } + $eastAsian = $eastAsian ?? $defaultEastAsian; + if ($eastAsian !== null) { + $objText->getFont()->setEastAsian($eastAsian); + $fontFound = true; + } + $complexScript = $complexScript ?? $defaultComplexScript; + if ($complexScript !== null) { + $objText->getFont()->setComplexScript($complexScript); + $fontFound = true; + } $fontName = $fontName ?? $defaultFontName; if ($fontName !== null) { + // @codeCoverageIgnoreStart $objText->getFont()->setName($fontName); + $fontFound = true; + // @codeCoverageIgnoreEnd } $fontSize = $fontSize ?? $defaultFontSize; if (is_int($fontSize)) { $objText->getFont()->setSize(floor($fontSize / 100)); + $fontFound = true; } $fontColor = $fontColor ?? $defaultColor; if ($fontColor !== null) { $objText->getFont()->setColor(new Color($fontColor)); + $fontFound = true; } $bold = $bold ?? $defaultBold; if ($bold !== null) { $objText->getFont()->setBold($bold); + $fontFound = true; } $italic = $italic ?? $defaultItalic; if ($italic !== null) { $objText->getFont()->setItalic($italic); + $fontFound = true; } $baseline = $baseline ?? $defaultBaseline; if ($baseline !== null) { + $objText->getFont()->setBaseLine($baseline); if ($baseline > 0) { $objText->getFont()->setSuperscript(true); } elseif ($baseline < 0) { $objText->getFont()->setSubscript(true); } + $fontFound = true; } $underscore = $underscore ?? $defaultUnderscore; @@ -628,18 +724,29 @@ class Chart $objText->getFont()->setUnderline(Font::UNDERLINE_SINGLE); } elseif ($underscore == 'dbl') { $objText->getFont()->setUnderline(Font::UNDERLINE_DOUBLE); + } elseif ($underscore !== '') { + $objText->getFont()->setUnderline($underscore); } else { $objText->getFont()->setUnderline(Font::UNDERLINE_NONE); } + $fontFound = true; + if ($uSchemeClr) { + $objText->getFont()->setUSchemeClr($uSchemeClr); + } } $strikethrough = $strikethrough ?? $defaultStrikethrough; if ($strikethrough !== null) { + $objText->getFont()->setStrikeType($strikethrough); if ($strikethrough == 'noStrike') { $objText->getFont()->setStrikethrough(false); } else { $objText->getFont()->setStrikethrough(true); } + $fontFound = true; + } + if ($fontFound === false) { + $objText->setFont(null); } } diff --git a/src/PhpSpreadsheet/Style/Font.php b/src/PhpSpreadsheet/Style/Font.php index 13fe2b67..e5b056c9 100644 --- a/src/PhpSpreadsheet/Style/Font.php +++ b/src/PhpSpreadsheet/Style/Font.php @@ -18,6 +18,29 @@ class Font extends Supervisor */ protected $name = 'Calibri'; + /** + * The following 6 are used only for chart titles, I think. + * + *@var string + */ + private $latin = ''; + + /** @var string */ + private $eastAsian = ''; + + /** @var string */ + private $complexScript = ''; + + /** @var int */ + private $baseLine = 0; + + /** @var string */ + private $strikeType = ''; + + /** @var string */ + private $uSchemeClr = ''; + // end of chart title items + /** * Font Size. * @@ -170,6 +193,15 @@ class Font extends Supervisor if (isset($styleArray['name'])) { $this->setName($styleArray['name']); } + if (isset($styleArray['latin'])) { + $this->setLatin($styleArray['latin']); + } + if (isset($styleArray['eastAsian'])) { + $this->setEastAsian($styleArray['eastAsian']); + } + if (isset($styleArray['complexScript'])) { + $this->setComplexScript($styleArray['complexScript']); + } if (isset($styleArray['bold'])) { $this->setBold($styleArray['bold']); } @@ -213,6 +245,33 @@ class Font extends Supervisor return $this->name; } + public function getLatin(): string + { + if ($this->isSupervisor) { + return $this->getSharedComponent()->getLatin(); + } + + return $this->latin; + } + + public function getEastAsian(): string + { + if ($this->isSupervisor) { + return $this->getSharedComponent()->getEastAsian(); + } + + return $this->eastAsian; + } + + public function getComplexScript(): string + { + if ($this->isSupervisor) { + return $this->getSharedComponent()->getComplexScript(); + } + + return $this->complexScript; + } + /** * Set Name. * @@ -235,6 +294,51 @@ class Font extends Supervisor return $this; } + public function setLatin(string $fontname): self + { + if ($fontname == '') { + $fontname = 'Calibri'; + } + if ($this->isSupervisor) { + $styleArray = $this->getStyleArray(['latin' => $fontname]); + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); + } else { + $this->latin = $fontname; + } + + return $this; + } + + public function setEastAsian(string $fontname): self + { + if ($fontname == '') { + $fontname = 'Calibri'; + } + if ($this->isSupervisor) { + $styleArray = $this->getStyleArray(['eastAsian' => $fontname]); + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); + } else { + $this->eastAsian = $fontname; + } + + return $this; + } + + public function setComplexScript(string $fontname): self + { + if ($fontname == '') { + $fontname = 'Calibri'; + } + if ($this->isSupervisor) { + $styleArray = $this->getStyleArray(['complexScript' => $fontname]); + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); + } else { + $this->complexScript = $fontname; + } + + return $this; + } + /** * Get Size. * @@ -418,6 +522,69 @@ class Font extends Supervisor return $this; } + public function getBaseLine(): int + { + if ($this->isSupervisor) { + return $this->getSharedComponent()->getBaseLine(); + } + + return $this->baseLine; + } + + public function setBaseLine(int $baseLine): self + { + if ($this->isSupervisor) { + $styleArray = $this->getStyleArray(['baseLine' => $baseLine]); + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); + } else { + $this->baseLine = $baseLine; + } + + return $this; + } + + public function getStrikeType(): string + { + if ($this->isSupervisor) { + return $this->getSharedComponent()->getStrikeType(); + } + + return $this->strikeType; + } + + public function setStrikeType(string $strikeType): self + { + if ($this->isSupervisor) { + $styleArray = $this->getStyleArray(['strikeType' => $strikeType]); + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); + } else { + $this->strikeType = $strikeType; + } + + return $this; + } + + public function getUSchemeClr(): string + { + if ($this->isSupervisor) { + return $this->getSharedComponent()->getUSchemeClr(); + } + + return $this->uSchemeClr; + } + + public function setUSchemeClr(string $uSchemeClr): self + { + if ($this->isSupervisor) { + $styleArray = $this->getStyleArray(['uSchemeClr' => $uSchemeClr]); + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); + } else { + $this->uSchemeClr = $uSchemeClr; + } + + return $this; + } + /** * Get Underline. * @@ -546,6 +713,15 @@ class Font extends Supervisor $this->underline . ($this->strikethrough ? 't' : 'f') . $this->color->getHashCode() . + '*' . + $this->latin . + '*' . + $this->eastAsian . + '*' . + $this->complexScript . + $this->strikeType . + $this->uSchemeClr . + (string) $this->baseLine . __CLASS__ ); } @@ -553,12 +729,17 @@ class Font extends Supervisor protected function exportArray1(): array { $exportedArray = []; + $this->exportArray2($exportedArray, 'baseLine', $this->getBaseLine()); $this->exportArray2($exportedArray, 'bold', $this->getBold()); $this->exportArray2($exportedArray, 'color', $this->getColor()); + $this->exportArray2($exportedArray, 'complexScript', $this->getComplexScript()); + $this->exportArray2($exportedArray, 'eastAsian', $this->getEastAsian()); $this->exportArray2($exportedArray, 'italic', $this->getItalic()); + $this->exportArray2($exportedArray, 'latin', $this->getLatin()); $this->exportArray2($exportedArray, 'name', $this->getName()); $this->exportArray2($exportedArray, 'size', $this->getSize()); $this->exportArray2($exportedArray, 'strikethrough', $this->getStrikethrough()); + $this->exportArray2($exportedArray, 'strikeType', $this->getStrikeType()); $this->exportArray2($exportedArray, 'subscript', $this->getSubscript()); $this->exportArray2($exportedArray, 'superscript', $this->getSuperscript()); $this->exportArray2($exportedArray, 'underline', $this->getUnderline()); diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index f18d9216..0f65ae3b 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -9,8 +9,8 @@ use PhpOffice\PhpSpreadsheet\Chart\GridLines; use PhpOffice\PhpSpreadsheet\Chart\Layout; use PhpOffice\PhpSpreadsheet\Chart\Legend; use PhpOffice\PhpSpreadsheet\Chart\PlotArea; +use PhpOffice\PhpSpreadsheet\Chart\Properties; use PhpOffice\PhpSpreadsheet\Chart\Title; -use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Shared\XMLWriter; use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException; @@ -121,6 +121,10 @@ class Chart extends WriterPart $objWriter->endElement(); $objWriter->startElement('a:p'); + $objWriter->startElement('a:pPr'); + $objWriter->startElement('a:defRPr'); + $objWriter->endElement(); + $objWriter->endElement(); $caption = $title->getCaption(); if ((is_array($caption)) && (count($caption) > 0)) { @@ -304,9 +308,9 @@ class Chart extends WriterPart if (($chartType !== DataSeries::TYPE_PIECHART) && ($chartType !== DataSeries::TYPE_PIECHART_3D) && ($chartType !== DataSeries::TYPE_DONUTCHART)) { if ($chartType === DataSeries::TYPE_BUBBLECHART) { - $this->writeValueAxis($objWriter, $xAxisLabel, $chartType, $id1, $id2, $catIsMultiLevelSeries, $xAxis, $majorGridlines, $minorGridlines); + $this->writeValueAxis($objWriter, $xAxisLabel, $chartType, $id2, $id1, $catIsMultiLevelSeries, $xAxis, $majorGridlines, $minorGridlines); } else { - $this->writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $catIsMultiLevelSeries, $xAxis, ($chartType === DataSeries::TYPE_SCATTERCHART) ? 'c:valAx' : 'c:catAx'); + $this->writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $catIsMultiLevelSeries, $xAxis); } $this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $yAxis, $majorGridlines, $minorGridlines); @@ -367,9 +371,19 @@ class Chart extends WriterPart * @param string $id2 * @param bool $isMultiLevelSeries */ - private function writeCategoryAxis(XMLWriter $objWriter, ?Title $xAxisLabel, $id1, $id2, $isMultiLevelSeries, Axis $yAxis, string $element = 'c:catAx'): void + private function writeCategoryAxis(XMLWriter $objWriter, ?Title $xAxisLabel, $id1, $id2, $isMultiLevelSeries, Axis $yAxis): void { - $objWriter->startElement($element); + // N.B. writeCategoryAxis may be invoked with the last parameter($yAxis) using $xAxis for ScatterChart, etc + // In that case, xAxis is NOT a category. + $AxisFormat = $yAxis->getAxisNumberFormat(); + if ( + $AxisFormat === Properties::FORMAT_CODE_DATE + || $AxisFormat == Properties::FORMAT_CODE_NUMBER + ) { + $objWriter->startElement('c:valAx'); + } else { + $objWriter->startElement('c:catAx'); + } if ($id1 > 0) { $objWriter->startElement('c:axId'); @@ -403,20 +417,20 @@ class Chart extends WriterPart $objWriter->endElement(); $objWriter->startElement('a:p'); - $objWriter->startElement('a:r'); + $objWriter->startElement('a:pPr'); + $objWriter->startElement('a:defRPr'); + $objWriter->endElement(); + $objWriter->endElement(); $caption = $xAxisLabel->getCaption(); if (is_array($caption)) { $caption = $caption[0]; } - $objWriter->startElement('a:t'); - $objWriter->writeRawData(StringHelper::controlCharacterPHP2OOXML($caption)); - $objWriter->endElement(); + $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a'); $objWriter->endElement(); $objWriter->endElement(); $objWriter->endElement(); - $objWriter->endElement(); $layout = $xAxisLabel->getLayout(); $this->writeLayout($objWriter, $layout); @@ -746,18 +760,17 @@ class Chart extends WriterPart $objWriter->endElement(); $objWriter->startElement('a:p'); - $objWriter->startElement('a:r'); + $objWriter->startElement('a:pPr'); + $objWriter->startElement('a:defRPr'); + $objWriter->endElement(); + $objWriter->endElement(); $caption = $yAxisLabel->getCaption(); if (is_array($caption)) { $caption = $caption[0]; } + $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a'); - $objWriter->startElement('a:t'); - $objWriter->writeRawData(StringHelper::controlCharacterPHP2OOXML($caption)); - $objWriter->endElement(); - - $objWriter->endElement(); $objWriter->endElement(); $objWriter->endElement(); $objWriter->endElement(); @@ -1104,13 +1117,26 @@ class Chart extends WriterPart } // Formatting for the points - if (($groupType == DataSeries::TYPE_LINECHART) || ($groupType == DataSeries::TYPE_STOCKCHART || ($groupType === DataSeries::TYPE_SCATTERCHART && $plotSeriesValues !== false && !$plotSeriesValues->getScatterLines()))) { + if ( + $groupType == DataSeries::TYPE_LINECHART + || $groupType == DataSeries::TYPE_STOCKCHART + || ($groupType === DataSeries::TYPE_SCATTERCHART && $plotSeriesValues !== false && !$plotSeriesValues->getScatterLines()) + || ($plotSeriesValues !== false && $plotSeriesValues->getSchemeClr()) + ) { $plotLineWidth = 12700; if ($plotSeriesValues) { $plotLineWidth = $plotSeriesValues->getLineWidth(); } $objWriter->startElement('c:spPr'); + $schemeClr = $plotLabel ? $plotLabel->getSchemeClr() : null; + if ($schemeClr) { + $objWriter->startElement('a:solidFill'); + $objWriter->startElement('a:schemeClr'); + $objWriter->writeAttribute('val', $schemeClr); + $objWriter->endElement(); + $objWriter->endElement(); + } $objWriter->startElement('a:ln'); $objWriter->writeAttribute('w', $plotLineWidth); if ($groupType == DataSeries::TYPE_STOCKCHART || $groupType === DataSeries::TYPE_SCATTERCHART) { @@ -1186,7 +1212,14 @@ class Chart extends WriterPart $objWriter->startElement('c:cat'); } - $this->writePlotSeriesValues($plotSeriesCategory, $objWriter, $groupType, 'str'); + // xVals (Categories) are not always 'str' + // Test X-axis Label's Datatype to decide 'str' vs 'num' + $CategoryDatatype = $plotSeriesCategory->getDataType(); + if ($CategoryDatatype == DataSeriesValues::DATASERIES_TYPE_NUMBER) { + $this->writePlotSeriesValues($plotSeriesCategory, $objWriter, $groupType, 'num'); + } else { + $this->writePlotSeriesValues($plotSeriesCategory, $objWriter, $groupType, 'str'); + } $objWriter->endElement(); } @@ -1377,7 +1410,7 @@ class Chart extends WriterPart $objWriter->endElement(); $objWriter->startElement('c:bubble3D'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', $plotSeriesValues->getBubble3D() ? '1' : '0'); $objWriter->endElement(); } diff --git a/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php b/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php index 6808f33e..da7d825b 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php @@ -203,7 +203,8 @@ class StringTable extends WriterPart if (!$richText instanceof RichText) { $textRun = $richText; $richText = new RichText(); - $richText->createTextRun($textRun); + $run = $richText->createTextRun($textRun); + $run->setFont(null); } if ($prefix !== null) { @@ -241,7 +242,11 @@ class StringTable extends WriterPart } $objWriter->writeAttribute('u', $underlineType); // Strikethrough - $objWriter->writeAttribute('strike', ($element->getFont()->getStrikethrough() ? 'sngStrike' : 'noStrike')); + $objWriter->writeAttribute('strike', ($element->getFont()->getStriketype() ?: 'noStrike')); + // Superscript/subscript + if ($element->getFont()->getBaseLine()) { + $objWriter->writeAttribute('baseline', (string) $element->getFont()->getBaseLine()); + } // Color $objWriter->startElement($prefix . 'solidFill'); @@ -250,10 +255,33 @@ class StringTable extends WriterPart $objWriter->endElement(); // srgbClr $objWriter->endElement(); // solidFill + // Underscore Color + if ($element->getFont()->getUSchemeClr()) { + $objWriter->startElement($prefix . 'uFill'); + $objWriter->startElement($prefix . 'solidFill'); + $objWriter->startElement($prefix . 'schemeClr'); + $objWriter->writeAttribute('val', $element->getFont()->getUSchemeClr()); + $objWriter->endElement(); // schemeClr + $objWriter->endElement(); // solidFill + $objWriter->endElement(); // uFill + } + // fontName - $objWriter->startElement($prefix . 'latin'); - $objWriter->writeAttribute('typeface', $element->getFont()->getName()); - $objWriter->endElement(); + if ($element->getFont()->getLatin()) { + $objWriter->startElement($prefix . 'latin'); + $objWriter->writeAttribute('typeface', $element->getFont()->getLatin()); + $objWriter->endElement(); + } + if ($element->getFont()->getEastAsian()) { + $objWriter->startElement($prefix . 'ea'); + $objWriter->writeAttribute('typeface', $element->getFont()->getEastAsian()); + $objWriter->endElement(); + } + if ($element->getFont()->getComplexScript()) { + $objWriter->startElement($prefix . 'cs'); + $objWriter->writeAttribute('typeface', $element->getFont()->getComplexScript()); + $objWriter->endElement(); + } $objWriter->endElement(); } diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/Charts32ColoredAxisLabelTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/Charts32ColoredAxisLabelTest.php new file mode 100644 index 00000000..71b8da28 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/Charts32ColoredAxisLabelTest.php @@ -0,0 +1,80 @@ +setIncludeCharts(true); + } + + public function writeCharts(XlsxWriter $writer): void + { + $writer->setIncludeCharts(true); + } + + public function testStock5(): void + { + $file = self::DIRECTORY . '32readwriteStockChart5.xlsx'; + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load($file); + $sheet = $spreadsheet->getActiveSheet(); + self::assertSame(1, $sheet->getChartCount()); + /** @var callable */ + $callableReader = [$this, 'readCharts']; + /** @var callable */ + $callableWriter = [$this, 'writeCharts']; + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $sheet = $reloadedSpreadsheet->getActiveSheet(); + self::assertSame('Charts', $sheet->getTitle()); + $charts = $sheet->getChartCollection(); + self::assertCount(1, $charts); + $chart = $charts[0]; + self::assertNotNull($chart); + + $xAxisLabel = $chart->getXAxisLabel(); + $captionArray = $xAxisLabel->getCaption(); + self::assertIsArray($captionArray); + self::assertCount(1, $captionArray); + $caption = $captionArray[0]; + self::assertInstanceOf(RichText::class, $caption); + self::assertSame('X-Axis Title in Green', $caption->getPlainText()); + $elements = $caption->getRichTextElements(); + self::assertCount(1, $elements); + $run = $elements[0]; + self::assertInstanceOf(Run::class, $run); + $font = $run->getFont(); + self::assertInstanceOf(Font::class, $font); + self::assertSame('00B050', $font->getColor()->getRGB()); + + $yAxisLabel = $chart->getYAxisLabel(); + $captionArray = $yAxisLabel->getCaption(); + self::assertIsArray($captionArray); + self::assertCount(1, $captionArray); + $caption = $captionArray[0]; + self::assertInstanceOf(RichText::class, $caption); + self::assertSame('Y-Axis Title in Red', $caption->getPlainText()); + $elements = $caption->getRichTextElements(); + self::assertCount(1, $elements); + $run = $elements[0]; + self::assertInstanceOf(Run::class, $run); + $font = $run->getFont(); + self::assertInstanceOf(Font::class, $font); + self::assertSame('FF0000', $font->getColor()->getRGB()); + + $reloadedSpreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/Charts32ScatterTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/Charts32ScatterTest.php index dc7fbc0b..76389727 100644 --- a/tests/PhpSpreadsheetTests/Writer/Xlsx/Charts32ScatterTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/Charts32ScatterTest.php @@ -57,7 +57,7 @@ class Charts32ScatterTest extends AbstractFunctional self::assertInstanceOf(Run::class, $run); $font = $run->getFont(); self::assertInstanceOf(Font::class, $font); - self::assertSame('Calibri', $font->getName()); + self::assertSame('Calibri', $font->getLatin()); self::assertEquals(12, $font->getSize()); self::assertTrue($font->getBold()); self::assertFalse($font->getItalic()); @@ -127,7 +127,7 @@ class Charts32ScatterTest extends AbstractFunctional self::assertInstanceOf(Run::class, $run); $font = $run->getFont(); self::assertInstanceOf(Font::class, $font); - self::assertSame('Calibri', $font->getName()); + self::assertSame('Calibri', $font->getLatin()); self::assertEquals(12, $font->getSize()); self::assertTrue($font->getBold()); self::assertFalse($font->getItalic()); @@ -141,7 +141,7 @@ class Charts32ScatterTest extends AbstractFunctional self::assertInstanceOf(Run::class, $run); $font = $run->getFont(); self::assertInstanceOf(Font::class, $font); - self::assertSame('Courier New', $font->getName()); + self::assertSame('Courier New', $font->getLatin()); self::assertEquals(10, $font->getSize()); self::assertFalse($font->getBold()); self::assertFalse($font->getItalic()); @@ -155,7 +155,7 @@ class Charts32ScatterTest extends AbstractFunctional self::assertInstanceOf(Run::class, $run); $font = $run->getFont(); self::assertInstanceOf(Font::class, $font); - self::assertSame('Calibri', $font->getName()); + self::assertSame('Calibri', $font->getLatin()); self::assertEquals(12, $font->getSize()); self::assertTrue($font->getBold()); self::assertFalse($font->getItalic()); @@ -224,7 +224,7 @@ class Charts32ScatterTest extends AbstractFunctional self::assertInstanceOf(Run::class, $run); $font = $run->getFont(); self::assertInstanceOf(Font::class, $font); - self::assertSame('Calibri', $font->getName()); + self::assertSame('Calibri', $font->getLatin()); self::assertEquals(12, $font->getSize()); self::assertTrue($font->getBold()); self::assertFalse($font->getItalic()); @@ -258,4 +258,76 @@ class Charts32ScatterTest extends AbstractFunctional $reloadedSpreadsheet->disconnectWorksheets(); } + + public function testScatter7(): void + { + $file = self::DIRECTORY . '32readwriteScatterChart7.xlsx'; + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load($file); + $sheet = $spreadsheet->getActiveSheet(); + self::assertSame(1, $sheet->getChartCount()); + /** @var callable */ + $callableReader = [$this, 'readCharts']; + /** @var callable */ + $callableWriter = [$this, 'writeCharts']; + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $sheet = $reloadedSpreadsheet->getActiveSheet(); + self::assertSame('Charts', $sheet->getTitle()); + $charts = $sheet->getChartCollection(); + self::assertCount(1, $charts); + $chart = $charts[0]; + self::assertNotNull($chart); + $title = $chart->getTitle(); + $captionArray = $title->getCaption(); + self::assertIsArray($captionArray); + self::assertCount(1, $captionArray); + $caption = $captionArray[0]; + self::assertInstanceOf(RichText::class, $caption); + self::assertSame('Latin/EA/CS Title ABCאבגDEFァ', $caption->getPlainText()); + $elements = $caption->getRichTextElements(); + self::assertGreaterThan(0, count($elements)); + foreach ($elements as $run) { + self::assertInstanceOf(Run::class, $run); + $font = $run->getFont(); + self::assertInstanceOf(Font::class, $font); + self::assertSame('Times New Roman', $font->getLatin()); + self::assertSame('Malgun Gothic', $font->getEastAsian()); + self::assertSame('Courier New', $font->getComplexScript()); + self::assertEquals(12, $font->getSize()); + self::assertTrue($font->getBold()); + self::assertFalse($font->getItalic()); + self::assertFalse($font->getSuperscript()); + self::assertFalse($font->getSubscript()); + self::assertFalse($font->getStrikethrough()); + self::assertSame('none', $font->getUnderline()); + self::assertSame('000000', $font->getColor()->getRGB()); + } + + $plotArea = $chart->getPlotArea(); + $plotSeries = $plotArea->getPlotGroup(); + self::assertCount(1, $plotSeries); + $dataSeries = $plotSeries[0]; + $plotValues = $dataSeries->getPlotValues(); + self::assertCount(3, $plotValues); + $values = $plotValues[0]; + self::assertFalse($values->getScatterLines()); + self::assertSame(28575, $values->getLineWidth()); + self::assertSame(3, $values->getPointSize()); + self::assertSame('', $values->getFillColor()); + $values = $plotValues[1]; + self::assertFalse($values->getScatterLines()); + self::assertSame(28575, $values->getLineWidth()); + self::assertSame(3, $values->getPointSize()); + self::assertSame('', $values->getFillColor()); + $values = $plotValues[2]; + self::assertFalse($values->getScatterLines()); + self::assertSame(28575, $values->getLineWidth()); + self::assertSame(7, $values->getPointSize()); + self::assertSame('FFFF00', $values->getFillColor()); + + $reloadedSpreadsheet->disconnectWorksheets(); + } }