Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/PhpSpreadsheet/Cell/Cell.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Style\Protection;
use PhpOffice\PhpSpreadsheet\Style\Style;
use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use Stringable;
Expand Down Expand Up @@ -307,6 +308,12 @@ public function setValueExplicit(mixed $value, string $dataType = DataType::TYPE
$this->value = SharedDate::convertIsoDate($value);
$dataType = DataType::TYPE_NUMERIC;

break;
case DataType::TYPE_DRAWING_IN_CELL:
if ($value instanceof BaseDrawing) {
$this->value = $value;
}

break;
case DataType::TYPE_ERROR:
$this->value = DataType::checkErrorCode($value);
Expand Down
1 change: 1 addition & 0 deletions src/PhpSpreadsheet/Cell/DataType.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class DataType
const TYPE_INLINE = 'inlineStr';
const TYPE_ERROR = 'e';
const TYPE_ISO_DATE = 'd';
const TYPE_DRAWING_IN_CELL = 'drawingCell';

/**
* List of error codes.
Expand Down
9 changes: 9 additions & 0 deletions src/PhpSpreadsheet/Cell/DefaultValueBinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
use Stringable;

class DefaultValueBinder implements IValueBinder
Expand All @@ -33,6 +34,11 @@ public function bindValue(Cell $cell, mixed $value): bool
$value = $value->format('Y-m-d H:i:s');
} elseif ($value instanceof Stringable) {
$value = (string) $value;
} elseif ($value instanceof BaseDrawing) {
$value->setCoordinates($cell->getCoordinate());
$value->setResizeProportional(false);
$value->setInCell(true);
$value->setWorksheet($cell->getWorksheet());
} else {
throw new SpreadsheetException('Unable to bind unstringable ' . gettype($value));
}
Expand Down Expand Up @@ -68,6 +74,9 @@ public static function dataTypeForValue(mixed $value): string
if ($value instanceof RichText) {
return DataType::TYPE_INLINE;
}
if ($value instanceof BaseDrawing) {
return DataType::TYPE_DRAWING_IN_CELL;
}
if ($value instanceof Stringable) {
$value = (string) $value;
}
Expand Down
34 changes: 34 additions & 0 deletions src/PhpSpreadsheet/Reader/Xlsx.php
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,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) {
Expand Down Expand Up @@ -940,6 +953,27 @@ 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
);

$objDrawing->setCoordinates($r);
$objDrawing->setResizeProportional(false);
$objDrawing->setInCell(true);
$objDrawing->setWorksheet($docSheet);

$value = $objDrawing;
$cellDataType = DataType::TYPE_DRAWING_IN_CELL;
$c->t = DataType::TYPE_ERROR;

break;
}

if (!$useFormula) {
$value = self::castToError($c);
} else {
Expand Down
8 changes: 8 additions & 0 deletions src/PhpSpreadsheet/Reader/Xlsx/Namespaces.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,12 @@ 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';

const RELATIONSHIPS_RICH_VALUE_TYPES = 'http://schemas.microsoft.com/office/2017/06/relationships/rdRichValueTypes';

const RELATIONSHIPS_RICH_VALUE_STRUCTURE = 'http://schemas.microsoft.com/office/2017/06/relationships/rdRichValueStructure';

const RELATIONSHIPS_RICH_VALUE = 'http://schemas.microsoft.com/office/2017/06/relationships/rdRichValue';

const RELATIONSHIPS_RICH_VALUE_REL = 'http://schemas.microsoft.com/office/2022/10/relationships/richValueRel';
}
15 changes: 15 additions & 0 deletions src/PhpSpreadsheet/Spreadsheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,21 @@ public function hasRibbonBinObjects(): bool
return $this->ribbonBinObjects !== null;
}

/**
* This workbook has in cell images.
*/
public function hasInCellDrawings(): bool
{
$sheetCount = $this->getSheetCount();
for ($i = 0; $i < $sheetCount; ++$i) {
if ($this->getSheet($i)->getInCellDrawingCollection()->count() > 0) {
return true;
}
}

return false;
}

/**
* Check if a sheet with a specified code name already exists.
*
Expand Down
37 changes: 35 additions & 2 deletions src/PhpSpreadsheet/Worksheet/BaseDrawing.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ class BaseDrawing implements IComparable
*/
protected ?int $opacity = null;

protected bool $inCell = false;

protected int $index = 0;

/**
* Create a new BaseDrawing.
*/
Expand Down Expand Up @@ -203,8 +207,13 @@ public function setWorksheet(?Worksheet $worksheet = null, bool $overrideOld = f
if (!($this instanceof Drawing && $this->getPath() === '')) {
$this->worksheet->getCell($this->coordinates);
}
$this->worksheet->getDrawingCollection()
->append($this);
if ($this->inCell) {
$this->worksheet->getInCellDrawingCollection()
->append($this);
} else {
$this->worksheet->getDrawingCollection()
->append($this);
}
}
} else {
if ($overrideOld) {
Expand Down Expand Up @@ -572,4 +581,28 @@ public function getOpacity(): ?int
{
return $this->opacity;
}

public function setInCell(bool $inCell): self
{
$this->inCell = $inCell;

return $this;
}

public function isInCell(): ?bool
{
return $this->inCell;
}

public function setIndex(int $index): self
{
$this->index = $index;

return $this;
}

public function getIndex(): int
{
return $this->index;
}
}
28 changes: 27 additions & 1 deletion src/PhpSpreadsheet/Worksheet/Worksheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ class Worksheet
*/
private ArrayObject $drawingCollection;

/**
* Collection of drawings.
*
* @var ArrayObject<int, BaseDrawing>
*/
private ArrayObject $inCellDrawingCollection;

/**
* Collection of Chart objects.
*
Expand Down Expand Up @@ -334,6 +341,8 @@ public function __construct(?Spreadsheet $parent = null, string $title = 'Worksh
$this->sheetView = new SheetView();
// Drawing collection
$this->drawingCollection = new ArrayObject();
// In Cell Drawing collection
$this->inCellDrawingCollection = new ArrayObject();
// Chart collection
$this->chartCollection = new ArrayObject();
// Protection
Expand Down Expand Up @@ -371,7 +380,7 @@ public function __destruct()
?->clearCalculationCacheForWorksheet($this->title);

$this->disconnectCells();
unset($this->rowDimensions, $this->columnDimensions, $this->tableCollection, $this->drawingCollection, $this->chartCollection, $this->autoFilter);
unset($this->rowDimensions, $this->columnDimensions, $this->tableCollection, $this->drawingCollection, $this->inCellDrawingCollection, $this->chartCollection, $this->autoFilter);
}

/**
Expand Down Expand Up @@ -519,6 +528,16 @@ public function getDrawingCollection(): ArrayObject
return $this->drawingCollection;
}

/**
* Get collection of drawings.
*
* @return ArrayObject<int, BaseDrawing>
*/
public function getInCellDrawingCollection(): ArrayObject
{
return $this->inCellDrawingCollection;
}

/**
* Get collection of charts.
*
Expand Down Expand Up @@ -3730,6 +3749,13 @@ public function __clone()
$newDrawing = clone $item;
$newDrawing->setWorksheet($this);
}
} elseif ($key === 'inCellDrawingCollection') {
$currentCollection = $this->inCellDrawingCollection;
$this->inCellDrawingCollection = new ArrayObject();
foreach ($currentCollection as $item) {
$newDrawing = clone $item;
$newDrawing->setWorksheet($this);
}
} elseif ($key === 'tableCollection') {
$currentCollection = $this->tableCollection;
$this->tableCollection = new ArrayObject();
Expand Down
16 changes: 15 additions & 1 deletion src/PhpSpreadsheet/Writer/Xlsx.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Rels;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsRibbon;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsVBA;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RichDataDrawing;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\StringTable;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Style;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Table;
Expand Down Expand Up @@ -329,9 +330,22 @@ public function save($filename, int $flags = 0): void

/** @var string[] */
$zipContent = [];
$richDataCount = 0;

if ($this->spreadSheet->hasInCellDrawings()) {
$richDataDrawing = new RichDataDrawing();
$richDataFiles = $richDataDrawing->generateFiles($this->spreadSheet);
$richDataCount = count($richDataFiles);

// Add all Rich Data files to ZIP
foreach ($richDataFiles as $path => $content) {
$zipContent[$path] = $content;
}
}

// Add [Content_Types].xml to ZIP file
$zipContent['[Content_Types].xml'] = $this->getWriterPartContentTypes()->writeContentTypes($this->spreadSheet, $this->includeCharts);
$metadataData = (new Xlsx\Metadata($this))->writeMetadata();
$metadataData = (new Xlsx\Metadata($this))->writeMetadata($richDataCount);
if ($metadataData !== '') {
$zipContent['xl/metadata.xml'] = $metadataData;
}
Expand Down
8 changes: 8 additions & 0 deletions src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ public function writeContentTypes(Spreadsheet $spreadsheet, bool $includeCharts
$this->writeDefaultContentType($objWriter, $extension, $mimeType);
}
}

