FPDF - calculating X and page breaks problem

I have asked something similar before but have never really got the whole problem resolved.

PDF’s are created from database content using FPDF and the problem I am having is that is a products descriptive text overflows from one page to another, then the associated image is pushed right to the bottom of the next page (with a large gap between the end of the text and the placement of the image), even if there is plenty of room to display the image on the bottom of the previous page.

I am presuming this is to do with my calculation to prevent images from being displayed at the bottom of a page if they will overflow the page - a page break is forced if this this the case but I really can’t work out how to resolve the problem. I either need the image to display where it should, or otherwise (if this occurrence is going to happen) force all the product text and image onto a new page (either way is fine, I just need to know how to resolve the problem).

The php code for creating the PDF is:

if (!empty($_GET['parts'])) { 

$pdf=new PDF();
$pdf->SetFont('Arial','',12);

        $objcategory = new category;
        $objcategory->PDFcatsearch();
        
        $current = '';
        
        while ($catRow=$objcategory->get_row())
        { 
                $count_subcats=0;
                $objsubcategory = new category;
                $objsubcategory->catid=$catRow['catid'];
                $objsubcategory->PDFsubcatsearch();
                $count_subcats=$objsubcategory->total_rows;
                
                    $count_cat_products=0;
                    $objcatproducts = new product;
                    $objcatproducts->catid=$catRow['catid'];
                    $objcatproducts->PDFcatprodsearch();
                    $count_cat_products=$objcatproducts->total_rows;
                
                $pdf->startPageNums();
                if ($count_subcats==0 || $count_cat_products>0) {
                    $pdf->PrintPage('',stripslashes(html_entity_decode($catRow['catname'])));
                    $pdf->TOC_Entry(stripslashes(html_entity_decode($catRow['catname'])), 0, 1);
                    
                    $objcatproduct = new product;
                    $objcatproduct->catid=$catRow['catid'];
                    $objcatproduct->searchPDFcatprodinclude();
    
                    while ($catprodRow = $objcatproduct->get_row()) {
                    
                        $cpfullwood='';
                        $cpfusion='';
                        $cppacko='';
                        // Get a list of images for this product
                        $cpresults=mysql_query("SELECT imgname FROM prodimgs WHERE prodid='".$catprodRow['prodid']."' LIMIT 1");
                        $cpimages=mysql_fetch_assoc($cpresults);
    
                        // If the total image height will push the images off of the page, start a new page
                        $cpimgheights=0;
                        if (!empty($cpimages)) {
                            foreach ($cpimages as $cpimage) {
                                list($x, $y)=getimagesize("../images/products_thumb/tb_$cpimage");
                                $cpimgheights+=($y / 3.78);
                            }
                            if ($cpimgheights + $pdf->GetY() >= 280) {  // <-- this number may need to be adjusted to account for margins, headers and footers (a full page with no margins is 297mm tall)
                                $pdf->AddPage();
                            }
                        }
    
                        $pdf->ProductName(html_entity_decode($catprodRow['prodname']));
                            
                        $cpy_init = $pdf->GetY();
                        if ($_GET['parts']=='all') {
                            if ($catprodRow['prodpart']!='' && $catprodRow['fusion_part']=='' && $catprodRow['packo_part']=='') {
                                $pdf->ProductPart('Part Number: '.$catprodRow['prodpart']);
                            }
                            elseif ($catprodRow['prodpart']!='' || $catprodRow['fusion_part']!='' || $catprodRow['packo_part']!='') {
                                if (!empty($catprodRow['prodpart'])) { $cpfullwood=$catprodRow['prodpart'];}
                                if ($catprodRow['fusion_part']!='') {
                                    if (!empty($cpfullwood)) {
                                    $cpfusion='  :::  '.$catprodRow['fusion_part'];
                                    }
                                    else {
                                    $cpfusion=$catprodRow['fusion_part'];
                                    }
                                }
                                if ($catprodRow['packo_part']!='') {
                                    if (!empty($cpfullwood) || !empty($cpfusion)) {
                                    $cppacko='  :::  '.$catprodRow['packo_part'];
                                    }
                                    else {
                                    $cppacko=$catprodRow['packo_part'];
                                    }
                                }
                                $pdf->ProductPart('Part Number(s): '.$cpfullwood.''.$cpfusion.''.$cppacko);
                            }
                        }
                        elseif ($_GET['parts']=='packo') {
                            if ($catprodRow['packo_part']!='') {
                                $pdf->ProductPart('Part Number: '.$catprodRow['packo_part']);
                            }
                        }
                        elseif ($_GET['parts']=='fusion') {
                            if ($catprodRow['fusion_part']!='') {
                                $pdf->ProductPart('Part Number: '.$catprodRow['fusion_part']);
                            }
                        }
                        elseif ($_GET['parts']=='fullwood') {
                            if ($catprodRow['prodpart']!='') {
                                $pdf->ProductPart('Part Number: '.$catprodRow['prodpart']);
                            }
                        }
                        if ($catprodRow['proddesc']!='') {
                        
                            $cpproddesc=trim(str_replace('[b]','',
                                str_replace('[/b]','',
                                str_replace('[i]','',
                                str_replace('[/i]','',
                                str_replace('[u]','',
                                str_replace('[/u]','',
                                str_replace('[ul]','',
                                str_replace('[/ul]','',
                                str_replace('[li]','',
                                str_replace('[/li]','',
                                str_replace('[a]','',
                                str_replace('','www.fullwood.com/',
                                str_replace('[exturl]','',
                                str_replace('',' (',
                                str_replace('[/exturl]',' (',
                                str_replace('[/a]',')',$catprodRow['proddesc'])))))))))))))))));
                                
                            $pdf->ProductDesc(html_entity_decode($cpproddesc));
                        }
                        if ($catprodRow['prodfeat']!='') {
                            $pdf->ProductFeat(html_entity_decode($catprodRow['prodfeat']));
                        }
                        $cpy_text = $pdf->GetY();
    
                        if (!empty($cpimages)) {
                            foreach ($cpimages as $cpimage) {
    
                                $pdf->SetY($cpy_init);
                            
                                // Get the images dimensions (in pixels)
                                list($x, $y)=getimagesize("../images/products_thumb/tb_$cpimage");
    
                                // Convert the image height form pixels to millimetres
                                $cpimage_height=$y / 3.78;
                                $cpimage_width=$x / 3.78;
    
                                // Set the current Y position to the bottom of the image (+5 mm margin)
                                $pdf->SetY($cpy_init + $cpimage_height + 5);
    
                                $pdf->Image("../images/products_thumb/tb_$cpimage", 160, $cpy_init, $cpimage_width, $cpimage_height);
    
                                $cpy_image=$pdf->getY();
                                if ($cpy_text>$cpy_image) $pdf->SetY($cpy_text);
     
                            }
    
                        }
                    }
                }
                else {
                    while ($subcatRow=$objsubcategory->get_row())
                    {
                        $count_subcat_products=0;
                        $objsubcatproducts = new product;
                        $objsubcatproducts->catid=$subcatRow['catid'];
                        $objsubcatproducts->PDFsubcatprodsearch();
                        $count_subcat_products=$objsubcatproducts->total_rows;
                        
                        if ($count_subcat_products>0) {
                            $pdf->PrintPage(stripslashes(html_entity_decode($subcatRow['catname'])),stripslashes(html_entity_decode($catRow['catname'])));
                                if ($catRow['catname'] != $current) {
                                    $pdf->TOC_Entry2(stripslashes(html_entity_decode($catRow['catname'])), 0, 0);
                                    $current = $catRow['catname'];
                                } 
                            $pdf->TOC_Entry(stripslashes(html_entity_decode($subcatRow['catname'])), 1, 1);
                            
                            $objsubcatproduct = new product;
                            $objsubcatproduct->catid=$subcatRow['catid'];
                            $objsubcatproduct->searchPDFsubcatprodinclude();
            
                            while ($subcatprodRow = $objsubcatproduct->get_row()) {
                            
                                $scpfullwood='';
                                $scpfusion='';
                                $scppacko='';
                                // Get a list of images for this product
                                $scpresults=mysql_query("SELECT imgname FROM prodimgs WHERE prodid='".$subcatprodRow['prodid']."' LIMIT 1");
                                $scpimages=mysql_fetch_assoc($scpresults);
            
                                // If the total image height will push the images off of the page, start a new page
                                $scpimgheights=0;
                                if (!empty($scpimages)) {
                                    foreach ($scpimages as $scpimage) {
                                        list($x, $y)=getimagesize("../images/products_thumb/tb_$scpimage");
                                        $scpimgheights+=($y / 3.78);
                                    }
                                    if ($scpimgheights + $pdf->GetY() >= 280) {  // <-- this number may need to be adjusted to account for margins, headers and footers (a full page with no margins is 297mm tall)
                                        $pdf->AddPage();
                                    }
                                }
            
                                $pdf->ProductName(html_entity_decode($subcatprodRow['prodname']));
                                    
                                $scpy_init = $pdf->GetY();
                                if ($_GET['parts']=='all') {
                                    if ($subcatprodRow['prodpart']!='' && $subcatprodRow['fusion_part']=='' && $subcatprodRow['packo_part']=='') {
                                        $pdf->ProductPart('Part Number: '.$subcatprodRow['prodpart']);
                                    }
                                    elseif ($subcatprodRow['prodpart']!='' || $subcatprodRow['fusion_part']!='' || $subcatprodRow['packo_part']!='') {
                                        if (!empty($subcatprodRow['prodpart'])) { $scpfullwood=$subcatprodRow['prodpart'];}
                                        if ($subcatprodRow['fusion_part']!='') {
                                            if (!empty($scpfullwood)) {
                                            $scpfusion='  :::  '.$subcatprodRow['fusion_part'];
                                            }
                                            else {
                                            $scpfusion=$subcatprodRow['fusion_part'];
                                            }
                                        }
                                        if ($subcatprodRow['packo_part']!='') {
                                            if (!empty($scpfullwood) || !empty($scpfusion)) {
                                            $scppacko='  :::  '.$subcatprodRow['packo_part'];
                                            }
                                            else {
                                            $scppacko=$subcatprodRow['packo_part'];
                                            }
                                        }
                                        $pdf->ProductPart('Part Number(s): '.$scpfullwood.''.$scpfusion.''.$scppacko);
                                    }
                                }
                                elseif ($_GET['parts']=='packo') {
                                    if ($subcatprodRow['packo_part']!='') {
                                        $pdf->ProductPart('Part Number: '.$subcatprodRow['packo_part']);
                                    }
                                }
                                elseif ($_GET['parts']=='fusion') {
                                    if ($subcatprodRow['fusion_part']!='') {
                                        $pdf->ProductPart('Part Number: '.$subcatprodRow['fusion_part']);
                                    }
                                }
                                elseif ($_GET['parts']=='fullwood') {
                                    if ($subcatprodRow['prodpart']!='') {
                                        $pdf->ProductPart('Part Number: '.$subcatprodRow['prodpart']);
                                    }
                                }
                                if ($subcatprodRow['proddesc']!='') {
                                
                                    $scpproddesc=trim(str_replace('[b]','',
                                        str_replace('[/b]','',
                                        str_replace('[i]','',
                                        str_replace('[/i]','',
                                        str_replace('[u]','',
                                        str_replace('[/u]','',
                                        str_replace('[ul]','',
                                        str_replace('[/ul]','',
                                        str_replace('[li]','',
                                        str_replace('[/li]','',
                                        str_replace('[a]','',
                                        str_replace('','www.fullwood.com/',
                                        str_replace('[exturl]','',
                                        str_replace('',' (',
                                        str_replace('[/exturl]',' (',
                                        str_replace('[/a]',')',$subcatprodRow['proddesc'])))))))))))))))));
                                        
                                    $pdf->ProductDesc(html_entity_decode($scpproddesc));
                                }
                                if ($subcatprodRow['prodfeat']!='') {
                                    $pdf->ProductFeat(html_entity_decode($subcatprodRow['prodfeat']));
                                }
                                $scpy_text = $pdf->GetY();
            
                                if (!empty($scpimages)) {
                                    foreach ($scpimages as $scpimage) {
            
                                        $pdf->SetY($scpy_init);
                                    
                                        // Get the images dimensions (in pixels)
                                        list($x, $y)=getimagesize("../images/products_thumb/tb_$scpimage");
            
                                        // Convert the image height form pixels to millimetres
                                        $scpimage_height=$y / 3.78;
                                        $scpimage_width=$x / 3.78;
            
                                        // Set the current Y position to the bottom of the image (+5 mm margin)
                                        $pdf->SetY($scpy_init + $scpimage_height + 5);
            
                                        $pdf->Image("../images/products_thumb/tb_$scpimage", 160, $scpy_init, $scpimage_width, $scpimage_height);
            
                                        $scpy_image=$pdf->getY();
                                        if ($scpy_text>$scpy_image) $pdf->SetY($scpy_text);
             
                                    }
            
                                }
                            }
                        }
                    }
                }
            }


$pdf->stopPageNums();
//Generate and insert TOC at page 2
$pdf->insertTOC(1);
$pdf->Output();
}

