Merge branch 'master' into Issue-2833_Null-handling-in-PRODUCT()-Function

This commit is contained in:
Mark Baker 2022-05-17 16:53:08 +02:00 committed by GitHub
commit a59b29ae11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 615 additions and 197 deletions

View File

@ -1215,26 +1215,6 @@ parameters:
count: 2
path: src/PhpSpreadsheet/Chart/DataSeries.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues\\:\\:refresh\\(\\) has parameter \\$flatten with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/DataSeriesValues.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues\\:\\:\\$dataSource \\(string\\) does not accept string\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/DataSeriesValues.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues\\:\\:\\$dataTypeValues has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/DataSeriesValues.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues\\:\\:\\$fillColor \\(array\\<string\\>\\|string\\) does not accept array\\<string\\>\\|string\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/DataSeriesValues.php
-
message: "#^Parameter \\#1 \\$angle of method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\:\\:setShadowAngle\\(\\) expects int, int\\|null given\\.$#"
count: 1
@ -2560,56 +2540,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php
-
message: "#^Cannot call method getFont\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\RichText\\\\Run\\|null\\.$#"
count: 12
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setBold\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setColor\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setItalic\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setName\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setSize\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setStrikethrough\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 2
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setSubscript\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setSuperscript\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setUnderline\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 3
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeries\\(\\) has no return type specified\\.$#"
count: 1
@ -2635,11 +2565,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValueSet\\(\\) has parameter \\$marker with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValueSet\\(\\) has parameter \\$namespacesChartMeta with no type specified\\.$#"
count: 1
@ -2715,21 +2640,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:readColor\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:readColor\\(\\) has parameter \\$background with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:readColor\\(\\) has parameter \\$color with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Parameter \\#1 \\$position of class PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Legend constructor expects string, bool\\|float\\|int\\|string\\|null given\\.$#"
count: 1
@ -4997,7 +4907,7 @@ parameters:
-
message: "#^Cannot call method getBold\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 2
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
-
@ -5007,12 +4917,12 @@ parameters:
-
message: "#^Cannot call method getItalic\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 2
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
-
message: "#^Cannot call method getName\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 2
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
-
@ -5022,7 +4932,7 @@ parameters:
-
message: "#^Cannot call method getStrikethrough\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 2
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
-
@ -5037,7 +4947,7 @@ parameters:
-
message: "#^Cannot call method getUnderline\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 2
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
-

Binary file not shown.

View File

