3

TL;DR: I have problems with PHP generating PDF's longer than 1 page total.


Hello again. My goal is to create a script that will basically get all the important data and create a A4 format PDF invoice/document for printing/mailing/archiving. Generating PDF document is fine as long as the document does not overflow.

I want the invoice pages to be outlined with a border, it should contain:

  • the needed stuff required for the invoice to be valid
  • billed products/other information
  • place for supplier/customer signature and stamp or other data

All the pages HAVE to contain header and footer (company logo) and footer (page # of # - Invoice/Document ID - Date and Time - office ID - Printer ID, assigned personnel, whatever someone can ask), as well as border around the document body (under header, above footer).

Everything is fine as long as the document size is not bigger than

$pageSize-$pageMargins-$header-$footer-$invoiceDataBlock-$signaturesBlock

which is basically just like 10 cm for the actual invoiced items. If the document is bigger, I actually create attachment for the invoice manually using spreadsheet editor.

The question is: What can I do to create a multipage PDF document that has no problems like invoiced items overlaying the header/footer? I need to know when to continue on the next page. How do I know this? What is the best way to accomplish this task?

Thank you in advance!

lmojzis
  • 551
  • 7
  • 17

2 Answers2

2

I've used both FPDF and TCPDF to generate multi-page invoice files. They are roughly the same in terms of how they work. (I started with FPDF, then switched to TCPDF when I needed to include Unicode characters, which FPDF didn't support at the time.)

As Eugen suggested, you can hand-roll your own headers and footers more easily than using the functions built in to either FPDF or TCPDF.

My strategy for making sure I don't overwrite footers is simply to be careful with the data included on the invoice. When adding new SKUs, I test long names to make sure they will fit in their field in the invoice PDF. For items that must be variable-length, I put unknown content onto its own line to reduce possible impact:

Domain registration (2 years)
example.com

As I generate each page of the invoice, I keep track of how many lines I've used. I know I can safely put 20 lines of items, and I know my maximum single item is 2 lines, then when I get to 20 lines, I start a new page. 15 items means 1 page. 25 items means two pages. The item counter goes up, and every time I hit the 20 line limit, it generates the next page and resets the page item counter.

Note that I'm not including any code in this answer because you didn't include any code in your question. If you'd like help with implementation, I suspect that will be grounds for an additional question. :-)

ghoti
  • 45,319
  • 8
  • 65
  • 104
  • So there's no way to ask the PDF generator whether the areas are overlapping or not? I would like this for the documents also (also mentioned in OP) and the line count thingy will simply not work there until I use typewriter font,which I don't want to. – lmojzis Jun 03 '12 at 05:29
  • 1
    Well ... TCPDF's [getNumLines()](http://www.tcpdf.org/doc/classTCPDF.html#af5cab8b0e1e29a3ee19a14007e37bd99) and [getStringHeight()](http://www.tcpdf.org/doc/classTCPDF.html#ad68e86a862fe437a8ac1728cecaaa2e9) functions may help you get the size of [MultiCell()](http://www.tcpdf.org/doc/classTCPDF.html#aa81d4b585de305c054760ec983ed3ece) text blocks. If you have single cells that need to flow text ... well, I'd look in to [AutoPageBreak()](http://www.tcpdf.org/doc/classTCPDF.html#a4430365ea0b9015b4aefedad1ba77ea9). I haven't used it, but it may be what you need. – ghoti Jun 03 '12 at 10:31
1

Use TCPDF. It has a very handy SetY() / GetY() pair of functions, that allows you to know, where on the page you are. You can use this to know when to do a page break.

Hint: Do not use the Header/Footer capabilities - they are clunky. Draw your own headers/footers.

Edit

As from discussion below, here are some details: To avoid overlaying you have 2 possibilities

  1. Use getStringHeight() and calculate
  2. Use Transactions

The first version draws its rationale from the fact, that of all objects you typically use in generating a PDF a text-flow is the only one, of which you cannot tell beforehand the height it will use. getStringHeight() provides you with a good enough estimate, so you know before adding the element, if it will fit on the page (leaving enough room on the bottom for the footer). So basically you extend your drawing loop to calculate the height of each element and test, if you need to start a new page first. This allows also for some sort of keeptogether, e.g. if the remaining space after a section title is too low, start a new page before, to keep section title and section body together.

The second version is even easier: In TCPDF you can use transactions simialr to a Database: Start a transaction, draw, if the result is not to your liking roll back, else commit. We found this to be quite a performance hog, ultimately deciding against it for long textual reports, but a 2-page invoice is a very different beast.

Eugen Rieck
  • 64,175
  • 10
  • 70
  • 92
  • Looks like we got an Answer candidate! – lmojzis Jun 03 '12 at 06:02
  • As it happens, I was in this up to the elbows a few weeks ago: Creating textual (long paragraphs and images) reports without ugly orphans or widows. I used `getStringHeight()` to calculate, if a paragraph will fit on the page (without overlaying the footer) starting from `GetY()` – Eugen Rieck Jun 03 '12 at 06:22
  • Hmmm... Looks pretty good. Will you fancy posting this comment again along with a little example so I can mark it as an answer? – lmojzis Jun 03 '12 at 11:32
  • I will ... in a couple of hours - the Memsahib is waiting quite impatiently right now. – Eugen Rieck Jun 03 '12 at 11:33