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
20 changes: 20 additions & 0 deletions src/PhpSpreadsheet/Reader/BaseReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ abstract class BaseReader implements IReader
*/
protected bool $createBlankSheetIfNoneRead = false;

/**
* Enable drawing pass-through?
* Identifies whether the Reader should preserve unsupported drawing elements (shapes, grouped images, etc.)
* by storing the original XML for pass-through during write operations.
* When enabled, drawings cannot be modified programmatically but are preserved exactly.
*/
protected bool $enableDrawingPassThrough = false;

/**
* IReadFilter instance.
*/
Expand Down Expand Up @@ -125,6 +133,18 @@ public function setIncludeCharts(bool $includeCharts): self
return $this;
}

public function getEnableDrawingPassThrough(): bool
{
return $this->enableDrawingPassThrough;
}

public function setEnableDrawingPassThrough(bool $enableDrawingPassThrough): self
{
$this->enableDrawingPassThrough = $enableDrawingPassThrough;

return $this;
}

/** @return null|string[] */
public function getLoadSheetsOnly(): ?array
{
Expand Down
14 changes: 14 additions & 0 deletions src/PhpSpreadsheet/Reader/Xlsx.php
Original file line number Diff line number Diff line change
Expand Up @@ -1481,6 +1481,20 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
$xmlDrawing = $this->loadZipNoNamespace($fileDrawing, '');
$xmlDrawingChildren = $xmlDrawing->children(Namespaces::SPREADSHEET_DRAWING);

// Store drawing XML for pass-through if enabled
if ($this->enableDrawingPassThrough) {
$unparsedDrawings[$drawingRelId] = $xmlDrawing->asXML();
// Mark that pass-through is enabled for this sheet
$sheetCodeName = $docSheet->getCodeName();
if (!isset($unparsedLoadedData['sheets']) || !is_array($unparsedLoadedData['sheets'])) {
$unparsedLoadedData['sheets'] = [];
}
if (!isset($unparsedLoadedData['sheets'][$sheetCodeName]) || !is_array($unparsedLoadedData['sheets'][$sheetCodeName])) {
$unparsedLoadedData['sheets'][$sheetCodeName] = [];
}
$unparsedLoadedData['sheets'][$sheetCodeName]['drawingPassThroughEnabled'] = true;
}

if ($xmlDrawingChildren->oneCellAnchor) {
foreach ($xmlDrawingChildren->oneCellAnchor as $oneCellAnchor) {
$oneCellAnchor = self::testSimpleXml($oneCellAnchor);
Expand Down
32 changes: 32 additions & 0 deletions src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ class Drawing extends WriterPart
*/
public function writeDrawings(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, bool $includeCharts = false): string
{
// Try to use pass-through drawing XML if available
if ($passThroughXml = $this->getPassThroughDrawingXml($worksheet)) {
return $passThroughXml;
}

// Create XML writer
$objWriter = null;
if ($this->getParentWriter()->getUseDiskCaching()) {
Expand Down Expand Up @@ -592,4 +597,31 @@ private static function writeAttributeIf(XMLWriter $objWriter, ?bool $condition,
$objWriter->writeAttribute($attr, $val);
}
}

/**
* Get pass-through drawing XML if available.
*
* Returns the original drawing XML stored during load (when Reader pass-through was enabled).
* This preserves unsupported drawing elements (shapes, textboxes) that PhpSpreadsheet cannot parse.
*
* @return ?string The pass-through XML, or null if not available or should not be used
*/
private function getPassThroughDrawingXml(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet): ?string
{
$unparsedLoadedData = $worksheet->getParentOrThrow()->getUnparsedLoadedData();
if (!isset($unparsedLoadedData['sheets']) || !is_array($unparsedLoadedData['sheets'])) {
return null;
}

$codeName = $worksheet->getCodeName();
$sheetData = $unparsedLoadedData['sheets'][$codeName] ?? null;
// Only use pass-through XML if the Reader flag was explicitly enabled
if (!is_array($sheetData) || ($sheetData['drawingPassThroughEnabled'] ?? false) !== true || !is_array($sheetData['Drawings'] ?? null)) {
return null;
}

$firstDrawing = reset($sheetData['Drawings']);

return is_string($firstDrawing) ? $firstDrawing : null;
}
}
Loading