@ -12,7 +12,7 @@ class DataSeriesValues
const DATASERIES_TYPE_STRING = 'String';
const DATASERIES_TYPE_NUMBER = 'Number';
private static $dataTypeValues = [
private const DATA_TYPE_VALUES = [
self::DATASERIES_TYPE_STRING,
self::DATASERIES_TYPE_NUMBER,
];
@ -27,7 +27,7 @@ class DataSeriesValues
/**
* Series Data Source.
*
* @var string
* @var ?string
*/
private $dataSource;
@ -69,7 +69,7 @@ class DataSeriesValues
/**
* Fill color (can be array with colors if dataseries have custom colors).
*
* @var string|string[]
* @var null|string|string[]
*/
private $fillColor;
@ -80,6 +80,9 @@ class DataSeriesValues
*/
private $lineWidth = 12700;
/** @var bool */
private $scatterLines = true;
/**
* Create a new DataSeriesValues object.
*
@ -90,8 +93,9 @@ class DataSeriesValues
* @param mixed $dataValues
* @param null|mixed $marker
* @param null|string|string[] $fillColor
* @param string $pointSize
*/
public function __construct($dataType = self::DATASERIES_TYPE_NUMBER, $dataSource = null, $formatCode = null, $pointCount = 0, $dataValues = [], $marker = null, $fillColor = null)
public function __construct($dataType = self::DATASERIES_TYPE_NUMBER, $dataSource = null, $formatCode = null, $pointCount = 0, $dataValues = [], $marker = null, $fillColor = null, $pointSize = '3')
{
$this->setDataType($dataType);
$this->dataSource = $dataSource;
@ -100,6 +104,9 @@ class DataSeriesValues
$this->dataValues = $dataValues;
$this->pointMarker = $marker;
$this->fillColor = $fillColor;
if (is_numeric($pointSize)) {
$this->pointSize = (int) $pointSize;
}
}
/**
@ -126,7 +133,7 @@ class DataSeriesValues
*/
public function setDataType($dataType)
{
if (!in_array($dataType, self::$dataTypeValues)) {
if (!in_array($dataType, self::DATA_TYPE_VALUES)) {
throw new Exception('Invalid datatype for chart data series values');
}
$this->dataType = $dataType;
@ -137,7 +144,7 @@ class DataSeriesValues
/**
* Get Series Data Source (formula).
*
* @return string
* @return ?string
*/
public function getDataSource()
{
@ -147,7 +154,7 @@ class DataSeriesValues
/**
* Set Series Data Source (formula).
*
* @param string $dataSource
* @param ?string $dataSource
*
* @return $this
*/
@ -239,7 +246,7 @@ class DataSeriesValues
/**
* Get fill color.
*
* @return string|string[] HEX color or array with HEX colors
* @return null|string|string[] HEX color or array with HEX colors
*/
public function getFillColor()
{
@ -249,7 +256,7 @@ class DataSeriesValues
/**
* Set fill color for series.
*
* @param string|string[] $color HEX color or array with HEX colors
* @param null|string|string[] $color HEX color or array with HEX colors
*
* @return DataSeriesValues
*/
@ -260,7 +267,7 @@ class DataSeriesValues
$this->validateColor($colorValue);
}
} else {
$this->validateColor($color);
$this->validateColor("$color");
}
$this->fillColor = $color;
@ -379,7 +386,7 @@ class DataSeriesValues
return $this;
}
public function refresh(Worksheet $worksheet, $flatten = true): void
public function refresh(Worksheet $worksheet, bool $flatten = true): void
{
if ($this->dataSource !== null) {
$calcEngine = Calculation::getInstance($worksheet->getParent());
@ -421,4 +428,16 @@ class DataSeriesValues
$this->pointCount = count($this->dataValues);
}
}
public function getScatterLines(): bool
{
return $this->scatterLines;
}
public function setScatterLines(bool $scatterLines): self
{
$this->scatterLines = $scatterLines;
return $this;
}
}

View File

@ -25,7 +25,7 @@ class Chart
private static function getAttribute(SimpleXMLElement $component, $name, $format)
{
$attributes = $component->attributes();
if (isset($attributes[$name])) {
if (@isset($attributes[$name])) {
if ($format == 'string') {
return (string) $attributes[$name];
} elseif ($format == 'integer') {
@ -42,15 +42,6 @@ class Chart
return null;
}
private static function readColor($color, $background = false)
{
if (isset($color['rgb'])) {
return (string) $color['rgb'];
} elseif (isset($color['indexed'])) {
return Color::indexedColor($color['indexed'] - 7, $background)->getARGB();
}
}
/**
* @param string $chartName
*
@ -293,6 +284,10 @@ class Chart
case 'ser':
$marker = null;
$seriesIndex = '';
$srgbClr = null;
$lineWidth = null;
$pointSize = null;
$noFill = false;
foreach ($seriesDetails as $seriesKey => $seriesDetail) {
switch ($seriesKey) {
case 'idx':
@ -307,9 +302,25 @@ class Chart
case 'tx':
$seriesLabel[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta);
break;
case 'spPr':
$ln = $seriesDetail->children($namespacesChartMeta['a'])->ln;
$lineWidth = self::getAttribute($ln, 'w', 'string');
if (is_countable($ln->noFill) && count($ln->noFill) === 1) {
$noFill = true;
}
break;
case 'marker':
$marker = self::getAttribute($seriesDetail->symbol, 'val', 'string');
$pointSize = self::getAttribute($seriesDetail->size, 'val', 'string');
$pointSize = is_numeric($pointSize) ? ((int) $pointSize) : null;
if (count($seriesDetail->spPr) === 1) {
$ln = $seriesDetail->spPr->children($namespacesChartMeta['a']);
if (count($ln->solidFill) === 1) {
$srgbClr = self::getattribute($ln->solidFill->srgbClr, 'val', 'string');
}
}
break;
case 'smooth':
@ -321,30 +332,52 @@ class Chart
break;
case 'val':
$seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
$seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, "$marker", "$srgbClr", "$pointSize");
break;
case 'xVal':
$seriesCategory[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
$seriesCategory[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, "$marker", "$srgbClr", "$pointSize");
break;
case 'yVal':
$seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
$seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, "$marker", "$srgbClr", "$pointSize");
break;
}
}
if ($noFill) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->setScatterLines(false);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->setScatterLines(false);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setScatterLines(false);
}
}
if (is_numeric($lineWidth)) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->setLineWidth((int) $lineWidth);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->setLineWidth((int) $lineWidth);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setLineWidth((int) $lineWidth);
}
}
}
}
return new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $smoothLine);
}
private static function chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker = null)
private static function chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, ?string $marker = null, ?string $srgbClr = null, ?string $pointSize = null)
{
if (isset($seriesDetail->strRef)) {
$seriesSource = (string) $seriesDetail->strRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker);
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker, $srgbClr, "$pointSize");
if (isset($seriesDetail->strRef->strCache)) {
$seriesData = self::chartDataSeriesValues($seriesDetail->strRef->strCache->children($namespacesChartMeta['c']), 's');
@ -356,7 +389,7 @@ class Chart
return $seriesValues;
} elseif (isset($seriesDetail->numRef)) {
$seriesSource = (string) $seriesDetail->numRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, null, null, null, $marker);
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, null, null, null, $marker, $srgbClr, "$pointSize");
if (isset($seriesDetail->numRef->numCache)) {
$seriesData = self::chartDataSeriesValues($seriesDetail->numRef->numCache->children($namespacesChartMeta['c']));
$seriesValues
@ -367,7 +400,7 @@ class Chart
return $seriesValues;
} elseif (isset($seriesDetail->multiLvlStrRef)) {
$seriesSource = (string) $seriesDetail->multiLvlStrRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker);
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker, $srgbClr, "$pointSize");
if (isset($seriesDetail->multiLvlStrRef->multiLvlStrCache)) {
$seriesData = self::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlStrRef->multiLvlStrCache->children($namespacesChartMeta['c']), 's');
@ -379,7 +412,7 @@ class Chart
return $seriesValues;
} elseif (isset($seriesDetail->multiLvlNumRef)) {
$seriesSource = (string) $seriesDetail->multiLvlNumRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker);
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker, $srgbClr, "$pointSize");
if (isset($seriesDetail->multiLvlNumRef->multiLvlNumCache)) {
$seriesData = self::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlNumRef->multiLvlNumCache->children($namespacesChartMeta['c']), 's');
@ -474,62 +507,138 @@ class Chart
{
$value = new RichText();
$objText = null;
$defaultFontSize = null;
$defaultBold = null;
$defaultItalic = null;
$defaultUnderscore = null;
$defaultStrikethrough = null;
$defaultBaseline = null;
$defaultFontName = null;
$defaultColor = null;
if (isset($titleDetailPart->pPr->defRPr)) {
/** @var ?int */
$defaultFontSize = self::getAttribute($titleDetailPart->pPr->defRPr, 'sz', 'integer');
/** @var ?bool */
$defaultBold = self::getAttribute($titleDetailPart->pPr->defRPr, 'b', 'boolean');
/** @var ?bool */
$defaultItalic = self::getAttribute($titleDetailPart->pPr->defRPr, 'i', 'boolean');
/** @var ?string */
$defaultUnderscore = self::getAttribute($titleDetailPart->pPr->defRPr, 'u', 'string');
/** @var ?string */
$defaultStrikethrough = self::getAttribute($titleDetailPart->pPr->defRPr, 'strike', 'string');
/** @var ?int */
$defaultBaseline = self::getAttribute($titleDetailPart->pPr->defRPr, 'baseline', 'integer');
if (isset($titleDetailPart->pPr->defRPr->latin)) {
/** @var ?string */
$defaultFontName = self::getAttribute($titleDetailPart->pPr->defRPr->latin, 'typeface', 'string');
}
if (isset($titleDetailPart->pPr->defRPr->solidFill->srgbClr)) {
/** @var ?string */
$defaultColor = self::getAttribute($titleDetailPart->pPr->defRPr->solidFill->srgbClr, 'val', 'string');
}
}
foreach ($titleDetailPart as $titleDetailElementKey => $titleDetailElement) {
if (isset($titleDetailElement->t)) {
$objText = $value->createTextRun((string) $titleDetailElement->t);
}
if ($objText === null || $objText->getFont() === null) {
continue;
}
$fontSize = null;
$bold = null;
$italic = null;
$underscore = null;
$strikethrough = null;
$baseline = null;
$fontName = null;
$fontColor = null;
if (isset($titleDetailElement->rPr)) {
// not used now, not sure it ever was, grandfathering
if (isset($titleDetailElement->rPr->rFont['val'])) {
$objText->getFont()->setName((string) $titleDetailElement->rPr->rFont['val']);
}
$fontSize = (self::getAttribute($titleDetailElement->rPr, 'sz', 'integer'));
if (is_int($fontSize)) {
$objText->getFont()->setSize(floor($fontSize / 100));
}
$fontColor = (self::getAttribute($titleDetailElement->rPr, 'color', 'string'));
if ($fontColor !== null) {
$objText->getFont()->setColor(new Color(self::readColor($fontColor)));
$fontName = (string) $titleDetailElement->rPr->rFont['val'];
}
if (isset($titleDetailElement->rPr->latin)) {
/** @var ?string */
$fontName = self::getAttribute($titleDetailElement->rPr->latin, 'typeface', 'string');
}
/** @var ?int */
$fontSize = self::getAttribute($titleDetailElement->rPr, 'sz', 'integer');
// not used now, not sure it ever was, grandfathering
/** @var ?string */
$fontColor = self::getAttribute($titleDetailElement->rPr, 'color', 'string');
if (isset($titleDetailElement->rPr->solidFill->srgbClr)) {
/** @var ?string */
$fontColor = self::getAttribute($titleDetailElement->rPr->solidFill->srgbClr, 'val', 'string');
}
/** @var ?bool */
$bold = self::getAttribute($titleDetailElement->rPr, 'b', 'boolean');
if ($bold !== null) {
$objText->getFont()->setBold($bold);
}
/** @var ?bool */
$italic = self::getAttribute($titleDetailElement->rPr, 'i', 'boolean');
if ($italic !== null) {
$objText->getFont()->setItalic($italic);
}
/** @var ?int */
$baseline = self::getAttribute($titleDetailElement->rPr, 'baseline', 'integer');
if ($baseline !== null) {
if ($baseline > 0) {
$objText->getFont()->setSuperscript(true);
} elseif ($baseline < 0) {
$objText->getFont()->setSubscript(true);
}
}
$underscore = (self::getAttribute($titleDetailElement->rPr, 'u', 'string'));
if ($underscore !== null) {
if ($underscore == 'sng') {
$objText->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
} elseif ($underscore == 'dbl') {
$objText->getFont()->setUnderline(Font::UNDERLINE_DOUBLE);
} else {
$objText->getFont()->setUnderline(Font::UNDERLINE_NONE);
}
}
/** @var ?string */
$underscore = self::getAttribute($titleDetailElement->rPr, 'u', 'string');
$strikethrough = (self::getAttribute($titleDetailElement->rPr, 's', 'string'));
if ($strikethrough !== null) {
if ($strikethrough == 'noStrike') {
$objText->getFont()->setStrikethrough(false);
} else {
$objText->getFont()->setStrikethrough(true);
}
/** @var ?string */
$strikethrough = self::getAttribute($titleDetailElement->rPr, 's', 'string');
}
$fontName = $fontName ?? $defaultFontName;
if ($fontName !== null) {
$objText->getFont()->setName($fontName);
}
$fontSize = $fontSize ?? $defaultFontSize;
if (is_int($fontSize)) {
$objText->getFont()->setSize(floor($fontSize / 100));
}
$fontColor = $fontColor ?? $defaultColor;
if ($fontColor !== null) {
$objText->getFont()->setColor(new Color($fontColor));
}
$bold = $bold ?? $defaultBold;
if ($bold !== null) {
$objText->getFont()->setBold($bold);
}
$italic = $italic ?? $defaultItalic;
if ($italic !== null) {
$objText->getFont()->setItalic($italic);
}
$baseline = $baseline ?? $defaultBaseline;
if ($baseline !== null) {
if ($baseline > 0) {
$objText->getFont()->setSuperscript(true);
} elseif ($baseline < 0) {
$objText->getFont()->setSubscript(true);
}
}
$underscore = $underscore ?? $defaultUnderscore;
if ($underscore !== null) {
if ($underscore == 'sng') {
$objText->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
} elseif ($underscore == 'dbl') {
$objText->getFont()->setUnderline(Font::UNDERLINE_DOUBLE);
} else {
$objText->getFont()->setUnderline(Font::UNDERLINE_NONE);
}
}
$strikethrough = $strikethrough ?? $defaultStrikethrough;
if ($strikethrough !== null) {
if ($strikethrough == 'noStrike') {
$objText->getFont()->setStrikethrough(false);
} else {
$objText->getFont()->setStrikethrough(true);
}
}
}

View File

@ -306,7 +306,7 @@ class Chart extends WriterPart
if ($chartType === DataSeries::TYPE_BUBBLECHART) {
$this->writeValueAxis($objWriter, $xAxisLabel, $chartType, $id1, $id2, $catIsMultiLevelSeries, $xAxis, $majorGridlines, $minorGridlines);
} else {
$this->writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $catIsMultiLevelSeries, $xAxis);
$this->writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $catIsMultiLevelSeries, $xAxis, ($chartType === DataSeries::TYPE_SCATTERCHART) ? 'c:valAx' : 'c:catAx');
}
$this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $yAxis, $majorGridlines, $minorGridlines);
@ -367,9 +367,9 @@ class Chart extends WriterPart
* @param string $id2
* @param bool $isMultiLevelSeries
*/
private function writeCategoryAxis(XMLWriter $objWriter, ?Title $xAxisLabel, $id1, $id2, $isMultiLevelSeries, Axis $yAxis): void
private function writeCategoryAxis(XMLWriter $objWriter, ?Title $xAxisLabel, $id1, $id2, $isMultiLevelSeries, Axis $yAxis, string $element = 'c:catAx'): void
{
$objWriter->startElement('c:catAx');
$objWriter->startElement($element);
if ($id1 > 0) {
$objWriter->startElement('c:axId');
@ -1016,7 +1016,7 @@ class Chart extends WriterPart
* @param bool $valIsMultiLevelSeries Is value set a multi-series set
* @param string $plotGroupingType Type of grouping for multi-series values
*/
private function writePlotGroup(?DataSeries $plotGroup, $groupType, XMLWriter $objWriter, &$catIsMultiLevelSeries, &$valIsMultiLevelSeries, &$plotGroupingType): void
private function writePlotGroup(?DataSeries $plotGroup, string $groupType, XMLWriter $objWriter, &$catIsMultiLevelSeries, &$valIsMultiLevelSeries, &$plotGroupingType): void
{
if ($plotGroup === null) {
return;
@ -1104,7 +1104,7 @@ class Chart extends WriterPart
}
// Formatting for the points
if (($groupType == DataSeries::TYPE_LINECHART) || ($groupType == DataSeries::TYPE_STOCKCHART)) {
if (($groupType == DataSeries::TYPE_LINECHART) || ($groupType == DataSeries::TYPE_STOCKCHART || ($groupType === DataSeries::TYPE_SCATTERCHART && $plotSeriesValues !== false && !$plotSeriesValues->getScatterLines()))) {
$plotLineWidth = 12700;
if ($plotSeriesValues) {
$plotLineWidth = $plotSeriesValues->getLineWidth();
@ -1113,7 +1113,7 @@ class Chart extends WriterPart
$objWriter->startElement('c:spPr');
$objWriter->startElement('a:ln');
$objWriter->writeAttribute('w', $plotLineWidth);
if ($groupType == DataSeries::TYPE_STOCKCHART) {
if ($groupType == DataSeries::TYPE_STOCKCHART || $groupType === DataSeries::TYPE_SCATTERCHART) {
$objWriter->startElement('a:noFill');
$objWriter->endElement();
} elseif ($plotLabel) {
@ -1142,6 +1142,16 @@ class Chart extends WriterPart
$objWriter->startElement('c:size');
$objWriter->writeAttribute('val', (string) $plotSeriesValues->getPointSize());
$objWriter->endElement();
$fillColor = $plotSeriesValues->getFillColor();
if (is_string($fillColor) && $fillColor !== '') {
$objWriter->startElement('c:spPr');
$objWriter->startElement('a:solidFill');
$objWriter->startElement('a:srgbClr');
$objWriter->writeAttribute('val', $fillColor);
$objWriter->endElement(); // srgbClr
$objWriter->endElement(); // solidFill
$objWriter->endElement(); // spPr
}
}
$objWriter->endElement();
@ -1192,6 +1202,11 @@ class Chart extends WriterPart
$this->writePlotSeriesValues($plotSeriesValues, $objWriter, $groupType, 'num');
$objWriter->endElement();
if ($groupType === DataSeries::TYPE_SCATTERCHART && $plotGroup->getPlotStyle() === 'smoothMarker') {
$objWriter->startElement('c:smooth');
$objWriter->writeAttribute('val', '1');
$objWriter->endElement();
}
}
if ($groupType === DataSeries::TYPE_BUBBLECHART) {

View File

@ -215,36 +215,48 @@ class StringTable extends WriterPart
foreach ($elements as $element) {
// r
$objWriter->startElement($prefix . 'r');
if ($element->getFont() !== null) {
// rPr
$objWriter->startElement($prefix . 'rPr');
$size = $element->getFont()->getSize();
if (is_numeric($size)) {
$objWriter->writeAttribute('sz', (string) (int) ($size * 100));
}
// rPr
$objWriter->startElement($prefix . 'rPr');
// Bold
$objWriter->writeAttribute('b', ($element->getFont()->getBold() ? 1 : 0));
// Italic
$objWriter->writeAttribute('i', ($element->getFont()->getItalic() ? 1 : 0));
// Underline
$underlineType = $element->getFont()->getUnderline();
switch ($underlineType) {
case 'single':
$underlineType = 'sng';
// Bold
$objWriter->writeAttribute('b', ($element->getFont()->getBold() ? 1 : 0));
// Italic
$objWriter->writeAttribute('i', ($element->getFont()->getItalic() ? 1 : 0));
// Underline
$underlineType = $element->getFont()->getUnderline();
switch ($underlineType) {
case 'single':
$underlineType = 'sng';
break;
case 'double':
$underlineType = 'dbl';
break;
case 'double':
$underlineType = 'dbl';
break;
}
$objWriter->writeAttribute('u', $underlineType);
// Strikethrough
$objWriter->writeAttribute('strike', ($element->getFont()->getStrikethrough() ? 'sngStrike' : 'noStrike'));
break;
// Color
$objWriter->startElement($prefix . 'solidFill');
$objWriter->startElement($prefix . 'srgbClr');
$objWriter->writeAttribute('val', $element->getFont()->getColor()->getRGB());
$objWriter->endElement(); // srgbClr
$objWriter->endElement(); // solidFill
// fontName
$objWriter->startElement($prefix . 'latin');
$objWriter->writeAttribute('typeface', $element->getFont()->getName());
$objWriter->endElement();
$objWriter->endElement();
}
$objWriter->writeAttribute('u', $underlineType);
// Strikethrough
$objWriter->writeAttribute('strike', ($element->getFont()->getStrikethrough() ? 'sngStrike' : 'noStrike'));
// rFont
$objWriter->startElement($prefix . 'latin');
$objWriter->writeAttribute('typeface', $element->getFont()->getName());
$objWriter->endElement();
$objWriter->endElement();
// t
$objWriter->startElement($prefix . 't');

View File

@ -19,10 +19,13 @@ abstract class AbstractFunctional extends TestCase
*
* @return Spreadsheet
*/
protected function writeAndReload(Spreadsheet $spreadsheet, $format, ?callable $readerCustomizer = null)
protected function writeAndReload(Spreadsheet $spreadsheet, $format, ?callable $readerCustomizer = null, ?callable $writerCustomizer = null)
{
$filename = File::temporaryFilename();
$writer = IOFactory::createWriter($spreadsheet, $format);
if ($writerCustomizer) {
$writerCustomizer($writer);
}
$writer->save($filename);
$reader = IOFactory::createReader($format);

View File

@ -0,0 +1,261 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\RichText\Run;
use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
class Charts32ScatterTest extends AbstractFunctional
{
private const DIRECTORY = 'samples' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR;
public function readCharts(XlsxReader $reader): void
{
$reader->setIncludeCharts(true);
}
public function writeCharts(XlsxWriter $writer): void
{
$writer->setIncludeCharts(true);
}
public function testScatter1(): void
{
$file = self::DIRECTORY . '32readwriteScatterChart1.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('Scatter - No Join and Markers', $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('Calibri', $font->getName());
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();
}
public function testScatter6(): void
{
$file = self::DIRECTORY . '32readwriteScatterChart6.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('Scatter - Rich Text Title No Join and Markers', $caption->getPlainText());
$elements = $caption->getRichTextElements();
self::assertCount(3, $elements);
$run = $elements[0];
self::assertInstanceOf(Run::class, $run);
$font = $run->getFont();
self::assertInstanceOf(Font::class, $font);
self::assertSame('Calibri', $font->getName());
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());
$run = $elements[1];
self::assertInstanceOf(Run::class, $run);
$font = $run->getFont();
self::assertInstanceOf(Font::class, $font);
self::assertSame('Courier New', $font->getName());
self::assertEquals(10, $font->getSize());
self::assertFalse($font->getBold());
self::assertFalse($font->getItalic());
self::assertFalse($font->getSuperscript());
self::assertFalse($font->getSubscript());
self::assertFalse($font->getStrikethrough());
self::assertSame('single', $font->getUnderline());
self::assertSame('00B0F0', $font->getColor()->getRGB());
$run = $elements[2];
self::assertInstanceOf(Run::class, $run);
$font = $run->getFont();
self::assertInstanceOf(Font::class, $font);
self::assertSame('Calibri', $font->getName());
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();
}
public function testScatter3(): void
{
$file = self::DIRECTORY . '32readwriteScatterChart3.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('Scatter - Join Straight Lines and Markers', $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('Calibri', $font->getName());
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::assertTrue($values->getScatterLines());
self::assertSame(12700, $values->getLineWidth());
self::assertSame(3, $values->getPointSize());
self::assertSame('', $values->getFillColor());
$values = $plotValues[1];
self::assertTrue($values->getScatterLines());
self::assertSame(12700, $values->getLineWidth());
self::assertSame(3, $values->getPointSize());
self::assertSame('', $values->getFillColor());
$values = $plotValues[2];
self::assertTrue($values->getScatterLines());
self::assertSame(12700, $values->getLineWidth());
self::assertSame(3, $values->getPointSize());
self::assertSame('', $values->getFillColor());
$reloadedSpreadsheet->disconnectWorksheets();
}
}

View File

@ -0,0 +1,89 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
use PHPUnit\Framework\TestCase;
class Charts32XmlTest extends TestCase
{
// These tests can only be performed by examining xml.
private const DIRECTORY = 'samples' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR;
/** @var string */
private $outputFileName = '';
protected function tearDown(): void
{
if ($this->outputFileName !== '') {
unlink($this->outputFileName);
$this->outputFileName = '';
}
}
/**
* @dataProvider providerScatterCharts
*/
public function testBezierCount(int $expectedCount, string $inputFile): void
{
$file = self::DIRECTORY . $inputFile;
$reader = new XlsxReader();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($file);
$writer = new XlsxWriter($spreadsheet);
$writer->setIncludeCharts(true);
$this->outputFileName = File::temporaryFilename();
$writer->save($this->outputFileName);
$spreadsheet->disconnectWorksheets();
$file = 'zip://';
$file .= $this->outputFileName;
$file .= '#xl/charts/chart2.xml';
$data = file_get_contents($file);
// confirm that file contains expected tags
if ($data === false) {
self::fail('Unable to read file');
} else {
self::assertSame(1, substr_count($data, '<c:scatterStyle val='));
self::assertSame($expectedCount ? 1 : 0, substr_count($data, '<c:scatterStyle val="smoothMarker"/>'));
self::assertSame($expectedCount, substr_count($data, '<c:smooth val="1"/>'));
}
}
public function providerScatterCharts(): array
{
return [
'no line' => [0, '32readwriteScatterChart1.xlsx'],
'smooth line (Bezier)' => [3, '32readwriteScatterChart2.xlsx'],
'straight line' => [0, '32readwriteScatterChart3.xlsx'],
];
}
public function testAreaPercentageNoCat(): void
{
$file = self::DIRECTORY . '32readwriteAreaPercentageChart1.xlsx';
$reader = new XlsxReader();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($file);
$writer = new XlsxWriter($spreadsheet);
$writer->setIncludeCharts(true);
$this->outputFileName = File::temporaryFilename();
$writer->save($this->outputFileName);
$spreadsheet->disconnectWorksheets();
$file = 'zip://';
$file .= $this->outputFileName;
$file .= '#xl/charts/chart1.xml';
$data = file_get_contents($file);
// confirm that file contains expected tags
if ($data === false) {
self::fail('Unable to read file');
} else {
self::assertSame(0, substr_count($data, '<c:cat>'));
}
}
}