|
|||||||
| Discussion & Feature Requests Post feature requests, and discussion regarding any aspect of the script that is not a bug. |
![]() |
|
|
Thread Tools | Display Modes |
|
#1
|
|||
|
|||
|
This post applies to the FPDF driver only, not pdflib or ghostscript.
Currently with FPDF, if you have a PNG with embedded alpha-channel transparency, the alpha channel is ignored. I found a nice add-on class for FPDF that enables alpha-channel transparency for PNGs here: http://staff.dasdeck.de/valentin/fpdf/fpdf_alpha/. I took it apart and put it in the html2ps code. Basically, if it runs into a PNG with an embedded alpha layer, it will make two flat PNGs and use one to mask the other. It's pretty groovy. I learned a lot by playing with this code. Both HTML2PS and FPDF are very impressive and well-made. Thanks to Derren and Konstantin and also to Valentin Schmidt, the writer of the FPDF extension I copied from. ( lines that are new have a ### after them, unless the entire function is new.) File: pdf.fpdf.php Class: FPDF All the changes to this file are to the FPDF class (line 1036) Add a var for keeping track of temp files to the FPDF class. (Around line 1092) Code:
var $_forms; var $_form_radios; var $_pages; var $tmpFiles = array(); ### Code:
function Close() {
//Terminate document
if ($this->state == FPDF_STATE_COMPLETED) {
return;
};
if ($this->page==0) {
$this->AddPage();
};
//Close page
$this->_endpage();
//Close document
$this->_enddoc();
foreach($this->tmpFiles as $tmp) @unlink($tmp); ###
}
Replace Image() function: Code:
function Image($file,$x,$y,$w=0,$h=0,$typeBogus='',$link='', $isMask=false, $maskImg=0, $type='', $alpha=true)
{
//Put an image on the page
if(!isset($this->images[$file]))
{
//First use of image, get info
if($type=='')
{
$pos=strrpos($file,'.');
if(!$pos)
$this->Error('Image file has no extension and no type was specified: '.$file);
$type=substr($file,$pos+1);
}
$type=strtolower($type);
$mqr=get_magic_quotes_runtime();
set_magic_quotes_runtime(0);
if($type=='jpg' || $type=='jpeg'){
$info=$this->_parsejpg($file);
}
elseif($type=='png'){
$info=$this->_parsepng($file);
if ($alpha){
$this->ImagePngWithAlpha($file,$x,$y,$w,$h,$link);
return;
}
}
else
{
//Allow for additional formats
$mtd='_parse'.$type;
if(!method_exists($this,$mtd))
$this->Error('Unsupported image type: '.$type);
$info=$this->$mtd($file);
}
set_magic_quotes_runtime($mqr);
if ($isMask){
$info['cs']="DeviceGray"; // try to force grayscale (instead of indexed)
}
$info['i']=count($this->images)+1;
if ($maskImg>0){
$info['masked'] = $maskImg;
}
$this->images[$file]=$info;
}
else
{
$info=$this->images[$file];
}
//Automatic width and height calculation if needed
if($w==0 && $h==0)
{
//Put image at 72 dpi
$w=$info['w']/$this->k;
$h=$info['h']/$this->k;
}
if($w==0)
$w=$h*$info['w']/$info['h'];
if($h==0)
$h=$w*$info['h']/$info['w'];
if ($isMask){
$x = $this->fwPt + 10; // embed hidden, ouside the canvas
}
$info = $this->images[$file];
$this->_out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q',
$w*$this->k,
$h*$this->k,
$x*$this->k,
($this->h-($y+$h))*$this->k,
$info['i']));
return $info['i'];
}
Add these two new functions after Image(): ImagePngWithAlpha() and _gamma() Code:
function ImagePngWithAlpha($file,$x,$y,$w=0,$h=0,$link='')
{
$tmp_alpha = tempnam(WRITER_TEMPDIR, 'mska');
$this->tmpFiles[] = $tmp_alpha;
$tmp_plain = tempnam(WRITER_TEMPDIR, 'mskp');
$this->tmpFiles[] = $tmp_plain;
list($wpx, $hpx) = getimagesize($file);
$img = imagecreatefrompng($file);
$alpha_img = imagecreate( $wpx, $hpx );
// generate gray scale pallete
for($c=0;$c<256;$c++){
imagecolorallocate($alpha_img, $c, $c, $c);
}
// extract alpha channel
$xpx=0;
while ($xpx<$wpx){
$ypx = 0;
while ($ypx<$hpx){
$color_index = imagecolorat($img, $xpx, $ypx);
$alpha = ($color_index & 0x7F000000) >> 24;
imagesetpixel($alpha_img, $xpx, $ypx, $this->_gamma( (127-$alpha)*255/127) );
$ypx++;
}
$xpx++;
}
imagepng($alpha_img, $tmp_alpha);
imagedestroy($alpha_img);
// extract image without alpha channel
$plain_img = imagecreatetruecolor ( $wpx, $hpx );
imagecopy ($plain_img, $img, 0, 0, 0, 0, $wpx, $hpx );
imagepng($plain_img, $tmp_plain);
imagedestroy($plain_img);
//first embed mask image (w, h, x, will be ignored)
$maskImg = $this->Image($tmp_alpha, 0,0,0,0, 'PNG', '', true, NULL, 'PNG', false);
//embed image, masked with previously embedded mask
$this->Image($tmp_plain,$x,$y,$w,$h,'PNG',$link, false, $maskImg, 'PNG', false);
}
// GD seems to use a different gamma, this method is used to correct it again
function _gamma($v){
return pow ($v/255, 2.2) * 255;
}
Add a new "mask" flag for masked images in _putimages(): Code:
function _putimages()
{
$filter=($this->compress) ? '/Filter /FlateDecode ' : '';
reset($this->images);
while(list($file,$info)=each($this->images))
{
$this->_newobj();
$this->images[$file]['n']=$this->n;
$this->_out('<</Type /XObject');
$this->_out('/Subtype /Image');
$this->_out('/Width '.$info['w']);
$this->_out('/Height '.$info['h']);
if (isset($info["masked"])) $this->_out('/SMask '.($this->n-1).' 0 R'); ###
if($info['cs']=='Indexed')
$this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]');
else
{
$this->_out('/ColorSpace /'.$info['cs']);
if($info['cs']=='DeviceCMYK')
$this->_out('/Decode [1 0 1 0 1 0 1 0]');
}
$this->_out('/BitsPerComponent '.$info['bpc']);
if(isset($info['f']))
$this->_out('/Filter /'.$info['f']);
if(isset($info['parms']))
$this->_out($info['parms']);
if(isset($info['trns']) && is_array($info['trns']))
{
$trns='';
for($i=0;$i<count($info['trns']);$i++)
$trns.=$info['trns'][$i].' '.$info['trns'][$i].' ';
$this->_out('/Mask ['.$trns.']');
}
$this->_out('/Length '.strlen($info['data']).'>>');
$this->_putstream($info['data']);
unset($this->images[$file]['data']);
$this->_out('endobj');
//Palette
if($info['cs']=='Indexed')
{
$this->_newobj();
$pal=($this->compress) ? gzcompress($info['pal']) : $info['pal'];
$this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
$this->_putstream($pal);
$this->_out('endobj');
}
}
}
Last edited by Guud Spelar : 03-23-2009 at 08:15 AM. Reason: replaced tempnam('.', 'mska') with tempnam(WRITER_TEMPDIR, 'mska') in ImagePngWithAlpha |
|
#2
|
|||
|
|||
|
Replace the 'alpha' error message in _parsepng with a string return
Code:
// Extract info from a PNG file
function _parsepng($file) {
$f = fopen($file,'rb');
if (!$f) {
$this->Error('Can\'t open image file: '.$file);
};
//Check signature
if (fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
$this->Error('Not a PNG file: '.$file);
};
//Read header chunk
fread($f,4);
if (fread($f,4)!='IHDR') {
$this->Error('Incorrect PNG file: '.$file);
};
$w = $this->_freadint($f);
$h = $this->_freadint($f);
$bpc = ord(fread($f,1));
if ($bpc>8) {
$this->Error('16-bit depth not supported: '.$file);
};
$ct=ord(fread($f,1));
if ($ct==0) {
$colspace='DeviceGray';
} elseif($ct==2) {
$colspace='DeviceRGB';
} elseif($ct==3) {
$colspace='Indexed';
} else {
//$this->Error('Alpha channel not supported: '.$file); ###
return 'alpha'; ###
};
if (ord(fread($f,1))!=0) {
$this->Error('Unknown compression method: '.$file);
};
if (ord(fread($f,1))!=0) {
$this->Error('Unknown filter method: '.$file);
};
if (ord(fread($f,1))!=0) {
$this->Error('Interlacing not supported: '.$file);
};
fread($f,4);
$parms='/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
//Scan chunks looking for palette, transparency and image data
$pal='';
$trns='';
$data='';
do {
$n=$this->_freadint($f);
$type=fread($f,4);
if ($type=='PLTE') {
//Read palette
$pal=fread($f,$n);
fread($f,4);
} elseif($type=='tRNS') {
//Read transparency info
$t=fread($f,$n);
if ($ct==0) {
$trns=array(ord(substr($t,1,1)));
} elseif($ct==2) {
$trns=array(ord(substr($t,1,1)),ord(substr($t,3,1)),ord(substr($t,5,1)));
} else {
$pos=strpos($t,chr(0));
if ($pos!==false) {
$trns=array($pos);
}
}
fread($f,4);
} elseif ($type=='IDAT') {
//Read image data block
$data.=fread($f,$n);
fread($f,4);
} elseif ($type=='IEND') {
break;
} else {
fread($f,$n+4);
};
} while($n);
if ($colspace=='Indexed' && empty($pal)) {
$this->Error('Missing palette in '.$file);
};
fclose($f);
return array('w' => $w,
'h' => $h,
'cs' => $colspace,
'bpc' => $bpc,
'f' => 'FlateDecode',
'parms' => $parms,
'pal' => $pal,
'trns' => $trns,
'data' => $data);
}
File: output.fpdf.class.php Class: OutputDriverFPDF Change imagepng() to copy(). I'm not sure if this is the best way to do this, but it worked for me. For some reason, I don't know why, when imagepng is used at this point, it causes the PNG to lose its alpha channel. So instead of using imagepng to create a new image in the temp directory from the cached file, it just copies the file from the cache folder to the temp folder. Code:
function _mktempimage($image) {
$tempnam = tempnam(WRITER_TEMPDIR, WRITER_FILE_PREFIX);
switch ($image->get_type()) {
case 'image/png':
$filename = $tempnam . '.png';
copy($image->get_filename(),$filename); ##
break;
case 'image/jpeg':
default:
$filename = $tempnam . '.jpg';
imagejpeg($image->get_handle(), $filename);
break;
}
unlink($tempnam);
return $filename;
}
|
|
#3
|
|||
|
|||
|
Will this be available in future release?
|
|
#4
|
|||
|
|||
|
Guud thank you so much. This was tremendously helpful
|
![]() |
| Thread Tools | |
| Display Modes | |
|
|