<?php
require('fpdf.php');
class PDF_Gradients extends FPDF{
    protected $gradients = array();
    function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)){
        $this->Clip($x,$y,$w,$h);
        $this->Gradient(2,$col1,$col2,$coords);
    }
    function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)){
        $this->Clip($x,$y,$w,$h);
        $this->Gradient(3,$col1,$col2,$coords);
    }
    function CoonsPatchMesh($x, $y, $w, $h, $col1=array(), $col2=array(), $col3=array(), $col4=array(), $coords=array(0.00,0.0,0.33,0.00,0.67,0.00,1.00,0.00,1.00,0.33,1.00,0.67,1.00,1.00,0.67,1.00,0.33,1.00,0.00,1.00,0.00,0.67,0.00,0.33), $coords_min=0, $coords_max=1){
        $this->Clip($x,$y,$w,$h);        
        $n = count($this->gradients)+1;
        $this->gradients[$n]['type']=6; //coons patch mesh
        //check the coords array if it is the simple array or the multi patch array
        if(!isset($coords[0]['f'])){
            //simple array -> convert to multi patch array
            if(!isset($col1[1]))
                $col1[1]=$col1[2]=$col1[0];
            if(!isset($col2[1]))
                $col2[1]=$col2[2]=$col2[0];
            if(!isset($col3[1]))
                $col3[1]=$col3[2]=$col3[0];
            if(!isset($col4[1]))
                $col4[1]=$col4[2]=$col4[0];
            $patch_array[0]['f']=0;
            $patch_array[0]['points']=$coords;
            $patch_array[0]['colors'][0]['r']=$col1[0];
            $patch_array[0]['colors'][0]['g']=$col1[1];
            $patch_array[0]['colors'][0]['b']=$col1[2];
            $patch_array[0]['colors'][1]['r']=$col2[0];
            $patch_array[0]['colors'][1]['g']=$col2[1];
            $patch_array[0]['colors'][1]['b']=$col2[2];
            $patch_array[0]['colors'][2]['r']=$col3[0];
            $patch_array[0]['colors'][2]['g']=$col3[1];
            $patch_array[0]['colors'][2]['b']=$col3[2];
            $patch_array[0]['colors'][3]['r']=$col4[0];
            $patch_array[0]['colors'][3]['g']=$col4[1];
            $patch_array[0]['colors'][3]['b']=$col4[2];
        }
        else{
            //multi patch array
            $patch_array=$coords;
        }
        $bpcd=65535; //16 BitsPerCoordinate
        //build the data stream
        $this->gradients[$n]['stream']='';
        for($i=0;$i<count($patch_array);$i++){
            $this->gradients[$n]['stream'].=chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
            for($j=0;$j<count($patch_array[$i]['points']);$j++){
                //each point as 16 bit
                $patch_array[$i]['points'][$j]=(($patch_array[$i]['points'][$j]-$coords_min)/($coords_max-$coords_min))*$bpcd;
                if($patch_array[$i]['points'][$j]<0) $patch_array[$i]['points'][$j]=0;
                if($patch_array[$i]['points'][$j]>$bpcd) $patch_array[$i]['points'][$j]=$bpcd;
                $val=(int)floor($patch_array[$i]['points'][$j]);
                $this->gradients[$n]['stream'].=chr(intdiv($val,256));
                $this->gradients[$n]['stream'].=chr($val%256);
            }
            for($j=0;$j<count($patch_array[$i]['colors']);$j++){
                //each color component as 8 bit
                $this->gradients[$n]['stream'].=chr($patch_array[$i]['colors'][$j]['r']);
                $this->gradients[$n]['stream'].=chr($patch_array[$i]['colors'][$j]['g']);
                $this->gradients[$n]['stream'].=chr($patch_array[$i]['colors'][$j]['b']);
            }
        }
        //paint the gradient
        $this->_out('/Sh'.$n.' sh');
        //restore previous Graphic State
        $this->_out('Q');
    }
    function Clip($x,$y,$w,$h){
        //save current Graphic State
        $s='q';
        //set clipping area
        $s.=sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
        //set up transformation matrix for gradient
        $s.=sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
        $this->_out($s);
    }
    function Gradient($type, $col1, $col2, $coords){
        $n = count($this->gradients)+1;
        $this->gradients[$n]['type']=$type;
        if(!isset($col1[1]))
            $col1[1]=$col1[2]=$col1[0];
        $this->gradients[$n]['col1']=sprintf('%.3F %.3F %.3F',($col1[0]/255),($col1[1]/255),($col1[2]/255));
        if(!isset($col2[1]))
            $col2[1]=$col2[2]=$col2[0];
        $this->gradients[$n]['col2']=sprintf('%.3F %.3F %.3F',($col2[0]/255),($col2[1]/255),($col2[2]/255));
        $this->gradients[$n]['coords']=$coords;
        //paint the gradient
        $this->_out('/Sh'.$n.' sh');
        //restore previous Graphic State
        $this->_out('Q');
    }
    function _putshaders(){
        foreach($this->gradients as $id=>$grad){  
            if($grad['type']==2 || $grad['type']==3){
                $this->_newobj();
                $this->_put('<<');
                $this->_put('/FunctionType 2');
                $this->_put('/Domain [0.0 1.0]');
                $this->_put('/C0 ['.$grad['col1'].']');
                $this->_put('/C1 ['.$grad['col2'].']');
                $this->_put('/N 1');
                $this->_put('>>');
                $this->_put('endobj');
                $f1=$this->n;
            }
            
            $this->_newobj();
            $this->_put('<<');
            $this->_put('/ShadingType '.$grad['type']);
            $this->_put('/ColorSpace /DeviceRGB');
            if($grad['type']=='2'){
                $this->_put(sprintf('/Coords [%.3F %.3F %.3F %.3F]',$grad['coords'][0],$grad['coords'][1],$grad['coords'][2],$grad['coords'][3]));
                $this->_put('/Function '.$f1.' 0 R');
                $this->_put('/Extend [true true] ');
                $this->_put('>>');
            }
            elseif($grad['type']==3){
                //x0, y0, r0, x1, y1, r1
                //at this time radius of inner circle is 0
                $this->_put(sprintf('/Coords [%.3F %.3F 0 %.3F %.3F %.3F]',$grad['coords'][0],$grad['coords'][1],$grad['coords'][2],$grad['coords'][3],$grad['coords'][4]));
                $this->_put('/Function '.$f1.' 0 R');
                $this->_put('/Extend [true true] ');
                $this->_put('>>');
            }
            elseif($grad['type']==6){
                $this->_put('/BitsPerCoordinate 16');
                $this->_put('/BitsPerComponent 8');
                $this->_put('/Decode[0 1 0 1 0 1 0 1 0 1]');
                $this->_put('/BitsPerFlag 8');
                $this->_put('/Length '.strlen($grad['stream']));
                $this->_put('>>');
                $this->_putstream($grad['stream']);
            }
            $this->_put('endobj');
            $this->gradients[$id]['id']=$this->n;
        }
    }
    function _putresourcedict(){
        parent::_putresourcedict();
        $this->_put('/Shading <<');
        foreach($this->gradients as $id=>$grad)
             $this->_put('/Sh'.$id.' '.$grad['id'].' 0 R');
        $this->_put('>>');
    }
    function _putresources(){
        $this->_putshaders();
        parent::_putresources();
    }
}
?>
 
