diff --git a/samples/Chart/33_Chart_create_line_dateaxis.php b/samples/Chart/33_Chart_create_line_dateaxis.php index c177de79a..8f05684e4 100644 --- a/samples/Chart/33_Chart_create_line_dateaxis.php +++ b/samples/Chart/33_Chart_create_line_dateaxis.php @@ -335,7 +335,7 @@ $helper->renderChart($chart, __FILE__); $spreadsheet->setActiveSheetIndex(1); // Save Excel 2007 file -$helper->write($spreadsheet, __FILE__, ['Xlsx'], true); +$helper->write($spreadsheet, __FILE__, ['Xlsx'], true, resetActiveSheet: false); $spreadsheet->disconnectWorksheets(); function dateRange(int $nrows, Spreadsheet $wrkbk): array diff --git a/samples/Chart/33_Chart_create_scatter5_trendlines.php b/samples/Chart/33_Chart_create_scatter5_trendlines.php index 7467c8206..2912b7f7c 100644 --- a/samples/Chart/33_Chart_create_scatter5_trendlines.php +++ b/samples/Chart/33_Chart_create_scatter5_trendlines.php @@ -270,4 +270,4 @@ $helper->renderChart($chart, __FILE__); $spreadsheet->setActiveSheetIndex(1); // Save Excel 2007 file -$helper->write($spreadsheet, __FILE__, ['Xlsx'], true); +$helper->write($spreadsheet, __FILE__, ['Xlsx'], true, resetActiveSheet: false); diff --git a/samples/Chart/33_Chart_create_scatter6_value_xaxis.php b/samples/Chart/33_Chart_create_scatter6_value_xaxis.php index 94f602a92..0ec7902e1 100644 --- a/samples/Chart/33_Chart_create_scatter6_value_xaxis.php +++ b/samples/Chart/33_Chart_create_scatter6_value_xaxis.php @@ -131,4 +131,4 @@ $helper->renderChart($chart, __FILE__); $spreadsheet->setActiveSheetIndex(1); // Save Excel 2007 file -$helper->write($spreadsheet, __FILE__, ['Xlsx'], true); +$helper->write($spreadsheet, __FILE__, ['Xlsx'], true, resetActiveSheet: false); diff --git a/src/PhpSpreadsheet/Chart/Axis.php b/src/PhpSpreadsheet/Chart/Axis.php index 5b20e91dd..9b804b2ef 100644 --- a/src/PhpSpreadsheet/Chart/Axis.php +++ b/src/PhpSpreadsheet/Chart/Axis.php @@ -49,6 +49,8 @@ class Axis extends Properties private ?AxisText $axisText = null; + private ?Title $dispUnitsTitle = null; + /** * Axis Options. * @@ -70,6 +72,28 @@ class Axis extends Properties 'majorTimeUnit' => self::TIME_UNIT_YEARS, 'minorTimeUnit' => self::TIME_UNIT_MONTHS, 'baseTimeUnit' => self::TIME_UNIT_DAYS, + 'logBase' => null, + 'dispUnitsBuiltIn' => null, + ]; + public const DISP_UNITS_HUNDREDS = 'hundreds'; + public const DISP_UNITS_THOUSANDS = 'thousands'; + public const DISP_UNITS_TEN_THOUSANDS = 'tenThousands'; + public const DISP_UNITS_HUNDRED_THOUSANDS = 'hundredThousands'; + public const DISP_UNITS_MILLIONS = 'millions'; + public const DISP_UNITS_TEN_MILLIONS = 'tenMillions'; + public const DISP_UNITS_HUNDRED_MILLIONS = 'hundredMillions'; + public const DISP_UNITS_BILLIONS = 'billions'; + public const DISP_UNITS_TRILLIONS = 'trillions'; + public const DISP_UNITS_BUILTIN_INT = [ + 100 => self::DISP_UNITS_HUNDREDS, + 1000 => self::DISP_UNITS_THOUSANDS, + 10000 => self::DISP_UNITS_TEN_THOUSANDS, + 100000 => self::DISP_UNITS_HUNDRED_THOUSANDS, + 1000000 => self::DISP_UNITS_MILLIONS, + 10000000 => self::DISP_UNITS_TEN_MILLIONS, + 100000000 => self::DISP_UNITS_HUNDRED_MILLIONS, + 1000000000 => self::DISP_UNITS_BILLIONS, + 1000000000000 => self::DISP_UNITS_TRILLIONS, ]; /** @@ -149,7 +173,9 @@ class Axis extends Properties ?string $hidden = null, ?string $baseTimeUnit = null, ?string $majorTimeUnit = null, - ?string $minorTimeUnit = null + ?string $minorTimeUnit = null, + null|float|int|string $logBase = null, + ?string $dispUnitsBuiltIn = null ): void { $this->axisOptions['axis_labels'] = $axisLabels; $this->setAxisOption('horizontal_crosses_value', $horizontalCrossesValue); @@ -166,6 +192,8 @@ class Axis extends Properties $this->setAxisOption('baseTimeUnit', $baseTimeUnit); $this->setAxisOption('majorTimeUnit', $majorTimeUnit); $this->setAxisOption('minorTimeUnit', $minorTimeUnit); + $this->setAxisOption('logBase', $logBase); + $this->setAxisOption('dispUnitsBuiltIn', $dispUnitsBuiltIn); } /** @@ -317,6 +345,18 @@ class Axis extends Properties return $this->noFill; } + public function setDispUnitsTitle(?Title $dispUnitsTitle): self + { + $this->dispUnitsTitle = $dispUnitsTitle; + + return $this; + } + + public function getDispUnitsTitle(): ?Title + { + return $this->dispUnitsTitle; + } + /** * Implement PHP __clone to create a deep clone, not just a shallow copy. */ @@ -326,6 +366,7 @@ class Axis extends Properties $this->majorGridlines = ($this->majorGridlines === null) ? null : clone $this->majorGridlines; $this->majorGridlines = ($this->minorGridlines === null) ? null : clone $this->minorGridlines; $this->axisText = ($this->axisText === null) ? null : clone $this->axisText; + $this->dispUnitsTitle = ($this->dispUnitsTitle === null) ? null : clone $this->dispUnitsTitle; $this->fillColor = clone $this->fillColor; } } diff --git a/src/PhpSpreadsheet/Chart/Title.php b/src/PhpSpreadsheet/Chart/Title.php index e9c42a4c4..3cdc4d1ee 100644 --- a/src/PhpSpreadsheet/Chart/Title.php +++ b/src/PhpSpreadsheet/Chart/Title.php @@ -16,7 +16,7 @@ class Title /** * Title Caption. * - * @var array|RichText|string + * @var null|array|RichText|string may be null */ private $caption = ''; @@ -39,7 +39,7 @@ class Title /** * Create a new Title. * - * @param array|RichText|string $caption + * @param null|array|RichText|string $caption may be null * @param bool $overlay */ public function __construct($caption = '', ?Layout $layout = null, $overlay = false) @@ -52,7 +52,7 @@ class Title /** * Get caption. * - * @return array|RichText|string + * @return null|array|RichText|string may be null */ public function getCaption() { @@ -68,6 +68,9 @@ class Title } } $caption = $this->caption; + if ($caption === null) { + return ''; + } if (is_string($caption)) { return $caption; } @@ -91,7 +94,7 @@ class Title /** * Set caption. * - * @param array|RichText|string $caption + * @param null|array|RichText|string $caption may be null * * @return $this */ diff --git a/src/PhpSpreadsheet/Helper/Sample.php b/src/PhpSpreadsheet/Helper/Sample.php index cb23cf597..2f7d95b06 100644 --- a/src/PhpSpreadsheet/Helper/Sample.php +++ b/src/PhpSpreadsheet/Helper/Sample.php @@ -111,13 +111,14 @@ class Sample /** * Write documents. * - * @param string $filename * @param string[] $writers */ - public function write(Spreadsheet $spreadsheet, $filename, array $writers = ['Xlsx', 'Xls'], bool $withCharts = false, ?callable $writerCallback = null): void + public function write(Spreadsheet $spreadsheet, string $filename, array $writers = ['Xlsx', 'Xls'], bool $withCharts = false, ?callable $writerCallback = null, bool $resetActiveSheet = true): void { // Set active sheet index to the first sheet, so Excel opens this as the first sheet - $spreadsheet->setActiveSheetIndex(0); + if ($resetActiveSheet) { + $spreadsheet->setActiveSheetIndex(0); + } // Write documents foreach ($writers as $writerType) { diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php index 32737f01a..c1d0eeb7e 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php @@ -494,7 +494,7 @@ class Chart private function chartTitle(SimpleXMLElement $titleDetails): Title { - $caption = []; + $caption = null; $titleLayout = null; $titleOverlay = false; $titleFormula = null; @@ -503,6 +503,7 @@ class Chart $chartDetail = Xlsx::testSimpleXml($chartDetail); switch ($titleDetailKey) { case 'tx': + $caption = []; if (isset($chartDetail->rich)) { $titleDetails = $chartDetail->rich->children($this->aNamespace); foreach ($titleDetails as $titleKey => $titleDetail) { @@ -1518,6 +1519,13 @@ class Chart if (isset($chartDetail->crossBetween)) { $whichAxis->setCrossBetween((string) self::getAttributeString($chartDetail->crossBetween, 'val')); } + if (isset($chartDetail->dispUnits, $chartDetail->dispUnits->builtInUnit)) { + $whichAxis->setAxisOption('dispUnitsBuiltIn', (string) self::getAttributeString($chartDetail->dispUnits->builtInUnit, 'val')); + if (isset($chartDetail->dispUnits->dispUnitsLbl)) { + $whichAxis->setDispUnitsTitle(new Title()); + // TODO parse title elements + } + } if (isset($chartDetail->majorTickMark)) { $whichAxis->setAxisOption('major_tick_mark', (string) self::getAttributeString($chartDetail->majorTickMark, 'val')); } @@ -1533,6 +1541,9 @@ class Chart if (isset($chartDetail->crossesAt)) { $whichAxis->setAxisOption('horizontal_crosses_value', (string) self::getAttributeString($chartDetail->crossesAt, 'val')); } + if (isset($chartDetail->scaling->logBase)) { + $whichAxis->setAxisOption('logBase', (string) self::getAttributeString($chartDetail->scaling->logBase, 'val')); + } if (isset($chartDetail->scaling->orientation)) { $whichAxis->setAxisOption('orientation', (string) self::getAttributeString($chartDetail->scaling->orientation, 'val')); } diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index a3f27d1b9..e8e6ea2fd 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -158,38 +158,40 @@ class Chart extends WriterPart } $objWriter->startElement('c:title'); - $objWriter->startElement('c:tx'); - $objWriter->startElement('c:rich'); - - $objWriter->startElement('a:bodyPr'); - $objWriter->endElement(); - - $objWriter->startElement('a:lstStyle'); - $objWriter->endElement(); - - $objWriter->startElement('a:p'); - $objWriter->startElement('a:pPr'); - $objWriter->startElement('a:defRPr'); - $objWriter->endElement(); - $objWriter->endElement(); - $caption = $title->getCaption(); - if (is_array($caption)) { - $caption = $caption[0] ?? ''; - } - $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a'); + if ($caption !== null) { + $objWriter->startElement('c:tx'); + $objWriter->startElement('c:rich'); - $objWriter->endElement(); - $objWriter->endElement(); - $objWriter->endElement(); + $objWriter->startElement('a:bodyPr'); + $objWriter->endElement(); // a:bodyPr + + $objWriter->startElement('a:lstStyle'); + $objWriter->endElement(); // a:lstStyle + + $objWriter->startElement('a:p'); + $objWriter->startElement('a:pPr'); + $objWriter->startElement('a:defRPr'); + $objWriter->endElement(); // a:defRPr + $objWriter->endElement(); // a:pPr + + if (is_array($caption)) { + $caption = $caption[0] ?? ''; + } + $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a'); + + $objWriter->endElement(); // a:p + $objWriter->endElement(); // c:rich + $objWriter->endElement(); // c:tx + } $this->writeLayout($objWriter, $title->getLayout()); $objWriter->startElement('c:overlay'); $objWriter->writeAttribute('val', ($title->getOverlay()) ? '1' : '0'); - $objWriter->endElement(); + $objWriter->endElement(); // c:overlay - $objWriter->endElement(); + $objWriter->endElement(); // c:title } /** @@ -587,6 +589,14 @@ class Chart extends WriterPart } $objWriter->startElement('c:scaling'); + if (is_numeric($yAxis->getAxisOptionsProperty('logBase'))) { + $logBase = $yAxis->getAxisOptionsProperty('logBase') + 0; + if ($logBase >= 2 && $logBase <= 1000) { + $objWriter->startElement('c:logBase'); + $objWriter->writeAttribute('val', (string) $logBase); + $objWriter->endElement(); + } + } if ($yAxis->getAxisOptionsProperty('maximum') !== null) { $objWriter->startElement('c:max'); $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('maximum')); @@ -632,35 +642,37 @@ class Chart extends WriterPart if ($xAxisLabel !== null) { $objWriter->startElement('c:title'); - $objWriter->startElement('c:tx'); - $objWriter->startElement('c:rich'); - - $objWriter->startElement('a:bodyPr'); - $objWriter->endElement(); - - $objWriter->startElement('a:lstStyle'); - $objWriter->endElement(); - - $objWriter->startElement('a:p'); - $caption = $xAxisLabel->getCaption(); - if (is_array($caption)) { - $caption = $caption[0]; - } - $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a'); + if ($caption !== null) { + $objWriter->startElement('c:tx'); + $objWriter->startElement('c:rich'); - $objWriter->endElement(); - $objWriter->endElement(); - $objWriter->endElement(); + $objWriter->startElement('a:bodyPr'); + $objWriter->endElement(); // a:bodyPr + + $objWriter->startElement('a:lstStyle'); + $objWriter->endElement(); // a::lstStyle + + $objWriter->startElement('a:p'); + + if (is_array($caption)) { + $caption = $caption[0]; + } + $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a'); + + $objWriter->endElement(); // a:p + $objWriter->endElement(); // c:rich + $objWriter->endElement(); // c:tx + } $layout = $xAxisLabel->getLayout(); $this->writeLayout($objWriter, $layout); $objWriter->startElement('c:overlay'); $objWriter->writeAttribute('val', '0'); - $objWriter->endElement(); + $objWriter->endElement(); // c:overlay - $objWriter->endElement(); + $objWriter->endElement(); // c:title } $objWriter->startElement('c:numFmt'); @@ -796,6 +808,14 @@ class Chart extends WriterPart } $objWriter->startElement('c:scaling'); + if (is_numeric($xAxis->getAxisOptionsProperty('logBase'))) { + $logBase = $xAxis->getAxisOptionsProperty('logBase') + 0; + if ($logBase >= 2 && $logBase <= 1000) { + $objWriter->startElement('c:logBase'); + $objWriter->writeAttribute('val', (string) $logBase); + $objWriter->endElement(); + } + } if ($xAxis->getAxisOptionsProperty('maximum') !== null) { $objWriter->startElement('c:max'); @@ -845,26 +865,28 @@ class Chart extends WriterPart if ($yAxisLabel !== null) { $objWriter->startElement('c:title'); - $objWriter->startElement('c:tx'); - $objWriter->startElement('c:rich'); - - $objWriter->startElement('a:bodyPr'); - $objWriter->endElement(); - - $objWriter->startElement('a:lstStyle'); - $objWriter->endElement(); - - $objWriter->startElement('a:p'); - $caption = $yAxisLabel->getCaption(); - if (is_array($caption)) { - $caption = $caption[0]; - } - $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a'); + if ($caption !== null) { + $objWriter->startElement('c:tx'); + $objWriter->startElement('c:rich'); - $objWriter->endElement(); - $objWriter->endElement(); - $objWriter->endElement(); + $objWriter->startElement('a:bodyPr'); + $objWriter->endElement(); // a:bodyPr + + $objWriter->startElement('a:lstStyle'); + $objWriter->endElement(); // a:lstStyle + + $objWriter->startElement('a:p'); + + if (is_array($caption)) { + $caption = $caption[0]; + } + $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a'); + + $objWriter->endElement(); // a:p + $objWriter->endElement(); // c:rich + $objWriter->endElement(); // c:tx + } if ($groupType !== DataSeries::TYPE_BUBBLECHART) { $layout = $yAxisLabel->getLayout(); @@ -873,9 +895,9 @@ class Chart extends WriterPart $objWriter->startElement('c:overlay'); $objWriter->writeAttribute('val', '0'); - $objWriter->endElement(); + $objWriter->endElement(); // c:overlay - $objWriter->endElement(); + $objWriter->endElement(); // c:title } $objWriter->startElement('c:numFmt'); @@ -950,6 +972,22 @@ class Chart extends WriterPart $objWriter->endElement(); } + if ($xAxis->getAxisType() === Axis::AXIS_TYPE_VALUE) { + $dispUnits = $xAxis->getAxisOptionsProperty('dispUnitsBuiltIn'); + $dispUnits = is_numeric($dispUnits) ? (Axis::DISP_UNITS_BUILTIN_INT[(int) $dispUnits] ?? '') : $dispUnits; + if (in_array($dispUnits, Axis::DISP_UNITS_BUILTIN_INT, true)) { + $objWriter->startElement('c:dispUnits'); + $objWriter->startElement('c:builtInUnit'); + $objWriter->writeAttribute('val', $dispUnits); + $objWriter->endElement(); // c:builtInUnit + if ($xAxis->getDispUnitsTitle() !== null) { + // TODO output title elements + $objWriter->writeElement('c:dispUnitsLbl'); + } + $objWriter->endElement(); // c:dispUnits + } + } + if ($xAxis->getAxisOptionsProperty('major_unit') !== null) { $objWriter->startElement('c:majorUnit'); $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('major_unit')); diff --git a/tests/PhpSpreadsheetTests/Chart/Issue3833Test.php b/tests/PhpSpreadsheetTests/Chart/Issue3833Test.php new file mode 100644 index 000000000..669e23306 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Chart/Issue3833Test.php @@ -0,0 +1,142 @@ +setIncludeCharts(true); + } + + public function writeCharts(XlsxWriter $writer): void + { + $writer->setIncludeCharts(true); + } + + public function testDisplayUnits1(): void + { + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load('tests/data/Reader/XLSX/issue.3833.units.xlsx'); + + /** @var callable */ + $callableReader = [$this, 'readCharts']; + /** @var callable */ + $callableWriter = [$this, 'writeCharts']; + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $sheet = $reloadedSpreadsheet->getSheetByNameOrThrow('charts'); + $charts2 = $sheet->getChartCollection(); + self::assertCount(1, $charts2); + $chart2 = $charts2[0]; + self::assertNotNull($chart2); + $yAxis = $chart2->getChartAxisY(); + $dispUnits = $yAxis->getAxisOptionsProperty('dispUnitsBuiltIn'); + self::assertSame('tenThousands', $dispUnits); + $logBase = $yAxis->getAxisOptionsProperty('logBase'); + self::assertNull($logBase); + + $reloadedSpreadsheet->disconnectWorksheets(); + } + + public function testDisplayUnits2(): void + { + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load('tests/data/Reader/XLSX/issue.3833.units.xlsx'); + $sheet1 = $spreadsheet->getSheetByNameOrThrow('charts'); + $charts1 = $sheet1->getChartCollection(); + self::assertCount(1, $charts1); + $chart1 = $charts1[0]; + self::assertNotNull($chart1); + $yAxis1 = $chart1->getChartAxisY(); + $yAxis1->setAxisOption('dispUnitsBuiltIn', 1000); + + /** @var callable */ + $callableReader = [$this, 'readCharts']; + /** @var callable */ + $callableWriter = [$this, 'writeCharts']; + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $sheet = $reloadedSpreadsheet->getSheetByNameOrThrow('charts'); + $charts2 = $sheet->getChartCollection(); + self::assertCount(1, $charts2); + $chart2 = $charts2[0]; + self::assertNotNull($chart2); + $yAxis = $chart2->getChartAxisY(); + $dispUnits = $yAxis->getAxisOptionsProperty('dispUnitsBuiltIn'); + self::assertSame('thousands', $dispUnits); + $logBase = $yAxis->getAxisOptionsProperty('logBase'); + self::assertNull($logBase); + + $reloadedSpreadsheet->disconnectWorksheets(); + } + + public function testDisplayUnits3(): void + { + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load('tests/data/Reader/XLSX/issue.3833.units.xlsx'); + $sheet1 = $spreadsheet->getSheetByNameOrThrow('charts'); + $charts1 = $sheet1->getChartCollection(); + self::assertCount(1, $charts1); + $chart1 = $charts1[0]; + self::assertNotNull($chart1); + $yAxis1 = $chart1->getChartAxisY(); + $yAxis1->setDispUnitsTitle(null); + + /** @var callable */ + $callableReader = [$this, 'readCharts']; + /** @var callable */ + $callableWriter = [$this, 'writeCharts']; + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $sheet = $reloadedSpreadsheet->getSheetByNameOrThrow('charts'); + $charts2 = $sheet->getChartCollection(); + self::assertCount(1, $charts2); + $chart2 = $charts2[0]; + self::assertNotNull($chart2); + $yAxis = $chart2->getChartAxisY(); + self::assertNull($yAxis->getDispUnitsTitle()); + + $reloadedSpreadsheet->disconnectWorksheets(); + } + + public function testLogBase(): void + { + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load('tests/data/Reader/XLSX/issue.3833.logarithm.xlsx'); + + /** @var callable */ + $callableReader = [$this, 'readCharts']; + /** @var callable */ + $callableWriter = [$this, 'writeCharts']; + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $sheet = $reloadedSpreadsheet->getSheetByNameOrThrow('charts'); + $charts2 = $sheet->getChartCollection(); + self::assertCount(1, $charts2); + $chart2 = $charts2[0]; + self::assertNotNull($chart2); + $yAxis = $chart2->getChartAxisY(); + $logBase = $yAxis->getAxisOptionsProperty('logBase'); + self::assertSame('10', $logBase); + $dispUnits = $yAxis->getAxisOptionsProperty('dispUnitsBuiltIn'); + self::assertNull($dispUnits); + + $reloadedSpreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/data/Reader/XLSX/issue.3833.logarithm.xlsx b/tests/data/Reader/XLSX/issue.3833.logarithm.xlsx new file mode 100644 index 000000000..c2560e3b0 Binary files /dev/null and b/tests/data/Reader/XLSX/issue.3833.logarithm.xlsx differ diff --git a/tests/data/Reader/XLSX/issue.3833.units.xlsx b/tests/data/Reader/XLSX/issue.3833.units.xlsx new file mode 100644 index 000000000..649fa8086 Binary files /dev/null and b/tests/data/Reader/XLSX/issue.3833.units.xlsx differ