PHPOffice / PhpSpreadsheet

A pure PHP library for reading and writing spreadsheet files
https://phpspreadsheet.readthedocs.io
MIT License
13.34k stars 3.46k forks source link

Artefacts with transparency in png drawing #3624

Closed artygrand closed 1 year ago

artygrand commented 1 year ago

This is:

What is the expected behavior?

In excel I can insert png image with transparency without problems

What is the current behavior?

Problem is artefacts in transparent background, like in example below. This is one file made from 2 in Photoshop.
Absolutely opaque, with grey zones.

image

What are the steps to reproduce?

Please provide a Minimal, Complete, and Verifiable example of code that exhibits the issue without relying on an external Excel file or a web server:

<?php

require __DIR__ . '/vendor/autoload.php';

// Create new Spreadsheet object
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();

$sheet = $spreadsheet->getActiveSheet();

$stamp = MemoryDrawing::fromString($stamp_file);
$stamp->setName('Stamp');
$stamp->setHeight(120);
$stamp->setCoordinates('A2');
$stamp->setWorksheet($sheet);

$writer = new PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->save($path);

If this is an issue with reading a specific spreadsheet file, then it may be appropriate to provide a sample file that demonstrates the problem; but please keep it as small as possible, and sanitize any confidential information before uploading.

What features do you think are causing the issue

Does an issue affect all spreadsheet file formats? If not, which formats are affected?

Which versions of PhpSpreadsheet and PHP are affected?

PhpSpreadsheet 1.28 PHP 8.1

oleibman commented 1 year ago

You do not show how you arrived at the value in $stamp_file. Please compare your code to the following:

        $stamp_file = file_get_contents('issue.3624.png'); // the file you uploaded above
        $stamp = PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::fromString($stamp_file);

Or, more simply (use drawing as-is rather than as memory drawing):

        $stamp = new PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
        $stamp->setPath('issue.3624.png');

In either case, the result appears to be exactly the same as if I had added the drawing directly in Excel (adjusting the height appropriately). If your results differ, please show how.

Excel: image

PhpSpreadsheet: image

artygrand commented 1 year ago

@oleibman , my attachment isn't original picture, but the broken result I use it with laravel S3 Storage, so I can get only as string (I don't want to copy it on server and then wipe)

Okay, I tested different approaches and looks like found the bug. Lets try these 3 snippents

file_get_contents with storage path

$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();

$stamp = PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::fromString(file_get_contents(storage_path('app/test.png')));
$stamp->setName('Stamp');
$stamp->setHeight(120);
$stamp->setCoordinates('A2');
$stamp->setWorksheet($sheet);

$writer = new PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->save(storage_path('app/res-fgc.xlsx'));

Storage getter, that I use. It is almost file_get_contents

$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();

$stamp = PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::fromString(Storage::disk('local')->get('test.png'));
$stamp->setName('Stamp');
$stamp->setHeight(120);
$stamp->setCoordinates('A2');
$stamp->setWorksheet($sheet);

$writer = new PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->save(storage_path('app/res-stor.xlsx'));

Not memory drawing but base

$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();

$stamp = new PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$stamp->setPath(storage_path('app/test.png'));
$stamp->setName('Stamp');
$stamp->setHeight(120);
$stamp->setCoordinates('A2');
$stamp->setWorksheet($sheet);

$writer = new PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->save(storage_path('app/res-path.xlsx'));

Original picture: test

Results: res-fgc.xlsx res-stor.xlsx res-path.xlsx

Drawing with path image

MemoryDrawing is absolutely broken image

Where is alpha channel?

oleibman commented 1 year ago

Thank you, I am now able to duplicate your problem. However, I believe the problem lies in Php, not in PhpSpreadsheet.

        $stamp_file = file_get_contents('issue.3624b.png'); // your new image
        $gdImage = imagecreatefromstring($stamp_file);
        imagepng($gdImage, 'issue.3624c.png');

This code snippet, which does not use PhpSpreadsheet at all, shows that its output file does not match the input file (and, in fact, matches your 'bad' image). When I substitute your original input file, the output file does match it, so I believe this code is correct, and therefore that one or both of the native Php functions imagecreatefromstring or imagepng has a problem. For the record, specifying PNG_NO_FILTER or PNG_ALL_FILTERS on the imagepng call does not change anything.

oleibman commented 1 year ago

I have opened an issue with Php.

oleibman commented 1 year ago

From the Php issue, an extra call was suggested (imagesavealpha). I will create a PR to incorporate this suggestion into PhpSpreadsheet. Look for it in a day or two.