<?php
require('gradients.php');
$pdf = new PDF_Gradients();
//first page
$pdf->AddPage();
$pdf->SetFont('Arial','',14);
$pdf->Cell(0,5,'Page 1',0,1,'C');
$pdf->Ln();
//set colors for gradients (r,g,b) or (grey 0-255)
$red=array(255,0,0);
$blue=array(0,0,200);
$yellow=array(255,255,0);
$green=array(0,255,0);
$white=array(255);
$black=array(0);
//set the coordinates x1,y1,x2,y2 of the gradient (see linear_gradient_coords.jpg)
$coords=array(0,0,1,0);
//paint a linear gradient
$pdf->LinearGradient(20,25,80,80,$red,$blue,$coords);
//set the coordinates fx,fy,cx,cy,r of the gradient (see radial_gradient_coords.jpg)
$coords=array(0.5,0.5,1,1,1.2);
//paint a radial gradient
$pdf->RadialGradient(110,25,80,80,$white,$black,$coords);
//paint a coons patch mesh with default coordinates
$pdf->CoonsPatchMesh(20,115,80,80,$yellow,$blue,$green,$red);
//set the coordinates for the cubic Bézier points x1,y1 ... x12, y12 of the patch (see coons_patch_mesh_coords.jpg)
$coords=array(0.00,0.00, 0.33,0.20,             //lower left
              0.67,0.00, 1.00,0.00, 0.80,0.33,  //lower right
              0.80,0.67, 1.00,1.00, 0.67,0.80,  //upper right
              0.33,1.00, 0.00,1.00, 0.20,0.67,  //upper left
              0.00,0.33);                       //lower left
