QPrinter - Fix OutputFormat when no printers

Currently the QPrinter constructor, setOutputFormat() and
setPrinterName() make bad assumptions about the availability of printers
when configuring NativeFormat which can lead to inconsistent print
engine set-ups leading to crashes in the print dialog, especially on
Windows where a valid DEVMODE is needed.

This change cleans up the init and methods to ensure NativeFormat can
only ever be set if we have both a valid plugin and a valid printer,
if not the PdfFormat is used.  One side-effect of this is that it is
now impossible to set an invalid printer name via QPrinter (but still
able to be done via QPrintEngine if really needed).  Also if no
default printer is set then use the first available one.

This also fixes a bug where setting a new printer name on Windows
reset all the saved settings.

[ChangeLog][Important Behavior Changes] QPrinter no longer allows you
to set an invalid printer name.

Task-number: QTBUG-34345
Task-number: QTBUG-26008
Task-number: QTBUG-26430
Change-Id: I19737e4209d8c8df5817ea83246b3dd0c483ee85
Reviewed-by: Gunnar Sletta <gunnar.sletta@digia.com>
This commit is contained in:
John Layt 2013-11-23 19:52:34 +01:00 committed by The Qt Project
parent 7079f11213
commit e8a45152b5
3 changed files with 128 additions and 86 deletions

View File

