2017-12-18 501 views
2

无法生成具有可接受输出质量的PDF我试图在Windows下使用Qt5生成pdf。我的文档包含文本,图像和图表。由于我熟悉Qt和Qwt,我相信最好的策略是创建一个带有我的文档布局的QWidget,并将其打印出来。但是我面临的问题并不能以可接受的结果结束。使用Qt

这是我的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:的256×256像素A 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样子(一切都是像素化) :

enter image description here

report_small_widget_to_high.pdf外观像(只有文字没有像素化):

enter image description here

report_big_widget_to_high.pdf样子(一切都太小):

enter image description here


注:我只是跑相同的测试与更大的.ui资源(2100x2970),然后图像分辨率看起来更好,但文字显示非常小。我想知道QWidget打印在这里是合适的解决方案,因为它看起来像文本大小取决于用户界面大小,所以你无法控制文本的大小(就像你在像Word这样的文档中使用字体大小......) )

+0

什么分辨率是您图标? – eyllanesc

+0

@eyllanesc:256x256像素 – jpo38

+0

您可以通过github或类似的工具共享您的项目,以节省时间。 – eyllanesc

回答

1

就我个人而言,我会强烈建议将内容创建为QTextDocument,然后将其打印出来。你可以(IIRC,有一段时间没有使用QwtPlot)将图表渲染成图像。然后很容易add the image到任何你想要的文件。当然,和你的静态图像一样。您可以在文档中使用HTML/CSS(尽管类名称中包含“文本”)以及许多其他选项。

实例添加要求:

这将是非常简单的,并没有完全形成,但希望它可以帮助...

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,但有点烦琐的遵循):

QTextDocument with embedded images

和PDF导出:http://max.wdg.us/docs/so/SO-47879329.pdf

+0

您能否考虑添加一个快速示例来显示结果会是什么? – jpo38

+0

添加的示例,如它是... –

+0

试图将其应用于我的项目。不过,我在这里报道了一些问题:https://stackoverflow.com/questions/47906629/how-to-fit-qtextdocument-to-printers-page-width-and-margins。你能帮忙吗? – jpo38