Xlsx image background in comments #1547 (#2422)

* XLSX Image background in comments

* XLSX-Image-Background-In-Comments (#1547)

* Test fixes, convertion for comment sizes from px to pt, fix for setting image sizes from zip, set image type

* Merge remote-tracking branch 'origin/XLSX-Image-Background-In-Comments' into XLSX-Image-Background-In-Comments

* Tests to check reloaded document.

Co-authored-by: Burkov Sergey
This commit is contained in:
leo-bsv 2021-12-17 17:10:59 +03:00 committed by GitHub
parent ea74c96e98
commit a7f687fe5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 737 additions and 16 deletions

View File

@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
- Xlsx Writer Support for WMF Files [#2339](https://github.com/PHPOffice/PhpSpreadsheet/issues/2339) - Xlsx Writer Support for WMF Files [#2339](https://github.com/PHPOffice/PhpSpreadsheet/issues/2339)
- Use standard temporary file for internal use of HTMLPurifier [#2383](https://github.com/PHPOffice/PhpSpreadsheet/issues/2383) - Use standard temporary file for internal use of HTMLPurifier [#2383](https://github.com/PHPOffice/PhpSpreadsheet/issues/2383)
- Ability to add a picture to the background of the comment. Supports four image formats: png, jpeg, gif, bmp. A Comment method setSizeAsBackgroundImage for changing the size of a comment to the size of an background image. [Issue #1547](https://github.com/PHPOffice/PhpSpreadsheet/issues/1547) [PR #2422](https://github.com/PHPOffice/PhpSpreadsheet/pull/2422)
### Changed ### Changed

View File

@ -1255,7 +1255,7 @@
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
<td style="padding-left: 1em;">Rich Text</td> <td style="padding-left: 2em;">Rich Text</td>
<td style="text-align: center; color: red;"><sup>2</sup></td> <td style="text-align: center; color: red;"><sup>2</sup></td>
<td style="text-align: center; color: green;"></td> <td style="text-align: center; color: green;"></td>
<td style="text-align: center; color: red;"></td> <td style="text-align: center; color: red;"></td>
@ -1273,7 +1273,7 @@
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
<td style="padding-left: 1em;">Alignment</td> <td style="padding-left: 2em;">Alignment</td>
<td style="text-align: center; color: red;"><sup>3</sup></td> <td style="text-align: center; color: red;"><sup>3</sup></td>
<td style="text-align: center; color: red;"></td> <td style="text-align: center; color: red;"></td>
<td style="text-align: center; color: red;"></td> <td style="text-align: center; color: red;"></td>
@ -1290,6 +1290,24 @@
<td></td> <td></td>
<td></td> <td></td>
</tr> </tr>
<tr>
<td style="padding-left: 2em;">Background Image</td>
<td style="text-align: center; color: red;"></td>
<td style="text-align: center; color: green;"></td>
<td style="text-align: center; color: red;"></td>
<td style="text-align: center; color: red;"></td>
<td style="text-align: center; color: red;"></td>
<td style="text-align: center; color: red;"></td>
<td style="text-align: center; color: red;"></td>
<td style="text-align: center; color: red;"></td>
<td style="text-align: center; color: green;"></td>
<td style="text-align: center; color: red;"></td>
<td style="text-align: center; color: red;"></td>
<td style="text-align: center; color: red;"></td>
<td style="text-align: center; color: red;"></td>
<td>$comment->getBackgroundImage()</td>
<td>$comment->setBackgroundImage()</td>
</tr>
<tr> <tr>
<td><strong>Cell Validation</strong></td> <td><strong>Cell Validation</strong></td>
<td style="text-align: center; color: green;"></td> <td style="text-align: center; color: green;"></td>

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -957,9 +957,26 @@ $spreadsheet->getActiveSheet()
->getComment('E11') ->getComment('E11')
->getText()->createTextRun('Total amount on the current invoice, excluding VAT.'); ->getText()->createTextRun('Total amount on the current invoice, excluding VAT.');
``` ```
![08-cell-comment.png](./images/08-cell-comment.png) ![08-cell-comment.png](./images/08-cell-comment.png)
## Add a comment with background image to a cell
To add a comment with background image to a cell, use the following code:
```php
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValue('B5', 'Gibli Chromo');
// Add png image to comment background
$drawing = new Drawing();
$drawing->setName('Gibli Chromo');
$drawing->setPath('/tmp/gibli_chromo.png');
$comment = $sheet->getComment('B5');
$comment->setBackgroundImage($drawing);
// Set the size of the comment equal to the size of the image
$comment->setSizeAsBackgroundImage();
```
![08-cell-comment-with-image.png](./images/08-cell-comment-with-image.png)
## Apply autofilter to a range of cells ## Apply autofilter to a range of cells
To apply an autofilter to a range of cells, use the following code: To apply an autofilter to a range of cells, use the following code:

View File

@ -6045,11 +6045,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Worksheet/Column.php path: src/PhpSpreadsheet/Worksheet/Column.php
-
message: "#^Cannot use array destructuring on array\\|false\\.$#"
count: 2
path: src/PhpSpreadsheet/Worksheet/Drawing.php
- -
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Drawing\\\\Shadow\\:\\:\\$color \\(PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Color\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Color\\|null\\.$#" message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Drawing\\\\Shadow\\:\\:\\$color \\(PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Color\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Color\\|null\\.$#"
count: 1 count: 1

View File

@ -2,10 +2,13 @@
namespace PhpOffice\PhpSpreadsheet; namespace PhpOffice\PhpSpreadsheet;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\Helper\Size; use PhpOffice\PhpSpreadsheet\Helper\Size;
use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\Shared\Drawing as SharedDrawing;
use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Color; use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
class Comment implements IComparable class Comment implements IComparable
{ {
@ -72,6 +75,13 @@ class Comment implements IComparable
*/ */
private $alignment; private $alignment;
/**
* Background image in comment.
*
* @var Drawing
*/
private $backgroundImage;
/** /**
* Create a new Comment. * Create a new Comment.
*/ */
@ -82,6 +92,7 @@ class Comment implements IComparable
$this->text = new RichText(); $this->text = new RichText();
$this->fillColor = new Color('FFFFFFE1'); $this->fillColor = new Color('FFFFFFE1');
$this->alignment = Alignment::HORIZONTAL_GENERAL; $this->alignment = Alignment::HORIZONTAL_GENERAL;
$this->backgroundImage = new Drawing();
} }
/** /**
@ -273,6 +284,7 @@ class Comment implements IComparable
($this->visible ? 1 : 0) . ($this->visible ? 1 : 0) .
$this->fillColor->getHashCode() . $this->fillColor->getHashCode() .
$this->alignment . $this->alignment .
($this->hasBackgroundImage() ? $this->backgroundImage->getHashCode() : '') .
__CLASS__ __CLASS__
); );
} }
@ -299,4 +311,52 @@ class Comment implements IComparable
{ {
return $this->text->getPlainText(); return $this->text->getPlainText();
} }
/**
* Check is background image exists.
*/
public function hasBackgroundImage(): bool
{
$path = $this->backgroundImage->getPath();
if (empty($path)) {
return false;
}
return getimagesize($path) !== false;
}
/**
* Returns background image.
*/
public function getBackgroundImage(): Drawing
{
return $this->backgroundImage;
}
/**
* Sets background image.
*/
public function setBackgroundImage(Drawing $objDrawing): self
{
if (!array_key_exists($objDrawing->getType(), Drawing::IMAGE_TYPES_CONVERTION_MAP)) {
throw new PhpSpreadsheetException('Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
}
$this->backgroundImage = $objDrawing;
return $this;
}
/**
* Sets size of comment as size of background image.
*/
public function setSizeAsBackgroundImage(): self
{
if ($this->hasBackgroundImage()) {
$this->setWidth(SharedDrawing::pixelsToPoints($this->backgroundImage->getWidth()) . 'pt');
$this->setHeight(SharedDrawing::pixelsToPoints($this->backgroundImage->getHeight()) . 'pt');
}
return $this;
}
} }

View File

@ -984,6 +984,19 @@ class Xlsx extends BaseReader
continue; continue;
} }
// Locate VML drawings image relations
$drowingImages = [];
$VMLDrawingsRelations = dirname($relPath) . '/_rels/' . basename($relPath) . '.rels';
if ($zip->locateName($VMLDrawingsRelations)) {
$relsVMLDrawing = $this->loadZip($VMLDrawingsRelations, Namespaces::RELATIONSHIPS);
foreach ($relsVMLDrawing->Relationship as $elex) {
$ele = self::getAttributes($elex);
if ($ele['Type'] == Namespaces::IMAGE) {
$drowingImages[(string) $ele['Id']] = (string) $ele['Target'];
}
}
}
$shapes = self::xpathNoFalse($vmlCommentsFile, '//v:shape'); $shapes = self::xpathNoFalse($vmlCommentsFile, '//v:shape');
foreach ($shapes as $shape) { foreach ($shapes as $shape) {
$shape->registerXPathNamespace('v', Namespaces::URN_VML); $shape->registerXPathNamespace('v', Namespaces::URN_VML);
@ -993,6 +1006,8 @@ class Xlsx extends BaseReader
$fillColor = strtoupper(substr((string) $shape['fillcolor'], 1)); $fillColor = strtoupper(substr((string) $shape['fillcolor'], 1));
$column = null; $column = null;
$row = null; $row = null;
$fillImageRelId = null;
$fillImageTitle = '';
$clientData = $shape->xpath('.//x:ClientData'); $clientData = $shape->xpath('.//x:ClientData');
if (is_array($clientData) && !empty($clientData)) { if (is_array($clientData) && !empty($clientData)) {
@ -1011,10 +1026,39 @@ class Xlsx extends BaseReader
} }
} }
$fillImageRelNode = $shape->xpath('.//v:fill/@o:relid');
if (is_array($fillImageRelNode) && !empty($fillImageRelNode)) {
$fillImageRelNode = $fillImageRelNode[0];
if (isset($fillImageRelNode['relid'])) {
$fillImageRelId = (string) $fillImageRelNode['relid'];
}
}
$fillImageTitleNode = $shape->xpath('.//v:fill/@o:title');
if (is_array($fillImageTitleNode) && !empty($fillImageTitleNode)) {
$fillImageTitleNode = $fillImageTitleNode[0];
if (isset($fillImageTitleNode['title'])) {
$fillImageTitle = (string) $fillImageTitleNode['title'];
}
}
if (($column !== null) && ($row !== null)) { if (($column !== null) && ($row !== null)) {
// Set comment properties // Set comment properties
$comment = $docSheet->getCommentByColumnAndRow($column + 1, $row + 1); $comment = $docSheet->getCommentByColumnAndRow($column + 1, $row + 1);
$comment->getFillColor()->setRGB($fillColor); $comment->getFillColor()->setRGB($fillColor);
if (isset($drowingImages[$fillImageRelId])) {
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$objDrawing->setName($fillImageTitle);
$imagePath = str_replace('../', 'xl/', $drowingImages[$fillImageRelId]);
$objDrawing->setPath(
'zip://' . File::realpath($filename) . '#' . $imagePath,
true,
$zip
);
$comment->setBackgroundImage($objDrawing);
}
// Parse style // Parse style
$styleArray = explode(';', str_replace(' ', '', $style)); $styleArray = explode(';', str_replace(' ', '', $style));

View File

@ -106,6 +106,13 @@ class BaseDrawing implements IComparable
*/ */
private $hyperlink; private $hyperlink;
/**
* Image type.
*
* @var int
*/
protected $type;
/** /**
* Create a new BaseDrawing. * Create a new BaseDrawing.
*/ */
@ -123,6 +130,7 @@ class BaseDrawing implements IComparable
$this->resizeProportional = true; $this->resizeProportional = true;
$this->rotation = 0; $this->rotation = 0;
$this->shadow = new Drawing\Shadow(); $this->shadow = new Drawing\Shadow();
$this->type = IMAGETYPE_UNKNOWN;
// Set image index // Set image index
++self::$imageCounter; ++self::$imageCounter;
@ -526,4 +534,28 @@ class BaseDrawing implements IComparable
{ {
return $this->hyperlink; return $this->hyperlink;
} }
/**
* Set Fact Sizes and Type of Image.
*/
protected function setSizesAndType(string $path): void
{
if ($this->width == 0 && $this->height == 0 && $this->type == IMAGETYPE_UNKNOWN) {
$imageData = getimagesize($path);
if (is_array($imageData)) {
$this->width = $imageData[0];
$this->height = $imageData[1];
$this->type = $imageData[2];
}
}
}
/**
* Get Image Type.
*/
public function getType(): int
{
return $this->type;
}
} }

View File

@ -3,9 +3,17 @@
namespace PhpOffice\PhpSpreadsheet\Worksheet; namespace PhpOffice\PhpSpreadsheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use ZipArchive;
class Drawing extends BaseDrawing class Drawing extends BaseDrawing
{ {
const IMAGE_TYPES_CONVERTION_MAP = [
IMAGETYPE_GIF => IMAGETYPE_PNG,
IMAGETYPE_JPEG => IMAGETYPE_JPEG,
IMAGETYPE_PNG => IMAGETYPE_PNG,
IMAGETYPE_BMP => IMAGETYPE_PNG,
];
/** /**
* Path. * Path.
* *
@ -63,6 +71,20 @@ class Drawing extends BaseDrawing
return $exploded[count($exploded) - 1]; return $exploded[count($exploded) - 1];
} }
/**
* Get full filepath to store drawing in zip archive.
*
* @return string
*/
public function getMediaFilename()
{
if (!array_key_exists($this->type, self::IMAGE_TYPES_CONVERTION_MAP)) {
throw new PhpSpreadsheetException('Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
}
return sprintf('image%d%s', $this->getImageIndex(), $this->getImageFileExtensionForSave());
}
/** /**
* Get Path. * Get Path.
* *
@ -78,10 +100,11 @@ class Drawing extends BaseDrawing
* *
* @param string $path File path * @param string $path File path
* @param bool $verifyFile Verify file * @param bool $verifyFile Verify file
* @param ZipArchive $zip Zip archive instance
* *
* @return $this * @return $this
*/ */
public function setPath($path, $verifyFile = true) public function setPath($path, $verifyFile = true, $zip = null)
{ {
if ($verifyFile) { if ($verifyFile) {
// Check if a URL has been passed. https://stackoverflow.com/a/2058596/1252979 // Check if a URL has been passed. https://stackoverflow.com/a/2058596/1252979
@ -94,18 +117,18 @@ class Drawing extends BaseDrawing
if ($filePath) { if ($filePath) {
file_put_contents($filePath, $imageContents); file_put_contents($filePath, $imageContents);
if (file_exists($filePath)) { if (file_exists($filePath)) {
if ($this->width == 0 && $this->height == 0) { $this->setSizesAndType($filePath);
// Get width/height
[$this->width, $this->height] = getimagesize($filePath);
}
unlink($filePath); unlink($filePath);
} }
} }
} elseif (file_exists($path)) { } elseif (file_exists($path)) {
$this->path = $path; $this->path = $path;
if ($this->width == 0 && $this->height == 0) { $this->setSizesAndType($path);
// Get width/height } elseif ($zip instanceof ZipArchive) {
[$this->width, $this->height] = getimagesize($path); $zipPath = explode('#', $path)[1];
if ($zip->locateName($zipPath) !== false) {
$this->path = $path;
$this->setSizesAndType($path);
} }
} else { } else {
throw new PhpSpreadsheetException("File $path not found!"); throw new PhpSpreadsheetException("File $path not found!");
@ -150,4 +173,42 @@ class Drawing extends BaseDrawing
__CLASS__ __CLASS__
); );
} }
/**
* Get Image Type for Save.
*/
public function getImageTypeForSave(): int
{
if (!array_key_exists($this->type, self::IMAGE_TYPES_CONVERTION_MAP)) {
throw new PhpSpreadsheetException('Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
}
return self::IMAGE_TYPES_CONVERTION_MAP[$this->type];
}
/**
* Get Image file extention for Save.
*/
public function getImageFileExtensionForSave(bool $includeDot = true): string
{
if (!array_key_exists($this->type, self::IMAGE_TYPES_CONVERTION_MAP)) {
throw new PhpSpreadsheetException('Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
}
$result = image_type_to_extension(self::IMAGE_TYPES_CONVERTION_MAP[$this->type], $includeDot);
return is_string($result) ? $result : '';
}
/**
* Get Image mime type.
*/
public function getImageMimeType(): string
{
if (!array_key_exists($this->type, self::IMAGE_TYPES_CONVERTION_MAP)) {
throw new PhpSpreadsheetException('Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
}
return image_type_to_mime_type(self::IMAGE_TYPES_CONVERTION_MAP[$this->type]);
}
} }

View File

@ -440,11 +440,22 @@ class Xlsx extends BaseWriter
// Add comment relationship parts // Add comment relationship parts
if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) { if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
// VML Comments relationships
$zipContent['xl/drawings/_rels/vmlDrawing' . ($i + 1) . '.vml.rels'] = $this->getWriterPartRels()->writeVMLDrawingRelationships($this->spreadSheet->getSheet($i));
// VML Comments // VML Comments
$zipContent['xl/drawings/vmlDrawing' . ($i + 1) . '.vml'] = $this->getWriterPartComments()->writeVMLComments($this->spreadSheet->getSheet($i)); $zipContent['xl/drawings/vmlDrawing' . ($i + 1) . '.vml'] = $this->getWriterPartComments()->writeVMLComments($this->spreadSheet->getSheet($i));
// Comments // Comments
$zipContent['xl/comments' . ($i + 1) . '.xml'] = $this->getWriterPartComments()->writeComments($this->spreadSheet->getSheet($i)); $zipContent['xl/comments' . ($i + 1) . '.xml'] = $this->getWriterPartComments()->writeComments($this->spreadSheet->getSheet($i));
// Media
foreach ($this->spreadSheet->getSheet($i)->getComments() as $comment) {
if ($comment->hasBackgroundImage()) {
$image = $comment->getBackgroundImage();
$zipContent['xl/media/' . $image->getMediaFilename()] = $this->processDrawing($image);
}
}
} }
// Add unparsed relationship parts // Add unparsed relationship parts
@ -668,4 +679,52 @@ class Xlsx extends BaseWriter
$this->addZipFile($path, $content); $this->addZipFile($path, $content);
} }
} }
/**
* @return mixed
*/
private function processDrawing(WorksheetDrawing $drawing)
{
$data = null;
$filename = $drawing->getPath();
$imageData = getimagesize($filename);
if (is_array($imageData)) {
switch ($imageData[2]) {
case 1: // GIF, not supported by BIFF8, we convert to PNG
$image = imagecreatefromgif($filename);
if ($image !== false) {
ob_start();
imagepng($image);
$data = ob_get_contents();
ob_end_clean();
}
break;
case 2: // JPEG
$data = file_get_contents($filename);
break;
case 3: // PNG
$data = file_get_contents($filename);
break;
case 6: // Windows DIB (BMP), we convert to PNG
$image = imagecreatefrombmp($filename);
if ($image !== false) {
ob_start();
imagepng($image);
$data = ob_get_contents();
ob_end_clean();
}
break;
}
}
return $data;
}
} }

