diff --git a/phpstan.neon.dist b/phpstan.neon.dist index ef2ae14fa..52de75803 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -20,7 +20,7 @@ parameters: processTimeout: 300.0 checkMissingIterableValueType: false ignoreErrors: - - '~^Parameter \#1 \$im(age)? of function (imagedestroy|imageistruecolor|imagealphablending|imagesavealpha|imagecolortransparent|imagecolorsforindex|imagesavealpha|imagesx|imagesy|imagepng) expects (GdImage|resource), GdImage\|resource given\.$~' + - '~^Parameter \#1 \$im(age)? of function (imagedestroy|imageistruecolor|imagealphablending|imagesavealpha|imagecolortransparent|imagecolorsforindex|imagesavealpha|imagesx|imagesy|imagepng|imagecolorat) expects (GdImage|resource), GdImage\|resource given\.$~' - '~^Parameter \#2 \$src_im(age)? of function imagecopy expects (GdImage|resource), GdImage\|resource given\.$~' # Accept a bit anything for assert methods - '~^Parameter \#2 .* of static method PHPUnit\\Framework\\Assert\:\:assert\w+\(\) expects .*, .* given\.$~' diff --git a/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php b/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php index 3e2baff6d..cf616eb75 100644 --- a/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php +++ b/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php @@ -162,6 +162,9 @@ class MemoryDrawing extends BaseDrawing } $mimeType = self::identifyMimeType($imageString); + if (imageistruecolor($gdImage) || imagecolortransparent($gdImage) >= 0) { + imagesavealpha($gdImage, true); + } $renderingFunction = self::identifyRenderingFunction($mimeType); $drawing = new self(); diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/SortByTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/SortByTest.php index 616ba9367..a2eb006d5 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/SortByTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/SortByTest.php @@ -20,7 +20,7 @@ class SortByTest extends TestCase * @dataProvider providerSortWithScalarArgumentErrorReturns * * @param mixed $sortIndex - * @param mixed$sortOrder + * @param mixed $sortOrder */ public function testSortByWithArgumentErrorReturns($sortIndex, $sortOrder = 1): void { diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/SortTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/SortTest.php index 1cd2a138c..3145cfd3e 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/SortTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/SortTest.php @@ -20,7 +20,7 @@ class SortTest extends TestCase * @dataProvider providerSortWithScalarArgumentErrorReturns * * @param mixed $sortIndex - * @param mixed$sortOrder + * @param mixed $sortOrder */ public function testSortWithScalarArgumentErrorReturns($sortIndex, $sortOrder = 1): void { diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/MemoryDrawingTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/MemoryDrawingTest.php new file mode 100644 index 000000000..bbc7fe230 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/MemoryDrawingTest.php @@ -0,0 +1,91 @@ +outfile !== '') { + unlink($this->outfile); + $this->outfile = ''; + } + } + + /** + * Test save and load XLSX file with transparent png. + */ + public function testIssue3624(): void + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $contents = file_get_contents('tests/data/Writer/XLSX/issue.3624b.png'); + $stamp = MemoryDrawing::fromString("$contents"); + $stamp->setName('Stamp'); + $stamp->setHeight(120); + $stamp->setCoordinates('A2'); + $stamp->setWorksheet($sheet); + $this->outfile = File::temporaryFilename(); + $writer = new XlsxWriter($spreadsheet); + $writer->save($this->outfile); + $spreadsheet->disconnectWorksheets(); + + $reader = new XlsxReader(); + $reloadedSpreadsheet = $reader->load($this->outfile); + $rsheet = $reloadedSpreadsheet->getActiveSheet(); + $drawings = $rsheet->getDrawingCollection(); + self::assertCount(1, $drawings); + foreach ($drawings as $drawing) { + if ($drawing instanceof Drawing) { + $path = $drawing->getPath(); + $contents = file_get_contents($path); + $gdImage = imagecreatefromstring("$contents"); + if ($gdImage === false) { + self::fail('unexpected failure in imagecreatefromstring'); + } else { + self::assertTrue(self::checkTransparent($gdImage)); + } + } else { + self::fail('Unexpected drawing not in Drawing class'); + } + } + $reloadedSpreadsheet->disconnectWorksheets(); + } + + /** + * Determine if image uses transparency. + * + * @see https://stackoverflow.com/questions/5495275/how-to-check-if-a-png-image-has-transparency-using-gd + * + * @param GdImage|resource $im + */ + private static function checkTransparent($im): bool + { + $width = imagesx($im); // Get the width of the image + $height = imagesy($im); // Get the height of the image + + // We run the image pixel by pixel and as soon as we find a transparent pixel we stop and return true. + for ($i = 0; $i < $width; ++$i) { + for ($j = 0; $j < $height; ++$j) { + $rgba = imagecolorat($im, $i, $j); + if (($rgba & 0x7F000000) >> 24) { + return true; + } + } + } + + // If we dont find any pixel the function will return false. + return false; + } +} diff --git a/tests/data/Writer/XLSX/issue.3624b.png b/tests/data/Writer/XLSX/issue.3624b.png new file mode 100644 index 000000000..48665d02c Binary files /dev/null and b/tests/data/Writer/XLSX/issue.3624b.png differ