$coords_min=0;   //minimum value of the coordinates
$coords_max=1;   //maximum value of the coordinates
//paint a coons patch gradient with the above coordinates 
$pdf->CoonsPatchMesh(110,115,80,80,$yellow,$blue,$green,$red,$coords,$coords_min,$coords_max);
//second page
$pdf->AddPage();
$pdf->Cell(0,5,'Page 2',0,1,'C');
$pdf->Ln();
//first patch: f = 0
$patch_array[0]['f']=0;
$patch_array[0]['points']=array(0.00,0.00, 0.33,0.00,
                                0.67,0.00, 1.00,0.00, 1.00,0.33,
                                0.8,0.67, 1.00,1.00, 0.67,0.8,
                                0.33,1.80, 0.00,1.00, 0.00,0.67,
                                0.00,0.33);
$patch_array[0]['colors'][0]=array('r'=>255,'g'=>255,'b'=>0);
$patch_array[0]['colors'][1]=array('r'=>0,'g'=>0,'b'=>255);
$patch_array[0]['colors'][2]=array('r'=>0,'g'=>255,'b'=>0);
$patch_array[0]['colors'][3]=array('r'=>255,'g'=>0,'b'=>0);
//second patch - above the other: f = 2
$patch_array[1]['f']=2;
$patch_array[1]['points']=array(0.00,1.33,
                                0.00,1.67, 0.00,2.00, 0.33,2.00,
                                0.67,2.00, 1.00,2.00, 1.00,1.67,
                                1.5,1.33);
$patch_array[1]['colors'][0]=array('r'=>0,'g'=>0,'b'=>0);
$patch_array[1]['colors'][1]=array('r'=>255,'g'=>0,'b'=>255);
//third patch - right of the above: f = 3
$patch_array[2]['f']=3;
$patch_array[2]['points']=array(1.33,0.80,
                                1.67,1.50, 2.00,1.00, 2.00,1.33,
                                2.00,1.67, 2.00,2.00, 1.67,2.00,
                                1.33,2.00);
$patch_array[2]['colors'][0]=array('r'=>0,'g'=>255,'b'=>255);
$patch_array[2]['colors'][1]=array('r'=>0,'g'=>0,'b'=>0);
//fourth patch - below the above, which means left(?) of the above: f = 1
$patch_array[3]['f']=1;
$patch_array[3]['points']=array(2.00,0.67,
                                2.00,0.33, 2.00,0.00, 1.67,0.00,
                                1.33,0.00, 1.00,0.00, 1.00,0.33,
                                0.8,0.67);
$patch_array[3]['colors'][0]=array('r'=>0,'g'=>0,'b'=>0);
$patch_array[3]['colors'][1]=array('r'=>0,'g'=>0,'b'=>255);
$coords_min=0;
$coords_max=2;
$pdf->CoonsPatchMesh(10,25,190,200,'','','','',$patch_array,$coords_min,$coords_max);
$pdf->Output();
?>
 
View the result