无法使用 Qt 生成具有可接受输出质量的 pdf
Posted
技术标签:
【中文标题】无法使用 Qt 生成具有可接受输出质量的 pdf【英文标题】:Can't generate pdf with acceptable output quality using Qt 【发布时间】:2017-12-18 14:20:38 【问题描述】:我正在尝试在 Windows 下使用 Qt5 生成 pdf。我的文档包含文本、图像和图表。由于我熟悉 Qt 和 Qwt,我认为最好的策略是使用我的文档布局创建一个 QWidget
并简单地打印它。但我面临一些问题,无法得到一个可以接受的结果。
这是我的 MCVE,一个简单的页面文档:
带有标题和图片的标题 一段文字 一个简单的图表基于Qt document 和How can I print a QWidget in Qt?,我最终得到了以下代码:
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: 256x256 像素的 Qt 图标:http://icons.iconarchive.com/icons/alecive/flatwoken/256/Apps-Qt-icon.png。
如您所见,这会生成 4 个文件:
report_small_widget_to_screen.pdf:其中小部件是使用 ui 文件 (252x742) 中的小 A4 比例创建的,然后使用QPrinter::ScreenResolution
打印
report_small_widget_to_high.pdf:其中小部件是使用 ui 文件 (252x742) 中的小 A4 比例创建的,然后使用 QPrinter::HighResolution
打印
report_big_widget_to_screen.pdf:小部件缩放到打印机的页面大小 (793x1123),然后使用QPrinter::ScreenResolution
打印
report_big_widget_to_high.pdf:小部件缩放到打印机的页面大小(9917x14033),然后用QPrinter::HighResolution
打印
没有给我一个可接受的结果:
report_small_widget_to_screen.pdf:文本、Qt 图标和 Qwt 图表像素化 report_small_widget_to_high.pdf:文本正常,但 Qt 图标和 Qwt 图表像素化 report_big_widget_to_screen.pdf:文本、Qt 图标和 Qwt 图表像素化 report_big_widget_to_high.pdf:文本、Qt 图标和 Qwt 图表太小以至于难以阅读。但现在 Qwt 图不再像素化了我应该改变什么以获得好的输出?
使用高分辨率(未像素化)绘制的文本,如 report_small_widget_to_high.pdf 中所示 以高分辨率绘制图标(未像素化) 以高分辨率绘制图表(未像素化)report_small_widget_to_screen.pdf 看起来像(一切都是像素化的):
report_small_widget_to_high.pdf 看起来像(只有文本没有像素化):
report_big_widget_to_high.pdf 看起来像(一切都太小了):
注意:我刚刚使用更大的 .ui 资源 (2100x2970) 进行了相同的测试,然后图像分辨率看起来更好,但文本显得非常小。我想知道 QWidget 打印在这里是合适的解决方案,因为它看起来像文本大小取决于 ui 大小,所以你无法控制文本的大小(就像你在 Word 等文档中处理字体大小一样...... )
【问题讨论】:
你的图标是什么分辨率的? @eyllanesc:256x256 像素 您可以通过 github 或类似方式分享您的项目以节省我的时间。 @eyllanesc:感谢您的帮助。刚刚添加了图片链接 【参考方案1】:我个人强烈考虑将内容创建为QTextDocument,然后打印出来。您可以(IIRC,有一段时间没有使用 QwtPlot)将图表渲染为图像。然后很容易add the image 到您想要的文档。当然,与您的静态图像相同。您可以在文档中使用 html/CSS(尽管类名中有“Text”),以及许多其他选项。
应要求添加的示例:
这将是非常简化且未完全形成的,但希望对您有所帮助...
QTextDocument qtdoc; // start with a QTextDocument
// prepare standard html with embedded image
QString html = "<html><body>" \
"<h1>Hello World!</h1>" \
"<img src='mydata://myimg.png' border='0' />" \
"</body></html>";
QImage image = getChartImage(); // theoretical function
// here we add the actual image data to the document
qtdoc.addResource(QTextDocument::ImageResource, QUrl("mydata://myimg.png"), image);
qtdoc.setHtml(html);
// document is now fully formed and ready for display/print
要打印到 PDF 或 HTML 文件:
void printToFile(const QTextDocument & qtdoc)
QString fn = QFileDialog::getSaveFileName(this, tr("Select output file"), QString(), tr("PDF Files(*.pdf);;HTML-Files (*.htm *.html)"));
if (fn.isEmpty())
return;
if (fn.endsWith(".pdf", Qt::CaseInsensitive))
QPrinter printer;
printer.setPageMargins(10.0,10.0,10.0,10.0,printer.Millimeter);
printer.setOutputFormat(QPrinter::PdfFormat);
printer.setColorMode(QPrinter::Color);
printer.setOutputFileName(fn);
qtdoc.print(&printer);
else // HTML
QTextDocumentWriter writer(fn);
writer.write(qtdoc);
输出到打印机:
void print(const QTextDocument & qtdoc)
QPrinter printer;
printer.setPageMargins(10.0,10.0,10.0,10.0,printer.Millimeter);
QPrintDialog *dialog = new QPrintDialog(&printer, this);
dialog->setWindowTitle(tr("Print Document"));
if (dialog->exec() != QDialog::Accepted)
return;
qtdoc.print(&printer);
工作版本的实际屏幕截图(代码以 here 开头,但有点繁琐):
PDF 导出:http://max.wdg.us/docs/so/SO-47879329.pdf
【讨论】:
您能否考虑添加一个简单的示例来显示结果会是什么? 尝试将此应用到我的项目中。但是,我遇到了这里报告的一些问题:***.com/questions/47906629/…。你能帮忙吗?以上是关于无法使用 Qt 生成具有可接受输出质量的 pdf的主要内容,如果未能解决你的问题,请参考以下文章
如何使用图像的 tesseract 输出从另一个图像创建可搜索的 pdf