Merge branch 'master' into CellRange-Validation-Performance-Tweak

This commit is contained in:
Mark Baker 2022-05-24 08:50:51 +02:00 committed by GitHub
commit 4f1d6d2cac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 652 additions and 73 deletions

View File

@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org).
Note that a ChartSheet is still only written as a normal Worksheet containing a single chart, not as an actual ChartSheet.
- Added Worksheet visibility in Ods Reader [PR #2851](https://github.com/PHPOffice/PhpSpreadsheet/pull/2851)
- Added Worksheet visibility in Ods Writer [PR #2850](https://github.com/PHPOffice/PhpSpreadsheet/pull/2850)
### Changed
- Memory and speed improvements, particularly for the Cell Collection, and the Writers.
@ -32,7 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
- Xls Reader resolving absolute named ranges to relative ranges [Issue #2826](https://github.com/PHPOffice/PhpSpreadsheet/issues/2826) [PR #2827](https://github.com/PHPOffice/PhpSpreadsheet/pull/2827)
- Null value handling in the Excel Math/Trig PRODUCT() function [Issue #2833](https://github.com/PHPOffice/PhpSpreadsheet/issues/2833) [PR #2834](https://github.com/PHPOffice/PhpSpreadsheet/pull/2834)
- Invalid Print Area defined in Xlsx corrupts internal storage of print area [Issue #2848](https://github.com/PHPOffice/PhpSpreadsheet/issues/2848) [PR #2849](https://github.com/PHPOffice/PhpSpreadsheet/pull/2849)
## 1.23.0 - 2022-04-24

View File

@ -1945,36 +1945,11 @@ parameters:
count: 6
path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$masterPrintStylesCrossReference has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$masterStylesCrossReference has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$officeNs has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$pageLayoutStyles has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$stylesFo has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$stylesNs has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\Properties\\:\\:load\\(\\) has parameter \\$namespacesMeta with no type specified\\.$#"
count: 1
@ -4800,26 +4775,6 @@ parameters:
count: 2
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#3 \\$id1 of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeCategoryAxis\\(\\) expects string, int\\|string given\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#4 \\$id1 of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeValueAxis\\(\\) expects string, int\\|string given\\.$#"
count: 2
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#4 \\$id2 of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeCategoryAxis\\(\\) expects string, int\\|string given\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#5 \\$id2 of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeValueAxis\\(\\) expects string, int\\|string given\\.$#"
count: 2
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#6 \\$yAxis of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeCategoryAxis\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Axis, PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Axis\\|null given\\.$#"
count: 1

View File

@ -25,7 +25,7 @@ $worksheet->fromArray(
['=DATEVALUE("2021-01-10")', 30.2, 32.2, 0.2],
]
);
$worksheet->getStyle('A2:A5')->getNumberFormat()->setFormatCode('yyyy-mm-dd');
$worksheet->getStyle('A2:A5')->getNumberFormat()->setFormatCode(Properties::FORMAT_CODE_DATE_ISO8601);
$worksheet->getColumnDimension('A')->setAutoSize(true);
$worksheet->setSelectedCells('A1');
@ -67,7 +67,7 @@ $dataSeriesValues = [
// 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');
$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601, true);
// Build the dataseries
$series = new DataSeries(
@ -78,7 +78,7 @@ $series = new DataSeries(
$xAxisTickValues, // plotCategory
$dataSeriesValues, // plotValues
null, // plotDirection
null, // smooth line
false, // smooth line
//DataSeries::STYLE_LINEMARKER // plotStyle
DataSeries::STYLE_MARKER // plotStyle
);
@ -107,7 +107,7 @@ $chart = new Chart(
// Set the position where the chart should appear in the worksheet
$chart->setTopLeftPosition('A7');
$chart->setBottomRightPosition('N20');
$chart->setBottomRightPosition('P20');
// Add the chart to the worksheet
$worksheet->addChart($chart);

View File

@ -18,6 +18,7 @@ class Axis extends Properties
private $axisNumber = [
'format' => self::FORMAT_CODE_GENERAL,
'source_linked' => 1,
'numeric' => null,
];
/**
@ -131,15 +132,26 @@ class Axis extends Properties
'size' => null,
];
private const NUMERIC_FORMAT = [
Properties::FORMAT_CODE_NUMBER,
Properties::FORMAT_CODE_DATE,
];
/**
* Get Series Data Type.
*
* @param mixed $format_code
*/
public function setAxisNumberProperties($format_code): void
public function setAxisNumberProperties($format_code, ?bool $numeric = null): void
{
$this->axisNumber['format'] = (string) $format_code;
$format = (string) $format_code;
$this->axisNumber['format'] = $format;
$this->axisNumber['source_linked'] = 0;
if (is_bool($numeric)) {
$this->axisNumber['numeric'] = $numeric;
} elseif (in_array($format, self::NUMERIC_FORMAT, true)) {
$this->axisNumber['numeric'] = true;
}
}
/**
@ -162,6 +174,11 @@ class Axis extends Properties
return (string) $this->axisNumber['source_linked'];
}
public function getAxisIsNumericFormat(): bool
{
return (bool) $this->axisNumber['numeric'];
}
/**
* Set Axis Options Properties.
*

View File

@ -140,6 +140,18 @@ class Chart
*/
private $bottomRightYOffset = 10;
/** @var ?int */
private $rotX;
/** @var ?int */
private $rotY;
/** @var ?int */
private $rAngAx;
/** @var ?int */
private $perspective;
/**
* Create a new Chart.
*
@ -351,8 +363,9 @@ class Chart
if ($this->yAxis !== null) {
return $this->yAxis;
}
$this->yAxis = new Axis();
return new Axis();
return $this->yAxis;
}
/**
@ -365,8 +378,9 @@ class Chart
if ($this->xAxis !== null) {
return $this->xAxis;
}
$this->xAxis = new Axis();
return new Axis();
return $this->xAxis;
}
/**
@ -681,4 +695,52 @@ class Chart
return $renderer->render($outputDestination);
}
public function getRotX(): ?int
{
return $this->rotX;
}
public function setRotX(?int $rotX): self
{
$this->rotX = $rotX;
return $this;
}
public function getRotY(): ?int
{
return $this->rotY;
}
public function setRotY(?int $rotY): self
{
$this->rotY = $rotY;
return $this;
}
public function getRAngAx(): ?int
{
return $this->rAngAx;
}
public function setRAngAx(?int $rAngAx): self
{
$this->rAngAx = $rAngAx;
return $this;
}
public function getPerspective(): ?int
{
return $this->perspective;
}
public function setPerspective(?int $perspective): self
{
$this->perspective = $perspective;
return $this;
}
}

View File

@ -37,6 +37,7 @@ abstract class Properties
const FORMAT_CODE_CURRENCY = '$#,##0.00';
const FORMAT_CODE_ACCOUNTING = '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)';
const FORMAT_CODE_DATE = 'm/d/yyyy';
const FORMAT_CODE_DATE_ISO8601 = 'yyyy-mm-dd';
const FORMAT_CODE_TIME = '[$-F400]h:mm:ss AM/PM';
const FORMAT_CODE_PERCENTAGE = '0.00%';
const FORMAT_CODE_FRACTION = '# ?/?';

View File

@ -588,6 +588,7 @@ class Ods extends BaseReader
break;
}
}
$pageSettings->setVisibilityForWorksheet($spreadsheet->getActiveSheet(), $worksheetStyleName);
$pageSettings->setPrintSettingsForWorksheet($spreadsheet->getActiveSheet(), $worksheetStyleName);
++$worksheetID;
}

View File

@ -8,16 +8,41 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class PageSettings
{
/**
* @var string
*/
private $officeNs;
/**
* @var string
*/
private $stylesNs;
/**
* @var string
*/
private $stylesFo;
/**
* @var string
*/
private $tableNs;
/**
* @var string[]
*/
private $tableStylesCrossReference = [];
private $pageLayoutStyles = [];
/**
* @var string[]
*/
private $masterStylesCrossReference = [];
/**
* @var string[]
*/
private $masterPrintStylesCrossReference = [];
public function __construct(DOMDocument $styleDom)
@ -32,6 +57,7 @@ class PageSettings
$this->officeNs = $styleDom->lookupNamespaceUri('office');
$this->stylesNs = $styleDom->lookupNamespaceUri('style');
$this->stylesFo = $styleDom->lookupNamespaceUri('fo');
$this->tableNs = $styleDom->lookupNamespaceUri('table');
}
private function readPageSettingStyles(DOMDocument $styleDom): void
@ -98,12 +124,33 @@ class PageSettings
foreach ($styleXReferences as $styleXreferenceSet) {
$styleXRefName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'name');
$stylePageLayoutName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'master-page-name');
$styleFamilyName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'family');
if (!empty($styleFamilyName) && $styleFamilyName === 'table') {
$styleVisibility = 'true';
foreach ($styleXreferenceSet->getElementsByTagNameNS($this->stylesNs, 'table-properties') as $tableProperties) {
$styleVisibility = $tableProperties->getAttributeNS($this->tableNs, 'display');
}
$this->tableStylesCrossReference[$styleXRefName] = $styleVisibility;
}
if (!empty($stylePageLayoutName)) {
$this->masterStylesCrossReference[$styleXRefName] = $stylePageLayoutName;
}
}
}
public function setVisibilityForWorksheet(Worksheet $worksheet, string $styleName): void
{
if (!array_key_exists($styleName, $this->tableStylesCrossReference)) {
return;
}
$worksheet->setSheetState(
$this->tableStylesCrossReference[$styleName] === 'false'
? Worksheet::SHEETSTATE_HIDDEN
: Worksheet::SHEETSTATE_VISIBLE
);
}
public function setPrintSettingsForWorksheet(Worksheet $worksheet, string $styleName): void
{
if (!array_key_exists($styleName, $this->masterStylesCrossReference)) {

View File

@ -1530,13 +1530,18 @@ class Xlsx extends BaseReader
$rangeSets = preg_split("/('?(?:.*?)'?(?:![A-Z0-9]+:[A-Z0-9]+)),?/", $extractedRange, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$newRangeSets = [];
foreach ($rangeSets as $rangeSet) {
[$sheetName, $rangeSet] = Worksheet::extractSheetTitle($rangeSet, true);
[, $rangeSet] = Worksheet::extractSheetTitle($rangeSet, true);
if (empty($rangeSet)) {
continue;
}
if (strpos($rangeSet, ':') === false) {
$rangeSet = $rangeSet . ':' . $rangeSet;
}
$newRangeSets[] = str_replace('$', '', $rangeSet);
}
if (count($newRangeSets) > 0) {
$docSheet->getPageSetup()->setPrintArea(implode(',', $newRangeSets));
}
break;
default:

View File

@ -55,12 +55,20 @@ class Chart
$XaxisLabel = $YaxisLabel = $legend = $title = null;
$dispBlanksAs = $plotVisOnly = null;
$plotArea = null;
$rotX = $rotY = $rAngAx = $perspective = null;
foreach ($chartElementsC as $chartElementKey => $chartElement) {
switch ($chartElementKey) {
case 'chart':
foreach ($chartElement as $chartDetailsKey => $chartDetails) {
$chartDetailsC = $chartDetails->children($namespacesChartMeta['c']);
switch ($chartDetailsKey) {
case 'view3D':
$rotX = self::getAttribute($chartDetails->rotX, 'val', 'integer');
$rotY = self::getAttribute($chartDetails->rotY, 'val', 'integer');
$rAngAx = self::getAttribute($chartDetails->rAngAx, 'val', 'integer');
$perspective = self::getAttribute($chartDetails->perspective, 'val', 'integer');
break;
case 'plotArea':
$plotAreaLayout = $XaxisLabel = $YaxisLabel = null;
$plotSeries = $plotAttributes = [];
@ -220,6 +228,18 @@ class Chart
}
}
$chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, $dispBlanksAs, $XaxisLabel, $YaxisLabel);
if (is_int($rotX)) {
$chart->setRotX($rotX);
}
if (is_int($rotY)) {
$chart->setRotY($rotY);
}
if (is_int($rAngAx)) {
$chart->setRAngAx($rAngAx);
}
if (is_int($perspective)) {
$chart->setPerspective($perspective);
}
return $chart;
}

View File

@ -10,12 +10,14 @@ use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Style\Style as CellStyle;
use PhpOffice\PhpSpreadsheet\Worksheet\ColumnDimension;
use PhpOffice\PhpSpreadsheet\Worksheet\RowDimension;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class Style
{
public const CELL_STYLE_PREFIX = 'ce';
public const COLUMN_STYLE_PREFIX = 'co';
public const ROW_STYLE_PREFIX = 'ro';
public const TABLE_STYLE_PREFIX = 'ta';
private $writer;
@ -221,6 +223,26 @@ class Style
$this->writer->endElement(); // Close style:style
}
public function writeTableStyle(Worksheet $worksheet, int $sheetId): void
{
$this->writer->startElement('style:style');
$this->writer->writeAttribute('style:family', 'table');
$this->writer->writeAttribute(
'style:name',
sprintf('%s%d', self::TABLE_STYLE_PREFIX, $sheetId)
);
$this->writer->startElement('style:table-properties');
$this->writer->writeAttribute(
'table:display',
$worksheet->getSheetState() === Worksheet::SHEETSTATE_VISIBLE ? 'true' : 'false'
);
$this->writer->endElement(); // Close style:table-properties
$this->writer->endElement(); // Close style:style
}
public function write(CellStyle $style): void
{
$this->writer->startElement('style:style');

View File

@ -123,6 +123,7 @@ class Content extends WriterPart
for ($sheetIndex = 0; $sheetIndex < $sheetCount; ++$sheetIndex) {
$objWriter->startElement('table:table');
$objWriter->writeAttribute('table:name', $spreadsheet->getSheet($sheetIndex)->getTitle());
$objWriter->writeAttribute('table:style-name', Style::TABLE_STYLE_PREFIX . (string) ($sheetIndex + 1));
$objWriter->writeElement('office:forms');
foreach ($spreadsheet->getSheet($sheetIndex)->getColumnDimensions() as $columnDimension) {
$objWriter->startElement('table:table-column');
@ -289,6 +290,8 @@ class Content extends WriterPart
$sheetCount = $spreadsheet->getSheetCount();
for ($i = 0; $i < $sheetCount; ++$i) {
$worksheet = $spreadsheet->getSheet($i);
$styleWriter->writeTableStyle($worksheet, $i + 1);
$worksheet->calculateColumnWidths();
foreach ($worksheet->getColumnDimensions() as $columnDimension) {
if ($columnDimension->getWidth() !== -1.0) {

View File

@ -9,7 +9,6 @@ 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\XMLWriter;
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
@ -75,6 +74,33 @@ class Chart extends WriterPart
$objWriter->writeAttribute('val', 0);
$objWriter->endElement();
$objWriter->startElement('c:view3D');
$rotX = $chart->getRotX();
if (is_int($rotX)) {
$objWriter->startElement('c:rotX');
$objWriter->writeAttribute('val', "$rotX");
$objWriter->endElement();
}
$rotY = $chart->getRotY();
if (is_int($rotY)) {
$objWriter->startElement('c:rotY');
$objWriter->writeAttribute('val', "$rotY");
$objWriter->endElement();
}
$rAngAx = $chart->getRAngAx();
if (is_int($rAngAx)) {
$objWriter->startElement('c:rAngAx');
$objWriter->writeAttribute('val', "$rAngAx");
$objWriter->endElement();
}
$perspective = $chart->getPerspective();
if (is_int($perspective)) {
$objWriter->startElement('c:perspective');
$objWriter->writeAttribute('val', "$perspective");
$objWriter->endElement();
}
$objWriter->endElement(); // view3D
$this->writePlotArea($objWriter, $chart->getPlotArea(), $chart->getXAxisLabel(), $chart->getYAxisLabel(), $chart->getChartAxisX(), $chart->getChartAxisY(), $chart->getMajorGridlines(), $chart->getMinorGridlines());
$this->writeLegend($objWriter, $chart->getLegend());
@ -200,7 +226,7 @@ class Chart extends WriterPart
return;
}
$id1 = $id2 = 0;
$id1 = $id2 = $id3 = '0';
$this->seriesIndex = 0;
$objWriter->startElement('c:plotArea');
@ -230,6 +256,10 @@ class Chart extends WriterPart
$objWriter->startElement('c:scatterStyle');
$objWriter->writeAttribute('val', $plotStyle);
$objWriter->endElement();
} elseif ($groupType === DataSeries::TYPE_SURFACECHART_3D || $groupType === DataSeries::TYPE_SURFACECHART) {
$objWriter->startElement('c:wireframe');
$objWriter->writeAttribute('val', $plotStyle ? '1' : '0');
$objWriter->endElement();
}
$this->writePlotGroup($plotGroup, $chartType, $objWriter, $catIsMultiLevelSeries, $valIsMultiLevelSeries, $plotGroupingType);
@ -280,9 +310,10 @@ class Chart extends WriterPart
$objWriter->endElement();
}
// Generate 2 unique numbers to use for axId values
$id1 = '75091328';
$id2 = '75089408';
// Generate 3 unique numbers to use for axId values
$id1 = '110438656';
$id2 = '110444544';
$id3 = '110365312'; // used in Surface Chart
if (($chartType !== DataSeries::TYPE_PIECHART) && ($chartType !== DataSeries::TYPE_PIECHART_3D) && ($chartType !== DataSeries::TYPE_DONUTCHART)) {
$objWriter->startElement('c:axId');
@ -291,6 +322,11 @@ class Chart extends WriterPart
$objWriter->startElement('c:axId');
$objWriter->writeAttribute('val', $id2);
$objWriter->endElement();
if ($chartType === DataSeries::TYPE_SURFACECHART_3D || $chartType === DataSeries::TYPE_SURFACECHART) {
$objWriter->startElement('c:axId');
$objWriter->writeAttribute('val', $id3);
$objWriter->endElement();
}
} else {
$objWriter->startElement('c:firstSliceAng');
$objWriter->writeAttribute('val', 0);
@ -314,6 +350,9 @@ class Chart extends WriterPart
}
$this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $yAxis, $majorGridlines, $minorGridlines);
if ($chartType === DataSeries::TYPE_SURFACECHART_3D || $chartType === DataSeries::TYPE_SURFACECHART) {
$this->writeSerAxis($objWriter, $id2, $id3);
}
}
$objWriter->endElement();
@ -375,17 +414,13 @@ class Chart extends WriterPart
{
// 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
) {
if ($yAxis->getAxisIsNumericFormat()) {
$objWriter->startElement('c:valAx');
} else {
$objWriter->startElement('c:catAx');
}
if ($id1 > 0) {
if ($id1 !== '0') {
$objWriter->startElement('c:axId');
$objWriter->writeAttribute('val', $id1);
$objWriter->endElement();
@ -459,7 +494,7 @@ class Chart extends WriterPart
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('axis_labels'));
$objWriter->endElement();
if ($id2 > 0) {
if ($id2 !== '0') {
$objWriter->startElement('c:crossAx');
$objWriter->writeAttribute('val', $id2);
$objWriter->endElement();
@ -501,7 +536,7 @@ class Chart extends WriterPart
{
$objWriter->startElement('c:valAx');
if ($id2 > 0) {
if ($id2 !== '0') {
$objWriter->startElement('c:axId');
$objWriter->writeAttribute('val', $id2);
$objWriter->endElement();
@ -926,7 +961,7 @@ class Chart extends WriterPart
$objWriter->endElement(); //effectList
$objWriter->endElement(); //end spPr
if ($id1 > 0) {
if ($id1 !== '0') {
$objWriter->startElement('c:crossAx');
$objWriter->writeAttribute('val', $id2);
$objWriter->endElement();
@ -969,6 +1004,54 @@ class Chart extends WriterPart
$objWriter->endElement();
}
/**
* Write Ser Axis, for Surface chart.
*/
private function writeSerAxis(XMLWriter $objWriter, string $id2, string $id3): void
{
$objWriter->startElement('c:serAx');
$objWriter->startElement('c:axId');
$objWriter->writeAttribute('val', $id3);
$objWriter->endElement(); // axId
$objWriter->startElement('c:scaling');
$objWriter->startElement('c:orientation');
$objWriter->writeAttribute('val', 'minMax');
$objWriter->endElement(); // orientation
$objWriter->endElement(); // scaling
$objWriter->startElement('c:delete');
$objWriter->writeAttribute('val', '0');
$objWriter->endElement(); // delete
$objWriter->startElement('c:axPos');
$objWriter->writeAttribute('val', 'b');
$objWriter->endElement(); // axPos
$objWriter->startElement('c:majorTickMark');
$objWriter->writeAttribute('val', 'out');
$objWriter->endElement(); // majorTickMark
$objWriter->startElement('c:minorTickMark');
$objWriter->writeAttribute('val', 'none');
$objWriter->endElement(); // minorTickMark
$objWriter->startElement('c:tickLblPos');
$objWriter->writeAttribute('val', 'nextTo');
$objWriter->endElement(); // tickLblPos
$objWriter->startElement('c:crossAx');
$objWriter->writeAttribute('val', $id2);
$objWriter->endElement(); // crossAx
$objWriter->startElement('c:crosses');
$objWriter->writeAttribute('val', 'autoZero');
$objWriter->endElement(); // crosses
$objWriter->endElement(); //serAx
}
/**
* Get the data series type(s) for a chart plot series.
*

View File

@ -0,0 +1,57 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Ods;
use PhpOffice\PhpSpreadsheet\Reader\Ods;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
class HiddenWorksheetTest extends TestCase
{
/**
* @var Spreadsheet
*/
private $spreadsheet;
protected function setup(): void
{
$filename = 'tests/data/Reader/Ods/HiddenSheet.ods';
$reader = new Ods();
$this->spreadsheet = $reader->load($filename);
}
public function testPageSetup(): void
{
$assertions = $this->worksheetAssertions();
foreach ($this->spreadsheet->getAllSheets() as $worksheet) {
if (!array_key_exists($worksheet->getTitle(), $assertions)) {
continue;
}
$sheetAssertions = $assertions[$worksheet->getTitle()];
foreach ($sheetAssertions as $test => $expectedResult) {
$testMethodName = 'get' . ucfirst($test);
$actualResult = $worksheet->getSheetState();
self::assertSame(
$expectedResult,
$actualResult,
"Failed asserting sheet state {$expectedResult} for Worksheet '{$worksheet->getTitle()}' {$test}"
);
}
}
}
private function worksheetAssertions(): array
{
return [
'Sheet1' => [
'sheetState' => Worksheet::SHEETSTATE_VISIBLE,
],
'Sheet2' => [
'sheetState' => Worksheet::SHEETSTATE_HIDDEN,
],
];
}
}

View File

@ -10,6 +10,7 @@ use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\Ods;
use PhpOffice\PhpSpreadsheet\Writer\Ods\Content;
use PHPUnit\Framework\TestCase;
@ -106,4 +107,26 @@ class ContentTest extends TestCase
self::assertXmlStringEqualsXmlFile($this->samplesPath . '/content-with-data.xml', $xml);
}
public function testWriteWithHiddenWorksheet(): void
{
$workbook = new Spreadsheet();
// Worksheet 1
$worksheet1 = $workbook->getActiveSheet();
$worksheet1->setCellValue('A1', 1);
// Worksheet 2
$worksheet2 = $workbook->createSheet();
$worksheet2->setTitle('New Worksheet');
$worksheet2->setCellValue('A1', 2);
$worksheet2->setSheetState(Worksheet::SHEETSTATE_HIDDEN);
// Write
$content = new Content(new Ods($workbook));
$xml = $content->write();
self::assertXmlStringEqualsXmlFile($this->samplesPath . '/content-hidden-worksheet.xml', $xml);
}
}

View File

@ -0,0 +1,171 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Chart\Axis;
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Properties;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
use PHPUnit\Framework\TestCase;
class Charts32CatAxValAxTest extends TestCase
{
// These tests can only be performed by examining xml.
// They are based on sample 33_Chart_Create_Scatter2.
/** @var string */
private $outputFileName = '';
protected function tearDown(): void
{
if ($this->outputFileName !== '') {
unlink($this->outputFileName);
$this->outputFileName = '';
}
}
/**
* @dataProvider providerCatAxValAx
*/
public function test1CatAx1ValAx(?bool $numeric): void
{
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->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(Properties::FORMAT_CODE_DATE_ISO8601);
$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 );
if (is_bool($numeric)) {
$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601, $numeric);
} else {
$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601);
}
// 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
false, // 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('P20');
// Add the chart to the worksheet
$worksheet->addChart($chart);
$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');
} elseif ($numeric === true) {
self::assertSame(0, substr_count($data, '<c:catAx'));
self::assertSame(2, substr_count($data, '<c:valAx'));
} else {
self::assertSame(1, substr_count($data, '<c:catAx'));
self::assertSame(1, substr_count($data, '<c:valAx'));
}
}
public function providerCatAxValAx(): array
{
return [
[true],
[false],
[null],
];
}
}

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Chart\Properties;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
@ -86,4 +87,61 @@ class Charts32XmlTest extends TestCase
self::assertSame(0, substr_count($data, '<c:cat>'));
}
}
/**
* @dataProvider providerCatAxValAx
*/
public function testCatAxValAx(?bool $numeric): void
{
$file = self::DIRECTORY . '32readwriteScatterChart1.xlsx';
$reader = new XlsxReader();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($file);
$sheet = $spreadsheet->getActiveSheet();
$charts = $sheet->getChartCollection();
self::assertCount(1, $charts);
$chart = $charts[0];
self::assertNotNull($chart);
$xAxis = $chart->getChartAxisX();
self::assertSame(Properties::FORMAT_CODE_GENERAL, $xAxis->getAxisNumberFormat());
if (is_bool($numeric)) {
$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_GENERAL, true);
}
$yAxis = $chart->getChartAxisY();
self::assertSame(Properties::FORMAT_CODE_GENERAL, $yAxis->getAxisNumberFormat());
if (is_bool($numeric)) {
$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_GENERAL, $numeric);
$yAxis->setAxisNumberProperties(Properties::FORMAT_CODE_GENERAL, $numeric);
}
$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');
} elseif ($numeric === true) {
self::assertSame(0, substr_count($data, '<c:catAx>'));
self::assertSame(2, substr_count($data, '<c:valAx>'));
} else {
self::assertSame(1, substr_count($data, '<c:catAx>'));
self::assertSame(1, substr_count($data, '<c:valAx>'));
}
}
public function providerCatAxValAx(): array
{
return [
[true],
[false],
[null],
];
}
}

