无法使用Qt生成具有可接受输出质量的pdf [英] Can't generate pdf with acceptable output quality using Qt

查看:122
本文介绍了无法使用Qt生成具有可接受输出质量的pdf的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在Windows下使用Qt5生成pdf.我的文档包含文本,图像和图表.正如我对Qt和Qwt所熟悉的那样,我相信最好的策略是使用文档布局创建QWidget并简单地打印它.但是我遇到了问题,最终没有得到令人满意的结果.

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.

这是我的MCVE,它是一个简单的页面文档,其中包含:

Here is my MCVE, a simple page document with:

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

基于 Qt文档

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 :256x256像素的Qt图标:

mainframe.png: A Qt icon of 256x256 pixels: http://icons.iconarchive.com/icons/alecive/flatwoken/256/Apps-Qt-icon.png.

如您所见,这将生成4个文件:

As you can see, this generates 4 files:

  • 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: 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:文本,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图不再像素化了

我应该改变以获得更好的输出?

What should I change to get a nice output?

  • 如report_small_widget_to_high.pdf所述,以高分辨率(未像素化)绘制的文本
  • 以高分辨率(未像素化)绘制图标
  • 以高分辨率绘制图表(未像素化)

report_small_widget_to_screen.pdf 看起来(一切都像素化):

report_small_widget_to_screen.pdf looks like (everything is pixelated):

report_small_widget_to_high.pdf看起来像(只有文本没有像素化):

report_small_widget_to_high.pdf looks like (only text is not pixelated):

report_big_widget_to_high.pdf看起来(一切都太小了):

report_big_widget_to_high.pdf looks like (everything is too small):

注意:我只是使用更大的.ui资源(2100x2970)运行了相同的测试,因此图像分辨率看起来更好,但是文本看起来很小.我想知道QWidget打印是这里合适的解决方案,因为它看起来文本大小取决于ui大小,因此您无法控制文本的大小(就像在Word这样的文档中使用字体大小一样... )

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...)

推荐答案

我个人会强烈考虑将内容创建为 QTextDocument ,然后打印出来.您可以(IIRC,已经有一段时间没有使用QwtPlot了)将图表渲染为图像.然后,您可以轻松地将图像添加到到您想要的任何位置.当然,与您的静态图片相同.您可以在文档中使用HTML/CSS(尽管在类名中使用文本"),也可以使用许多其他选项.

Personally I would strongly consider creating the content as a QTextDocument and then printing it. You can (IIRC, haven't used QwtPlot in a while) render the charts to images. Then it is easy to add the image to the document wherever you want. Same with your static image(s), of course. You can use HTML/CSS in the document (despite "Text" in the class name), and lots of other options.

根据要求添加了示例:

这将是非常简化且尚未完全形成的,但希望对您有帮助...

This is going to be very simplified and not fully formed, but hope it helps...

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文件:

To print to a PDF or HTML file:

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);
}

工作版本的实际屏幕截图(其代码开始这里,但有点麻烦):

An actual screenshot of a working version (code for it starts here but is a bit of a chore to follow):

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

这篇关于无法使用Qt生成具有可接受输出质量的pdf的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