fix png image embed

This commit is contained in:
Dennis Eichhorn 2023-10-23 01:43:07 +00:00
parent 937fa14da3
commit e29901ab09

View File

@ -456,12 +456,32 @@ class ImageProcessor implements \Psr\Log\LoggerAwareInterface
$pngalpha = true; $pngalpha = true;
} }
if ($ct < 4 && strpos($data, 'tRNS') !== false) { $chunkTypes = [];
$p = 8;
do {
$n = $this->fourBytesToInt(substr($data, $p, 4));
$p += 4;
$type = substr($data, $p, 4);
if ($type === 'IEND') {
break;
}
if (!preg_match('/[a-zA-Z]{4}/', $type)) {
return $this->imageError($file, $firsttime, 'Error parsing PNG image data');
}
$p += 4;
$chunkTypes[$type][] = [
'bytes' => $n,
'data' => substr($data, $p, $n)
];
$p += $n + 4;
} while ($n);
if ($ct < 4 && \array_key_exists('tRNS', $chunkTypes)) {
$errpng = 'transparency'; $errpng = 'transparency';
$pngalpha = true; $pngalpha = true;
} // mPDF 6 } // mPDF 6
if ($ct === 3 && strpos($data, 'iCCP') !== false) { if ($ct === 3 && \array_key_exists('iCCP', $chunkTypes)) {
$errpng = 'indexed plus ICC'; $errpng = 'indexed plus ICC';
} // mPDF 6 } // mPDF 6
@ -479,12 +499,11 @@ class ImageProcessor implements \Psr\Log\LoggerAwareInterface
$errpng = 'interlaced file'; $errpng = 'interlaced file';
} }
$j = strpos($data, 'pHYs'); if (array_key_exists('pHYs', $chunkTypes)) {
if ($j) {
//Read resolution //Read resolution
$unitSp = ord(substr($data, $j + 12, 1)); $unitSp = ord(substr($chunkTypes['pHYs'][0]['data'], 8, 1));
if ($unitSp === 1) { if ($unitSp === 1) {
$ppUx = $this->fourBytesToInt(substr($data, $j + 4, 4)); // horizontal pixels per meter, usually set to zero $ppUx = $this->fourBytesToInt(substr($chunkTypes['pHYs'][0]['data'], 0, 4)); // horizontal pixels per meter, usually set to zero
$ppUx = round($ppUx / 1000 * 25.4); $ppUx = round($ppUx / 1000 * 25.4);
} }
} }
@ -492,9 +511,8 @@ class ImageProcessor implements \Psr\Log\LoggerAwareInterface
// mPDF 6 Gamma correction // mPDF 6 Gamma correction
$gamma = 0; $gamma = 0;
$gAMA = 0; $gAMA = 0;
$j = strpos($data, 'gAMA'); if (array_key_exists('gAMA', $chunkTypes) && array_key_exists('sRGB', $chunkTypes)) { // sRGB colorspace - overrides gAMA
if ($j && strpos($data, 'sRGB') === false) { // sRGB colorspace - overrides gAMA $gAMA = $this->fourBytesToInt($chunkTypes['gAMA'][0]['data']); // Gamma value times 100000
$gAMA = $this->fourBytesToInt(substr($data, $j + 4, 4)); // Gamma value times 100000
$gAMA /= 100000; $gAMA /= 100000;
// http://www.libpng.org/pub/png/spec/1.2/PNG-Encoders.html // http://www.libpng.org/pub/png/spec/1.2/PNG-Encoders.html
@ -585,10 +603,8 @@ class ImageProcessor implements \Psr\Log\LoggerAwareInterface
// mPDF 6 // mPDF 6
if ($colspace === 'Indexed') { // generate Alpha channel values from tRNS if ($colspace === 'Indexed') { // generate Alpha channel values from tRNS
// Read transparency info // Read transparency info
$p = strpos($data, 'tRNS'); if (array_key_exists('tRNS', $chunkTypes)) {
if ($p) { $transparency = $chunkTypes['tRNS'][0]['data'];
$n = $this->fourBytesToInt(substr($data, $p - 4, 4));
$transparency = substr($data, $p + 4, $n);
// ord($transparency[$index]) = the alpha value for that index // ord($transparency[$index]) = the alpha value for that index
// generate alpha channel // generate alpha channel
for ($ypx = 0; $ypx < $h; ++$ypx) { for ($ypx = 0; $ypx < $h; ++$ypx) {
@ -607,11 +623,9 @@ class ImageProcessor implements \Psr\Log\LoggerAwareInterface
} }
} elseif ($ct === 0 || $ct === 2) { // generate Alpha channel values from tRNS } elseif ($ct === 0 || $ct === 2) { // generate Alpha channel values from tRNS
// Get transparency as array of RGB // Get transparency as array of RGB
$p = strpos($data, 'tRNS'); if (\array_key_exists('tRNS', $chunkTypes)) {
if ($p) {
$trns = ''; $trns = '';
$n = $this->fourBytesToInt(substr($data, $p - 4, 4)); $t = $chunkTypes['tRNS'][0]['data'];
$t = substr($data, $p + 4, $n);
if ($colspace === 'DeviceGray') { // ct===0 if ($colspace === 'DeviceGray') { // ct===0
$trns = [$this->translateValue(substr($t, 0, 2), $bpc)]; $trns = [$this->translateValue(substr($t, 0, 2), $bpc)];
} else /* $colspace=='DeviceRGB' */ { // ct==2 } else /* $colspace=='DeviceRGB' */ { // ct==2
@ -716,26 +730,8 @@ class ImageProcessor implements \Psr\Log\LoggerAwareInterface
// No alpha/transparency set (but cannot read directly because e.g. bit-depth != 8, interlaced etc) // No alpha/transparency set (but cannot read directly because e.g. bit-depth != 8, interlaced etc)
// ICC profile // ICC profile
$icc = false; $icc = false;
$p = strpos($data, 'iCCP'); if (array_key_exists('iCCP', $chunkTypes) && $colspace === "Indexed") { // Cannot have ICC profile and Indexed together
if ($p && $colspace === "Indexed") { // Cannot have ICC profile and Indexed together $icc = $this->getICCP($chunkTypes['iCCP'][0]['data']);
$p += 4;
$n = $this->fourBytesToInt(substr($data, ($p - 8), 4));
$nullsep = strpos(substr($data, $p, 80), chr(0));
$icc = substr($data, ($p + $nullsep + 2), ($n - ($nullsep + 2)));
$icc = @gzuncompress($icc); // Ignored if fails
if ($icc) {
if (substr($icc, 36, 4) !== 'acsp') {
$icc = false;
} // invalid ICC profile
else {
$input = substr($icc, 16, 4);
$output = substr($icc, 20, 4);
// Ignore Color profiles for conversion to other colorspaces e.g. CMYK/Lab
if ($input !== 'RGB ' || $output !== 'XYZ ') {
$icc = false;
}
}
}
// Convert to RGB colorspace so can use ICC Profile // Convert to RGB colorspace so can use ICC Profile
if ($icc) { if ($icc) {
imagepalettetotruecolor($im); imagepalettetotruecolor($im);
@ -783,68 +779,25 @@ class ImageProcessor implements \Psr\Log\LoggerAwareInterface
$parms = '/DecodeParms <</Predictor 15 /Colors ' . $channels . ' /BitsPerComponent ' . $bpc . ' /Columns ' . $w . '>>'; $parms = '/DecodeParms <</Predictor 15 /Colors ' . $channels . ' /BitsPerComponent ' . $bpc . ' /Columns ' . $w . '>>';
//Scan chunks looking for palette, transparency and image data //Scan chunks looking for palette, transparency and image data
$pal = ''; $pal = \array_key_exists('PLTE', $chunkTypes) ? $chunkTypes['PLTE'][0]['data'] : '';
$trns = ''; $trns = '';
$pngdata = ''; if (array_key_exists('tRNS', $chunkTypes)) {
$icc = false; $t = $chunkTypes['tRNS'][0]['data'];
$p = 33; if ($ct === 0) {
$trns = [ord(substr($t, 1, 1))];
do { } elseif ($ct === 2) {
$n = $this->fourBytesToInt(substr($data, $p, 4)); $trns = [ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1))];
$p += 4;
$type = substr($data, $p, 4);
$p += 4;
if ($type === 'PLTE') {
//Read palette
$pal = substr($data, $p, $n);
$p += $n;
$p += 4;
} elseif ($type === 'tRNS') {
//Read transparency info
$t = substr($data, $p, $n);
$p += $n;
if ($ct === 0) {
$trns = [ord(substr($t, 1, 1))];
} elseif ($ct === 2) {
$trns = [ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1))];
} else {
$pos = strpos($t, chr(0));
if (is_int($pos)) {
$trns = [$pos];
}
}
$p += 4;
} elseif ($type === 'IDAT') {
$pngdata.=substr($data, $p, $n);
$p += $n;
$p += 4;
} elseif ($type === 'iCCP') {
$nullsep = strpos(substr($data, $p, 80), chr(0));
$icc = substr($data, $p + $nullsep + 2, $n - ($nullsep + 2));
$icc = @gzuncompress($icc); // Ignored if fails
if ($icc) {
if (substr($icc, 36, 4) !== 'acsp') {
$icc = false;
} // invalid ICC profile
else {
$input = substr($icc, 16, 4);
$output = substr($icc, 20, 4);
// Ignore Color profiles for conversion to other colorspaces e.g. CMYK/Lab
if ($input !== 'RGB ' || $output !== 'XYZ ') {
$icc = false;
}
}
}
$p += $n;
$p += 4;
} elseif ($type === 'IEND') {
break;
} elseif (preg_match('/[a-zA-Z]{4}/', $type)) {
$p += $n + 4;
} else { } else {
return $this->imageError($file, $firsttime, 'Error parsing PNG image data'); $pos = strpos($t, chr(0));
if (is_int($pos)) {
$trns = [$pos];
}
} }
} while ($n); }
$pngdata = implode('', array_column($chunkTypes['IDAT'], 'data'));
$icc = array_key_exists('iCCP', $chunkTypes) ? $this->getICCP($chunkTypes['iCCP'][0]['data']) : false;
if (!$pngdata) { if (!$pngdata) {
return $this->imageError($file, $firsttime, 'Error parsing PNG image data - no IDAT data found'); return $this->imageError($file, $firsttime, 'Error parsing PNG image data - no IDAT data found');
} }
@ -1080,6 +1033,27 @@ class ImageProcessor implements \Psr\Log\LoggerAwareInterface
return $this->imageError($file, $firsttime, 'Error parsing image file - image type not recognised'); return $this->imageError($file, $firsttime, 'Error parsing image file - image type not recognised');
} }
private function getICCP($data)
{
$nullsep = strpos(substr($data, 0, 79), chr(0));
$icc = @gzuncompress(substr($data, ($nullsep + 2))); // Ignored if fails
if ($icc) {
if (substr($icc, 36, 4) !== 'acsp') {
$icc = false;
} // invalid ICC profile
else {
$input = substr($icc, 16, 4);
$output = substr($icc, 20, 4);
// Ignore Color profiles for conversion to other colorspaces e.g. CMYK/Lab
if ($input !== 'RGB ' || $output !== 'XYZ ') {
$icc = false;
}
}
}
return $icc;
}
private function convertImage(&$data, $colspace, $targetcs, $w, $h, $dpi, $mask, $gamma_correction = false, $pngcolortype = false) private function convertImage(&$data, $colspace, $targetcs, $w, $h, $dpi, $mask, $gamma_correction = false, $pngcolortype = false)
{ {
if ($this->mpdf->PDFA || $this->mpdf->PDFX) { if ($this->mpdf->PDFA || $this->mpdf->PDFX) {