I am generating pdf invoice using fpdf.
Some invoices containing many items, and details need to go into the second page. However, I need the total, and other details
I had a very similar issue and I went ahead and just added the function to the class:
function SetPage($num) {
$this->page = $num;
}
This does literally what you'd expect. But, when you try to go back to the next page, you need to either set it explicitly or add to the Cell() functionality. Here is where it adds a page:
if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())
This checks for AcceptPageBreak and obviously whether the height of the page is less than where we'll end up if we stay on the page. What I did was add a conditional here to check to see if there WAS another page below. If there was, rather than ADDING a new page, I went ahead and just set my Y to my margin and used the above function to visit that page. As such:
if ($this->page < count($this->pages)) {
$this->SetPage(($this->page)+1);
$this->y = .5;
}
If you're uncomfortable with messing with FPDF, this might not be for you, but my realization after solving this issue was actually that the script was very easily navigated and left a great deal of room for simple improvements. Note that instead of checking above for another page, you could also add a variable that you could call that would temporarily tell the script that instead of adding a page, you just jump to next.
Some clever solutions here, but I don't recommend this trickery, because it goes against the basic FPDF model and will sooner or later probably cause other problems down the road.
Programming is already complex enough!
So, separate the model of the invoice or whatever it is from the presentation. Construct the entire model. Then output the presentation (the PDF in this case).
I found very suitable the *str_replace* method, due to the need to write "n of m pages". First wrote the n number normally as I constructed every page and finally replaced the m cipher in $pdf->pages[$i] page by page.
I ran into this issue by having a two-column layout, where one column could be so large it would span to the next page. I wanted to set the Y position back to the same Y position as the first column, so that they are aligned to the top of each other, but also continue the document under the largest of the two columns.
My solution was to record the "Vertical Position" of the document, which includes the Page Number ($pdf->PageNo()
) and the Y Position ($pdf->GetY()
).
You need to store two different vertical positions. First, store the "Starting point" which is where you will start your second column. Second, store the "Largest point" which is the furthest down the document. The largest point was tricky because you cannot look at page number or Y value alone, you must look at both.
I created these three methods to help me out.
This solution does not include X-position in the example.
public function GetVerticalPosition() {
// Include page and Y position of the document
return array(
'page' => $this->PageNo(),
'y' => $this->GetY(),
);
}
public function SetVerticalPosition( $pos ) {
// Set the page and Y position of the document
$this->page = $pos['page'];
$this->SetY( $pos['y'] );
}
public function FurthestVerticalPosition( $aPos, $bPos = null ) {
if ( $bPos === null ) $bPos = $this->GetVerticalPosition();
// Returns the "furthest" vertical position between two points, based on page and Y position
if (
($aPos['page'] > $bPos['page']) // Furthest position is located on another page
||
($aPos['page'] == $bPos['page'] && $aPos['y'] > $bPos['y'] ) // Furthest position is within the same page, but further down
) {
return $aPos;
}else{
return $bPos;
}
}
Usage is pretty straightforward. Before you draw your variable-height columns you need to grab the starting position, and start collecting the maximum position.
$startPos = $this->GetVerticalPosition();
$furthestPos = $this->GetVerticalPosition();
After rendering each cell, before rendering another cell on the same level, you want to update the furthest position (if needed), then set back to the starting position.
// Returns the furthest of the two possibilites
$furthestPos = $this->FurthestVerticalPosition( $this->GetVerticalPosition(), $furthestPos );
$this->SetVerticalPosition( $startPos );
When you finish rendering your columns, set the document to the maximum distance that you have recorded so far.
$this->SetVerticalPosition( $furthestPos );
Now your columns are properly aligned, and the document pointer is located immediately after the furthest-drawn column.
FPDF doesn't allow you to go back to a previous page. Once you're done with a page - either by calling AddPage() or by running out of space when SetAutoPageBreak() is turned on - you're done with it.
The workaround is to generate your document without the total, write it to a temporary file then load it back (using FPDI: http://www.setasign.de/products/pdf-php-solutions/fpdi/) and add the total in the correct place.
if(!empty($this->replace) && !empty($this->replacement)){
$this->pages[$n]=str_replace($this->replace,$this->replacement,$this->pages[$n]);
}
I added this code to the fpdf class in _putpages() right under /Page content line 2851
Example after adding above to code:
$pdf->replace = array("{paid_msg}","{balance}");
$pdf->replacement = array("Paid","0.00");
It can be a string or an array.