The PDF class I am using (as well as the FDPF class) is:

class PDF extends FPDF
{

    var $_toc=array();
    var $_numbering=false;
    var $_numberingFooter=false;
    var $_numPageNum=1;
    
    var $B;
    var $I;
    var $U;
    var $HREF;

    function PDF($orientation='P',$unit='mm',$format='A4')
    {
        //Call parent constructor
        $this->FPDF($orientation,$unit,$format);
        //Initialization
        $this->B=0;
        $this->I=0;
        $this->U=0;
        $this->HREF='';
    }
    
    function WriteHTML($html)
    {
        //HTML parser
        $html=str_replace("\
",' ',$html);
        $a=preg_split('/<(.*)>/U',$html,-1,PREG_SPLIT_DELIM_CAPTURE);
        foreach($a as $i=>$e)
        {
            if($i%2==0)
            {
                //Text
                if($this->HREF)
                    $this->PutLink($this->HREF,$e);
                else
                    $this->Write(5,$e);
            }
            else
            {
                //Tag
                if($e{0}=='/')
                    $this->CloseTag(strtoupper(substr($e,1)));
                else
                {
                    //Extract attributes
                    $a2=explode(' ',$e);
                    $tag=strtoupper(array_shift($a2));
                    $attr=array();
                    foreach($a2 as $v)
                        if(ereg('^([^=]*)=["\\']?([^"\\']*)["\\']?$',$v,$a3))
                            $attr[strtoupper($a3[1])]=$a3[2];
                    $this->OpenTag($tag,$attr);
                }
            }
        }
    }
    
    function OpenTag($tag,$attr)
    {
        //Opening tag
        if($tag=='B' or $tag=='I' or $tag=='U')
            $this->SetStyle($tag,true);
        if($tag=='A')
            $this->HREF=$attr['HREF'];
        if($tag=='BR')
            $this->Ln(5);
    }
    
    function CloseTag($tag)
    {
        //Closing tag
        if($tag=='B' or $tag=='I' or $tag=='U')
            $this->SetStyle($tag,false);
        if($tag=='A')
            $this->HREF='';
    }
    
    function SetStyle($tag,$enable)
    {
        //Modify style and select corresponding font
        $this->$tag+=($enable ? 1 : -1);
        $style='';
        foreach(array('B','I','U') as $s)
            if($this->$s>0)
                $style.=$s;
        $this->SetFont('',$style);
    }
    
    function PutLink($URL,$txt)
    {
        //Put a hyperlink
        $this->SetTextColor(0,0,255);
        $this->SetStyle('U',true);
        $this->Write(5,$txt,$URL);
        $this->SetStyle('U',false);
        $this->SetTextColor(0);
    }    
    
    function AddPage($orientation='') {
        parent::AddPage($orientation);
        if($this->_numbering)
            $this->_numPageNum++;
    }

    function startPageNums() {
        $this->_numbering=true;
        $this->_numberingFooter=true;
    }

    function stopPageNums() {
        $this->_numbering=false;
    }

    function numPageNo() {
        return $this->_numPageNum;
    }

    function TOC_Entry($txt,$level=0, $dotted=0) {
        $this->_toc[]=array('t'=>$txt,'l'=>$level, '1'=>$dotted, 'p'=>$this->numPageNo());
    }
    
    function TOC_Entry2($txt,$level=0, $dotted=0) {
        $this->_toc[]=array('t'=>$txt,'l'=>$level, '1'=>$dotted);
    }
    function insertTOC( $location=1,
                        $labelSize=20,
                        $entrySize=10,
                        $tocfont='Times',
                        $label='Table of Contents'
                        ) {
        //make toc at end
        $this->stopPageNums();
        $this->AddPage();
        $tocstart=$this->page;

        $this->SetFont($tocfont,'B',$labelSize);
        $this->Cell(0,5,$label,0,1,'C');
        $this->Ln(10);
        
        
        foreach($this->_toc as $t) {

            //Offset
            $level=$t['l'];
            $dotted=$t['1'];
            if($level>0)
                $this->Cell($level*8);
            $weight='';
            if($level==0)
                $weight='B';
            $str=$t['t'];
            $this->SetFont($tocfont,$weight,$entrySize);
            $strsize=$this->GetStringWidth($str);
            $this->Cell($strsize+2,$this->FontSize+2,$str);

            //Filling dots
            $this->SetFont($tocfont,'',$entrySize);
            $PageCellSize=$this->GetStringWidth($t['p'])+2;
            $w=$this->w-$this->lMargin-$this->rMargin-$PageCellSize-($level*8)-($strsize+2);
            if($dotted>0)
            {
            $nb=$w/$this->GetStringWidth('.');
            $dots=str_repeat('.',$nb);
            }
            else 
            {
            $nb=$w/$this->GetStringWidth(' ');
            $dots=str_repeat(' ',$nb);
            }
            $this->Cell($w,$this->FontSize+2,$dots,0,0,'R');

            //Page number
            $this->Cell($PageCellSize,$this->FontSize+2,$t['p'],0,1,'R');
        }

        //grab it and move to selected location
        $n=$this->page;
        $n_toc = $n - $tocstart + 1;
        $last = array();

        //store toc pages
        for($i = $tocstart;$i <= $n;$i++)
            $last[]=$this->pages[$i];

        //move pages
        for($i=$tocstart - 1;$i>=$location-1;$i--)
            $this->pages[$i+$n_toc]=$this->pages[$i];

        //Put toc pages at insert point
        for($i = 0;$i < $n_toc;$i++)
            $this->pages[$location + $i]=$last[$i];
    }


//Page header
function Header()
{
    list($x, $y)=getimagesize("images/logo-header-pdf.jpg");
    $image_width=$x / 3.78;
    $image_height=$y / 3.78;
    $this->Image('images/logo-header-pdf.jpg',173,2,$image_width,$image_height);
    $this->Ln(20);
}

//Page footer
function Footer()
{
    if($this->_numberingFooter==false)
            return;
    $this->SetTextColor(255,255,255);
    $this->SetFont('Arial','',10);
    //Position at 1.5 cm from bottom
    $this->SetY(-18);
    list($x, $y)=getimagesize("images/fp-pdf-footer.jpg");
    $image_width=$x / 3.78;
    $image_height=$y / 3.78;
    $this->Image('images/fp-pdf-footer.jpg',10,280,$image_width,$image_height);
    //Page number
    $this->Cell(0,10,$this->numPageNo(),0,0,'C');
 if($this->_numbering==false)
            $this->_numberingFooter=false;
}


function SubCat($subcat)
{
    $this->SetFont('Arial','',14);
    $this->SetTextColor(255,255,255);
    $this->SetFillColor(0,108,176);
    $this->Cell(0,7,"$subcat",0,0,'L',1);
}

function Cat($cat)
{
    $this->SetFont('Arial','',14);
    $this->SetTextColor(255,204,51);
    //Background color
    $this->SetFillColor(0,108,176);
    $this->Cell(0,7,"$cat",0,1,'R',1);
    $this->Ln(4);
}

function SubCat2($subcat2)
{
    $this->SetFont('Arial','',14);
    $this->SetTextColor(255,255,255);
    $this->Cell(0,7,"$subcat2",0,0,'L',1);
}

function Cat2($cat2)
{
    $this->SetFont('Arial','',14);
    $this->SetTextColor(255,204,51);
    //Background color
    $this->Cell(0,7,"$cat2",0,1,'R',1);
    $this->Ln(4);
}

function ProdName($title)
{
    $this->SetFont('Arial','',12);
    $this->SetTextColor(0,108,176);
    $this->SetFillColor(200,220,255);
    $this->Cell(0,6,"$title",0,1,'L',1);
    $this->Ln(4);
}

function ProdPartNo($part)
{
    $this->SetFont('Arial','',11);
    $this->SetTextColor(0,0,0);
    $this->Cell(146,6,"$part",0,1,'L');
    $this->Ln(4);
}

function ProdDesc($file)
{
    $this->SetFont('Arial','',10);
    $this->SetTextColor(0,0,0);
    $this->MultiCell(146,5,$file);
    $this->Ln();
}

function ProdFeat($features)
{
    $this->SetFont('Arial','',10);
    $this->SetTextColor(0,0,0);
    $this->Cell(14);
    $this->MultiCell(122,5,$features);
    $this->Ln();
}

function LineBreak($line)
{
    $this->Cell(0,6,"$line",0,0,'');
}


function PrintPage($subcat,$cat)
{
    $this->SetMargins(10,10,10);
    $this->AddPage();
    $this->SubCat($subcat);
    $this->Cat($cat);
}

function PrintProductPage($subcat2,$cat2)
{
    $this->SetMargins(10,10,10);
    $this->AddPage();
    $this->SubCat2($subcat2);
    $this->Cat2($cat2);
}

function ProductName($title)
{
    $this->ProdName($title);

}

function ProductPart($part)
{
    $this->ProdPartNo($part);
}

function ProductDesc($file)
{
    $this->ProdDesc($file);
}

function ProductFeat($feat)
{
    $this->ProdFeat($feat);
}

function Line($lbr)
{
    $this->LineBreak($lbr);
}



function PrintCoverPage($file)
{
    $this->SetMargins(0,0,0);
    $this->AddPage();
}

}

And an example of what is happening (the first page is fine, the last product on the second page overflows onto the 3rd page followed by a large amount of white space and then the associated image) can be viewed in the attachment.

Any (simply explained please) help on this would be appreciated as it has been ongoing for a long time now and I have tried so many things but just can’t work out a resolution.

From memory the fpdf website has a forum, and that was always well maintained by Olivier, whose project it is (was?).

You haven’t mentioned that you have been there for help, so I thought I’d point it out fpdf.org

Thank you but I have looked through the site and forum numerous times and when I have posted a couple of times the replies have been so brief that they make no sense to me, hence why I am asking on here - especially as there appear to be a lot of people who have used / have experience with it.

I have again looked at the FDPF forum and found that if I use NbLines I should be able to calculate the number of lines in a MultiCell, times it be the height of each line, add it to the current Y position and use the final total to see if the text will overflow onto the next page and if so then force a page break.

However, for some reason the NbLines total only ever returns as a total of 1 line and I don’t know why. I am again presuming I have to find the X position of the end of each line of text and use this to get the NbLines to calulate but I am not sure.

I have asked on the FPDF forum but as yet no reply and thought I would ask here as well just in case someone has the answer.

Thanks

Anyone please??? I can’t seem to get any reply to my thread on the FPDF forum or on here and I really thought someone somewhere would be able to point me in the right direction.