Skip to content

Commit

Permalink
fix error: Images inserted to cells in Excel 365 are not detected
Browse files Browse the repository at this point in the history
  • Loading branch information
aVadim483 committed Oct 12, 2024
1 parent 3184b7b commit c938cc3
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 23 deletions.
152 changes: 146 additions & 6 deletions src/FastExcelReader/Excel.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class Excel implements InterfaceBookReader

protected array $styles = [];

protected array $valueMetadataImages = [];

/** @var Sheet[] */
protected array $sheets = [];

Expand All @@ -64,6 +66,9 @@ class Excel implements InterfaceBookReader

protected ?array $themeColors = null;

protected int $countImages = -1; // -1 - unknown


/**
* Excel constructor
*
Expand Down Expand Up @@ -176,6 +181,9 @@ protected function _prepare(string $file)
if (isset($this->relations['styles'])) {
$this->_loadStyles(reset($this->relations['styles']));
}
if (isset($this->relations['sheetMetadata'], $this->relations['richValueRel'])) {
$this->_loadMetadataImages(reset($this->relations['sheetMetadata']), reset($this->relations['richValueRel']));
}

if ($this->sheets) {
// set current sheet
Expand Down Expand Up @@ -332,6 +340,99 @@ protected function _loadStyles(string $innerFile = null)
$this->xmlReader->close();
}

/**
* @param string|null $metadataFile
*/
protected function _loadMetadataImages(string $metadataFile, string $richValueRelFile)
{
$this->xmlReader->openZip($metadataFile);
$metadataTypesCount = 0;
$metadataTypes = [];
while ($this->xmlReader->read()) {
if ($this->xmlReader->name === 'metadataType') {
if ($this->xmlReader->nodeType === \XMLReader::ELEMENT) {
$metadataTypesCount++;
if ((string)$this->xmlReader->getAttribute('name') === 'XLRICHVALUE') {
// we need only <metadataType name="XLRICHVALUE" ...>
$metadataTypes[$metadataTypesCount] = 'XLRICHVALUE';
}
}
else {
break;
}
}
}
$futureMetadata = [];
while ($this->xmlReader->read()) {
if ($this->xmlReader->name === 'futureMetadata') {
if ($this->xmlReader->nodeType === \XMLReader::ELEMENT && (string)$this->xmlReader->getAttribute('name') === 'XLRICHVALUE') {
while ($this->xmlReader->read()) {
if ($this->xmlReader->name === 'xlrd:rvb') {
$futureMetadata[] = (int)$this->xmlReader->getAttribute('i');
}
elseif ($this->xmlReader->name === 'futureMetadata' && $this->xmlReader->nodeType === \XMLReader::END_ELEMENT) {
break 2;
}
}
}
elseif ($this->xmlReader->nodeType === \XMLReader::END_ELEMENT) {
break;
}
}
}

while ($this->xmlReader->read()) {
if ($this->xmlReader->name === 'rc') {
$type = (int)$this->xmlReader->getAttribute('t');
$value = (int)$this->xmlReader->getAttribute('v');
if (isset($metadataTypes[$type])) { // metadataType name="XLRICHVALUE"
if (isset($futureMetadata[$value])) {
$this->valueMetadataImages[] = ['i' => $futureMetadata[$value]];
}
}
}
}
$this->xmlReader->close();

$this->xmlReader->openZip($richValueRelFile);
$count = 0;
while ($this->xmlReader->read()) {
if ($this->xmlReader->name === 'rel' && ($rId = $this->xmlReader->getAttribute('r:id'))) {
$this->valueMetadataImages[$count++]['r_id'] = $rId;
}
}
$this->xmlReader->close();

$images = [];
$xmlRels = 'xl/richData/_rels/richValueRel.xml.rels';
$this->xmlReader->openZip($xmlRels);
while ($this->xmlReader->read()) {
if ($this->xmlReader->name === 'Relationship' && $this->xmlReader->nodeType === \XMLReader::ELEMENT && ($Id = (string)$this->xmlReader->getAttribute('Id'))) {
if (substr((string)$this->xmlReader->getAttribute('Type'), -6) === '/image') {
$images[$Id] = (string)$this->xmlReader->getAttribute('Target');
}
}
}
$this->xmlReader->close();

foreach ($this->valueMetadataImages as $index => $metadataImage) {
$rId = $this->valueMetadataImages[$index]['r_id'];
if (isset($images[$rId])) {
$this->valueMetadataImages[$index]['file_name'] = str_replace('../media/', 'xl/media/', $images[$rId]);
}
}
}

/**
* @param int $vmIndex
*
* @return string|null
*/
public function metadataImage(int $vmIndex): ?string
{
return $this->valueMetadataImages[$vmIndex - 1]['file_name'] ?? null;
}

/**
* @param int|null $numFmtId
* @param string $pattern
Expand Down Expand Up @@ -1191,21 +1292,41 @@ public function hasImages(): bool
return false;
}

/**
* @return array
*/
public function mediaImageFiles(): array
{
$result = [];
if (!empty($this->relations['media'])) {
foreach ($this->relations['media'] as $mediaFile) {
$extension = strtolower(pathinfo($mediaFile, PATHINFO_EXTENSION));
if (in_array($extension, ['jpg', 'jpeg', 'png', 'bmp', 'ico', 'webp', 'tif', 'tiff', 'gif'])) {
$result[] = basename($mediaFile);
}
}
}

return $result;
}

/**
* Returns the total count of images in the workbook
*
* @return int
*/
public function countImages(): int
{
$result = 0;
if ($this->hasDrawings()) {
foreach ($this->sheets as $sheet) {
$result += $sheet->countImages();
if ($this->countImages === -1) {
$this->countImages = 0;
if ($this->hasDrawings() || $this->mediaImageFiles()) {
foreach ($this->sheets as $sheet) {
$this->countImages += $sheet->countImages();
}
}
}

return $result;
return $this->countImages;
}

/**
Expand All @@ -1216,7 +1337,7 @@ public function countImages(): int
public function getImageList(): array
{
$result = [];
if ($this->hasDrawings()) {
if ($this->countImages()) {
foreach ($this->sheets as $sheet) {
$result[$sheet->name()] = $sheet->getImageList();
}
Expand All @@ -1225,6 +1346,25 @@ public function getImageList(): array
return $result;
}

/**
* @return bool
*/
public function hasExtraImages(): bool
{
$drawingImageFiles = [];
if ($this->hasDrawings()) {
foreach ($this->sheets as $sheet) {
$imageFiles = $sheet->_getDrawingsImageFiles();
if ($imageFiles) {
$drawingImageFiles += $imageFiles;
}
}
}
$imageFiles = $this->mediaImageFiles();

return (count($imageFiles) !== count($drawingImageFiles));
}

/**
* @return array
*/
Expand Down
Loading

0 comments on commit c938cc3

Please sign in to comment.