fpdf2 icon indicating copy to clipboard operation
fpdf2 copied to clipboard

[QUESTION] How to generate a single page PDF with dynamic height?

Open FilipeMarch opened this issue 1 year ago • 8 comments

Hello, I'm trying to print some pdfs using a thermal printer (58mm) on my mobile app.

from fpdf import FPDF

pdf = FPDF("P", "mm", (135, "What do I put here?"))

The issue is that the pdf page is generated dynamically from the data of each user. We generate an image that I don't know beforehand what's gonna be the final height.

So the page can have a final height of 100, 300, 1200 pixels, I simply do not know.

I want to generate a single page pdf that will grow until it fits all the content. The main issue for me is making sure that all the content is going to fit in a single page.

I know that 135 is the correct width, but because the height is dynamic, how can I deal with that situation?

I would appreciate any help or guidance on how to approach this, thanks!

FilipeMarch avatar May 02 '24 20:05 FilipeMarch

This is essentially the same scenario as described in #678. The general approach I suggested there is likely to work for you as well.

gmischler avatar May 02 '24 20:05 gmischler

This is essentially the same scenario as described in #678. The general approach I suggested there is likely to work for you as well.

I don't see how your example at https://github.com/py-pdf/fpdf2/discussions/678#discussioncomment-5087430 can solve this issue. You created three pages, changed the dimension of the second and third page, and rotated the third page.

How does that relate to dynamically changing the height of a single page based on content?

Look at this very simple example:

from fpdf import FPDF

pdf = FPDF()
pdf.set_font('Courier', '', 11)
pdf.add_page()

x = 200
for i in range(x):
    pdf.cell(txt=f'{i:02}: a'*5, new_x="LEFT", new_y="NEXT")

doc = pdf.output('pagesizetest.pdf')

Would you be able to change that code above such that it will result in a pdf containing only one single page with the whole text and no space left? This is the output I'm expecting:

image

pagesizetest.pdf

I want the result to always fit in one single page. The width of the page needs to be 135, the height depends on the size of the content, which I can't predict. The content may have text and images.

FilipeMarch avatar May 09 '24 00:05 FilipeMarch

The code below is taking your example and applying the approach taken on #678 to adjust the page size:

from fpdf import FPDF

pdf = FPDF("P", "mm", (135, 999999))
pdf.set_font('Courier', '', 11)
pdf.add_page()
with pdf.rotation(180, x=pdf.w/2, y=pdf.h/2):
    x = 500
    for i in range(x):
        pdf.cell(text=f'{i:02}: a'*5, new_x="LEFT", new_y="NEXT")

pdf.pages[1].set_dimensions(pdf.w_pt, (pdf.y + pdf.t_margin)*pdf.k)

doc = pdf.output('pagesizetest.pdf')

The PDF standard interprets the vertical coordinates from bottom to top, so when you re-dimension your page you lose the content at the top - that's why rotating the content will make your life a lot easier. As your use case is to print the result I believe it shouldn't be an issue.

andersonhc avatar May 09 '24 03:05 andersonhc

Woooow, it works! That's awesome. Thanks @andersonhc and @gmischler, I'm really glad this workaround exists, that was quite unexpected.

Later I will see if I can rotate the pdf 180 degrees again after the whole process, because we are printing all other receipts in the "normal" orientation, it would be strange if only this receipt had a different behavior.

FilipeMarch avatar May 09 '24 06:05 FilipeMarch

Later I will see if I can rotate the pdf 180 degrees again after the whole process,

I thought about that quite a bit, and couldn't figure out a really good way yet. Given how fpdf2 is organized internally, you need to know the pivot point of a rotation in advance.

There might be a (very hacky) way to do two nested rotations around the original center and later replace the pivot coordinates of the outer one in the output data stream.

But the easiest way will be to do some postprocessing with another tool. Right next door in the same organisation for example we have pypdf, which has no problem at all to rotate a page.

gmischler avatar May 09 '24 13:05 gmischler

Do you have other questions regarding this @FilipeMarch? Or can we close this issue?

@gmischler & @andersonhc: do you think that we should document this workaround in our documentation?

Lucas-C avatar May 24 '24 08:05 Lucas-C

Do you have other questions regarding this @FilipeMarch? Or can we close this issue?

@gmischler & @andersonhc: do you think that we should document this workaround in our documentation?

Hello, I ended up finding a solution to print the image directly on the thermal printer, without having to create a pdf first containing the image, which made the whole process much faster.

I really would like this library to support dynamic height without the side-effect of ending up with the content rotated, and needing another library to fix it. The whole concept is strange, and on the other hand I believe creating a pdf with dynamic height is very common in general.

Thanks for the help and support, this issue can be closed.

FilipeMarch avatar May 24 '24 09:05 FilipeMarch

Thank you for the feedback @FilipeMarch! 👍

I have to say that I am a bit hesitant about this: do we want to support this feature in fpdf2? What do you think about this @andersonhc & @gmischler?

Lucas-C avatar May 24 '24 09:05 Lucas-C