Skip to content

Lightweight rendering context #1832

@azelcer

Description

@azelcer

Hi. I've been using fpdf2 for a while now, it works great and I'd like to contribute some changes I've made that may be useful to others.

I usually need to avoid page breaks in some parts that uses a lot of unbreakable incompatible methods like get_y(), etc. What I usually do is equivalent (I wrap this in a context manager) to:

with pdf.offset_rendering() as dummy:
    funny_function(dummy)
if dummy.page_break_triggered:
    pdf.add_page()
    funny_function(pdf)
...

where funny_function encapsulates the functions that are not compatible with unbreakable.

As noted in the docs, this can be memory-demanding if the document is large. In my use case it is also unnecessary slow: most of the times no page break is triggered, yet it is necessary to run funny_function twice, and the memory management of the PDFRecorder used by the context manager takes some time.

I gained a lot of efficiency creating an alternative trimmed recorder that uses less memory and has almost zero overhead in the case that no page break has been triggered. I had no problems in my use case (just plain text and lines added inside the context), but I assume it can break things in some cases.

The difference with PDFRecorder, is that instead of copying the whole pdf pages contents it just saves the position where the contents end to roll back. This is incompatible with closed FPDF objects where the page contents is a PDFContentStream instead of a bytearray (I was confused at first, as the docs state otherwise). If no page break as been triggered, nothing has to be done. If a page break has been triggered, restoring the the contents to the previous state is cheaper than with PDFRecorder. I guess something similar was originally planned with the context manager, as it has a accept_page_breaks parameter that does not seems to be used even if it has it own test.

I guess that the idea can be of value to add to the project, yet I am sure that there are many more things that must be taken into account. I can make a PR with the bare-bones structure so it's easier to evaluate the proposal. I tried to adapt PDFRecorder to my needs, but ended up with unnecessary complicated code just to avoid making a new class that serves for a similar yet different purpose.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions