2017-12-18 305 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