PDA

View Full Version : PyQt5 QPrinting setFullpage doesn't works



Saelyth
11th August 2017, 16:53
Hi, newbie on the forums and also newbie at Qt programming.

I am here because I have a problem, (which I researched for a fix and I am aware it's the same issue as This one on C++ (https://stackoverflow.com/questions/29881075/qprinter-output-to-pdf-is-not-equivalent-to-the-paper-page-size-a4)) and I can't find a way to fix on Python 3.5.

The issue: When I try to print my widget to a PDF the result is this one:
It should fit the entire A4 page but it resizes it to that small section (yeah, that red rectangle).
12547

The code:

from PyQt5.QtGui import QPainter
from PyQt5.QtPrintSupport import QPrinter

# Print options
printer = QPrinter(QPrinter.HighResolution)
printer.setOrientation(QPrinter.Portrait)
printer.setPaperSize(QPrinter.A4)
printer.setPageSize(QPrinter.A4)
printer.setPageMargins(15, 15, 15, 15, QPrinter.Millimeter)
printer.setFullPage(True)
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName("testfile.pdf")

# Render/Paint it
painter = QPainter()
painter.begin(printer)
MyWidget.render(painter)
painter.end()

Assuming This documentation (http://doc.qt.io/qt-5/qprinter.html#setFullPage) is accurate, the setFullpage on true should fix it. so what am I missing?

ChrisW67
12th August 2017, 08:47
The setFullPage() function determines only whether Qt considers the entire page printable, or only that area the print device reports as printable (generally smaller). For a PDF virtual printer the two areas are the same.

The size of the content put on the page is driven entirely by you, in this case MyWidget.render().
The printer has a coordinate system that is device dependent. The addressable area of the page is represented by a rectangle you can query with QPrinter::pageRect(). Your widget has a coordinate based on screen pixels. Printer pixels are likely much smaller than screen pixels (think 600 DPI versus 72 DPI). If, as likely the case here, you assume a screen pixel is the same as a device pixel then the output will be very small on the page. To get what you were expecting, a widget image scaled to fit inside the margin, you need to do the scaling.



#!env python
import sys
from PyQt5.QtGui import QPainter
from PyQt5.QtPrintSupport import QPrinter
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import (QLabel, QApplication)

app = QApplication(sys.argv)

font = QFont('Times', 24)
widget = QLabel('Lorem ipsum dolor sit amet, consectetur adipiscing elit')
widget.setFont(font)
widget.resize(256, 256)
widget.setWordWrap(True)

# Print options
printer = QPrinter(QPrinter.HighResolution)
printer.setOrientation(QPrinter.Portrait)
printer.setPaperSize(QPrinter.A4)
printer.setPageSize(QPrinter.A4)
printer.setPageMargins(15, 15, 15, 15, QPrinter.Millimeter)
# Setting the margins and then calling setFullPage() will discard the margins.
#printer.setFullPage(True)
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName("testfile.pdf")

# Render/Paint it
painter = QPainter()
painter.begin(printer)

# Establish scaling transform
scaleX = printer.pageRect().width() / widget.rect().width()
scaleY = printer.pageRect().height() / widget.rect().height()
useScale = min(scaleX, scaleY)
painter.scale(useScale, useScale)

widget.render(painter)
painter.end()

Saelyth
12th August 2017, 11:05
Oh, I see, I though that setFullpage would scale the contents automatically. Lesson learned.
Thank you, ChrisW67, that pretty much fixes my issue. The printing of my widget is now a full page after scaling it.

However, now I'm stuck on the very next step (not sure if I should make a new thread for it).
The result is a PDF printed with just an image and no selectable text. MyWidget is suppose to mimic a MSAccess report for invoices, with its Logos, text, tables and stuff, so just an screenshot with the data won't do the job, it needs to be selected.
Researching this issue, I've been reading about printing to pdf as a QTextDocument with html formatting, but that sounds like a very undesirable workaround for something like this. I also found something about QGraphicsScene but I don't think that's the best solution either.

Is there any simple way to print a QWidget (with some QlineEdits and QLabels inside) as a PDF that can read text and not just an screenshotted version of MyWidget?

Edit: For easier understanding, I need this (which is already a working Qwidget) in PDF:
12548

ChrisW67
13th August 2017, 08:34
I have previously had to generate a lot of forms of this nature. There is no dead easy way, and I ended either:

Painting the form entirely using the QPainter primitives. I draw as if the page was A4 sized with 0.1 point grid (ugly units I know) and used transforms to fit it to the actual page (A4, A5, US Letter, whatever) when printed.
Painting the fixed elements of the form from an SVG file and using the QPainter to fill in the variable information. I queried the SVG to find the boxes to contain text (by XML id) so that if I adjusted the SVG template the output would follow. You have a bit of fun with the SVG/painter/Device coordinates, but worked quite well for long tabular lists.
Sometimes use QTextDocument for simpler layouts. For more narrative text output a QTextDoument would be a good option.

Either way there is quite a bit of work.

There were some third-party (OS and paid) tools that I vaguely recall; CuteReport, NCReport, KD Reports. Never used them.