View File

@ -178,6 +178,12 @@ class Comments extends WriterPart
// v:fill // v:fill
$objWriter->startElement('v:fill'); $objWriter->startElement('v:fill');
$objWriter->writeAttribute('color2', '#' . $comment->getFillColor()->getRGB()); $objWriter->writeAttribute('color2', '#' . $comment->getFillColor()->getRGB());
if ($comment->hasBackgroundImage()) {
$bgImage = $comment->getBackgroundImage();
$objWriter->writeAttribute('o:relid', 'rId' . $bgImage->getImageIndex());
$objWriter->writeAttribute('o:title', $bgImage->getName());
$objWriter->writeAttribute('type', 'frame');
}
$objWriter->endElement(); $objWriter->endElement();
// v:shadow // v:shadow

View File

@ -158,6 +158,23 @@ class ContentTypes extends WriterPart
} }
} }
} }
if (count($spreadsheet->getSheet($i)->getComments()) > 0) {
foreach ($spreadsheet->getSheet($i)->getComments() as $comment) {
if (!$comment->hasBackgroundImage()) {
continue;
}
$bgImage = $comment->getBackgroundImage();
$bgImageExtentionKey = strtolower($bgImage->getImageFileExtensionForSave(false));
if (!isset($aMediaContentTypes[$bgImageExtentionKey])) {
$aMediaContentTypes[$bgImageExtentionKey] = $bgImage->getImageMimeType();
$this->writeDefaultContentType($objWriter, $bgImageExtentionKey, $aMediaContentTypes[$bgImageExtentionKey]);
}
}
}
} }
// unparsed defaults // unparsed defaults

