Skip to content

Optimize PDF rendering to save memory#58

Merged
bmjcode merged 2 commits into
masterfrom
optimize-pdf-rendering
May 25, 2026
Merged

Optimize PDF rendering to save memory#58
bmjcode merged 2 commits into
masterfrom
optimize-pdf-rendering

Conversation

@bmjcode
Copy link
Copy Markdown
Member

@bmjcode bmjcode commented May 15, 2026

Because QPdfDocument.render() already returns a QImage, we can save memory in PdfRenderer.render() by reusing that image rather than creating a second QImage to draw it onto. This can reduce Frescobaldi's peak memory usage by hundreds of MB when rendering at higher zoom levels.

@bmjcode
Copy link
Copy Markdown
Member Author

bmjcode commented May 15, 2026

Note: The current draft of this PR includes #54 for convenience because it changes some lines that were already changed in that PR, but the two are otherwise unrelated. Ignore this; I've now rebased on the current master because anything else is a Bad Life Choice. Ask me how I know.

This branch is usable by itself, but correct background painting does depend on the fixes in #54. My intent is to finish and merge that PR before we tackle this one. Ignore this too. I've rebased now that the background fix is merged.

@bmjcode bmjcode force-pushed the optimize-pdf-rendering branch 2 times, most recently from d9fe395 to 365a889 Compare May 16, 2026 00:28
bmjcode added 2 commits May 17, 2026 19:38
Because QPdfDocument.render() already returns a QImage, we can save
memory in PdfRenderer.render() by reusing that image rather than
creating a second QImage to draw it onto. This can reduce peak memory
usage by hundreds of MB when rendering at higher zoom levels.
This makes the rendering code easier to read, and allows for future
reuse in other backends if needed.

While I was at it, I also documented why 96 DPI was chosen as the
threshold. (It was inherited from the old Poppler code, where no
explanation was given, but experimenting with different values led
me to conclude it was based on screen density as noted here.)
@bmjcode bmjcode force-pushed the optimize-pdf-rendering branch from 365a889 to 43b928d Compare May 17, 2026 23:40
@bmjcode bmjcode marked this pull request as ready for review May 17, 2026 23:40
@bmjcode
Copy link
Copy Markdown
Member Author

bmjcode commented May 17, 2026

This is finally ready to review -- and I promise I don't have any more surprise PRs waiting after this. :)

Comment thread qpageview/pdf.py
# this is only doable by drawing on a second QImage
return super().render(page, key, tile, paperColor)
else:
# reuse the QImage returned by QPdfDocument.render() to save memory
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For clarification: the default implementation in render.py creates a new QImage, fills its background if needed, then calls draw() to add the content. But QtPDF already returns the content as a QImage (on new line 302), so there's no need to create a second QImage most of the time. This can save a considerable amount of memory.

We do need two images when performing manipulations like rotating the page, which is implemented by using QPainter to draw the content rotated on top of the background image.

Comment thread qpageview/pdf.py
import platform

from PyQt6.QtCore import Qt, QByteArray, QModelIndex, QRect, QRectF, QSize, QUrl
from PyQt6.QtGui import QPainter, QTransform
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread qpageview/util.py
return QRegion(QBitmap.fromImage(mask)).boundingRect()


def oversampleFactor(key, pageSize):
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I was going to have to duplicate a lot more code when I refactored the rendering logic. That turned out not to be the case, but separating this out seemed like a good idea anyway for code clarity and reuse.

Copy link
Copy Markdown
Member

@fedelibre fedelibre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I trust you know what you are doing.

I'm waiting for this to be merged to do a new qpageview and Frescobaldi releases. Feel free to merge it if it's final.

@fedelibre fedelibre mentioned this pull request May 25, 2026
@bmjcode bmjcode merged commit d4f1c5e into master May 25, 2026
3 checks passed
@bmjcode bmjcode deleted the optimize-pdf-rendering branch May 25, 2026 23:09
@bmjcode
Copy link
Copy Markdown
Member Author

bmjcode commented May 25, 2026

Done. Should I merge #59 as well? That one is also final.

fedelibre added a commit that referenced this pull request May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants