vendor/mpdf/mpdf/src/Image/ImageProcessor.php line 127

Open in your IDE?
  1. <?php
  2. namespace Mpdf\Image;
  3. use Mpdf\AssetFetcher;
  4. use Mpdf\Cache;
  5. use Mpdf\Color\ColorConverter;
  6. use Mpdf\Color\ColorModeConverter;
  7. use Mpdf\CssManager;
  8. use Mpdf\Gif\Gif;
  9. use Mpdf\Language\LanguageToFontInterface;
  10. use Mpdf\Language\ScriptToLanguageInterface;
  11. use Mpdf\Log\Context as LogContext;
  12. use Mpdf\Mpdf;
  13. use Mpdf\Otl;
  14. use Mpdf\PsrLogAwareTrait\PsrLogAwareTrait;
  15. use Mpdf\SizeConverter;
  16. use Psr\Log\LoggerInterface;
  17. class ImageProcessor implements \Psr\Log\LoggerAwareInterface
  18. {
  19.     use PsrLogAwareTrait;
  20.     /**
  21.      * @var \Mpdf\Mpdf
  22.      */
  23.     private $mpdf;
  24.     /**
  25.      * @var \Mpdf\Otl
  26.      */
  27.     private $otl;
  28.     /**
  29.      * @var \Mpdf\CssManager
  30.      */
  31.     private $cssManager;
  32.     /**
  33.      * @var \Mpdf\SizeConverter
  34.      */
  35.     private $sizeConverter;
  36.     /**
  37.      * @var \Mpdf\Color\ColorConverter
  38.      */
  39.     private $colorConverter;
  40.     /**
  41.      * @var \Mpdf\Color\ColorModeConverter
  42.      */
  43.     private $colorModeConverter;
  44.     /**
  45.      * @var \Mpdf\Cache
  46.      */
  47.     private $cache;
  48.     /**
  49.      * @var \Mpdf\Image\ImageTypeGuesser
  50.      */
  51.     private $guesser;
  52.     /**
  53.      * @var string[]
  54.      */
  55.     private $failedImages;
  56.     /**
  57.      * @var \Mpdf\Image\Bmp
  58.      */
  59.     private $bmp;
  60.     /**
  61.      * @var \Mpdf\Image\Wmf
  62.      */
  63.     private $wmf;
  64.     /**
  65.      * @var \Mpdf\Language\LanguageToFontInterface
  66.      */
  67.     private $languageToFont;
  68.     /**
  69.      * @var \Mpdf\Language\ScriptToLanguageInterface
  70.      */
  71.     public $scriptToLanguage;
  72.     /**
  73.      * @var \Mpdf\AssetFetcher
  74.      */
  75.     private $assetFetcher;
  76.     public function __construct(
  77.         Mpdf $mpdf,
  78.         Otl $otl,
  79.         CssManager $cssManager,
  80.         SizeConverter $sizeConverter,
  81.         ColorConverter $colorConverter,
  82.         ColorModeConverter $colorModeConverter,
  83.         Cache $cache,
  84.         LanguageToFontInterface $languageToFont,
  85.         ScriptToLanguageInterface $scriptToLanguage,
  86.         AssetFetcher $assetFetcher,
  87.         LoggerInterface $logger
  88.     ) {
  89.         $this->mpdf $mpdf;
  90.         $this->otl $otl;
  91.         $this->cssManager $cssManager;
  92.         $this->sizeConverter $sizeConverter;
  93.         $this->colorConverter $colorConverter;
  94.         $this->colorModeConverter $colorModeConverter;
  95.         $this->cache $cache;
  96.         $this->languageToFont $languageToFont;
  97.         $this->scriptToLanguage $scriptToLanguage;
  98.         $this->assetFetcher $assetFetcher;
  99.         $this->logger $logger;
  100.         $this->guesser = new ImageTypeGuesser();
  101.         $this->failedImages = [];
  102.     }
  103.     public function getImage(&$file$firstTime true$allowvector true$orig_srcpath false$interpolation false)
  104.     {
  105.         // mPDF 6
  106.         // firsttime i.e. whether to add to this->images - use false when calling iteratively
  107.         // Image Data passed directly as var:varname
  108.         $type null;
  109.         $data '';
  110.         if (preg_match('/var:\s*(.*)/'$file$v)) {
  111.             if (!isset($this->mpdf->imageVars[$v[1]])) {
  112.                 return $this->imageError($file$firstTime'Unknown image variable');
  113.             }
  114.             $data $this->mpdf->imageVars[$v[1]];
  115.             $file md5($data);
  116.         }
  117.         if (preg_match('/data:image\/(gif|jpe?g|png|webp|svg\+xml);base64,(.*)/'$file$v)) {
  118.             $type $v[1];
  119.             $data base64_decode($v[2]);
  120.             $file md5($data);
  121.         }
  122.         // mPDF 5.7.4 URLs
  123.         if ($firstTime && $file && strpos($file'data:') !== 0) {
  124.             $file str_replace(' ''%20'$file);
  125.         }
  126.         if ($firstTime && $orig_srcpath) {
  127.             // If orig_srcpath is a relative file path (and not a URL), then it needs to be URL decoded
  128.             if (strpos($orig_srcpath'data:') !== 0) {
  129.                 $orig_srcpath str_replace(' ''%20'$orig_srcpath);
  130.             }
  131.             if (!preg_match('/^(http|ftp)/'$orig_srcpath)) {
  132.                 $orig_srcpath $this->urldecodeParts($orig_srcpath);
  133.             }
  134.         }
  135.         if ($orig_srcpath && isset($this->mpdf->images[$orig_srcpath])) {
  136.             $file $orig_srcpath;
  137.             return $this->mpdf->images[$orig_srcpath];
  138.         }
  139.         if (isset($this->mpdf->images[$file])) {
  140.             return $this->mpdf->images[$file];
  141.         }
  142.         if ($orig_srcpath && isset($this->mpdf->formobjects[$orig_srcpath])) {
  143.             $file $orig_srcpath;
  144.             return $this->mpdf->formobjects[$file];
  145.         }
  146.         if (isset($this->mpdf->formobjects[$file])) {
  147.             return $this->mpdf->formobjects[$file];
  148.         }
  149.         if ($firstTime && isset($this->failedImages[$file])) { // Save re-trying image URL's which have already failed
  150.             return $this->imageError($file$firstTime'');
  151.         }
  152.         if (!$data) {
  153.             try {
  154.                 $data $this->assetFetcher->fetchDataFromPath($file$orig_srcpath);
  155.             } catch (\Mpdf\Exception\AssetFetchingException $e) {
  156.                 return $this->imageError($orig_srcpath$firstTime$e->getMessage());
  157.             }
  158.         }
  159.         if (!$data) {
  160.             return $this->imageError($file$firstTime'Could not find image file');
  161.         }
  162.         if ($type === null) {
  163.             $type $this->guesser->guess($data);
  164.         }
  165.         if ($type === 'svg' || $type === 'svg+xml') {
  166.             if (!$allowvector) {
  167.                 return $this->imageError($file$firstTime'SVG image file not supported in this context');
  168.             }
  169.             return $this->processSvg($data$file$firstTime);
  170.         }
  171.         if ($type === 'wmf') {
  172.             if (!$allowvector) {
  173.                 return $this->imageError($file$firstTime'WMF image file not supported in this context');
  174.             }
  175.             return $this->processWmf($data$file$firstTime);
  176.         }
  177.         if ($type === 'webp') {
  178.             // Convert webp images to JPG and treat them as such
  179.             $data $this->processWebp($data$file$firstTime);
  180.             $type 'jpeg';
  181.         }
  182.         // JPEG
  183.         if ($type === 'jpeg' || $type === 'jpg') {
  184.             return $this->processJpg($data$file$firstTime$interpolation);
  185.         }
  186.         if ($type === 'png') {
  187.             return $this->processPng($data$file$firstTime$interpolation);
  188.         }
  189.         if ($type === 'gif') { // GIF
  190.             return $this->processGif($data$file$firstTime$interpolation);
  191.         }
  192.         if ($type === 'bmp') {
  193.             return $this->processBmp($data$file$firstTime$interpolation);
  194.         }
  195.         return $this->processUnknownType($data$file$firstTime$interpolation);
  196.     }
  197.     private function convertImage(&$data$colspace$targetcs$w$h$dpi$mask$gamma_correction false$pngcolortype false)
  198.     {
  199.         if (!function_exists('gd_info')) {
  200.             return $this->imageError(''false'GD library needed to parse image files');
  201.         }
  202.         if ($this->mpdf->PDFA || $this->mpdf->PDFX) {
  203.             $mask false;
  204.         }
  205.         $im = @imagecreatefromstring($data);
  206.         $info = [];
  207.         $bpc ord(substr($data241));
  208.         if ($im) {
  209.             $imgdata '';
  210.             $mimgdata '';
  211.             $minfo = [];
  212.             // mPDF 6 Gamma correction
  213.             // Need to extract alpha channel info before imagegammacorrect (which loses the data)
  214.             if ($mask) { // i.e. $pngalpha for PNG
  215.                 // mPDF 6
  216.                 if ($colspace === 'Indexed') { // generate Alpha channel values from tRNS - only from PNG
  217.                     //Read transparency info
  218.                     $transparency '';
  219.                     $p strpos($data'tRNS');
  220.                     if ($p) {
  221.                         $n $this->fourBytesToInt(substr($data$p 44));
  222.                         $transparency substr($data$p 4$n);
  223.                         // ord($transparency[$index]) = the alpha value for that index
  224.                         // generate alpha channel
  225.                         for ($ypx 0$ypx $h; ++$ypx) {
  226.                             for ($xpx 0$xpx $w; ++$xpx) {
  227.                                 $colorindex imagecolorat($im$xpx$ypx);
  228.                                 if ($colorindex >= $n) {
  229.                                     $alpha 255;
  230.                                 } else {
  231.                                     $alpha ord($transparency[$colorindex]);
  232.                                 } // 0-255
  233.                                 $mimgdata .= chr($alpha);
  234.                             }
  235.                         }
  236.                     }
  237.                 } elseif ($pngcolortype === || $pngcolortype === 2) { // generate Alpha channel values from tRNS
  238.                     // Get transparency as array of RGB
  239.                     $p strpos($data'tRNS');
  240.                     if ($p) {
  241.                         $trns '';
  242.                         $n $this->fourBytesToInt(substr($data$p 44));
  243.                         $t substr($data$p 4$n);
  244.                         if ($colspace === 'DeviceGray') {  // ct===0
  245.                             $trns = [$this->translateValue(substr($t02), $bpc)];
  246.                         } else /* $colspace=='DeviceRGB' */ {  // ct==2
  247.                             $trns = [];
  248.                             $trns[0] = $this->translateValue(substr($t02), $bpc);
  249.                             $trns[1] = $this->translateValue(substr($t22), $bpc);
  250.                             $trns[2] = $this->translateValue(substr($t42), $bpc);
  251.                         }
  252.                         // generate alpha channel
  253.                         for ($ypx 0$ypx $h; ++$ypx) {
  254.                             for ($xpx 0$xpx $w; ++$xpx) {
  255.                                 $rgb imagecolorat($im$xpx$ypx);
  256.                                 $r = ($rgb >> 16) & 0xFF;
  257.                                 $g = ($rgb >> 8) & 0xFF;
  258.                                 $b $rgb 0xFF;
  259.                                 if ($colspace === 'DeviceGray' && $b == $trns[0]) {
  260.                                     $alpha 0;
  261.                                 } elseif ($r == $trns[0] && $g == $trns[1] && $b == $trns[2]) {
  262.                                     $alpha 0;
  263.                                 } // ct==2
  264.                                 else {
  265.                                     $alpha 255;
  266.                                 }
  267.                                 $mimgdata .= chr($alpha);
  268.                             }
  269.                         }
  270.                     }
  271.                 } else {
  272.                     for ($i 0$i $h$i++) {
  273.                         for ($j 0$j $w$j++) {
  274.                             $rgb imagecolorat($im$j$i);
  275.                             $alpha = ($rgb 0x7F000000) >> 24;
  276.                             if ($alpha 127) {
  277.                                 $mimgdata .= chr(255 - ($alpha 2));
  278.                             } else {
  279.                                 $mimgdata .= chr(0);
  280.                             }
  281.                         }
  282.                     }
  283.                 }
  284.             }
  285.             // mPDF 6 Gamma correction
  286.             if ($gamma_correction) {
  287.                 imagegammacorrect($im$gamma_correction2.2);
  288.             }
  289.             // Read transparency info
  290.             $trns = [];
  291.             $trnsrgb false;
  292.             if (!$this->mpdf->PDFA && !$this->mpdf->PDFX && !$mask) {  // mPDF 6 added NOT mask
  293.                 $p strpos($data'tRNS');
  294.                 if ($p) {
  295.                     $n $this->fourBytesToInt(substr($data, ($p 4), 4));
  296.                     $t substr($data$p 4$n);
  297.                     if ($colspace === 'DeviceGray') {  // ct===0
  298.                         $trns = [$this->translateValue(substr($t02), $bpc)];
  299.                     } elseif ($colspace === 'DeviceRGB') {  // ct==2
  300.                         $trns[0] = $this->translateValue(substr($t02), $bpc);
  301.                         $trns[1] = $this->translateValue(substr($t22), $bpc);
  302.                         $trns[2] = $this->translateValue(substr($t42), $bpc);
  303.                         $trnsrgb $trns;
  304.                         if ($targetcs === 'DeviceCMYK') {
  305.                             $col $this->colorModeConverter->rgb2cmyk([3$trns[0], $trns[1], $trns[2]]);
  306.                             $c1 = (int) ($col[1] * 2.55);
  307.                             $c2 = (int) ($col[2] * 2.55);
  308.                             $c3 = (int) ($col[3] * 2.55);
  309.                             $c4 = (int) ($col[4] * 2.55);
  310.                             $trns = [$c1$c2$c3$c4];
  311.                         } elseif ($targetcs === 'DeviceGray') {
  312.                             $c = (int) (($trns[0] * .21) + ($trns[1] * .71) + ($trns[2] * .07));
  313.                             $trns = [$c];
  314.                         }
  315.                     } else { // Indexed
  316.                         $pos strpos($tchr(0));
  317.                         if (is_int($pos)) {
  318.                             $pal imagecolorsforindex($im$pos);
  319.                             $r $pal['red'];
  320.                             $g $pal['green'];
  321.                             $b $pal['blue'];
  322.                             $trns = [$r$g$b]; // ****
  323.                             $trnsrgb $trns;
  324.                             if ($targetcs === 'DeviceCMYK') {
  325.                                 $col $this->colorModeConverter->rgb2cmyk([3$r$g$b]);
  326.                                 $c1 = (int) ($col[1] * 2.55);
  327.                                 $c2 = (int) ($col[2] * 2.55);
  328.                                 $c3 = (int) ($col[3] * 2.55);
  329.                                 $c4 = (int) ($col[4] * 2.55);
  330.                                 $trns = [$c1$c2$c3$c4];
  331.                             } elseif ($targetcs === 'DeviceGray') {
  332.                                 $c = (int) (($r .21) + ($g .71) + ($b .07));
  333.                                 $trns = [$c];
  334.                             }
  335.                         }
  336.                     }
  337.                 }
  338.             }
  339.             for ($i 0$i $h$i++) {
  340.                 for ($j 0$j $w$j++) {
  341.                     $rgb imagecolorat($im$j$i);
  342.                     $r = ($rgb >> 16) & 0xFF;
  343.                     $g = ($rgb >> 8) & 0xFF;
  344.                     $b $rgb 0xFF;
  345.                     if ($colspace === 'Indexed') {
  346.                         $pal imagecolorsforindex($im$rgb);
  347.                         $r $pal['red'];
  348.                         $g $pal['green'];
  349.                         $b $pal['blue'];
  350.                     }
  351.                     if ($targetcs === 'DeviceCMYK') {
  352.                         $col $this->colorModeConverter->rgb2cmyk([3$r$g$b]);
  353.                         $c1 = (int) ($col[1] * 2.55);
  354.                         $c2 = (int) ($col[2] * 2.55);
  355.                         $c3 = (int) ($col[3] * 2.55);
  356.                         $c4 = (int) ($col[4] * 2.55);
  357.                         if ($trnsrgb) {
  358.                             // original pixel was not set as transparent but processed color does match
  359.                             if ($trnsrgb !== [$r$g$b] && $trns === [$c1$c2$c3$c4]) {
  360.                                 if ($c4 === 0) {
  361.                                     $c4 1;
  362.                                 } else {
  363.                                     $c4--;
  364.                                 }
  365.                             }
  366.                         }
  367.                         $imgdata .= chr($c1) . chr($c2) . chr($c3) . chr($c4);
  368.                     } elseif ($targetcs === 'DeviceGray') {
  369.                         $c = (int) (($r .21) + ($g .71) + ($b .07));
  370.                         if ($trnsrgb) {
  371.                             // original pixel was not set as transparent but processed color does match
  372.                             if ($trnsrgb !== [$r$g$b] && $trns === [$c]) {
  373.                                 if ($c === 0) {
  374.                                     $c 1;
  375.                                 } else {
  376.                                     $c--;
  377.                                 }
  378.                             }
  379.                         }
  380.                         $imgdata .= chr($c);
  381.                     } elseif ($targetcs === 'DeviceRGB') {
  382.                         $imgdata .= chr($r) . chr($g) . chr($b);
  383.                     }
  384.                 }
  385.             }
  386.             if ($targetcs === 'DeviceGray') {
  387.                 $ncols 1;
  388.             } elseif ($targetcs === 'DeviceRGB') {
  389.                 $ncols 3;
  390.             } elseif ($targetcs === 'DeviceCMYK') {
  391.                 $ncols 4;
  392.             }
  393.             $imgdata $this->gzCompress($imgdata);
  394.             $info = ['w' => $w'h' => $h'cs' => $targetcs'bpc' => 8'f' => 'FlateDecode''data' => $imgdata'type' => 'png',
  395.                 'parms' => '/DecodeParms <</Colors ' $ncols ' /BitsPerComponent 8 /Columns ' $w '>>'];
  396.             if ($dpi) {
  397.                 $info['set-dpi'] = $dpi;
  398.             }
  399.             if ($mask) {
  400.                 $mimgdata $this->gzCompress($mimgdata);
  401.                 $minfo = ['w' => $w'h' => $h'cs' => 'DeviceGray''bpc' => 8'f' => 'FlateDecode''data' => $mimgdata'type' => 'png',
  402.                     'parms' => '/DecodeParms <</Colors ' $ncols ' /BitsPerComponent 8 /Columns ' $w '>>'];
  403.                 if ($dpi) {
  404.                     $minfo['set-dpi'] = $dpi;
  405.                 }
  406.                 $tempfile '_tempImgPNG' md5($data) . random_int(110000) . '.png';
  407.                 $imgmask count($this->mpdf->images) + 1;
  408.                 $minfo['i'] = $imgmask;
  409.                 $this->mpdf->images[$tempfile] = $minfo;
  410.                 $info['masked'] = $imgmask;
  411.             } elseif ($trns) {
  412.                 $info['trns'] = $trns;
  413.             }
  414.             imagedestroy($im);
  415.         }
  416.         return $info;
  417.     }
  418.     private function jpgHeaderFromString(&$data)
  419.     {
  420.         $p 4;
  421.         $p += $this->twoBytesToInt(substr($data$p2)); // Length of initial marker block
  422.         $marker substr($data$p2);
  423.         while ($marker !== chr(255) . chr(192) && $marker !== chr(255) . chr(194)  && $marker !== chr(255) . chr(193) && $p strlen($data)) {
  424.             // Start of frame marker (FFC0) (FFC1) or (FFC2)
  425.             $p += $this->twoBytesToInt(substr($data$p 22)) + 2// Length of marker block
  426.             $marker substr($data$p2);
  427.         }
  428.         if ($marker !== chr(255) . chr(192) && $marker !== chr(255) . chr(194) && $marker !== chr(255) . chr(193)) {
  429.             return false;
  430.         }
  431.         return substr($data$p 210);
  432.     }
  433.     private function jpgDataFromHeader($hdr)
  434.     {
  435.         $bpc ord(substr($hdr21));
  436.         if (!$bpc) {
  437.             $bpc 8;
  438.         }
  439.         $h $this->twoBytesToInt(substr($hdr32));
  440.         $w $this->twoBytesToInt(substr($hdr52));
  441.         $channels ord(substr($hdr71));
  442.         if ($channels === 3) {
  443.             $colspace 'DeviceRGB';
  444.         } elseif ($channels === 4) {
  445.             $colspace 'DeviceCMYK';
  446.         } else {
  447.             $colspace 'DeviceGray';
  448.         }
  449.         return [$w$h$colspace$bpc$channels];
  450.     }
  451.     /**
  452.      * Corrects 2-byte integer to 8-bit depth value
  453.      * If original image is bpc != 8, tRNS will be in this bpc
  454.      * $im from imagecreatefromstring will always be in bpc=8
  455.      * So why do we only need to correct 16-bit tRNS and NOT 2 or 4-bit???
  456.      */
  457.     private function translateValue($s$bpc)
  458.     {
  459.         $n $this->twoBytesToInt($s);
  460.         if ($bpc == 16) {
  461.             $n = ($n >> 8);
  462.         }
  463.         //elseif ($bpc==4) { $n = ($n << 2); }
  464.         //elseif ($bpc==2) { $n = ($n << 4); }
  465.         return $n;
  466.     }
  467.     /**
  468.      * Read a 4-byte integer from string
  469.      */
  470.     private function fourBytesToInt($s)
  471.     {
  472.         return (ord($s[0]) << 24) + (ord($s[1]) << 16) + (ord($s[2]) << 8) + ord($s[3]);
  473.     }
  474.     /**
  475.      * Equivalent to _get_ushort
  476.      * Read a 2-byte integer from string
  477.      */
  478.     private function twoBytesToInt($s)
  479.     {
  480.         return (ord(substr($s01)) << 8) + ord(substr($s11));
  481.     }
  482.     private function gzCompress($data)
  483.     {
  484.         if (!function_exists('gzcompress')) {
  485.             throw new \Mpdf\MpdfException('gzcompress is not available. install ext-zlib extension.');
  486.         }
  487.         return gzcompress($data);
  488.     }
  489.     /**
  490.      * Throw an exception and save re-trying image URL's which have already failed
  491.      */
  492.     private function imageError($file$firstTime$msg)
  493.     {
  494.         $this->failedImages[$file] = true;
  495.         if ($firstTime && ($this->mpdf->showImageErrors || $this->mpdf->debug)) {
  496.             throw new \Mpdf\MpdfImageException(sprintf('%s (%s)'$msgsubstr($file0256)));
  497.         }
  498.         $this->logger->warning(sprintf('%s (%s)'$msg$file), ['context' => LogContext::IMAGES]);
  499.     }
  500.     /**
  501.      * @since mPDF 5.7.4
  502.      * @param string $url
  503.      * @return string
  504.      */
  505.     private function urldecodeParts($url)
  506.     {
  507.         $file $url;
  508.         $query '';
  509.         if (preg_match('/[?]/'$url)) {
  510.             $bits preg_split('/[?]/'$url2);
  511.             $file $bits[0];
  512.             $query '?' $bits[1];
  513.         }
  514.         $file rawurldecode($file);
  515.         $query urldecode($query);
  516.         return $file $query;
  517.     }
  518.     public function processJpg($data$file$firstTime$interpolation)
  519.     {
  520.         $ppUx 0;
  521.         $hdr $this->jpgHeaderFromString($data);
  522.         if (!$hdr) {
  523.             return $this->imageError($file$firstTime'Error parsing JPG header');
  524.         }
  525.         $a $this->jpgDataFromHeader($hdr);
  526.         $channels = (int) $a[4];
  527.         $j strpos($data'JFIF');
  528.         if ($j) {
  529.             // Read resolution
  530.             $unitSp ord(substr($data$j 71));
  531.             if ($unitSp 0) {
  532.                 $ppUx $this->twoBytesToInt(substr($data$j 82)); // horizontal pixels per meter, usually set to zero
  533.                 if ($unitSp === 2) { // = dots per cm (if == 1 set as dpi)
  534.                     $ppUx round($ppUx 10 25.4);
  535.                 }
  536.             }
  537.         }
  538.         if ($a[2] === 'DeviceCMYK' && ($this->mpdf->restrictColorSpace === || ($this->mpdf->PDFA && $this->mpdf->restrictColorSpace !== 3))) {
  539.             // convert to RGB image
  540.             if (!function_exists('gd_info')) {
  541.                 throw new \Mpdf\MpdfException(sprintf('JPG image may not use CMYK color space (%s).'$file));
  542.             }
  543.             if ($this->mpdf->PDFA && !$this->mpdf->PDFAauto) {
  544.                 $this->mpdf->PDFAXwarnings[] = sprintf('JPG image "%s" may not use CMYK color space. Image converted to RGB. The colour profile was altered'$file);
  545.             }
  546.             $im = @imagecreatefromstring($data);
  547.             if ($im) {
  548.                 $tempfile $this->cache->tempFilename('_tempImgPNG' md5($file) . random_int(110000) . '.png');
  549.                 imageinterlace($imfalse);
  550.                 $check = @imagepng($im$tempfile);
  551.                 if (!$check) {
  552.                     return $this->imageError($file$firstTimesprintf('Error creating temporary file "%s" when using GD library to parse JPG (CMYK) image'$tempfile));
  553.                 }
  554.                 // $info = $this->getImage($tempfile, false);
  555.                 $data file_get_contents($tempfile);
  556.                 $info $this->processPng($data$tempfilefalse$interpolation);
  557.                 if (!$info) {
  558.                     return $this->imageError($file$firstTimesprintf('Error parsing temporary file "%s" created with GD library to parse JPG (CMYK) image'$tempfile));
  559.                 }
  560.                 imagedestroy($im);
  561.                 unlink($tempfile);
  562.                 $info['type'] = 'jpg';
  563.                 if ($firstTime) {
  564.                     $info['i'] = count($this->mpdf->images) + 1;
  565.                     $info['interpolation'] = $interpolation// mPDF 6
  566.                     $this->mpdf->images[$file] = $info;
  567.                 }
  568.                 return $info;
  569.             }
  570.             return $this->imageError($file$firstTime'Error creating GD image file from JPG(CMYK) image');
  571.         }
  572.         if ($a[2] === 'DeviceRGB' && ($this->mpdf->PDFX || $this->mpdf->restrictColorSpace === 3)) {
  573.             // Convert to CMYK image stream - nominally returned as type='png'
  574.             $info $this->convertImage($data$a[2], 'DeviceCMYK'$a[0], $a[1], $ppUxfalse);
  575.             if (($this->mpdf->PDFA && !$this->mpdf->PDFAauto) || ($this->mpdf->PDFX && !$this->mpdf->PDFXauto)) {
  576.                 $this->mpdf->PDFAXwarnings[] = sprintf('JPG image may not use RGB color space - %s - (Image converted to CMYK. NB This will alter the colour profile of the image.)'$file);
  577.             }
  578.         } elseif (($a[2] === 'DeviceRGB' || $a[2] === 'DeviceCMYK') && $this->mpdf->restrictColorSpace === 1) {
  579.             // Convert to Grayscale image stream - nominally returned as type='png'
  580.             $info $this->convertImage($data$a[2], 'DeviceGray'$a[0], $a[1], $ppUxfalse);
  581.         } else {
  582.             // mPDF 6 Detect Adobe APP14 Tag
  583.             //$pos = strpos($data, "\xFF\xEE\x00\x0EAdobe\0");
  584.             //if ($pos !== false) {
  585.             //}
  586.             // mPDF 6 ICC profile
  587.             $offset 0;
  588.             $icc = [];
  589.             while (($pos strpos($data"ICC_PROFILE\0"$offset)) !== false) {
  590.                 // get ICC sequence length
  591.                 $length $this->twoBytesToInt(substr($data$pos 22)) - 16;
  592.                 $sn max(1ord($data[$pos 12]));
  593.                 $nom max(1ord($data[$pos 13]));
  594.                 $icc[$sn 1] = substr($data$pos 14$length);
  595.                 $offset = ($pos 14 $length);
  596.             }
  597.             // order and compact ICC segments
  598.             if (count($icc) > 0) {
  599.                 ksort($icc);
  600.                 $icc implode(''$icc);
  601.                 if (substr($icc364) !== 'acsp') {
  602.                     // invalid ICC profile
  603.                     $icc false;
  604.                 }
  605.                 $input substr($icc164);
  606.                 $output substr($icc204);
  607.                 // Ignore Color profiles for conversion to other colorspaces e.g. CMYK/Lab
  608.                 if ($input !== 'RGB ' || $output !== 'XYZ ') {
  609.                     $icc false;
  610.                 }
  611.             } else {
  612.                 $icc false;
  613.             }
  614.             $info = ['w' => $a[0], 'h' => $a[1], 'cs' => $a[2], 'bpc' => $a[3], 'f' => 'DCTDecode''data' => $data'type' => 'jpg''ch' => $channels'icc' => $icc];
  615.             if ($ppUx) {
  616.                 $info['set-dpi'] = $ppUx;
  617.             }
  618.         }
  619.         if (!$info) {
  620.             return $this->imageError($file$firstTime'Error parsing or converting JPG image');
  621.         }
  622.         if ($firstTime) {
  623.             $info['i'] = count($this->mpdf->images) + 1;
  624.             $info['interpolation'] = $interpolation// mPDF 6
  625.             $this->mpdf->images[$file] = $info;
  626.         }
  627.         return $info;
  628.     }
  629.     public function processPng($data$file$firstTime$interpolation)
  630.     {
  631.         $ppUx 0;
  632.         // Check signature
  633.         if (strpos($datachr(137) . 'PNG' chr(13) . chr(10) . chr(26) . chr(10)) !== 0) {
  634.             return $this->imageError($file$firstTime'Error parsing PNG identifier');
  635.         }
  636.         // Read header chunk
  637.         if (substr($data124) !== 'IHDR') {
  638.             return $this->imageError($file$firstTime'Incorrect PNG file (no IHDR block found)');
  639.         }
  640.         $w $this->fourBytesToInt(substr($data164));
  641.         $h $this->fourBytesToInt(substr($data204));
  642.         $bpc ord(substr($data241));
  643.         $errpng false;
  644.         $pngalpha false;
  645.         $channels 0;
  646.         //    if($bpc>8) { $errpng = 'not 8-bit depth'; }    // mPDF 6 Allow through to be handled as native PNG
  647.         $ct ord(substr($data251));
  648.         if ($ct === 0) {
  649.             $colspace 'DeviceGray';
  650.             $channels 1;
  651.         } elseif ($ct === 2) {
  652.             $colspace 'DeviceRGB';
  653.             $channels 3;
  654.         } elseif ($ct === 3) {
  655.             $colspace 'Indexed';
  656.             $channels 1;
  657.         } elseif ($ct === 4) {
  658.             $colspace 'DeviceGray';
  659.             $channels 1;
  660.             $errpng 'alpha channel';
  661.             $pngalpha true;
  662.         } else {
  663.             $colspace 'DeviceRGB';
  664.             $channels 3;
  665.             $errpng 'alpha channel';
  666.             $pngalpha true;
  667.         }
  668.         if ($ct && strpos($data'tRNS') !== false) {
  669.             $errpng 'transparency';
  670.             $pngalpha true;
  671.         } // mPDF 6
  672.         if ($ct === && strpos($data'iCCP') !== false) {
  673.             $errpng 'indexed plus ICC';
  674.         } // mPDF 6
  675.         // $pngalpha is used as a FLAG of any kind of transparency which COULD be tranferred to an alpha channel
  676.         // incl. single-color tarnsparency, depending which type of handling occurs later
  677.         if (ord(substr($data261)) !== 0) {
  678.             $errpng 'compression method';
  679.         } // only 0 should be specified
  680.         if (ord(substr($data271)) !== 0) {
  681.             $errpng 'filter method';
  682.         } // only 0 should be specified
  683.         if (ord(substr($data281)) !== 0) {
  684.             $errpng 'interlaced file';
  685.         }
  686.         $j strpos($data'pHYs');
  687.         if ($j) {
  688.             //Read resolution
  689.             $unitSp ord(substr($data$j 121));
  690.             if ($unitSp === 1) {
  691.                 $ppUx $this->fourBytesToInt(substr($data$j 44)); // horizontal pixels per meter, usually set to zero
  692.                 $ppUx round($ppUx 1000 25.4);
  693.             }
  694.         }
  695.         // mPDF 6 Gamma correction
  696.         $gamma 0;
  697.         $gAMA 0;
  698.         $j strpos($data'gAMA');
  699.         if ($j && strpos($data'sRGB') === false) { // sRGB colorspace - overrides gAMA
  700.             $gAMA $this->fourBytesToInt(substr($data$j 44)); // Gamma value times 100000
  701.             $gAMA /= 100000;
  702.             // http://www.libpng.org/pub/png/spec/1.2/PNG-Encoders.html
  703.             // "If the source file's gamma value is greater than 1.0, it is probably a display system exponent,..."
  704.             // ("..and you should use its reciprocal for the PNG gamma.")
  705.             //if ($gAMA > 1) { $gAMA = 1/$gAMA; }
  706.             // (Some) Applications seem to ignore it... appearing how it was probably intended
  707.             // Test Case - image(s) on http://www.w3.org/TR/CSS21/intro.html  - PNG has gAMA set as 1.45454
  708.             // Probably unintentional as mentioned above and should be 0.45454 which is 1 / 2.2
  709.             // Tested on Windows PC
  710.             // Firefox and Opera display gray as 234 (correct, but looks wrong)
  711.             // IE9 and Safari display gray as 193 (incorrect but looks right)
  712.             // See test different gamma chunks at http://www.libpng.org/pub/png/pngsuite-all-good.html
  713.         }
  714.         if ($gAMA) {
  715.             $gamma $gAMA;
  716.         }
  717.         // Don't need to apply gamma correction if == default i.e. 2.2
  718.         if ($gamma 2.15 && $gamma 2.25) {
  719.             $gamma 0;
  720.         }
  721.         // NOT supported at present
  722.         //$j = strpos($data,'sRGB');    // sRGB colorspace - overrides gAMA
  723.         //$j = strpos($data,'cHRM');    // Chromaticity and Whitepoint
  724.         // $firstTime added mPDF 6 so when PNG Grayscale with alpha using resrtictcolorspace to CMYK
  725.         // the alpha channel is sent through as secondtime as Indexed and should not be converted to CMYK
  726.         if ($firstTime && ($colspace === 'DeviceRGB' || $colspace === 'Indexed') && ($this->mpdf->PDFX || $this->mpdf->restrictColorSpace === 3)) {
  727.             // Convert to CMYK image stream - nominally returned as type='png'
  728.             $info $this->convertImage($data$colspace'DeviceCMYK'$w$h$ppUx$pngalpha$gamma$ct); // mPDF 5.7.2 Gamma correction
  729.             if (($this->mpdf->PDFA && !$this->mpdf->PDFAauto) || ($this->mpdf->PDFX && !$this->mpdf->PDFXauto)) {
  730.                 $this->mpdf->PDFAXwarnings[] = sprintf('PNG image may not use RGB color space - %s - (Image converted to CMYK. NB This will alter the colour profile of the image.)'$file);
  731.             }
  732.         } elseif ($firstTime && ($colspace === 'DeviceRGB' || $colspace === 'Indexed') && $this->mpdf->restrictColorSpace === 1) {
  733.             // $firstTime added mPDF 6 so when PNG Grayscale with alpha using resrtictcolorspace to CMYK
  734.             // the alpha channel is sent through as secondtime as Indexed and should not be converted to CMYK
  735.             // Convert to Grayscale image stream - nominally returned as type='png'
  736.             $info $this->convertImage($data$colspace'DeviceGray'$w$h$ppUx$pngalpha$gamma$ct); // mPDF 5.7.2 Gamma correction
  737.         } elseif (($this->mpdf->PDFA || $this->mpdf->PDFX) && $pngalpha) {
  738.             // Remove alpha channel
  739.             if ($this->mpdf->restrictColorSpace === 1) { // Grayscale
  740.                 $info $this->convertImage($data$colspace'DeviceGray'$w$h$ppUx$pngalpha$gamma$ct); // mPDF 5.7.2 Gamma correction
  741.             } elseif ($this->mpdf->restrictColorSpace === 3) { // CMYK
  742.                 $info $this->convertImage($data$colspace'DeviceCMYK'$w$h$ppUx$pngalpha$gamma$ct); // mPDF 5.7.2 Gamma correction
  743.             } elseif ($this->mpdf->PDFA) { // RGB
  744.                 $info $this->convertImage($data$colspace'DeviceRGB'$w$h$ppUx$pngalpha$gamma$ct); // mPDF 5.7.2 Gamma correction
  745.             }
  746.             if (($this->mpdf->PDFA && !$this->mpdf->PDFAauto) || ($this->mpdf->PDFX && !$this->mpdf->PDFXauto)) {
  747.                 $this->mpdf->PDFAXwarnings[] = sprintf('Transparency (alpha channel) not permitted in PDFA or PDFX files - %s - (Image converted to one without transparency.)'$file);
  748.             }
  749.         } elseif ($firstTime && ($errpng || $pngalpha || $gamma)) { // mPDF 5.7.2 Gamma correction
  750.             $gd function_exists('gd_info') ? gd_info() : [];
  751.             if (!isset($gd['PNG Support'])) {
  752.                 return $this->imageError($file$firstTimesprintf('GD library with PNG support required for image (%s)'$errpng));
  753.             }
  754.             $im = @imagecreatefromstring($data);
  755.             if (!$im) {
  756.                 return $this->imageError($file$firstTimesprintf('Error creating GD image from PNG file (%s)'$errpng));
  757.             }
  758.             $w imagesx($im);
  759.             $h imagesy($im);
  760.             $tempfile =  $this->cache->tempFilename('_tempImgPNG' md5($file) . random_int(110000) . '.png');
  761.             // Alpha channel set (including using tRNS for Paletted images)
  762.             if ($pngalpha) {
  763.                 if ($this->mpdf->PDFA) {
  764.                     throw new \Mpdf\MpdfException(sprintf('PDFA1-b does not permit images with alpha channel transparency (%s).'$file));
  765.                 }
  766.                 $imgalpha imagecreate($w$h);
  767.                 // generate gray scale pallete
  768.                 for ($c 0$c 256; ++$c) {
  769.                     imagecolorallocate($imgalpha$c$c$c);
  770.                 }
  771.                 // mPDF 6
  772.                 if ($colspace === 'Indexed') { // generate Alpha channel values from tRNS
  773.                     // Read transparency info
  774.                     $p strpos($data'tRNS');
  775.                     if ($p) {
  776.                         $n $this->fourBytesToInt(substr($data$p 44));
  777.                         $transparency substr($data$p 4$n);
  778.                         // ord($transparency[$index]) = the alpha value for that index
  779.                         // generate alpha channel
  780.                         for ($ypx 0$ypx $h; ++$ypx) {
  781.                             for ($xpx 0$xpx $w; ++$xpx) {
  782.                                 $colorindex imagecolorat($im$xpx$ypx);
  783.                                 if ($colorindex >= $n) {
  784.                                     $alpha 255;
  785.                                 } else {
  786.                                     $alpha ord($transparency[$colorindex]);
  787.                                 } // 0-255
  788.                                 if ($alpha 0) {
  789.                                     imagesetpixel($imgalpha$xpx$ypx$alpha);
  790.                                 }
  791.                             }
  792.                         }
  793.                     }
  794.                 } elseif ($ct === || $ct === 2) { // generate Alpha channel values from tRNS
  795.                     // Get transparency as array of RGB
  796.                     $p strpos($data'tRNS');
  797.                     if ($p) {
  798.                         $trns '';
  799.                         $n $this->fourBytesToInt(substr($data$p 44));
  800.                         $t substr($data$p 4$n);
  801.                         if ($colspace === 'DeviceGray') {  // ct===0
  802.                             $trns = [$this->translateValue(substr($t02), $bpc)];
  803.                         } else /* $colspace=='DeviceRGB' */ {  // ct==2
  804.                             $trns = [];
  805.                             $trns[0] = $this->translateValue(substr($t02), $bpc);
  806.                             $trns[1] = $this->translateValue(substr($t22), $bpc);
  807.                             $trns[2] = $this->translateValue(substr($t42), $bpc);
  808.                         }
  809.                         // generate alpha channel
  810.                         for ($ypx 0$ypx $h; ++$ypx) {
  811.                             for ($xpx 0$xpx $w; ++$xpx) {
  812.                                 $rgb imagecolorat($im$xpx$ypx);
  813.                                 $r = ($rgb >> 16) & 0xFF;
  814.                                 $g = ($rgb >> 8) & 0xFF;
  815.                                 $b $rgb 0xFF;
  816.                                 if ($colspace === 'DeviceGray' && $b == $trns[0]) {
  817.                                     $alpha 0;
  818.                                 } elseif ($r == $trns[0] && $g == $trns[1] && $b == $trns[2]) {
  819.                                     $alpha 0;
  820.                                 } else { // ct==2
  821.                                     $alpha 255;
  822.                                 }
  823.                                 if ($alpha 0) {
  824.                                     imagesetpixel($imgalpha$xpx$ypx$alpha);
  825.                                 }
  826.                             }
  827.                         }
  828.                     }
  829.                 } else {
  830.                     // extract alpha channel
  831.                     for ($ypx 0$ypx $h; ++$ypx) {
  832.                         for ($xpx 0$xpx $w; ++$xpx) {
  833.                             $alpha = (imagecolorat($im$xpx$ypx) & 0x7F000000) >> 24;
  834.                             if ($alpha 127) {
  835.                                 imagesetpixel($imgalpha$xpx$ypx255 - ($alpha 2));
  836.                             }
  837.                         }
  838.                     }
  839.                 }
  840.                 // NB This must happen after the Alpha channel is extracted
  841.                 // imagegammacorrect() removes the alpha channel data in $im - (I think this is a bug in PHP)
  842.                 if ($gamma) {
  843.                     imagegammacorrect($im$gamma2.2);
  844.                 }
  845.                 $tempfile_alpha =  $this->cache->tempFilename('_tempMskPNG' md5($file) . random_int(110000) . '.png');
  846.                 $check = @imagepng($imgalpha$tempfile_alpha);
  847.                 if (!$check) {
  848.                     return $this->imageError($file$firstTime'Failed to create temporary image file (' $tempfile_alpha ') parsing PNG image with alpha channel (' $errpng ')');
  849.                 }
  850.                 imagedestroy($imgalpha);
  851.                 // extract image without alpha channel
  852.                 $imgplain imagecreatetruecolor($w$h);
  853.                 imagealphablending($imgplainfalse); // mPDF 5.7.2
  854.                 imagecopy($imgplain$im0000$w$h);
  855.                 // create temp image file
  856.                 $check = @imagepng($imgplain$tempfile);
  857.                 if (!$check) {
  858.                     return $this->imageError($file$firstTime'Failed to create temporary image file (' $tempfile ') parsing PNG image with alpha channel (' $errpng ')');
  859.                 }
  860.                 imagedestroy($imgplain);
  861.                 // embed mask image
  862.                 //$minfo = $this->getImage($tempfile_alpha, false);
  863.                 $data file_get_contents($tempfile_alpha);
  864.                 $minfo $this->processPng($data$tempfile_alphafalse$interpolation);
  865.                 unlink($tempfile_alpha);
  866.                 if (!$minfo) {
  867.                     return $this->imageError($file$firstTime'Error parsing temporary file (' $tempfile_alpha ') created with GD library to parse PNG image');
  868.                 }
  869.                 $imgmask count($this->mpdf->images) + 1;
  870.                 $minfo['cs'] = 'DeviceGray';
  871.                 $minfo['i'] = $imgmask;
  872.                 $this->mpdf->images[$tempfile_alpha] = $minfo;
  873.                 // embed image, masked with previously embedded mask
  874.                 // $info = $this->getImage($tempfile, false);
  875.                 $data file_get_contents($tempfile);
  876.                 $info $this->processPng($data$tempfilefalse$interpolation);
  877.                 unlink($tempfile);
  878.                 if (!$info) {
  879.                     return $this->imageError($file$firstTime'Error parsing temporary file (' $tempfile ') created with GD library to parse PNG image');
  880.                 }
  881.                 $info['masked'] = $imgmask;
  882.                 if ($ppUx) {
  883.                     $info['set-dpi'] = $ppUx;
  884.                 }
  885.                 $info['type'] = 'png';
  886.                 if ($firstTime) {
  887.                     $info['i'] = count($this->mpdf->images) + 1;
  888.                     $info['interpolation'] = $interpolation// mPDF 6
  889.                     $this->mpdf->images[$file] = $info;
  890.                 }
  891.                 return $info;
  892.             }
  893.             // No alpha/transparency set (but cannot read directly because e.g. bit-depth != 8, interlaced etc)
  894.             // ICC profile
  895.             $icc false;
  896.             $p strpos($data'iCCP');
  897.             if ($p && $colspace === "Indexed") { // Cannot have ICC profile and Indexed together
  898.                 $p += 4;
  899.                 $n $this->fourBytesToInt(substr($data, ($p 8), 4));
  900.                 $nullsep strpos(substr($data$p80), chr(0));
  901.                 $icc substr($data, ($p $nullsep 2), ($n - ($nullsep 2)));
  902.                 $icc = @gzuncompress($icc); // Ignored if fails
  903.                 if ($icc) {
  904.                     if (substr($icc364) !== 'acsp') {
  905.                         $icc false;
  906.                     } // invalid ICC profile
  907.                     else {
  908.                         $input substr($icc164);
  909.                         $output substr($icc204);
  910.                         // Ignore Color profiles for conversion to other colorspaces e.g. CMYK/Lab
  911.                         if ($input !== 'RGB ' || $output !== 'XYZ ') {
  912.                             $icc false;
  913.                         }
  914.                     }
  915.                 }
  916.                 // Convert to RGB colorspace so can use ICC Profile
  917.                 if ($icc) {
  918.                     imagepalettetotruecolor($im);
  919.                     $colspace 'DeviceRGB';
  920.                     $channels 3;
  921.                 }
  922.             }
  923.             if ($gamma) {
  924.                 imagegammacorrect($im$gamma2.2);
  925.             }
  926.             imagealphablending($imfalse);
  927.             imagesavealpha($imfalse);
  928.             imageinterlace($imfalse);
  929.             $check = @imagepng($im$tempfile);
  930.             if (!$check) {
  931.                 return $this->imageError($file$firstTime'Failed to create temporary image file (' $tempfile ') parsing PNG image (' $errpng ')');
  932.             }
  933.             imagedestroy($im);
  934.             // $info = $this->getImage($tempfile, false);
  935.             $data file_get_contents($tempfile);
  936.             $info $this->processPng($data$tempfilefalse$interpolation);
  937.             unlink($tempfile);
  938.             if (!$info) {
  939.                 return $this->imageError($file$firstTime'Error parsing temporary file (' $tempfile ') created with GD library to parse PNG image');
  940.             }
  941.             if ($ppUx) {
  942.                 $info['set-dpi'] = $ppUx;
  943.             }
  944.             $info['type'] = 'png';
  945.             if ($firstTime) {
  946.                 $info['i'] = count($this->mpdf->images) + 1;
  947.                 $info['interpolation'] = $interpolation// mPDF 6
  948.                 if ($icc) {
  949.                     $info['ch'] = $channels;
  950.                     $info['icc'] = $icc;
  951.                 }
  952.                 $this->mpdf->images[$file] = $info;
  953.             }
  954.             return $info;
  955.         } else { // PNG image with no need to convert alph channels, bpc <> 8 etc.
  956.             $parms '/DecodeParms <</Predictor 15 /Colors ' $channels ' /BitsPerComponent ' $bpc ' /Columns ' $w '>>';
  957.             //Scan chunks looking for palette, transparency and image data
  958.             $pal '';
  959.             $trns '';
  960.             $pngdata '';
  961.             $icc false;
  962.             $p 33;
  963.             do {
  964.                 $n $this->fourBytesToInt(substr($data$p4));
  965.                 $p += 4;
  966.                 $type substr($data$p4);
  967.                 $p += 4;
  968.                 if ($type === 'PLTE') {
  969.                     //Read palette
  970.                     $pal substr($data$p$n);
  971.                     $p += $n;
  972.                     $p += 4;
  973.                 } elseif ($type === 'tRNS') {
  974.                     //Read transparency info
  975.                     $t substr($data$p$n);
  976.                     $p += $n;
  977.                     if ($ct === 0) {
  978.                         $trns = [ord(substr($t11))];
  979.                     } elseif ($ct === 2) {
  980.                         $trns = [ord(substr($t11)), ord(substr($t31)), ord(substr($t51))];
  981.                     } else {
  982.                         $pos strpos($tchr(0));
  983.                         if (is_int($pos)) {
  984.                             $trns = [$pos];
  985.                         }
  986.                     }
  987.                     $p += 4;
  988.                 } elseif ($type === 'IDAT') {
  989.                     $pngdata.=substr($data$p$n);
  990.                     $p += $n;
  991.                     $p += 4;
  992.                 } elseif ($type === 'iCCP') {
  993.                     $nullsep strpos(substr($data$p80), chr(0));
  994.                     $icc substr($data$p $nullsep 2$n - ($nullsep 2));
  995.                     $icc = @gzuncompress($icc); // Ignored if fails
  996.                     if ($icc) {
  997.                         if (substr($icc364) !== 'acsp') {
  998.                             $icc false;
  999.                         } // invalid ICC profile
  1000.                         else {
  1001.                             $input substr($icc164);
  1002.                             $output substr($icc204);
  1003.                             // Ignore Color profiles for conversion to other colorspaces e.g. CMYK/Lab
  1004.                             if ($input !== 'RGB ' || $output !== 'XYZ ') {
  1005.                                 $icc false;
  1006.                             }
  1007.                         }
  1008.                     }
  1009.                     $p += $n;
  1010.                     $p += 4;
  1011.                 } elseif ($type === 'IEND') {
  1012.                     break;
  1013.                 } elseif (preg_match('/[a-zA-Z]{4}/'$type)) {
  1014.                     $p += $n 4;
  1015.                 } else {
  1016.                     return $this->imageError($file$firstTime'Error parsing PNG image data');
  1017.                 }
  1018.             } while ($n);
  1019.             if (!$pngdata) {
  1020.                 return $this->imageError($file$firstTime'Error parsing PNG image data - no IDAT data found');
  1021.             }
  1022.             if ($colspace === 'Indexed' && empty($pal)) {
  1023.                 return $this->imageError($file$firstTime'Error parsing PNG image data - missing colour palette');
  1024.             }
  1025.             // mPDF 6 cannot have ICC profile and Indexed in a PDF document as both use the colorspace tag.
  1026.             if ($colspace === 'Indexed' && $icc) {
  1027.                 $icc false;
  1028.             }
  1029.             $info = [
  1030.                 'w' => $w,
  1031.                 'h' => $h,
  1032.                 'cs' => $colspace,
  1033.                 'bpc' => $bpc,
  1034.                 'f' => 'FlateDecode',
  1035.                 'parms' => $parms,
  1036.                 'pal' => $pal,
  1037.                 'trns' => $trns,
  1038.                 'data' => $pngdata,
  1039.                 'ch' => $channels,
  1040.                 'icc' => $icc
  1041.             ];
  1042.             $info['type'] = 'png';
  1043.             if ($ppUx) {
  1044.                 $info['set-dpi'] = $ppUx;
  1045.             }
  1046.         }
  1047.         if (!$info) {
  1048.             return $this->imageError($file$firstTime'Error parsing or converting PNG image');
  1049.         }
  1050.         if ($firstTime) {
  1051.             $info['i'] = count($this->mpdf->images) + 1;
  1052.             $info['interpolation'] = $interpolation// mPDF 6
  1053.             $this->mpdf->images[$file] = $info;
  1054.         }
  1055.         return $info;
  1056.     }
  1057.     public function processWebp($data$file$firstTime)
  1058.     {
  1059.         $im = @imagecreatefromstring($data);
  1060.         if (!function_exists('imagewebp') || false === $im) {
  1061.             return $this->imageError($file$firstTime'Missing GD support for WEBP images.');
  1062.         }
  1063.         $tempfile $this->cache->tempFilename('_tempImgPNG' md5($file) . random_int(110000) . '.jpg');
  1064.         $checkfile $this->cache->tempFilename('_tempImgPNG' md5($file) . random_int(110000) . '.jpg');
  1065.         $check imagewebp($im$checkfile);
  1066.         if (!$check) {
  1067.             return $this->imageError($file$firstTimesprintf('Error creating temporary file "%s" when using GD library to parse WEBP image'$checkfile));
  1068.         }
  1069.         @imagejpeg($im$tempfile);
  1070.         $data file_get_contents($tempfile);
  1071.         imagedestroy($im);
  1072.         unlink($tempfile);
  1073.         unlink($checkfile);
  1074.         return $data;
  1075.     }
  1076.     public function processSvg($data$file$firstTime)
  1077.     {
  1078.         $svg = new Svg($this->mpdf$this->otl$this->cssManager$this$this->sizeConverter$this->colorConverter$this->languageToFont$this->scriptToLanguage);
  1079.         $family $this->mpdf->FontFamily;
  1080.         $style $this->mpdf->FontStyle;
  1081.         $size $this->mpdf->FontSizePt;
  1082.         $info $svg->ImageSVG($data);
  1083.         // Restore font
  1084.         if ($family) {
  1085.             $this->mpdf->SetFont($family$style$sizefalse);
  1086.         }
  1087.         if (!$info) {
  1088.             return $this->imageError($file$firstTime'Error parsing SVG file');
  1089.         }
  1090.         $info['type'] = 'svg';
  1091.         $info['i'] = count($this->mpdf->formobjects) + 1;
  1092.         $this->mpdf->formobjects[$file] = $info;
  1093.         return $info;
  1094.     }
  1095.     public function processGif($data$file$firstTime$interpolation)
  1096.     {
  1097.         $gd function_exists('gd_info')
  1098.             ? gd_info()
  1099.             : [];
  1100.         if (isset($gd['GIF Read Support']) && $gd['GIF Read Support']) {
  1101.             $im = @imagecreatefromstring($data);
  1102.             if ($im) {
  1103.                 $tempfile $this->cache->tempFilename('_tempImgPNG' md5($file) . random_int(110000) . '.png');
  1104.                 imagealphablending($imfalse);
  1105.                 imagesavealpha($imfalse);
  1106.                 imageinterlace($imfalse);
  1107.                 $check = @imagepng($im$tempfile);
  1108.                 if (!$check) {
  1109.                     return $this->imageError($file$firstTime'Error creating temporary file (' $tempfile ') when using GD library to parse GIF image');
  1110.                 }
  1111.                 // $info = $this->getImage($tempfile, false);
  1112.                 $data file_get_contents($tempfile);
  1113.                 $info $this->processPng($data$tempfilefalse$interpolation);
  1114.                 if (!$info) {
  1115.                     return $this->imageError($file$firstTime'Error parsing temporary file (' $tempfile ') created with GD library to parse GIF image');
  1116.                 }
  1117.                 imagedestroy($im);
  1118.                 unlink($tempfile);
  1119.                 $info['type'] = 'gif';
  1120.                 if ($firstTime) {
  1121.                     $info['i'] = count($this->mpdf->images) + 1;
  1122.                     $info['interpolation'] = $interpolation// mPDF 6
  1123.                     $this->mpdf->images[$file] = $info;
  1124.                 }
  1125.                 return $info;
  1126.             }
  1127.             return $this->imageError($file$firstTime'Error creating GD image file from GIF image');
  1128.         }
  1129.         $gif = new Gif();
  1130.         $h 0;
  1131.         $w 0;
  1132.         $gif->loadFile($data0);
  1133.         $nColors 0;
  1134.         $bgColor = -1;
  1135.         $colspace 'DeviceGray';
  1136.         $pal '';
  1137.         if (isset($gif->m_img->m_gih->m_bLocalClr) && $gif->m_img->m_gih->m_bLocalClr) {
  1138.             $nColors $gif->m_img->m_gih->m_nTableSize;
  1139.             $pal $gif->m_img->m_gih->m_colorTable->toString();
  1140.             if ((isset($bgColor)) && $bgColor !== -1) { // mPDF 5.7.3
  1141.                 $bgColor $gif->m_img->m_gih->m_colorTable->colorIndex($bgColor);
  1142.             }
  1143.             $colspace 'Indexed';
  1144.         } elseif (isset($gif->m_gfh->m_bGlobalClr) && $gif->m_gfh->m_bGlobalClr) {
  1145.             $nColors $gif->m_gfh->m_nTableSize;
  1146.             $pal $gif->m_gfh->m_colorTable->toString();
  1147.             if ((isset($bgColor)) && $bgColor != -1) {
  1148.                 $bgColor $gif->m_gfh->m_colorTable->colorIndex($bgColor);
  1149.             }
  1150.             $colspace 'Indexed';
  1151.         }
  1152.         $trns '';
  1153.         if (isset($gif->m_img->m_bTrans) && $gif->m_img->m_bTrans && ($nColors 0)) {
  1154.             $trns = [$gif->m_img->m_nTrans];
  1155.         }
  1156.         $gifdata $gif->m_img->m_data;
  1157.         $w $gif->m_gfh->m_nWidth;
  1158.         $h $gif->m_gfh->m_nHeight;
  1159.         $gif->ClearData();
  1160.         if ($colspace === 'Indexed' && empty($pal)) {
  1161.             return $this->imageError($file$firstTime'Error parsing GIF image - missing colour palette');
  1162.         }
  1163.         if ($this->mpdf->compress) {
  1164.             $gifdata $this->gzCompress($gifdata);
  1165.             $info = ['w' => $w'h' => $h'cs' => $colspace'bpc' => 8'f' => 'FlateDecode''pal' => $pal'trns' => $trns'data' => $gifdata];
  1166.         } else {
  1167.             $info = ['w' => $w'h' => $h'cs' => $colspace'bpc' => 8'pal' => $pal'trns' => $trns'data' => $gifdata];
  1168.         }
  1169.         $info['type'] = 'gif';
  1170.         if ($firstTime) {
  1171.             $info['i'] = count($this->mpdf->images) + 1;
  1172.             $info['interpolation'] = $interpolation// mPDF 6
  1173.             $this->mpdf->images[$file] = $info;
  1174.         }
  1175.         return $info;
  1176.     }
  1177.     public function processBmp($data$file$firstTime$interpolation)
  1178.     {
  1179.         if ($this->bmp === null) {
  1180.             $this->bmp = new Bmp($this->mpdf);
  1181.         }
  1182.         $info $this->bmp->_getBMPimage($data$file);
  1183.         if (isset($info['error'])) {
  1184.             return $this->imageError($file$firstTime$info['error']);
  1185.         }
  1186.         if ($firstTime) {
  1187.             $info['i'] = count($this->mpdf->images) + 1;
  1188.             $info['interpolation'] = $interpolation// mPDF 6
  1189.             $this->mpdf->images[$file] = $info;
  1190.         }
  1191.         return $info;
  1192.     }
  1193.     public function processWmf($data$file$firstTime)
  1194.     {
  1195.         if ($this->wmf === null) {
  1196.             $this->wmf = new Wmf($this->mpdf$this->colorConverter);
  1197.         }
  1198.         $wmfres $this->wmf->_getWMFimage($data);
  1199.         if ($wmfres[0] == 0) {
  1200.             if ($wmfres[1]) {
  1201.                 return $this->imageError($file$firstTime$wmfres[1]);
  1202.             }
  1203.             return $this->imageError($file$firstTime'Error parsing WMF image');
  1204.         }
  1205.         $info = ['x' => $wmfres[2][0], 'y' => $wmfres[2][1], 'w' => $wmfres[3][0], 'h' => $wmfres[3][1], 'data' => $wmfres[1]];
  1206.         $info['i'] = count($this->mpdf->formobjects) + 1;
  1207.         $info['type'] = 'wmf';
  1208.         $this->mpdf->formobjects[$file] = $info;
  1209.         return $info;
  1210.     }
  1211.     public function processUnknownType($data$file$firstTime$interpolation)
  1212.     {
  1213.         $gd function_exists('gd_info')
  1214.             ? gd_info()
  1215.             : [];
  1216.         if (isset($gd['PNG Support']) && $gd['PNG Support']) {
  1217.             $im = @imagecreatefromstring($data);
  1218.             if (!$im) {
  1219.                 return $this->imageError($file$firstTime'Error parsing image file - image type not recognised and/or not supported by GD imagecreate');
  1220.             }
  1221.             $tempfile $this->cache->tempFilename('_tempImgPNG' md5($file) . random_int(110000) . '.png');
  1222.             imagealphablending($imfalse);
  1223.             imagesavealpha($imfalse);
  1224.             imageinterlace($imfalse);
  1225.             $check = @imagepng($im$tempfile);
  1226.             if (!$check) {
  1227.                 return $this->imageError($file$firstTimesprintf('Error creating temporary file "%s" when using GD library to parse unknown image type'$tempfile));
  1228.             }
  1229.             //$info = $this->getImage($tempfile, false);
  1230.             $data file_get_contents($tempfile);
  1231.             $info $this->processPng($data$tempfilefalse$interpolation);
  1232.             imagedestroy($im);
  1233.             unlink($tempfile);
  1234.             if (!$info) {
  1235.                 return $this->imageError($file$firstTimesprintf('Error parsing temporary file "%s" created with GD library to parse unknown image type'$tempfile));
  1236.             }
  1237.             $info['type'] = 'png';
  1238.             if ($firstTime) {
  1239.                 $info['i'] = count($this->mpdf->images) + 1;
  1240.                 $info['interpolation'] = $interpolation// mPDF 6
  1241.                 $this->mpdf->images[$file] = $info;
  1242.             }
  1243.             return $info;
  1244.         }
  1245.     }
  1246. }