使用Qt无法生成具有可接受输出质量的pdf

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Qt无法生成具有可接受输出质量的pdf相关的知识,希望对你有一定的参考价值。

我正在尝试使用Windows下的Qt5生成pdf。我的文档包含文本,图像和图表。由于我熟悉Qt和Qwt,我认为最好的策略是用我的文档布局创建一个QWidget并简单地打印它。但是我遇到了问题,最终无法获得可接受的结果。

这是我的MCVE,一个简单的页面文档:

  • 标题和图像的标题
  • 一段文字
  • 一个简单的图表

基于Qt documentHow 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:Text,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 ressource(2100x2970)运行相同的测试,然后图像分辨率看起来更好,但文本显得非常小。我想知道QWidget打印是适当的解决方案,因为它看起来像文本大小取决于ui大小,所以你无法控制文本的大小(就像你在Word等文档中使用字体大小... )

答案

就个人而言,我会强烈考虑将内容创建为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但是有点杂事要遵循):

QTextDocument with embedded images

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

以上是关于使用Qt无法生成具有可接受输出质量的pdf的主要内容,如果未能解决你的问题,请参考以下文章

如何使用图像的 tesseract 输出从另一个图像创建可搜索的 pdf

将多个输出中的hls属性设置为单独的片段代码

使用 evo pdf 转换 word 文档时图像质量差

NSView 和 PDF 不同的输出

Qt编译生成的exe无法执行

Latex绘图(word中自己绘的图如何存为高质量