@ -48,7 +48,6 @@
#include <qpa/qplatformprintersupport.h>
#include "qprintengine.h"
#include "qprinterinfo.h"
#include "qlist.h"
#include <qcoreapplication.h>
#include <qfileinfo.h>
@ -163,10 +162,39 @@ Q_PRINTSUPPORT_EXPORT QSizeF qt_printerPaperSize(QPrinter::Orientation orientati
(qt_paperSizes[paperSize][height_index] * 72 / 25.4) / multiplier);
}
void QPrinterPrivate::createDefaultEngines()
QPrinterInfo QPrinterPrivate::findValidPrinter(const QPrinterInfo &printer)
{
QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get();
if (outputFormat == QPrinter::NativeFormat && ps) {
// Try find a valid printer to use, either the one given, the default or the first available
QPrinterInfo printerToUse = printer;
if (printerToUse.isNull()) {
printerToUse = QPrinterInfo::defaultPrinter();
if (printerToUse.isNull()) {
QList<QPrinterInfo> availablePrinters = QPrinterInfo::availablePrinters();
if (!availablePrinters.isEmpty())
printerToUse = availablePrinters.at(0);
}
}
return printerToUse;
}
void QPrinterPrivate::initEngines(QPrinter::OutputFormat format, const QPrinterInfo &printer)
{
// Default to PdfFormat
outputFormat = QPrinter::PdfFormat;
QPlatformPrinterSupport *ps = 0;
QString printerName;
// Only set NativeFormat if we have a valid plugin and printer to use
if (format == QPrinter::NativeFormat) {
ps = QPlatformPrinterSupportPlugin::get();
QPrinterInfo printerToUse = findValidPrinter(printer);
if (ps && !printerToUse.isNull()) {
outputFormat = QPrinter::NativeFormat;
printerName = printerToUse.printerName();
}
}
if (outputFormat == QPrinter::NativeFormat) {
printEngine = ps->createNativePrintEngine(printerMode);
paintEngine = ps->createPaintEngine(printEngine, printerMode);
} else {
@ -174,8 +202,42 @@ void QPrinterPrivate::createDefaultEngines()
paintEngine = pdfEngine;
printEngine = pdfEngine;
}
use_default_engine = true;
had_default_engines = true;
printEngine->setProperty(QPrintEngine::PPK_PrinterName, printerName);
addToManualSetList(QPrintEngine::PPK_PrinterName);
validPrinter = true;
}
void QPrinterPrivate::changeEngines(QPrinter::OutputFormat format, const QPrinterInfo &printer)
{
QPrintEngine *oldPrintEngine = printEngine;
const bool def_engine = use_default_engine;
initEngines(format, printer);
if (oldPrintEngine) {
for (int i = 0; i < manualSetList.size(); ++i) {
QPrintEngine::PrintEnginePropertyKey key = manualSetList[i];
QVariant prop;
// PPK_NumberOfCopies need special treatmeant since it in most cases
// will return 1, disregarding the actual value that was set
// PPK_PrinterName also needs special treatment as initEngines has set it already
if (key == QPrintEngine::PPK_NumberOfCopies)
prop = QVariant(q_ptr->copyCount());
else if (key != QPrintEngine::PPK_PrinterName)
prop = oldPrintEngine->property(key);
if (prop.isValid()) {
printEngine->setProperty(key, prop);
addToManualSetList(key);
}
}
}
if (def_engine)
delete oldPrintEngine;
}
#ifndef QT_NO_PRINTPREVIEWWIDGET
@ -510,13 +572,7 @@ QPrinter::QPrinter(PrinterMode mode)
: QPagedPaintDevice(),
d_ptr(new QPrinterPrivate(this))
{
d_ptr->init(mode);
QPrinterInfo defPrn(QPrinterInfo::defaultPrinter());
if (!defPrn.isNull()) {
setPrinterName(defPrn.printerName());
} else if (QPrinterInfo::availablePrinters().isEmpty()) {
setOutputFormat(QPrinter::PdfFormat);
}
d_ptr->init(QPrinterInfo(), mode);
}
/*!
@ -528,11 +584,10 @@ QPrinter::QPrinter(const QPrinterInfo& printer, PrinterMode mode)
: QPagedPaintDevice(),
d_ptr(new QPrinterPrivate(this))
{
d_ptr->init(mode);
setPrinterName(printer.printerName());
d_ptr->init(printer, mode);
}
void QPrinterPrivate::init(QPrinter::PrinterMode mode)
void QPrinterPrivate::init(const QPrinterInfo &printer, QPrinter::PrinterMode mode)
{
if (!QCoreApplication::instance()) {
qFatal("QPrinter: Must construct a QCoreApplication before a QPrinter");
@ -540,14 +595,8 @@ void QPrinterPrivate::init(QPrinter::PrinterMode mode)
}
printerMode = mode;
outputFormat = QPrinter::NativeFormat;
createDefaultEngines();
#ifndef QT_NO_PRINTPREVIEWWIDGET
previewEngine = 0;
#endif
realPrintEngine = 0;
realPaintEngine = 0;
initEngines(QPrinter::NativeFormat, printer);
}
/*!
@ -612,40 +661,30 @@ QPrinter::~QPrinter()
\since 4.1
Sets the output format for this printer to \a format.
If \a format is the same value as currently set then no change will be made.
If \a format is NativeFormat then the printerName will be set to the default
printer. If there are no valid printers configured then no change will be made.
If you want to set NativeFormat with a specific printerName then use
setPrinterName().
\sa setPrinterName()
*/
void QPrinter::setOutputFormat(OutputFormat format)
{
Q_D(QPrinter);
if (d->validPrinter && d->outputFormat == format)
if (d->outputFormat == format)
return;
d->outputFormat = format;
QPrintEngine *oldPrintEngine = d->printEngine;
const bool def_engine = d->use_default_engine;
d->printEngine = 0;
d->createDefaultEngines();
if (oldPrintEngine) {
for (int i = 0; i < d->manualSetList.size(); ++i) {
QPrintEngine::PrintEnginePropertyKey key = d->manualSetList[i];
QVariant prop;
// PPK_NumberOfCopies need special treatmeant since it in most cases
// will return 1, disregarding the actual value that was set
if (key == QPrintEngine::PPK_NumberOfCopies)
prop = QVariant(copyCount());
else
prop = oldPrintEngine->property(key);
if (prop.isValid())
d->printEngine->setProperty(key, prop);
}
if (format == QPrinter::NativeFormat) {
QPrinterInfo printerToUse = d->findValidPrinter();
if (!printerToUse.isNull())
d->changeEngines(format, printerToUse);
} else {
d->changeEngines(format, QPrinterInfo());
}
if (def_engine)
delete oldPrintEngine;
if (d->outputFormat == QPrinter::PdfFormat)
d->validPrinter = true;
}
/*!
@ -683,30 +722,38 @@ QString QPrinter::printerName() const
/*!
Sets the printer name to \a name.
\sa printerName(), isValid()
If the \a name is empty then the output format will be set to PdfFormat.
If the \a name is not a valid printer then no change will be made.
If the \a name is a valid printer then the output format will be set to NativeFormat.
\sa printerName(), isValid(), setOutputFormat()
*/
void QPrinter::setPrinterName(const QString &name)
{
Q_D(QPrinter);
ABORT_IF_ACTIVE("QPrinter::setPrinterName");
QList<QPrinterInfo> prnList = QPrinterInfo::availablePrinters();
if (printerName() == name)
return;
if (name.isEmpty()) {
d->validPrinter = d->outputFormat == QPrinter::PdfFormat;
} else {
d->validPrinter = false;
for (int i = 0; i < prnList.size(); ++i) {
if (prnList[i].printerName() == name) {
d->validPrinter = true;
break;
}
}
setOutputFormat(QPrinter::PdfFormat);
return;
}
d->printEngine->setProperty(QPrintEngine::PPK_PrinterName, name);
d->addToManualSetList(QPrintEngine::PPK_PrinterName);
}
QPrinterInfo printerToUse = QPrinterInfo::printerInfo(name);
if (printerToUse.isNull())
return;
if (outputFormat() == QPrinter::PdfFormat) {
d->changeEngines(QPrinter::NativeFormat, printerToUse);
} else {
d->printEngine->setProperty(QPrintEngine::PPK_PrinterName, name);
d->addToManualSetList(QPrintEngine::PPK_PrinterName);
}
}
/*!
\since 4.4

View File

@ -59,6 +59,7 @@
#ifndef QT_NO_PRINTER
#include "QtPrintSupport/qprinter.h"
#include "QtPrintSupport/qprinterinfo.h"
#include "QtPrintSupport/qprintengine.h"
#include "QtCore/qpointer.h"
@ -75,14 +76,19 @@ class Q_PRINTSUPPORT_EXPORT QPrinterPrivate
Q_DECLARE_PUBLIC(QPrinter)
public:
QPrinterPrivate(QPrinter *printer)
: printEngine(0)
, paintEngine(0)
, q_ptr(printer)
, printRange(QPrinter::AllPages)
, use_default_engine(true)
, validPrinter(false)
, hasCustomPageMargins(false)
, hasUserSetPageSize(false)
: printEngine(0),
paintEngine(0),
realPrintEngine(0),
realPaintEngine(0),
#ifndef QT_NO_PRINTPREVIEWWIDGET
previewEngine(0),
#endif
q_ptr(printer),
printRange(QPrinter::AllPages),
use_default_engine(true),
validPrinter(false),
hasCustomPageMargins(false),
hasUserSetPageSize(false)
{
}
@ -90,9 +96,11 @@ public:
}
void init(QPrinter::PrinterMode mode);
void init(const QPrinterInfo &printer, QPrinter::PrinterMode mode);
void createDefaultEngines();
QPrinterInfo findValidPrinter(const QPrinterInfo &printer = QPrinterInfo());
void initEngines(QPrinter::OutputFormat format, const QPrinterInfo &printer);
void changeEngines(QPrinter::OutputFormat format, const QPrinterInfo &printer);
#ifndef QT_NO_PRINTPREVIEWWIDGET
QList<const QPicture *> previewPages() const;
void setPreviewMode(bool);

View File

@ -207,9 +207,6 @@ void tst_QPrinter::getSetCheck()
QCOMPARE(obj1.printerSelectionOption(), QString::fromLatin1("--option"));
#endif
obj1.setPrinterName(QString::fromLatin1("myPrinter"));
QCOMPARE(obj1.printerName(), QString::fromLatin1("myPrinter"));
// bool QPrinter::fontEmbeddingEnabled()
// void QPrinter::setFontEmbeddingEnabled(bool)
obj1.setFontEmbeddingEnabled(false);
@ -461,7 +458,7 @@ void tst_QPrinter::testNonExistentPrinter()
QPainter painter;
// Make sure it doesn't crash on setting or getting properties
printer.setPrinterName("some non existing printer");
printer.printEngine()->setProperty(QPrintEngine::PPK_PrinterName, "some non existing printer");
printer.setPageSize(QPrinter::A4);
printer.setOrientation(QPrinter::Portrait);
printer.setFullPage(true);
@ -709,9 +706,6 @@ void tst_QPrinter::valuePreservation()
printer.setCollateCopies(!status);
printer.setOutputFormat(newFormat);
#ifdef Q_OS_MAC
QEXPECT_FAIL("","QTBUG-26430", Abort);
#endif
QCOMPARE(printer.collateCopies(), !status);
printer.setOutputFormat(oldFormat);
QCOMPARE(printer.collateCopies(), !status);
@ -909,13 +903,6 @@ void tst_QPrinter::valuePreservation()
printer.setOutputFormat(newFormat);
printer.setOutputFormat(oldFormat);
QCOMPARE(printer.printerName(), status);
status = QString::fromLatin1("SuperDuperPrinter");
printer.setPrinterName(status);
printer.setOutputFormat(newFormat);
QCOMPARE(printer.printerName(), status);
printer.setOutputFormat(oldFormat);
QCOMPARE(printer.printerName(), status);
}
// QPrinter::printerSelectionOption is explicitly documented not to be available on Windows.
#ifndef Q_OS_WIN