if ($spreadsheet->hasInCellDrawings()) {
$this->writeOverrideContentType($objWriter, '/xl/richData/richValueRel.xml', 'application/vnd.ms-excel.richvaluerel+xml');
$this->writeOverrideContentType($objWriter, '/xl/richData/rdrichvalue.xml', 'application/vnd.ms-excel.rdrichvalue+xml');
$this->writeOverrideContentType($objWriter, '/xl/richData/rdrichvaluestructure.xml', 'application/vnd.ms-excel.rdrichvaluestructure+xml');
$this->writeOverrideContentType($objWriter, '/xl/richData/rdRichValueTypes.xml', 'application/vnd.ms-excel.rdrichvaluetypes+xml');
}

if ($spreadsheet->hasRibbonBinObjects()) {
// Some additional objects in the ribbon ?
// we need to write "Extension" but not already write for media content
Expand Down
6 changes: 6 additions & 0 deletions src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,12 @@ public function allDrawings(Spreadsheet $spreadsheet): array
while ($iterator->valid()) {
$aDrawings[] = $iterator->current();

$iterator->next();
}
$iterator = $spreadsheet->getSheet($i)->getInCellDrawingCollection()->getIterator();
while ($iterator->valid()) {
$aDrawings[] = $iterator->current();

$iterator->next();
}
}
Expand Down
Loading