I'm trying to generate a pdf using Qt5 under Windows. My document contains texts, images and charts. As I'm familiar with Qt and Qwt, I believed the best strategy was to create a QWidget
with my document layout and simply print it. But I face problems and could not end up with an acceptable result.
Here is my MCVE, a simple page document with:
- A header with title and image
- A piece of text
- A simple chart
Based on Qt document and How can I print a QWidget in Qt?, I ended up with this code:
main.cpp:
#include <QApplication>
#include <QIcon>
#include <QDesktopServices>
#include <QWidget>
#include <QPrinter>
#include <QPainter>
#include <QPagedPaintDevice>
#include <QUrl>
#include "ui_report.h"
#include "qwt_plot.h"
#include "qwt_plot_curve.h"
#include "qwt_plot_canvas.h"
#include "qwt_point_data.h"
#include "qwt_legend.h"
#include <sstream>
#include <memory>
bool printWidget( QWidget& widget, bool highResolution, const std::string& fileName )
{
QPrinter printer( highResolution ? QPrinter::HighResolution : QPrinter::ScreenResolution );
printer.setOutputFormat(QPrinter::PdfFormat);
printer.setOrientation(QPrinter::Portrait);
printer.setPaperSize(QPrinter::A4);
printer.setPageMargins(15,15,15,15,QPrinter::Millimeter);
printer.setFullPage(true);
printer.setOutputFileName(fromSDEString(fileName.c_str()));
QPainter painter(&printer);
double xscale = printer.pageRect().width()/double(widget.width());
double yscale = printer.pageRect().height()/double(widget.height());
double scale = qMin(xscale, yscale);
painter.translate(printer.paperRect().x() + printer.pageRect().width()/2,
printer.paperRect().y() + printer.pageRect().height()/2);
painter.scale(scale, scale);
painter.translate(-widget.width()/2, -widget.height()/2);
widget.render(&painter, QPoint(), QRegion(), QWidget::DrawChildren);
return painter.end();
}
bool generateReport( bool drawWithPrinterResolution, bool printHighResolution, const std::string& fileName )
{
QWidget widget;
Ui::Report ui;
ui.setupUi( &widget );
if ( drawWithPrinterResolution )
{
QPrinter printer( printHighResolution ? QPrinter::HighResolution : QPrinter::ScreenResolution );
printer.setOrientation(QPrinter::Portrait);
printer.setPaperSize(QPrinter::A4);
printer.setPageMargins(15,15,15,15,QPrinter::Millimeter);
printer.setFullPage(true);
// force printer page size to be used:
QSize pageSize = printer.pageRect().size();
widget.resize( pageSize );
}
ui.header->setFrameShape( QFrame::Shape::Box );
QHBoxLayout* headerLayout = new QHBoxLayout(ui.header);
QLabel* icon = new QLabel(ui.header);
QSize size = ui.header->size();
icon->setPixmap( QPixmap( ":/gui_test/mainframe.png" ).scaledToHeight( size.height() ) );
headerLayout->addWidget( icon );
headerLayout->addWidget( new QLabel("Document title",ui.header) );
headerLayout->setStretch( 0, 0 );
headerLayout->setStretch( 1, 1 );
ui.inputs->setText( "<b>Info</b>: Information" );
QwtPlot* plot = new QwtPlot( &widget );
QwtPlotCurve* curve = new QwtPlotCurve("Plots");
curve->setStyle( QwtPlotCurve::Lines );
QVector<QPointF> samples;
for ( size_t i = 0; i != 100; ++i )
{
samples.push_back(QPointF(i,20*i+10));
}
curve->setData(new QwtPointSeriesData(samples));
curve->attach(plot);
plot->setTitle( "Result" );
plot->setAxisScale( QwtPlot::xBottom, samples.front().rx(), samples.back().rx() );
plot->replot();
ui.graphLayout->addWidget( plot );
if ( printWidget( widget, printHighResolution, fileName ) )
{
QDesktopServices::openUrl(QUrl::fromLocalFile(fileName.c_str()));
return true;
}
else
{
return false;
}
}
int main( int argc, char* argv[] )
{
QApplication app( argc, argv );
app.setWindowIcon( QIcon( ":/gui_test/mainframe.png" ) );
generateReport( false, false, "report_small_widget_to_screen.pdf" );
generateReport( false, true, "report_small_widget_to_high.pdf" );
generateReport( true, false, "report_big_widget_to_screen.pdf" );
generateReport( true, true, "report_big_widget_to_high.pdf" );
return 0;
}
report.ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Report</class>
<widget class="QWidget" name="Report">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>525</width>
<height>742</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,100,0">
<item>
<widget class="QFrame" name="header"/>
</item>
<item>
<widget class="QLabel" name="inputs">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="graphLayout"/>
</item>
<item>
<widget class="QWidget" name="footer" native="true"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
mainframe.png: A Qt icon of 256x256 pixels: http://icons.iconarchive.com/icons/alecive/flatwoken/256/Apps-Qt-icon.png.
As you can see, this generates 4 files:
- report_small_widget_to_screen.pdf: Where widget is created with a small A4 ratio from ui file (252x742) and then printed with
QPrinter::ScreenResolution
- report_small_widget_to_high.pdf: Where widget is created with a small A4 ratio from ui file (252x742) and then printed with
QPrinter::HighResolution
- report_big_widget_to_screen.pdf: Where widget is scaled to printer's page size (793x1123) and then printed with
QPrinter::ScreenResolution
- report_big_widget_to_high.pdf: Where widget is scaled to printer's page size (9917x14033) and then printed with
QPrinter::HighResolution
None gives me an acceptable result:
- report_small_widget_to_screen.pdf: Text, Qt icon and Qwt chart are pixelated
- report_small_widget_to_high.pdf: Text is OK, but Qt icon and Qwt chart are pixelated
- report_big_widget_to_screen.pdf: Text, Qt icon and Qwt chart are pixelated
- report_big_widget_to_high.pdf: Text, Qt icon and Qwt chart are so small they are hardly readable. But now Qwt plot is not pixelated anymore
What should I change to get a nice output?
- With text drawn in high resolution (not pixelated) as in report_small_widget_to_high.pdf
- With icon being drawn with high resolution (not pixelated)
- With chart being plotted with high resolution (not pixelated)
report_small_widget_to_screen.pdf looks like (everything is pixelated):
report_small_widget_to_high.pdf looks like (only text is not pixelated):
report_big_widget_to_high.pdf looks like (everything is too small):
Note: I just ran the same tests with a bigger .ui ressource (2100x2970), then image resolution looks better, but text appears very small. I'm wondering the QWidget printing is the appropriate solution here, because it looks like text size depends on ui size, so you can't control the size of your text (as you do with font sizes in a document like Word...)