View File

@ -396,6 +396,43 @@ class Rels extends WriterPart
return $objWriter->getData(); return $objWriter->getData();
} }
public function writeVMLDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet): string
{
// Create XML writer
$objWriter = null;
if ($this->getParentWriter()->getUseDiskCaching()) {
$objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
} else {
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
}
// XML header
$objWriter->startDocument('1.0', 'UTF-8', 'yes');
// Relationships
$objWriter->startElement('Relationships');
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
// Loop through images and write relationships
foreach ($worksheet->getComments() as $comment) {
if (!$comment->hasBackgroundImage()) {
continue;
}
$bgImage = $comment->getBackgroundImage();
$this->writeRelationship(
$objWriter,
$bgImage->getImageIndex(),
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
'../media/' . $bgImage->getMediaFilename()
);
}
$objWriter->endElement();
return $objWriter->getData();
}
/** /**
* Write Override content type. * Write Override content type.
* *

View File

@ -2,9 +2,13 @@
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx; namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Comment;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Shared\File; use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional; use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
class DrawingsTest extends AbstractFunctional class DrawingsTest extends AbstractFunctional
@ -56,4 +60,374 @@ class DrawingsTest extends AbstractFunctional
// Fake assert. The only thing we need is to ensure the file is loaded without exception // Fake assert. The only thing we need is to ensure the file is loaded without exception
self::assertNotNull($reloadedSpreadsheet); self::assertNotNull($reloadedSpreadsheet);
} }
/**
* Test save and load XLSX file with drawing in comment.
*/
public function testSaveLoadWithDrawingInComment(): void
{
// Read spreadsheet from file
$originalFileName = 'tests/data/Writer/XLSX/drawing_in_comment.xlsx';
$originalFile = file_get_contents($originalFileName);
$tempFileName = File::sysGetTempDir() . '/drawing_in_comment.xlsx';
file_put_contents($tempFileName, $originalFile);
// Load native xlsx file with drawing in comment background
$reader = new Xlsx();
$spreadsheet = $reader->load($tempFileName);
$sheet = $spreadsheet->getActiveSheet();
$comment = $sheet->getComment('A1');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getWidth(), 178);
self::assertEquals($comment->getBackgroundImage()->getHeight(), 140);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_JPEG);
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save($tempFileName);
$reloadedSpreadsheet = $reader->load($tempFileName);
unlink($tempFileName);
self::assertNotNull($reloadedSpreadsheet);
}
/**
* Test save and load XLSX file with drawing in comment, image in BMP/GIF format saved as PNG.
*/
public function testDrawingInCommentImageFormatsConversions(): void
{
$reader = new Xlsx();
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// Add gif image to comment background
$sheet->setCellValue('A1', '.gif');
$drawing = new Drawing();
$drawing->setName('Green Square');
$drawing->setPath('tests/data/Writer/XLSX/green_square.gif');
self::assertEquals($drawing->getWidth(), 150);
self::assertEquals($drawing->getHeight(), 150);
$comment = $sheet->getComment('A1');
$comment->setBackgroundImage($drawing);
$comment->setSizeAsBackgroundImage();
self::assertEquals($comment->getWidth(), '112.5pt');
self::assertEquals($comment->getHeight(), '112.5pt');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_GIF);
// Add bmp image to comment background
$sheet->setCellValue('A2', '.bmp 16 colors');
$drawing = new Drawing();
$drawing->setName('Yellow Square');
$drawing->setPath('tests/data/Writer/XLSX/yellow_square_16.bmp');
self::assertEquals($drawing->getWidth(), 70);
self::assertEquals($drawing->getHeight(), 70);
$comment = $sheet->getComment('A2');
$comment->setBackgroundImage($drawing);
$comment->setSizeAsBackgroundImage();
self::assertEquals($comment->getWidth(), '52.5pt');
self::assertEquals($comment->getHeight(), '52.5pt');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_BMP);
// Write file
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$tempFileName = File::sysGetTempDir() . '/drawings_in_comments_conversions.xlsx';
$writer->save($tempFileName);
// Read new file
$reloadedSpreadsheet = $reader->load($tempFileName);
$sheet = $reloadedSpreadsheet->getActiveSheet();
// Check first image in comment background
$comment = $sheet->getComment('A1');
self::assertEquals($comment->getWidth(), '112.5pt');
self::assertEquals($comment->getHeight(), '112.5pt');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getWidth(), 150);
self::assertEquals($comment->getBackgroundImage()->getHeight(), 150);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_PNG);
// Check second image in comment background
$comment = $sheet->getComment('A2');
self::assertEquals($comment->getWidth(), '52.5pt');
self::assertEquals($comment->getHeight(), '52.5pt');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getWidth(), 70);
self::assertEquals($comment->getBackgroundImage()->getHeight(), 70);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_PNG);
unlink($tempFileName);
self::assertNotNull($reloadedSpreadsheet);
}
/**
* Test build and save XLSX with drawings in comments with comment size correction.
*/
public function testBuildWithDifferentImageFormats(): void
{
$reader = new Xlsx();
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// Add png image to comment background
$sheet->setCellValue('A1', '.png');
$drawing = new Drawing();
$drawing->setName('Blue Square');
$drawing->setPath('tests/data/Writer/XLSX/blue_square.png');
self::assertEquals($drawing->getWidth(), 100);
self::assertEquals($drawing->getHeight(), 100);
$comment = $sheet->getComment('A1');
$comment->setBackgroundImage($drawing);
$comment->setSizeAsBackgroundImage();
self::assertEquals($comment->getWidth(), '75pt');
self::assertEquals($comment->getHeight(), '75pt');
$comment = $sheet->getComment('A1');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_PNG);
// Add gif image to comment background
$sheet->setCellValue('A2', '.gif');
$drawing = new Drawing();
$drawing->setName('Green Square');
$drawing->setPath('tests/data/Writer/XLSX/green_square.gif');
self::assertEquals($drawing->getWidth(), 150);
self::assertEquals($drawing->getHeight(), 150);
$comment = $sheet->getComment('A2');
$comment->setBackgroundImage($drawing);
$comment->setSizeAsBackgroundImage();
self::assertEquals($comment->getWidth(), '112.5pt');
self::assertEquals($comment->getHeight(), '112.5pt');
$comment = $sheet->getComment('A2');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_GIF);
// Add jpeg image to comment background
$sheet->setCellValue('A3', '.jpeg');
$drawing = new Drawing();
$drawing->setName('Red Square');
$drawing->setPath('tests/data/Writer/XLSX/red_square.jpeg');
self::assertEquals($drawing->getWidth(), 50);
self::assertEquals($drawing->getHeight(), 50);
$comment = $sheet->getComment('A3');
$comment->setBackgroundImage($drawing);
$comment->setSizeAsBackgroundImage();
self::assertEquals($comment->getWidth(), '37.5pt');
self::assertEquals($comment->getHeight(), '37.5pt');
$comment = $sheet->getComment('A3');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_JPEG);
// Add bmp image to comment background
$sheet->setCellValue('A4', '.bmp 16 colors');
$drawing = new Drawing();
$drawing->setName('Yellow Square');
$drawing->setPath('tests/data/Writer/XLSX/yellow_square_16.bmp');
self::assertEquals($drawing->getWidth(), 70);
self::assertEquals($drawing->getHeight(), 70);
$comment = $sheet->getComment('A4');
$comment->setBackgroundImage($drawing);
$comment->setSizeAsBackgroundImage();
self::assertEquals($comment->getWidth(), '52.5pt');
self::assertEquals($comment->getHeight(), '52.5pt');
$comment = $sheet->getComment('A4');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_BMP);
// Add bmp image to comment background
$sheet->setCellValue('A5', '.bmp 256 colors');
$drawing = new Drawing();
$drawing->setName('Brown Square');
$drawing->setPath('tests/data/Writer/XLSX/brown_square_256.bmp');
self::assertEquals($drawing->getWidth(), 70);
self::assertEquals($drawing->getHeight(), 70);
$comment = $sheet->getComment('A5');
$comment->setBackgroundImage($drawing);
$comment->setSizeAsBackgroundImage();
self::assertEquals($comment->getWidth(), '52.5pt');
self::assertEquals($comment->getHeight(), '52.5pt');
$comment = $sheet->getComment('A5');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_BMP);
// Add bmp image to comment background
$sheet->setCellValue('A6', '.bmp 24 bit');
$drawing = new Drawing();
$drawing->setName('Orange Square');
$drawing->setPath('tests/data/Writer/XLSX/orange_square_24_bit.bmp');
self::assertEquals($drawing->getWidth(), 70);
self::assertEquals($drawing->getHeight(), 70);
$comment = $sheet->getComment('A6');
$comment->setBackgroundImage($drawing);
$comment->setSizeAsBackgroundImage();
self::assertEquals($comment->getWidth(), '52.5pt');
self::assertEquals($comment->getHeight(), '52.5pt');
$comment = $sheet->getComment('A6');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_BMP);
// Add unsupported tiff image to comment background
$sheet->setCellValue('A7', '.tiff');
$drawing = new Drawing();
$drawing->setName('Purple Square');
$drawing->setPath('tests/data/Writer/XLSX/purple_square.tiff');
$comment = $sheet->getComment('A7');
self::assertTrue($comment instanceof Comment);
self::assertFalse($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_UNKNOWN);
try {
$comment->setBackgroundImage($drawing);
self::fail('Should throw exception when attempting to add tiff');
} catch (PhpSpreadsheetException $e) {
self::assertTrue($e instanceof PhpSpreadsheetException);
self::assertEquals($e->getMessage(), 'Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
}
try {
$drawing->getImageTypeForSave();
self::fail('Should throw exception when attempting to get image type for tiff');
} catch (PhpSpreadsheetException $e) {
self::assertTrue($e instanceof PhpSpreadsheetException);
self::assertEquals($e->getMessage(), 'Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
}
try {
$drawing->getImageFileExtensionForSave();
self::fail('Should throw exception when attempting to get image file extention for tiff');
} catch (PhpSpreadsheetException $e) {
self::assertTrue($e instanceof PhpSpreadsheetException);
self::assertEquals($e->getMessage(), 'Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
}
try {
$drawing->getImageMimeType();
self::fail('Should throw exception when attempting to get image mime type for tiff');
} catch (PhpSpreadsheetException $e) {
self::assertTrue($e instanceof PhpSpreadsheetException);
self::assertEquals($e->getMessage(), 'Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
}
// Write file
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$tempFileName = File::sysGetTempDir() . '/drawings_in_comments.xlsx';
$writer->save($tempFileName);
// Read new file
$reloadedSpreadsheet = $reader->load($tempFileName);
$sheet = $reloadedSpreadsheet->getActiveSheet();
// Check first image in comment background
$comment = $sheet->getComment('A1');
self::assertEquals($comment->getWidth(), '75pt');
self::assertEquals($comment->getHeight(), '75pt');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getWidth(), 100);
self::assertEquals($comment->getBackgroundImage()->getHeight(), 100);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_PNG);
// Check second image in comment background
$comment = $sheet->getComment('A2');
self::assertEquals($comment->getWidth(), '112.5pt');
self::assertEquals($comment->getHeight(), '112.5pt');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getWidth(), 150);
self::assertEquals($comment->getBackgroundImage()->getHeight(), 150);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_PNG);
// Check third image in comment background
$comment = $sheet->getComment('A3');
self::assertEquals($comment->getWidth(), '37.5pt');
self::assertEquals($comment->getHeight(), '37.5pt');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getWidth(), 50);
self::assertEquals($comment->getBackgroundImage()->getHeight(), 50);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_JPEG);
// Check fourth image in comment background
$comment = $sheet->getComment('A4');
self::assertEquals($comment->getWidth(), '52.5pt');
self::assertEquals($comment->getHeight(), '52.5pt');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getWidth(), 70);
self::assertEquals($comment->getBackgroundImage()->getHeight(), 70);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_PNG);
// Check fifth image in comment background
$comment = $sheet->getComment('A5');
self::assertEquals($comment->getWidth(), '52.5pt');
self::assertEquals($comment->getHeight(), '52.5pt');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getWidth(), 70);
self::assertEquals($comment->getBackgroundImage()->getHeight(), 70);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_PNG);
// Check sixth image in comment background
$comment = $sheet->getComment('A6');
self::assertEquals($comment->getWidth(), '52.5pt');
self::assertEquals($comment->getHeight(), '52.5pt');
self::assertTrue($comment instanceof Comment);
self::assertTrue($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getWidth(), 70);
self::assertEquals($comment->getBackgroundImage()->getHeight(), 70);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_PNG);
// Check seventh image in comment background
$comment = $sheet->getComment('A7');
self::assertTrue($comment instanceof Comment);
self::assertFalse($comment->hasBackgroundImage());
self::assertTrue($comment->getBackgroundImage() instanceof Drawing);
self::assertEquals($comment->getBackgroundImage()->getWidth(), 0);
self::assertEquals($comment->getBackgroundImage()->getHeight(), 0);
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_UNKNOWN);
unlink($tempFileName);
self::assertNotNull($reloadedSpreadsheet);
}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB