diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index f8a0b11324..16f681ecc1 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -38,6 +38,7 @@ use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\Style; use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing; +use PhpOffice\PhpSpreadsheet\Worksheet\RichValueDrawing; use PhpOffice\PhpSpreadsheet\Worksheet\Table\TableDxfsStyle; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use SimpleXMLElement; @@ -784,6 +785,19 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet $charts = $chartDetails = []; + // Add richData (contains relation of in-cell images) + $richData = []; + $relationsFileName = $dir . '/richData/_rels/richValueRel.xml.rels'; + if ($zip->locateName($relationsFileName)) { + $relsWorksheet = $this->loadZip($relationsFileName, Namespaces::RELATIONSHIPS); + foreach ($relsWorksheet->Relationship as $elex) { + $ele = self::getAttributes($elex); + if ($ele['Type'] == Namespaces::IMAGE) { + $richData['image'][(string) $ele['Id']] = (string) $ele['Target']; + } + } + } + $sheetCreated = false; if ($xmlWorkbookNS->sheets) { foreach ($xmlWorkbookNS->sheets->sheet as $eleSheet) { @@ -940,6 +954,28 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet break; case DataType::TYPE_ERROR: + + if (isset($cAttr->vm, $richData['image']['rId' . $cAttr->vm]) && !$useFormula) { + $imagePath = $dir . '/' . str_replace('../', '', $richData['image']['rId' . $cAttr->vm]); + $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing(); + $objDrawing + ->setPath( + 'zip://' . File::realpath($filename) . '#' . $imagePath, + false, + $zip + ) + ->setInCell(true) + ->setCoordinates($r) + ->setResizeProportional(false) + ->setWorksheet($docSheet); + + $value = $objDrawing; + $cellDataType = DataType::TYPE_NULL; + $c->t = DataType::TYPE_NULL; + + break; + } + if (!$useFormula) { $value = self::castToError($c); } else { diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Namespaces.php b/src/PhpSpreadsheet/Reader/Xlsx/Namespaces.php index 7356cd8829..b912221647 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Namespaces.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Namespaces.php @@ -121,4 +121,9 @@ class Namespaces const DYNAMIC_ARRAY = 'http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray'; const DYNAMIC_ARRAY_RICHDATA = 'http://schemas.microsoft.com/office/spreadsheetml/2017/richdata'; + + /** + * @see https://learn.microsoft.com/en-us/openspecs/office_standards/ms-xlsx/896934fd-8df7-43f4-b154-2d39371c270d + */ + const RICH_VALUE_DATA = 'http://schemas.microsoft.com/office/2017/06/relationships/rdRichValue'; } diff --git a/src/PhpSpreadsheet/Worksheet/Drawing.php b/src/PhpSpreadsheet/Worksheet/Drawing.php index 213351426e..920ac01851 100644 --- a/src/PhpSpreadsheet/Worksheet/Drawing.php +++ b/src/PhpSpreadsheet/Worksheet/Drawing.php @@ -24,6 +24,11 @@ class Drawing extends BaseDrawing */ private bool $isUrl; + /** + * check if the drawing is in cell so should be a richData image + */ + private bool $inCell = false; + /** * Create a new Drawing. */ @@ -246,4 +251,20 @@ public function getImageMimeType(): string return image_type_to_mime_type(self::IMAGE_TYPES_CONVERTION_MAP[$this->type]); } + + /** + * @param bool $inCell + * @return Drawing + */ + public function setInCell(bool $inCell): static + { + $this->inCell = $inCell; + + return $this; + } + + public function getInCell(): bool + { + return $this->inCell; + } } diff --git a/src/PhpSpreadsheet/Writer/Xlsx.php b/src/PhpSpreadsheet/Writer/Xlsx.php index a38c930e42..824619a9da 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx.php +++ b/src/PhpSpreadsheet/Writer/Xlsx.php @@ -440,6 +440,8 @@ public function save($filename, int $flags = 0): void if (($drawingCount > 0) || ($chartCount > 0)) { // Drawing relationships $zipContent['xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels'] = $this->getWriterPartRels()->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts); + // richData relationships + $zipContent['xl/richData/_rels/richValueRel.xml.rels'] = $this->getWriterPartRels()->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts); // Drawings $zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'] = $this->getWriterPartDrawing()->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts); diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/DrawingInCellTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/DrawingInCellTest.php new file mode 100644 index 0000000000..9816703b6f --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/DrawingInCellTest.php @@ -0,0 +1,36 @@ +load($file); + $sheet = $spreadsheet->getActiveSheet(); + $drawingCollection = $sheet->getDrawingCollection(); + self::assertCount(1, $drawingCollection); + + if ($drawingCollection[0] === null) { + self::fail('Unexpected null drawing'); + } else { + self::assertSame(IMAGETYPE_PNG, $drawingCollection[0]->getType()); + self::assertSame('B2', $drawingCollection[0]->getCoordinates()); + self::assertSame(0, $drawingCollection[0]->getOffsetX()); + self::assertSame(0, $drawingCollection[0]->getOffsetY()); + self::assertSame(296, $drawingCollection[0]->getWidth()); + self::assertSame(154, $drawingCollection[0]->getHeight()); + self::assertSame(296, $drawingCollection[0]->getImageWidth()); + self::assertSame(154, $drawingCollection[0]->getImageHeight()); + } + + $spreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/RichValueDrawingTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/RichValueDrawingTest.php new file mode 100644 index 0000000000..568c7ba9fc --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/RichValueDrawingTest.php @@ -0,0 +1,33 @@ +load($inputFilename); + $sheet = $spreadsheet->getActiveSheet(); + $drawingCollection = $sheet->getDrawingCollection(); + + // Save spreadsheet to file and read it back + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx'); + $spreadsheet->disconnectWorksheets(); + + $rsheet = $reloadedSpreadsheet->getActiveSheet(); + $drawingCollection2 = $rsheet->getDrawingCollection(); + + $reloadedSpreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/data/Reader/XLSX/drawing_in_cell.xlsx b/tests/data/Reader/XLSX/drawing_in_cell.xlsx new file mode 100644 index 0000000000..7ba1881fa1 Binary files /dev/null and b/tests/data/Reader/XLSX/drawing_in_cell.xlsx differ diff --git a/tests/data/Reader/XLSX/drawing_in_cell.xlsx:Zone.Identifier b/tests/data/Reader/XLSX/drawing_in_cell.xlsx:Zone.Identifier new file mode 100644 index 0000000000..43a1a01e37 --- /dev/null +++ b/tests/data/Reader/XLSX/drawing_in_cell.xlsx:Zone.Identifier @@ -0,0 +1,3 @@ +[ZoneTransfer] +ZoneId=3 +HostUrl=https://github.com/ diff --git a/tests/data/Writer/XLSX/drawing_in_cell.xlsx b/tests/data/Writer/XLSX/drawing_in_cell.xlsx new file mode 100644 index 0000000000..7ba1881fa1 Binary files /dev/null and b/tests/data/Writer/XLSX/drawing_in_cell.xlsx differ