diff --git a/samples/Chart/32_Chart_read_write.php b/samples/Chart/32_Chart_read_write.php
index a1f2f54681..42944c0ccb 100644
--- a/samples/Chart/32_Chart_read_write.php
+++ b/samples/Chart/32_Chart_read_write.php
@@ -42,7 +42,7 @@
foreach ($chartNames as $i => $chartName) {
$chart = $worksheet->getChartByName($chartName);
if ($chart->getTitle() !== null) {
- $caption = '"' . implode(' ', $chart->getTitle()->getCaption()) . '"';
+ $caption = '"' . $chart->getTitle()->getCaptionText($spreadsheet) . '"';
} else {
$caption = 'Untitled';
}
diff --git a/samples/Chart/37_Chart_dynamic_title.php b/samples/Chart/37_Chart_dynamic_title.php
new file mode 100644
index 0000000000..b8b801faae
--- /dev/null
+++ b/samples/Chart/37_Chart_dynamic_title.php
@@ -0,0 +1,83 @@
+log('File ' . $inputFileNameShort . ' does not exist');
+
+ continue;
+ }
+ $reader = IOFactory::createReader($inputFileType);
+ $reader->setIncludeCharts(true);
+ $callStartTime = microtime(true);
+ $spreadsheet = $reader->load($inputFileName);
+ $helper->logRead($inputFileType, $inputFileName, $callStartTime);
+
+ $helper->log('Iterate worksheets looking at the charts');
+ foreach ($spreadsheet->getWorksheetIterator() as $worksheet) {
+ $sheetName = $worksheet->getTitle();
+ $worksheet->getCell('A1')->setValue('Changed Title');
+ $helper->log('Worksheet: ' . $sheetName);
+
+ $chartNames = $worksheet->getChartNames();
+ if (empty($chartNames)) {
+ $helper->log(' There are no charts in this worksheet');
+ } else {
+ natsort($chartNames);
+ foreach ($chartNames as $i => $chartName) {
+ $chart = $worksheet->getChartByName($chartName);
+ if ($chart->getTitle() !== null) {
+ $caption = '"' . $chart->getTitle()->getCaptionText($spreadsheet) . '"';
+ } else {
+ $caption = 'Untitled';
+ }
+ $helper->log(' ' . $chartName . ' - ' . $caption);
+ $indentation = str_repeat(' ', strlen($chartName) + 3);
+ $groupCount = $chart->getPlotArea()->getPlotGroupCount();
+ if ($groupCount == 1) {
+ $chartType = $chart->getPlotArea()->getPlotGroupByIndex(0)->getPlotType();
+ $helper->log($indentation . ' ' . $chartType);
+ $helper->renderChart($chart, __FILE__, $spreadsheet);
+ } else {
+ $chartTypes = [];
+ for ($i = 0; $i < $groupCount; ++$i) {
+ $chartTypes[] = $chart->getPlotArea()->getPlotGroupByIndex($i)->getPlotType();
+ }
+ $chartTypes = array_unique($chartTypes);
+ if (count($chartTypes) == 1) {
+ $chartType = 'Multiple Plot ' . array_pop($chartTypes);
+ $helper->log($indentation . ' ' . $chartType);
+ $helper->renderChart($chart, __FILE__);
+ } elseif (count($chartTypes) == 0) {
+ $helper->log($indentation . ' *** Type not yet implemented');
+ } else {
+ $helper->log($indentation . ' Combination Chart');
+ $helper->renderChart($chart, __FILE__);
+ }
+ }
+ }
+ }
+ }
+
+ $callStartTime = microtime(true);
+ $helper->write($spreadsheet, $inputFileName, ['Xlsx'], true);
+
+ Settings::setChartRenderer(\PhpOffice\PhpSpreadsheet\Chart\Renderer\MtJpGraphRenderer::class);
+ $callStartTime = microtime(true);
+ $helper->write($spreadsheet, $inputFileName, ['Html'], true);
+
+ $spreadsheet->disconnectWorksheets();
+ unset($spreadsheet);
+}
diff --git a/samples/templates/37dynamictitle.xlsx b/samples/templates/37dynamictitle.xlsx
new file mode 100644
index 0000000000..6a5215e861
Binary files /dev/null and b/samples/templates/37dynamictitle.xlsx differ
diff --git a/src/PhpSpreadsheet/Chart/Title.php b/src/PhpSpreadsheet/Chart/Title.php
index d8e6e7497f..378987446e 100644
--- a/src/PhpSpreadsheet/Chart/Title.php
+++ b/src/PhpSpreadsheet/Chart/Title.php
@@ -3,9 +3,16 @@
namespace PhpOffice\PhpSpreadsheet\Chart;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\Font;
class Title
{
+ public const TITLE_CELL_REFERENCE
+ = '/^(.*)!' // beginning of string, everything up to ! is match[1]
+ . '[$]([A-Z]{1,3})' // absolute column string match[2]
+ . '[$](\d{1,7})$/i'; // absolute row string match[3]
+
/**
* Title Caption.
*
@@ -25,6 +32,10 @@ class Title
*/
private ?Layout $layout;
+ private string $cellReference = '';
+
+ private ?Font $font = null;
+
/**
* Create a new Title.
*
@@ -48,8 +59,14 @@ public function getCaption()
return $this->caption;
}
- public function getCaptionText(): string
+ public function getCaptionText(?Spreadsheet $spreadsheet = null): string
{
+ if ($spreadsheet !== null) {
+ $caption = $this->getCalculatedTitle($spreadsheet);
+ if ($caption !== null) {
+ return $caption;
+ }
+ }
$caption = $this->caption;
if (is_string($caption)) {
return $caption;
@@ -100,13 +117,50 @@ public function getOverlay()
*
* @param bool $overlay
*/
- public function setOverlay($overlay): void
+ public function setOverlay($overlay): static
{
$this->overlay = $overlay;
+
+ return $this;
}
public function getLayout(): ?Layout
{
return $this->layout;
}
+
+ public function setCellReference(string $cellReference): self
+ {
+ $this->cellReference = $cellReference;
+
+ return $this;
+ }
+
+ public function getCellReference(): string
+ {
+ return $this->cellReference;
+ }
+
+ public function getCalculatedTitle(?Spreadsheet $spreadsheet): ?string
+ {
+ preg_match(self::TITLE_CELL_REFERENCE, $this->cellReference, $matches);
+ if (count($matches) === 0 || $spreadsheet === null) {
+ return null;
+ }
+ $sheetName = preg_replace("/^'(.*)'$/", '$1', $matches[1]) ?? '';
+
+ return $spreadsheet->getSheetByName($sheetName)?->getCell($matches[2] . $matches[3])?->getFormattedValue();
+ }
+
+ public function getFont(): ?Font
+ {
+ return $this->font;
+ }
+
+ public function setFont(?Font $font): self
+ {
+ $this->font = $font;
+
+ return $this;
+ }
}
diff --git a/src/PhpSpreadsheet/Helper/Sample.php b/src/PhpSpreadsheet/Helper/Sample.php
index 3d53f1e053..cb23cf597c 100644
--- a/src/PhpSpreadsheet/Helper/Sample.php
+++ b/src/PhpSpreadsheet/Helper/Sample.php
@@ -198,7 +198,7 @@ public function log(string $message): void
*
* @codeCoverageIgnore
*/
- public function renderChart(Chart $chart, string $fileName): void
+ public function renderChart(Chart $chart, string $fileName, ?Spreadsheet $spreadsheet = null): void
{
if ($this->isCli() === true) {
return;
@@ -206,17 +206,32 @@ public function renderChart(Chart $chart, string $fileName): void
Settings::setChartRenderer(MtJpGraphRenderer::class);
$fileName = $this->getFilename($fileName, 'png');
+ $title = $chart->getTitle();
+ $caption = null;
+ if ($title !== null) {
+ $calculatedTitle = $title->getCalculatedTitle($spreadsheet);
+ if ($calculatedTitle !== null) {
+ $caption = $title->getCaption();
+ $title->setCaption($calculatedTitle);
+ }
+ }
try {
$chart->render($fileName);
$this->log('Rendered image: ' . $fileName);
- $imageData = file_get_contents($fileName);
+ $imageData = @file_get_contents($fileName);
if ($imageData !== false) {
echo '
 . ')
';
+ } else {
+ $this->log('Unable to open chart' . PHP_EOL);
}
} catch (Throwable $e) {
$this->log('Error rendering chart: ' . $e->getMessage() . PHP_EOL);
}
+ if (isset($title, $caption)) {
+ $title->setCaption($caption);
+ }
+ Settings::unsetChartRenderer();
}
public function titles(string $category, string $functionName, ?string $description = null): void
diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php
index 789d46ee78..32737f01a6 100644
--- a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php
+++ b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php
@@ -497,6 +497,8 @@ private function chartTitle(SimpleXMLElement $titleDetails): Title
$caption = [];
$titleLayout = null;
$titleOverlay = false;
+ $titleFormula = null;
+ $titleFont = null;
foreach ($titleDetails as $titleDetailKey => $chartDetail) {
$chartDetail = Xlsx::testSimpleXml($chartDetail);
switch ($titleDetailKey) {
@@ -517,6 +519,9 @@ private function chartTitle(SimpleXMLElement $titleDetails): Title
$caption[] = (string) $pt->v;
}
}
+ if (isset($chartDetail->strRef->f)) {
+ $titleFormula = (string) $chartDetail->strRef->f;
+ }
}
break;
@@ -527,11 +532,24 @@ private function chartTitle(SimpleXMLElement $titleDetails): Title
case 'layout':
$titleLayout = $this->chartLayoutDetails($chartDetail);
+ break;
+ case 'txPr':
+ if (isset($chartDetail->children($this->aNamespace)->p)) {
+ $titleFont = $this->parseFont($chartDetail->children($this->aNamespace)->p);
+ }
+
break;
}
}
+ $title = new Title($caption, $titleLayout, (bool) $titleOverlay);
+ if (!empty($titleFormula)) {
+ $title->setCellReference($titleFormula);
+ }
+ if ($titleFont !== null) {
+ $title->setFont($titleFont);
+ }
- return new Title($caption, $titleLayout, (bool) $titleOverlay);
+ return $title;
}
private function chartLayoutDetails(SimpleXMLElement $chartDetail): ?Layout
@@ -1185,6 +1203,7 @@ private function parseFont(SimpleXMLElement $titleDetailPart): ?Font
$fontArray['italic'] = self::getAttributeBoolean($titleDetailPart->pPr->defRPr, 'i');
$fontArray['underscore'] = self::getAttributeString($titleDetailPart->pPr->defRPr, 'u');
$fontArray['strikethrough'] = self::getAttributeString($titleDetailPart->pPr->defRPr, 'strike');
+ $fontArray['cap'] = self::getAttributeString($titleDetailPart->pPr->defRPr, 'cap');
if (isset($titleDetailPart->pPr->defRPr->latin)) {
$fontArray['latin'] = self::getAttributeString($titleDetailPart->pPr->defRPr->latin, 'typeface');
diff --git a/src/PhpSpreadsheet/Settings.php b/src/PhpSpreadsheet/Settings.php
index 9f2ef0cd89..f7daa45750 100644
--- a/src/PhpSpreadsheet/Settings.php
+++ b/src/PhpSpreadsheet/Settings.php
@@ -78,6 +78,11 @@ public static function setChartRenderer(string $rendererClassName): void
self::$chartRenderer = $rendererClassName;
}
+ public static function unsetChartRenderer(): void
+ {
+ self::$chartRenderer = null;
+ }
+
/**
* Return the Chart Rendering Library that PhpSpreadsheet is currently configured to use.
*
diff --git a/src/PhpSpreadsheet/Style/Font.php b/src/PhpSpreadsheet/Style/Font.php
index 09a24d10ab..ea26b8ce64 100644
--- a/src/PhpSpreadsheet/Style/Font.php
+++ b/src/PhpSpreadsheet/Style/Font.php
@@ -13,6 +13,13 @@ class Font extends Supervisor
const UNDERLINE_SINGLE = 'single';
const UNDERLINE_SINGLEACCOUNTING = 'singleAccounting';
+ const CAP_ALL = 'all';
+ const CAP_SMALL = 'small';
+ const CAP_NONE = 'none';
+ private const VALID_CAPS = [self::CAP_ALL, self::CAP_SMALL, self::CAP_NONE];
+
+ protected ?string $cap = null;
+
/**
* Font Name.
*
@@ -236,6 +243,9 @@ public function applyFromArray(array $styleArray): static
if (isset($styleArray['scheme'])) {
$this->setScheme($styleArray['scheme']);
}
+ if (isset($styleArray['cap'])) {
+ $this->setCap($styleArray['cap']);
+ }
}
return $this;
@@ -795,6 +805,7 @@ public function getHashCode()
$this->hashChartColor($this->chartColor),
$this->hashChartColor($this->underlineColor),
(string) $this->baseLine,
+ (string) $this->cap,
]
)
. __CLASS__
@@ -806,6 +817,7 @@ protected function exportArray1(): array
$exportedArray = [];
$this->exportArray2($exportedArray, 'baseLine', $this->getBaseLine());
$this->exportArray2($exportedArray, 'bold', $this->getBold());
+ $this->exportArray2($exportedArray, 'cap', $this->getCap());
$this->exportArray2($exportedArray, 'chartColor', $this->getChartColor());
$this->exportArray2($exportedArray, 'color', $this->getColor());
$this->exportArray2($exportedArray, 'complexScript', $this->getComplexScript());
@@ -847,4 +859,23 @@ public function setScheme(string $scheme): self
return $this;
}
+
+ /**
+ * Set capitalization attribute. If not one of the permitted
+ * values (all, small, or none), set it to null.
+ * This will be honored only for the font for chart titles.
+ * None is distinguished from null because null will inherit
+ * the current value, whereas 'none' will override it.
+ */
+ public function setCap(string $cap): self
+ {
+ $this->cap = in_array($cap, self::VALID_CAPS, true) ? $cap : null;
+
+ return $this;
+ }
+
+ public function getCap(): ?string
+ {
+ return $this->cap;
+ }
}
diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php
index b4547637cf..5df0525f11 100644
--- a/src/PhpSpreadsheet/Writer/Html.php
+++ b/src/PhpSpreadsheet/Writer/Html.php
@@ -23,6 +23,7 @@
use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Style\Style;
+use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
@@ -150,6 +151,12 @@ class Html extends BaseWriter
*/
private $editHtmlCallback;
+ /** @var BaseDrawing[] */
+ private $sheetDrawings;
+
+ /** @var Chart[] */
+ private $sheetCharts;
+
/**
* Create a new HTML.
*/
@@ -479,11 +486,14 @@ public function generateSheetData(): string
foreach ($sheets as $sheet) {
// Write table header
$html .= $this->generateTableHeader($sheet);
+ $this->sheetCharts = [];
+ $this->sheetDrawings = [];
// Get worksheet dimension
[$min, $max] = explode(':', $sheet->calculateWorksheetDataDimension());
[$minCol, $minRow] = Coordinate::indexesFromString($min);
[$maxCol, $maxRow] = Coordinate::indexesFromString($max);
+ $this->extendRowsAndColumns($sheet, $maxCol, $maxRow);
[$theadStart, $theadEnd, $tbodyStart] = $this->generateSheetStarts($sheet, $minRow);
@@ -510,8 +520,6 @@ public function generateSheetData(): string
$html .= $endTag;
}
- --$row;
- $html .= $this->extendRowsForChartsAndImages($sheet, $row);
// Write table footer
$html .= $this->generateTableFooter();
@@ -563,78 +571,33 @@ public function generateNavigation(): string
return $html;
}
- /**
- * Extend Row if chart is placed after nominal end of row.
- * This code should be exercised by sample:
- * Chart/32_Chart_read_write_PDF.php.
- *
- * @param int $row Row to check for charts
- */
- private function extendRowsForCharts(Worksheet $worksheet, int $row): array
+ private function extendRowsAndColumns(Worksheet $worksheet, int &$colMax, int &$rowMax): void
{
- $rowMax = $row;
- $colMax = 'A';
- $anyfound = false;
if ($this->includeCharts) {
foreach ($worksheet->getChartCollection() as $chart) {
if ($chart instanceof Chart) {
- $anyfound = true;
$chartCoordinates = $chart->getTopLeftPosition();
- $chartTL = Coordinate::coordinateFromString($chartCoordinates['cell']);
- $chartCol = Coordinate::columnIndexFromString($chartTL[0]);
+ $this->sheetCharts[$chartCoordinates['cell']] = $chart;
+ $chartTL = Coordinate::indexesFromString($chartCoordinates['cell']);
if ($chartTL[1] > $rowMax) {
$rowMax = $chartTL[1];
}
- if ($chartCol > Coordinate::columnIndexFromString($colMax)) {
+ if ($chartTL[0] > $colMax) {
$colMax = $chartTL[0];
}
}
}
}
-
- return [$rowMax, $colMax, $anyfound];
- }
-
- private function extendRowsForChartsAndImages(Worksheet $worksheet, int $row): string
- {
- [$rowMax, $colMax, $anyfound] = $this->extendRowsForCharts($worksheet, $row);
-
foreach ($worksheet->getDrawingCollection() as $drawing) {
- $anyfound = true;
- $imageTL = Coordinate::coordinateFromString($drawing->getCoordinates());
- $imageCol = Coordinate::columnIndexFromString($imageTL[0]);
+ $imageTL = Coordinate::indexesFromString($drawing->getCoordinates());
+ $this->sheetDrawings[$drawing->getCoordinates()] = $drawing;
if ($imageTL[1] > $rowMax) {
$rowMax = $imageTL[1];
}
- if ($imageCol > Coordinate::columnIndexFromString($colMax)) {
+ if ($imageTL[0] > $colMax) {
$colMax = $imageTL[0];
}
}
-
- // Don't extend rows if not needed
- if ($row === $rowMax || !$anyfound) {
- return '';
- }
-
- $html = '';
- ++$colMax;
- ++$row;
- while ($row <= $rowMax) {
- $html .= '';
- for ($col = 'A'; $col != $colMax; ++$col) {
- $htmlx = $this->writeImageInCell($worksheet, $col . $row);
- $htmlx .= $this->includeCharts ? $this->writeChartInCell($worksheet, $col . $row) : '';
- if ($htmlx) {
- $html .= "$htmlx | ";
- } else {
- $html .= " | ";
- }
- }
- ++$row;
- $html .= '
' . PHP_EOL;
- }
-
- return $html;
}
/**
@@ -658,19 +621,16 @@ public static function winFileToUrl($filename, bool $mpdf = false)
/**
* Generate image tag in cell.
*
- * @param Worksheet $worksheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
* @param string $coordinates Cell coordinates
*/
- private function writeImageInCell(Worksheet $worksheet, string $coordinates): string
+ private function writeImageInCell(string $coordinates): string
{
// Construct HTML
$html = '';
// Write images
- foreach ($worksheet->getDrawingCollection() as $drawing) {
- if ($drawing->getCoordinates() != $coordinates) {
- continue;
- }
+ $drawing = $this->sheetDrawings[$coordinates] ?? null;
+ if ($drawing !== null) {
$filedesc = $drawing->getDescription();
$filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded image';
if ($drawing instanceof Drawing) {
@@ -744,37 +704,47 @@ private function writeChartInCell(Worksheet $worksheet, string $coordinates): st
$html = '';
// Write charts
- foreach ($worksheet->getChartCollection() as $chart) {
- if ($chart instanceof Chart) {
- $chartCoordinates = $chart->getTopLeftPosition();
- if ($chartCoordinates['cell'] == $coordinates) {
- $chartFileName = File::sysGetTempDir() . '/' . uniqid('', true) . '.png';
- $renderedWidth = $chart->getRenderedWidth();
- $renderedHeight = $chart->getRenderedHeight();
- if ($renderedWidth === null || $renderedHeight === null) {
- $this->adjustRendererPositions($chart, $worksheet);
- }
- $renderSuccessful = $chart->render($chartFileName);
- $chart->setRenderedWidth($renderedWidth);
- $chart->setRenderedHeight($renderedHeight);
- if (!$renderSuccessful) {
- return '';
- }
+ $chart = $this->sheetCharts[$coordinates] ?? null;
+ if ($chart !== null) {
+ $chartCoordinates = $chart->getTopLeftPosition();
+ $chartFileName = File::sysGetTempDir() . '/' . uniqid('', true) . '.png';
+ $renderedWidth = $chart->getRenderedWidth();
+ $renderedHeight = $chart->getRenderedHeight();
+ if ($renderedWidth === null || $renderedHeight === null) {
+ $this->adjustRendererPositions($chart, $worksheet);
+ }
+ $title = $chart->getTitle();
+ $caption = null;
+ $filedesc = '';
+ if ($title !== null) {
+ $calculatedTitle = $title->getCalculatedTitle($worksheet->getParent());
+ if ($calculatedTitle !== null) {
+ $caption = $title->getCaption();
+ $title->setCaption($calculatedTitle);
+ }
+ $filedesc = $title->getCaptionText($worksheet->getParent());
+ }
+ $renderSuccessful = $chart->render($chartFileName);
+ $chart->setRenderedWidth($renderedWidth);
+ $chart->setRenderedHeight($renderedHeight);
+ if (isset($title, $caption)) {
+ $title->setCaption($caption);
+ }
+ if (!$renderSuccessful) {
+ return '';
+ }
- $html .= PHP_EOL;
- $imageDetails = getimagesize($chartFileName) ?: ['', '', 'mime' => ''];
- $filedesc = $chart->getTitle();
- $filedesc = $filedesc ? $filedesc->getCaptionText() : '';
- $filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded chart';
- $picture = file_get_contents($chartFileName);
- if ($picture !== false) {
- $base64 = base64_encode($picture);
- $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
+ $html .= PHP_EOL;
+ $imageDetails = getimagesize($chartFileName) ?: ['', '', 'mime' => ''];
- $html .= '
' . PHP_EOL;
- }
- unlink($chartFileName);
- }
+ $filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded chart';
+ $picture = file_get_contents($chartFileName);
+ unlink($chartFileName);
+ if ($picture !== false) {
+ $base64 = base64_encode($picture);
+ $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
+
+ $html .= '
' . PHP_EOL;
}
}
@@ -1444,7 +1414,7 @@ private function generateRowSpans(string $html, int $rowSpan, int $colSpan): str
private function generateRowWriteCell(string &$html, Worksheet $worksheet, string $coordinate, string $cellType, string $cellData, int $colSpan, int $rowSpan, $cssClass, int $colNum, int $sheetIndex, int $row): void
{
// Image?
- $htmlx = $this->writeImageInCell($worksheet, $coordinate);
+ $htmlx = $this->writeImageInCell($coordinate);
// Chart?
$htmlx .= $this->generateRowIncludeCharts($worksheet, $coordinate);
// Column start
diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php
index f27156507a..a3f27d1b93 100644
--- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php
+++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php
@@ -99,9 +99,11 @@ public function writeChart(\PhpOffice\PhpSpreadsheet\Chart\Chart $chart, mixed $
$objWriter->writeAttribute('val', (string) (int) $chart->getPlotVisibleOnly());
$objWriter->endElement();
- $objWriter->startElement('c:dispBlanksAs');
- $objWriter->writeAttribute('val', $chart->getDisplayBlanksAs());
- $objWriter->endElement();
+ if ($chart->getDisplayBlanksAs() !== '') {
+ $objWriter->startElement('c:dispBlanksAs');
+ $objWriter->writeAttribute('val', $chart->getDisplayBlanksAs());
+ $objWriter->endElement();
+ }
$objWriter->startElement('c:showDLblsOverMax');
$objWriter->writeAttribute('val', '0');
@@ -151,6 +153,9 @@ private function writeTitle(XMLWriter $objWriter, ?Title $title = null): void
if ($title === null) {
return;
}
+ if ($this->writeCalculatedTitle($objWriter, $title)) {
+ return;
+ }
$objWriter->startElement('c:title');
$objWriter->startElement('c:tx');
@@ -187,6 +192,60 @@ private function writeTitle(XMLWriter $objWriter, ?Title $title = null): void
$objWriter->endElement();
}
+ /**
+ * Write Calculated Chart Title.
+ */
+ private function writeCalculatedTitle(XMLWriter $objWriter, Title $title): bool
+ {
+ $calc = $title->getCalculatedTitle($this->getParentWriter()->getSpreadsheet());
+ if (empty($calc)) {
+ return false;
+ }
+
+ $objWriter->startElement('c:title');
+ $objWriter->startElement('c:tx');
+ $objWriter->startElement('c:strRef');
+ $objWriter->writeElement('c:f', $title->getCellReference());
+ $objWriter->startElement('c:strCache');
+
+ $objWriter->startElement('c:ptCount');
+ $objWriter->writeAttribute('val', '1');
+ $objWriter->endElement(); // c:ptCount
+ $objWriter->startElement('c:pt');
+ $objWriter->writeAttribute('idx', '0');
+ $objWriter->writeElement('c:v', $calc);
+ $objWriter->endElement(); // c:pt
+
+ $objWriter->endElement(); // c:strCache
+ $objWriter->endElement(); // c:strRef
+ $objWriter->endElement(); // c:tx
+
+ $this->writeLayout($objWriter, $title->getLayout());
+
+ $objWriter->startElement('c:overlay');
+ $objWriter->writeAttribute('val', ($title->getOverlay()) ? '1' : '0');
+ $objWriter->endElement(); // c:overlay
+ // c:spPr
+
+ // c:txPr
+ $labelFont = $title->getFont();
+ if ($labelFont !== null) {
+ $objWriter->startElement('c:txPr');
+
+ $objWriter->startElement('a:bodyPr');
+ $objWriter->endElement(); // a:bodyPr
+ $objWriter->startElement('a:lstStyle');
+ $objWriter->endElement(); // a:lstStyle
+ $this->writeLabelFont($objWriter, $labelFont, null);
+
+ $objWriter->endElement(); // c:txPr
+ }
+
+ $objWriter->endElement(); // c:title
+
+ return true;
+ }
+
/**
* Write Chart Legend.
*/
@@ -1804,6 +1863,10 @@ private function writeLabelFont(XMLWriter $objWriter, ?Font $labelFont, ?Propert
if ($labelFont->getItalic() === true) {
$objWriter->writeAttribute('i', '1');
}
+ $cap = $labelFont->getCap();
+ if ($cap !== null) {
+ $objWriter->writeAttribute('cap', $cap);
+ }
$fontColor = $labelFont->getChartColor();
if ($fontColor !== null) {
$this->writeColor($objWriter, $fontColor);
diff --git a/tests/PhpSpreadsheetTests/Chart/ChartsDynamicTitleTest.php b/tests/PhpSpreadsheetTests/Chart/ChartsDynamicTitleTest.php
new file mode 100644
index 0000000000..2546d60de6
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/Chart/ChartsDynamicTitleTest.php
@@ -0,0 +1,141 @@
+setIncludeCharts(true);
+ }
+
+ public function writeCharts(XlsxWriter $writer): void
+ {
+ $writer->setIncludeCharts(true);
+ }
+
+ public function testDynamicTitle(): void
+ {
+ // based on samples/templates/issue.3797.2007.xlsx
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->setTitle('Only Sheet');
+ $sheet->fromArray(
+ [
+ ['Some Title'],
+ [],
+ [null, null, 'Data'],
+ [null, 'L1', 1.3],
+ [null, 'L2', 1.3],
+ [null, 'L3', 2.3],
+ [null, 'L4', 1.6],
+ [null, 'L5', 1.5],
+ [null, 'L6', 1.4],
+ [null, 'L7', 2.2],
+ [null, 'L8', 1.8],
+ [null, 'L9', 1.1],
+ [null, 'L10', 1.8],
+ [null, 'L11', 1.6],
+ [null, 'L12', 2.7],
+ [null, 'L13', 2.2],
+ [null, 'L14', 1.3],
+ ]
+ );
+
+ $dataSeriesLabels = [
+ new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, '\'Only Sheet\'!$B$4', null, 1), // 2010
+ ];
+ // Set the X-Axis Labels
+ $xAxisTickValues = [
+ new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, '\'Only Sheet\'!$B$4:$B$17'),
+ ];
+ // Set the Data values for each data series we want to plot
+ $dataSeriesValues = [
+ new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, '\'Only Sheet\'!$C$4:$C$17'),
+ ];
+
+ // Build the dataseries
+ $series = new DataSeries(
+ DataSeries::TYPE_BARCHART, // plotType
+ DataSeries::GROUPING_STANDARD, // plotGrouping
+ range(0, count($dataSeriesValues) - 1), // plotOrder
+ $dataSeriesLabels, // plotLabel
+ $xAxisTickValues, // plotCategory
+ $dataSeriesValues, // plotValues
+ );
+
+ // Set the series in the plot area
+ $plotArea = new PlotArea(null, [$series]);
+ $title = new Title();
+ $title->setCellReference('\'Only Sheet\'!$A$1');
+ $font = new Font();
+ $font->setCap(Font::CAP_ALL);
+ $title->setFont($font);
+
+ // Create the chart
+ $chart = new Chart(
+ 'chart1', // name
+ $title, // title
+ null, // legend
+ $plotArea, // plotArea
+ true, // plotVisibleOnly
+ DataSeries::EMPTY_AS_GAP, // displayBlanksAs
+ null, // xAxisLabel
+ null, // yAxisLabel
+ null, // xAxis
+ );
+
+ // Set the position where the chart should appear in the worksheet
+ $chart->setTopLeftPosition('G7');
+ $chart->setBottomRightPosition('N21');
+ // Add the chart to the worksheet
+ $sheet->addChart($chart);
+ $sheet->setSelectedCells('D1');
+
+ /** @var callable */
+ $callableReader = [$this, 'readCharts'];
+ /** @var callable */
+ $callableWriter = [$this, 'writeCharts'];
+ $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter);
+ $spreadsheet->disconnectWorksheets();
+
+ $rsheet = $reloadedSpreadsheet->getActiveSheet();
+ $charts2 = $rsheet->getChartCollection();
+ self::assertCount(1, $charts2);
+ $chart2 = $charts2[0];
+ self::assertNotNull($chart2);
+ $original = $chart2->getTitle()?->getCalculatedTitle($reloadedSpreadsheet);
+ self::assertSame('Some Title', $original);
+ $rsheet->getCell('A1')->setValue('Changed Title');
+ self::assertNotNull($chart2->getTitle());
+ self::assertSame('Changed Title', $chart2->getTitle()->getCalculatedTitle($reloadedSpreadsheet));
+ self::assertSame(Font::CAP_ALL, $chart2->getTitle()->getFont()?->getCap());
+
+ $writer = new Html($reloadedSpreadsheet);
+ Settings::setChartRenderer(\PhpOffice\PhpSpreadsheet\Chart\Renderer\MtJpGraphRenderer::class);
+ $writer->setIncludeCharts(true);
+ $content = $writer->generateHtmlAll();
+ self::assertStringContainsString('alt="Changed Title"', $content);
+ $reloadedSpreadsheet->disconnectWorksheets();
+ }
+}