Binary file not shown.

View File

@ -3,6 +3,9 @@
<office:scripts />
<office:font-face-decls />
<office:automatic-styles>
<style:style style:family="table" style:name="ta1">
<style:table-properties table:display="true" />
</style:style>
<style:style style:name="ce0" style:family="table-cell" style:parent-style-name="Default">
<style:table-cell-properties style:rotation-align="none" style:vertical-align="bottom" />
<style:paragraph-properties fo:text-align="start" />
@ -12,7 +15,7 @@
<office:body>
<office:spreadsheet>
<table:calculation-settings />
<table:table table:name="Worksheet">
<table:table table:name="Worksheet" table:style-name="ta1">
<office:forms />
<table:table-row>
<table:table-cell table:style-name="ce0" />

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2">
<office:scripts />
<office:font-face-decls />
<office:automatic-styles>
<style:style style:family="table" style:name="ta1">
<style:table-properties table:display="true" />
</style:style>
<style:style style:family="table" style:name="ta2">
<style:table-properties table:display="false"/>
</style:style>
<style:style style:name="ce0" style:family="table-cell" style:parent-style-name="Default">
<style:table-cell-properties style:rotation-align="none" style:vertical-align="bottom" />
<style:paragraph-properties fo:text-align="start" />
<style:text-properties fo:color="#000000" fo:font-family="Calibri" fo:font-size="11.0pt" />
</style:style>
</office:automatic-styles>
<office:body>
<office:spreadsheet>
<table:calculation-settings />
<table:table table:name="Worksheet" table:style-name="ta1">
<office:forms />
<table:table-row>
<table:table-cell office:value="1" office:value-type="float" table:style-name="ce0">
<text:p>1</text:p>
</table:table-cell>
<table:table-cell table:number-columns-repeated="1023"/>
</table:table-row>
</table:table>
<table:table table:name="New Worksheet" table:style-name="ta2">
<office:forms/>
<table:table-row>
<table:table-cell office:value="2" office:value-type="float" table:style-name="ce0">
<text:p>2</text:p>
</table:table-cell>
<table:table-cell table:number-columns-repeated="1023" />
</table:table-row>
</table:table>
<table:named-expressions />
</office:spreadsheet>
</office:body>
</office:document-content>

View File

@ -3,6 +3,12 @@
<office:scripts/>
<office:font-face-decls/>
<office:automatic-styles>
<style:style style:family="table" style:name="ta1">
<style:table-properties table:display="true" />
</style:style>
<style:style style:family="table" style:name="ta2">
<style:table-properties table:display="true" />
</style:style>
<style:style style:family="table-cell" style:name="ce0" style:parent-style-name="Default">
<style:table-cell-properties style:rotation-align="none" style:vertical-align="bottom" />
<style:paragraph-properties fo:text-align="start" />
@ -62,7 +68,7 @@
<office:body>
<office:spreadsheet>
<table:calculation-settings/>
<table:table table:name="Worksheet">
<table:table table:name="Worksheet" table:style-name="ta1">
<office:forms/>
<table:table-row>
<table:table-cell office:value="1" office:value-type="float" table:style-name="ce2">
@ -107,7 +113,7 @@
<table:table-cell table:number-columns-repeated="1017"/>
</table:table-row>
</table:table>
<table:table table:name="New Worksheet">
<table:table table:name="New Worksheet" table:style-name="ta2">
<office:forms/>
<table:table-row>
<table:table-cell office:value="2" office:value-type="float" table:style-name="ce0">