diff --git a/samples/Chart/33_Chart_create_line_dateaxis.php b/samples/Chart/33_Chart_create_line_dateaxis.php new file mode 100644 index 00000000..1a47e5aa --- /dev/null +++ b/samples/Chart/33_Chart_create_line_dateaxis.php @@ -0,0 +1,375 @@ +getActiveSheet(); +$dataSheet->setTitle('Data'); +// changed data to simulate a trend chart - Xaxis are dates; Yaxis are 3 meausurements from each date +// Dates changed not to fall on exact quarter start +$dataSheet->fromArray( + [ + ['', 'date', 'metric1', 'metric2', 'metric3'], + ['=DATEVALUE(B2)', '2021-01-10', 12.1, 15.1, 21.1], + ['=DATEVALUE(B3)', '2021-04-21', 56.2, 73.2, 86.2], + ['=DATEVALUE(B4)', '2021-07-31', 52.2, 61.2, 69.2], + ['=DATEVALUE(B5)', '2021-10-11', 30.2, 22.2, 0.2], + ['=DATEVALUE(B6)', '2022-01-21', 40.1, 38.1, 65.1], + ['=DATEVALUE(B7)', '2022-04-11', 45.2, 44.2, 96.2], + ['=DATEVALUE(B8)', '2022-07-01', 52.2, 51.2, 55.2], + ['=DATEVALUE(B9)', '2022-10-31', 41.2, 72.2, 56.2], + ] +); + +$dataSheet->getStyle('A2:A9')->getNumberFormat()->setFormatCode(Properties::FORMAT_CODE_DATE_ISO8601); +$dataSheet->getColumnDimension('A')->setAutoSize(true); +$dataSheet->getColumnDimension('B')->setAutoSize(true); +$dataSheet->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 +$dataSeriesLabels = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Data!$C$1', null, 1), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Data!$D$1', null, 1), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Data!$E$1', null, 1), +]; +// Set the X-Axis Labels +// NUMBER, not STRING +// added x-axis values for each of the 3 metrics +// added FORMATE_CODE_NUMBER +// Number of datapoints in series +$xAxisTickValues = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Data!$A$2:$A$9', Properties::FORMAT_CODE_DATE_ISO8601, 8), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Data!$A$2:$A$9', Properties::FORMAT_CODE_DATE_ISO8601, 8), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Data!$A$2:$A$9', Properties::FORMAT_CODE_DATE_ISO8601, 8), +]; +// 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 +// Data Marker Color fill/[fill,Border] +// Data Marker size +// Color(s) added +// added FORMAT_CODE_NUMBER +$dataSeriesValues = [ + new DataSeriesValues( + DataSeriesValues::DATASERIES_TYPE_NUMBER, + 'Data!$C$2:$C$9', + Properties::FORMAT_CODE_NUMBER, + 8, + null, + 'diamond', + null, + 5 + ), + new DataSeriesValues( + DataSeriesValues::DATASERIES_TYPE_NUMBER, + 'Data!$D$2:$D$9', + Properties::FORMAT_CODE_NUMBER, + 8, + null, + 'square', + '*accent1', + 6 + ), + new DataSeriesValues( + DataSeriesValues::DATASERIES_TYPE_NUMBER, + 'Data!$E$2:$E$9', + Properties::FORMAT_CODE_NUMBER, + 8, + null, + null, + null, + 7 + ), // let Excel choose marker shape +]; +// series 1 - metric1 +// marker details +$dataSeriesValues[0] + ->getMarkerFillColor() + ->setColorProperties('0070C0', null, ChartColor::EXCEL_COLOR_TYPE_ARGB); +$dataSeriesValues[0] + ->getMarkerBorderColor() + ->setColorProperties('002060', null, ChartColor::EXCEL_COLOR_TYPE_ARGB); + +// line details - dashed, smooth line (Bezier) with arrows, 40% transparent +$dataSeriesValues[0] + ->setSmoothLine(true) + ->setScatterLines(true) + ->setLineColorProperties('accent1', 40, ChartColor::EXCEL_COLOR_TYPE_SCHEME); // value, alpha, type +$dataSeriesValues[0]->setLineStyleProperties( + 2.5, // width in points + Properties::LINE_STYLE_COMPOUND_TRIPLE, // compound + Properties::LINE_STYLE_DASH_SQUARE_DOT, // dash + Properties::LINE_STYLE_CAP_SQUARE, // cap + Properties::LINE_STYLE_JOIN_MITER, // join + Properties::LINE_STYLE_ARROW_TYPE_OPEN, // head type + Properties::LINE_STYLE_ARROW_SIZE_4, // head size preset index + Properties::LINE_STYLE_ARROW_TYPE_ARROW, // end type + Properties::LINE_STYLE_ARROW_SIZE_6 // end size preset index +); + +// series 2 - metric2, straight line - no special effects, connected +$dataSeriesValues[1] // square marker border color + ->getMarkerBorderColor() + ->setColorProperties('accent6', 3, ChartColor::EXCEL_COLOR_TYPE_SCHEME); +$dataSeriesValues[1] // square marker fill color + ->getMarkerFillColor() + ->setColorProperties('0FFF00', null, ChartColor::EXCEL_COLOR_TYPE_ARGB); +$dataSeriesValues[1] + ->setScatterLines(true) + ->setSmoothLine(false) + ->setLineColorProperties('FF0000', 80, ChartColor::EXCEL_COLOR_TYPE_ARGB); +$dataSeriesValues[1]->setLineWidth(2.0); + +// series 3 - metric3, markers, no line +$dataSeriesValues[2] // triangle? fill + //->setPointMarker('triangle') // let Excel choose shape, which is predicted to be a triangle + ->getMarkerFillColor() + ->setColorProperties('FFFF00', null, ChartColor::EXCEL_COLOR_TYPE_ARGB); +$dataSeriesValues[2] // triangle border + ->getMarkerBorderColor() + ->setColorProperties('accent4', null, ChartColor::EXCEL_COLOR_TYPE_SCHEME); +$dataSeriesValues[2]->setScatterLines(false); // points not connected +// Added so that Xaxis shows dates instead of Excel-equivalent-year1900-numbers +$xAxis = new Axis(); +$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601); + +// Build the dataseries +$series = new DataSeries( + DataSeries::TYPE_SCATTERCHART, // plotType + null, // plotGrouping (Scatter charts don't have grouping) + range(0, count($dataSeriesValues) - 1), // plotOrder + $dataSeriesLabels, // plotLabel + $xAxisTickValues, // plotCategory + $dataSeriesValues, // plotValues + null, // plotDirection + null, // smooth line + DataSeries::STYLE_SMOOTHMARKER // 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 Chart'); +$yAxisLabel = new Title('Value ($k)'); +$yAxis = new Axis(); +$yAxis->setMajorGridlines(new GridLines()); + +// 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 + $yAxis, // yAxis +); + +// Set the position of the chart in the chart sheet +$chart->setTopLeftPosition('A1'); +$chart->setBottomRightPosition('P12'); + +// create a 'Chart' worksheet, add $chart to it +$spreadsheet->createSheet(); +$chartSheet = $spreadsheet->getSheet(1); +$chartSheet->setTitle('Scatter+Line Chart'); + +$chartSheet = $spreadsheet->getSheetByName('Scatter+Line Chart'); +// Add the chart to the worksheet +$chartSheet->addChart($chart); + +// ------- Demonstrate Date Xaxis in Line Chart, not possible using Scatter Chart ------------ + +// Set the Labels (Column header) for each data series we want to plot +$dataSeriesLabels = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Data!$E$1', null, 1), +]; + +// Set the X-Axis Labels - dates, N.B. 01/10/2021 === Jan 10, NOT Oct 1 !! +// x-axis values are the Excel numeric representation of the date - so set +// formatCode=General for the xAxis VALUES, but we want the labels to be +// DISPLAYED as 'yyyy-mm-dd' That is, read a number, display a date. +$xAxisTickValues = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Data!$A$2:$A$9', Properties::FORMAT_CODE_DATE_ISO8601, 8), +]; + +// X axis (date) settings +$xAxisLabel = new Title('Date'); +$xAxis = new Axis(); +$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601); // yyyy-mm-dd + +// Set the Data values for each data series we want to plot +$dataSeriesValues = [ + new DataSeriesValues( + DataSeriesValues::DATASERIES_TYPE_NUMBER, + 'Data!$E$2:$E$9', + Properties::FORMAT_CODE_NUMBER, + 8, + null, + 'triangle', + null, + 7 + ), +]; + +// series - metric3, markers, no line +$dataSeriesValues[0] + ->setScatterlines(false); // disable connecting lines +$dataSeriesValues[0] + ->getMarkerFillColor() + ->setColorProperties('FFFF00', null, ChartColor::EXCEL_COLOR_TYPE_ARGB); +$dataSeriesValues[0] + ->getMarkerBorderColor() + ->setColorProperties('accent4', null, ChartColor::EXCEL_COLOR_TYPE_SCHEME); + +// Build the dataseries +// must now use LineChart instead of ScatterChart, since ScatterChart does not +// support "dateAx" axis type. +$series = new DataSeries( + DataSeries::TYPE_LINECHART, // plotType + 'standard', // plotGrouping + range(0, count($dataSeriesValues) - 1), // plotOrder + $dataSeriesLabels, // plotLabel + $xAxisTickValues, // plotCategory + $dataSeriesValues, // plotValues + null, // plotDirection + false, // smooth line + DataSeries::STYLE_LINEMARKER // plotStyle + // DataSeries::STYLE_SMOOTHMARKER // plotStyle +); + +// Set the series in the plot area +$plotArea = new PlotArea(null, [$series]); +// Set the chart legend +$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false); + +$title = new Title('Test Line-Chart with Date Axis - metric3 values'); + +// X axis (date) settings +$xAxisLabel = new Title('Game Date'); +$xAxis = new Axis(); +// date axis values are Excel numbers, not yyyy-mm-dd Date strings +$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601); + +$xAxis->setAxisType('dateAx'); // dateAx available ONLY for LINECHART, not SCATTERCHART + +// measure the time span in Quarters, of data. +$dateMinMax = dateRange(8, $spreadsheet); // array 'min'=>earliest date of first Q, 'max'=>latest date of final Q +// change xAxis tick marks to match Qtr boundaries + +$nQtrs = sprintf('%3.2f', (($dateMinMax['max'] - $dateMinMax['min']) / 30.5) / 4); +$tickMarkInterval = ($nQtrs > 20) ? 6 : 3; // tick marks every ? months + +$xAxis->setAxisOptionsProperties( + Properties::AXIS_LABELS_NEXT_TO, // axis_label pos + null, // horizontalCrossesValue + null, // horizontalCrosses + null, // axisOrientation + 'in', // major_tick_mark + null, // minor_tick_mark + $dateMinMax['min'], // minimum calculate this from the earliest data: 'Data!$A$2' + $dateMinMax['max'], // maximum calculate this from the last data: 'Data!$A$'.($nrows+1) + $tickMarkInterval, // majorUnit determines tickmarks & Gridlines ? + null, // minorUnit + null, // textRotation + null, // hidden + 'days', // baseTimeUnit + 'months', // majorTimeUnit, + 'months', // minorTimeUnit +); + +$yAxisLabel = new Title('Value ($k)'); +$yAxis = new Axis(); +$yAxis->setMajorGridlines(new GridLines()); +$xAxis->setMajorGridlines(new GridLines()); +$minorGridLines = new GridLines(); +$minorGridLines->activateObject(); +$xAxis->setMinorGridlines($minorGridLines); + +// Create the chart +$chart = new Chart( + 'chart2', // 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 + $yAxis, // yAxis +); + +// Set the position of the chart in the chart sheet below the first chart +$chart->setTopLeftPosition('A13'); +$chart->setBottomRightPosition('P25'); +$chart->setRoundedCorners('true'); // Rounded corners in Chart Outline + +// Add the chart to the worksheet $chartSheet +$chartSheet->addChart($chart); +$spreadsheet->setActiveSheetIndex(1); + +// Save Excel 2007 file +$filename = $helper->getFilename(__FILE__); +$writer = IOFactory::createWriter($spreadsheet, 'Xlsx'); +$writer->setIncludeCharts(true); +$callStartTime = microtime(true); +$writer->save($filename); +$helper->logWrite($writer, $filename, $callStartTime); +$spreadsheet->disconnectWorksheets(); + +function dateRange(int $nrows, Spreadsheet $wrkbk): array +{ + $dataSheet = $wrkbk->getSheetByName('Data'); + + // start the xaxis at the beginning of the quarter of the first date + $startDateStr = $dataSheet->getCell('B2')->getValue(); // yyyy-mm-dd date string + $startDate = DateTime::createFromFormat('Y-m-d', $startDateStr); // php date obj + + // get date of first day of the quarter of the start date + $startMonth = $startDate->format('n'); // suppress leading zero + $startYr = $startDate->format('Y'); + $qtr = intdiv($startMonth, 3) + (($startMonth % 3 > 0) ? 1 : 0); + $qtrStartMonth = sprintf('%02d', 1 + (($qtr - 1) * 3)); + $qtrStartStr = "$startYr-$qtrStartMonth-01"; + $ExcelQtrStartDateVal = SharedDate::convertIsoDate($qtrStartStr); + + // end the xaxis at the end of the quarter of the last date + $lastDateStr = $dataSheet->getCellByColumnAndRow(2, $nrows + 1)->getValue(); + $lastDate = DateTime::createFromFormat('Y-m-d', $lastDateStr); + $lastMonth = $lastDate->format('n'); + $lastYr = $lastDate->format('Y'); + $qtr = intdiv($lastMonth, 3) + (($lastMonth % 3 > 0) ? 1 : 0); + $qtrEndMonth = 3 + (($qtr - 1) * 3); + $lastDOM = cal_days_in_month(CAL_GREGORIAN, $qtrEndMonth, $lastYr); + $qtrEndMonth = sprintf('%02d', $qtrEndMonth); + $qtrEndStr = "$lastYr-$qtrEndMonth-$lastDOM"; + $ExcelQtrEndDateVal = SharedDate::convertIsoDate($qtrEndStr); + + $minMaxDates = ['min' => $ExcelQtrStartDateVal, 'max' => $ExcelQtrEndDateVal]; + + return $minMaxDates; +} diff --git a/samples/Chart/33_Chart_create_scatter5_trendlines.php b/samples/Chart/33_Chart_create_scatter5_trendlines.php index da831fa9..a640f735 100644 --- a/samples/Chart/33_Chart_create_scatter5_trendlines.php +++ b/samples/Chart/33_Chart_create_scatter5_trendlines.php @@ -263,6 +263,7 @@ $chart->setBottomRightPosition('P25'); // Add the chart to the worksheet $chartSheet $chartSheet->addChart($chart); +$spreadsheet->setActiveSheetIndex(1); // Save Excel 2007 file $filename = $helper->getFilename(__FILE__); diff --git a/samples/templates/32readwriteLineDateAxisChart1.xlsx b/samples/templates/32readwriteLineDateAxisChart1.xlsx new file mode 100644 index 00000000..42470303 Binary files /dev/null and b/samples/templates/32readwriteLineDateAxisChart1.xlsx differ diff --git a/src/PhpSpreadsheet/Chart/Axis.php b/src/PhpSpreadsheet/Chart/Axis.php index bd7082ff..3ac5c3cd 100644 --- a/src/PhpSpreadsheet/Chart/Axis.php +++ b/src/PhpSpreadsheet/Chart/Axis.php @@ -10,6 +10,14 @@ namespace PhpOffice\PhpSpreadsheet\Chart; */ class Axis extends Properties { + const AXIS_TYPE_CATEGORY = 'catAx'; + const AXIS_TYPE_DATE = 'dateAx'; + const AXIS_TYPE_VALUE = 'valAx'; + + const TIME_UNIT_DAYS = 'days'; + const TIME_UNIT_MONTHS = 'months'; + const TIME_UNIT_YEARS = 'years'; + public function __construct() { parent::__construct(); @@ -62,6 +70,9 @@ class Axis extends Properties 'horizontal_crosses_value' => null, 'textRotation' => null, 'hidden' => null, + 'majorTimeUnit' => self::TIME_UNIT_YEARS, + 'minorTimeUnit' => self::TIME_UNIT_MONTHS, + 'baseTimeUnit' => self::TIME_UNIT_DAYS, ]; /** @@ -74,6 +85,7 @@ class Axis extends Properties private const NUMERIC_FORMAT = [ Properties::FORMAT_CODE_NUMBER, Properties::FORMAT_CODE_DATE, + Properties::FORMAT_CODE_DATE_ISO8601, ]; /** @@ -115,12 +127,12 @@ class Axis extends Properties public function getAxisIsNumericFormat(): bool { - return (bool) $this->axisNumber['numeric']; + return $this->axisType === self::AXIS_TYPE_DATE || (bool) $this->axisNumber['numeric']; } public function setAxisOption(string $key, ?string $value): void { - if (!empty($value)) { + if ($value !== null && $value !== '') { $this->axisOptions[$key] = $value; } } @@ -140,7 +152,10 @@ class Axis extends Properties ?string $majorUnit = null, ?string $minorUnit = null, ?string $textRotation = null, - ?string $hidden = null + ?string $hidden = null, + ?string $baseTimeUnit = null, + ?string $majorTimeUnit = null, + ?string $minorTimeUnit = null ): void { $this->axisOptions['axis_labels'] = $axisLabels; $this->setAxisOption('horizontal_crosses_value', $horizontalCrossesValue); @@ -154,6 +169,9 @@ class Axis extends Properties $this->setAxisOption('minor_unit', $minorUnit); $this->setAxisOption('textRotation', $textRotation); $this->setAxisOption('hidden', $hidden); + $this->setAxisOption('baseTimeUnit', $baseTimeUnit); + $this->setAxisOption('majorTimeUnit', $majorTimeUnit); + $this->setAxisOption('minorTimeUnit', $minorTimeUnit); } /** @@ -185,7 +203,7 @@ class Axis extends Properties public function setAxisType(string $type): self { - if ($type === 'catAx' || $type === 'valAx') { + if ($type === self::AXIS_TYPE_CATEGORY || $type === self::AXIS_TYPE_VALUE || $type === self::AXIS_TYPE_DATE) { $this->axisType = $type; } else { $this->axisType = ''; diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php index dab2b410..76316db9 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php @@ -139,12 +139,13 @@ class Chart $plotAreaLayout = $this->chartLayoutDetails($chartDetail); break; - case 'catAx': + case Axis::AXIS_TYPE_CATEGORY: + case Axis::AXIS_TYPE_DATE: $catAxRead = true; if (isset($chartDetail->title)) { $XaxisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace)); } - $xAxis->setAxisType('catAx'); + $xAxis->setAxisType($chartDetailKey); $this->readEffects($chartDetail, $xAxis); if (isset($chartDetail->spPr)) { $sppr = $chartDetail->spPr->children($this->aNamespace); @@ -173,13 +174,7 @@ class Chart $this->setAxisProperties($chartDetail, $xAxis); break; - case 'dateAx': - if (isset($chartDetail->title)) { - $XaxisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace)); - } - - break; - case 'valAx': + case Axis::AXIS_TYPE_VALUE: $whichAxis = null; $axPos = null; if (isset($chartDetail->axPos)) { @@ -1375,6 +1370,15 @@ class Chart if (isset($chartDetail->minorUnit)) { $whichAxis->setAxisOption('minor_unit', (string) self::getAttribute($chartDetail->minorUnit, 'val', 'string')); } + if (isset($chartDetail->baseTimeUnit)) { + $whichAxis->setAxisOption('baseTimeUnit', (string) self::getAttribute($chartDetail->baseTimeUnit, 'val', 'string')); + } + if (isset($chartDetail->majorTimeUnit)) { + $whichAxis->setAxisOption('majorTimeUnit', (string) self::getAttribute($chartDetail->majorTimeUnit, 'val', 'string')); + } + if (isset($chartDetail->minorTimeUnit)) { + $whichAxis->setAxisOption('minorTimeUnit', (string) self::getAttribute($chartDetail->minorTimeUnit, 'val', 'string')); + } if (isset($chartDetail->txPr)) { $children = $chartDetail->txPr->children($this->aNamespace); if (isset($children->bodyPr)) { diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index ad746a0a..48f7d255 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -490,13 +490,14 @@ class Chart extends WriterPart private function writeCategoryAxis(XMLWriter $objWriter, ?Title $xAxisLabel, $id1, $id2, $isMultiLevelSeries, Axis $yAxis): void { // N.B. writeCategoryAxis may be invoked with the last parameter($yAxis) using $xAxis for ScatterChart, etc - // In that case, xAxis is NOT a category. - if ($yAxis->getAxisType() !== '') { - $objWriter->startElement('c:' . $yAxis->getAxisType()); + // In that case, xAxis may contain values like the yAxis, or it may be a date axis (LINECHART). + $axisType = $yAxis->getAxisType(); + if ($axisType !== '') { + $objWriter->startElement("c:$axisType"); } elseif ($yAxis->getAxisIsNumericFormat()) { - $objWriter->startElement('c:valAx'); + $objWriter->startElement('c:' . Axis::AXIS_TYPE_VALUE); } else { - $objWriter->startElement('c:catAx'); + $objWriter->startElement('c:' . Axis::AXIS_TYPE_CATEGORY); } $majorGridlines = $yAxis->getMajorGridlines(); $minorGridlines = $yAxis->getMinorGridlines(); @@ -654,7 +655,8 @@ class Chart extends WriterPart } $objWriter->startElement('c:auto'); - $objWriter->writeAttribute('val', '1'); + // LineChart with dateAx wants '0' + $objWriter->writeAttribute('val', ($axisType === Axis::AXIS_TYPE_DATE) ? '0' : '1'); $objWriter->endElement(); $objWriter->startElement('c:lblAlgn'); @@ -665,6 +667,30 @@ class Chart extends WriterPart $objWriter->writeAttribute('val', '100'); $objWriter->endElement(); + if ($axisType === Axis::AXIS_TYPE_DATE) { + $property = 'baseTimeUnit'; + $propertyVal = $yAxis->getAxisOptionsProperty($property); + if (!empty($propertyVal)) { + $objWriter->startElement("c:$property"); + $objWriter->writeAttribute('val', $propertyVal); + $objWriter->endElement(); + } + $property = 'majorTimeUnit'; + $propertyVal = $yAxis->getAxisOptionsProperty($property); + if (!empty($propertyVal)) { + $objWriter->startElement("c:$property"); + $objWriter->writeAttribute('val', $propertyVal); + $objWriter->endElement(); + } + $property = 'minorTimeUnit'; + $propertyVal = $yAxis->getAxisOptionsProperty($property); + if (!empty($propertyVal)) { + $objWriter->startElement("c:$property"); + $objWriter->writeAttribute('val', $propertyVal); + $objWriter->endElement(); + } + } + if ($isMultiLevelSeries) { $objWriter->startElement('c:noMultiLvlLbl'); $objWriter->writeAttribute('val', '0'); @@ -683,7 +709,7 @@ class Chart extends WriterPart */ private function writeValueAxis(XMLWriter $objWriter, ?Title $yAxisLabel, $groupType, $id1, $id2, $isMultiLevelSeries, Axis $xAxis): void { - $objWriter->startElement('c:valAx'); + $objWriter->startElement('c:' . Axis::AXIS_TYPE_VALUE); $majorGridlines = $xAxis->getMajorGridlines(); $minorGridlines = $xAxis->getMinorGridlines(); @@ -1079,7 +1105,7 @@ class Chart extends WriterPart $objWriter->endElement(); // a:ln } } - $nofill = $groupType == DataSeries::TYPE_STOCKCHART || ($groupType === DataSeries::TYPE_SCATTERCHART && !$plotSeriesValues->getScatterLines()); + $nofill = $groupType === DataSeries::TYPE_STOCKCHART || (($groupType === DataSeries::TYPE_SCATTERCHART || $groupType === DataSeries::TYPE_LINECHART) && !$plotSeriesValues->getScatterLines()); if ($callLineStyles) { $this->writeLineStyles($objWriter, $plotSeriesValues, $nofill); $this->writeEffects($objWriter, $plotSeriesValues); diff --git a/tests/PhpSpreadsheetTests/Chart/AxisPropertiesTest.php b/tests/PhpSpreadsheetTests/Chart/AxisPropertiesTest.php index 91df25cb..0bfc2966 100644 --- a/tests/PhpSpreadsheetTests/Chart/AxisPropertiesTest.php +++ b/tests/PhpSpreadsheetTests/Chart/AxisPropertiesTest.php @@ -109,7 +109,9 @@ class AxisPropertiesTest extends AbstractFunctional '8', //minimum '68', //maximum '20', //majorUnit - '5' //minorUnit + '5', //minorUnit + '6', //textRotation + '0', //hidden ); self::assertSame(Properties::AXIS_LABELS_HIGH, $xAxis->getAxisOptionsProperty('axis_labels')); self::assertNull($xAxis->getAxisOptionsProperty('horizontal_crosses_value')); @@ -121,6 +123,8 @@ class AxisPropertiesTest extends AbstractFunctional self::assertSame('68', $xAxis->getAxisOptionsProperty('maximum')); self::assertSame('20', $xAxis->getAxisOptionsProperty('major_unit')); self::assertSame('5', $xAxis->getAxisOptionsProperty('minor_unit')); + self::assertSame('6', $xAxis->getAxisOptionsProperty('textRotation')); + self::assertSame('0', $xAxis->getAxisOptionsProperty('hidden')); $yAxis = new Axis(); $yAxis->setFillParameters('accent1', 30, 'schemeClr'); @@ -158,6 +162,8 @@ class AxisPropertiesTest extends AbstractFunctional self::assertSame('68', $xAxis2->getAxisOptionsProperty('maximum')); self::assertSame('20', $xAxis2->getAxisOptionsProperty('major_unit')); self::assertSame('5', $xAxis2->getAxisOptionsProperty('minor_unit')); + self::assertSame('6', $xAxis2->getAxisOptionsProperty('textRotation')); + self::assertSame('0', $xAxis2->getAxisOptionsProperty('hidden')); $yAxis2 = $chart->getChartAxisY(); self::assertSame('accent1', $yAxis2->getFillProperty('value')); @@ -198,6 +204,8 @@ class AxisPropertiesTest extends AbstractFunctional self::assertSame('68', $xAxis3->getAxisOptionsProperty('maximum')); self::assertSame('20', $xAxis3->getAxisOptionsProperty('major_unit')); self::assertSame('5', $xAxis3->getAxisOptionsProperty('minor_unit')); + self::assertSame('6', $xAxis3->getAxisOptionsProperty('textRotation')); + self::assertSame('0', $xAxis3->getAxisOptionsProperty('hidden')); $yAxis3 = $chart2->getChartAxisY(); self::assertSame('accent1', $yAxis3->getFillProperty('value')); diff --git a/tests/PhpSpreadsheetTests/Chart/Charts32CatAxValAxTest.php b/tests/PhpSpreadsheetTests/Chart/Charts32CatAxValAxTest.php index 268ee094..1f046af9 100644 --- a/tests/PhpSpreadsheetTests/Chart/Charts32CatAxValAxTest.php +++ b/tests/PhpSpreadsheetTests/Chart/Charts32CatAxValAxTest.php @@ -23,6 +23,8 @@ class Charts32CatAxValAxTest extends TestCase /** @var string */ private $outputFileName = ''; + private const FORMAT_CODE_DATE_ISO8601_SLASH = 'yyyy/mm/dd'; // not automatically treated as numeric + protected function tearDown(): void { if ($this->outputFileName !== '') { @@ -48,7 +50,7 @@ class Charts32CatAxValAxTest extends TestCase ['=DATEVALUE("2021-01-10")', 30.2, 32.2, 0.2], ] ); - $worksheet->getStyle('A2:A5')->getNumberFormat()->setFormatCode(Properties::FORMAT_CODE_DATE_ISO8601); + $worksheet->getStyle('A2:A5')->getNumberFormat()->setFormatCode(self::FORMAT_CODE_DATE_ISO8601_SLASH); $worksheet->getColumnDimension('A')->setAutoSize(true); $worksheet->setSelectedCells('A1'); @@ -91,9 +93,9 @@ class Charts32CatAxValAxTest extends TestCase $xAxis = new Axis(); //$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE ); if (is_bool($numeric)) { - $xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601, $numeric); + $xAxis->setAxisNumberProperties(self::FORMAT_CODE_DATE_ISO8601_SLASH, $numeric); } else { - $xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601); + $xAxis->setAxisNumberProperties(self::FORMAT_CODE_DATE_ISO8601_SLASH); } // Build the dataseries diff --git a/tests/PhpSpreadsheetTests/Chart/Charts32XmlTest.php b/tests/PhpSpreadsheetTests/Chart/Charts32XmlTest.php index 6a4673fd..4cc62360 100644 --- a/tests/PhpSpreadsheetTests/Chart/Charts32XmlTest.php +++ b/tests/PhpSpreadsheetTests/Chart/Charts32XmlTest.php @@ -177,4 +177,45 @@ class Charts32XmlTest extends TestCase ) ); } + + public function testDateAx(): void + { + $file = self::DIRECTORY . '32readwriteLineDateAxisChart1.xlsx'; + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load($file); + $sheet = $spreadsheet->getActiveSheet(); + $charts = $sheet->getChartCollection(); + self::assertCount(2, $charts); + $chart = $charts[1]; + self::assertNotNull($chart); + + $writer = new XlsxWriter($spreadsheet); + $writer->setIncludeCharts(true); + $writerChart = new XlsxWriter\Chart($writer); + $data = $writerChart->writeChart($chart); + $spreadsheet->disconnectWorksheets(); + + self::assertSame( + 1, + substr_count( + $data, + '' + ) + ); + self::assertSame( + 1, + substr_count( + $data, + '' + ) + ); + self::assertSame( + 1, + substr_count( + $data, + '' + ) + ); + } }