Match up the specified paper size to an existing one if possible

When EnumForms was used then the dmPaperSize was not always correct for
the custom paper sizes available on some printers. By using
DeviceCapabilities we can be sure that the information is correct in this
respect.

This also fixes respecting of the custom paper size if one is given and
there is no corresponding existing paper size for it.

Task-number: QTBUG-34276

Change-Id: I9924d5be8527027fc434261e37f6c7aae66210c3
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
This commit is contained in:
Andy Shaw 2013-09-13 23:20:38 +02:00 committed by The Qt Project
parent 127c18ff11
commit 3396ba5612
2 changed files with 159 additions and 48 deletions

View File

@ -67,6 +67,8 @@ QT_BEGIN_NAMESPACE
Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0);
extern QPainterPath qt_regionToPath(const QRegion &region);
Q_PRINTSUPPORT_EXPORT QSizeF qt_SizeFromUnitToMillimeter(const QSizeF &, QPrinter::Unit, double);
Q_PRINTSUPPORT_EXPORT double qt_multiplierForUnit(QPrinter::Unit unit, int resolution);
// #define QT_DEBUG_DRAW
@ -114,6 +116,52 @@ static const struct {
{ 0, QPrinter::Custom }
};
// Return a list of printer paper sizes in millimeters with the corresponding dmPaperSize value
static QList<QPair<QSizeF, int> > printerPaperSizes(const QString &printerName)
{
QList<QPair<QSizeF, int> > result;
const wchar_t *name = reinterpret_cast<const wchar_t*>(printerName.utf16());
DWORD paperNameCount = DeviceCapabilities(name, NULL, DC_PAPERS, NULL, NULL);
if ((int)paperNameCount > 0) {
// If they are not equal, then there seems to be a problem with the driver
if (paperNameCount != DeviceCapabilities(name, NULL, DC_PAPERSIZE, NULL, NULL))
return result;
QScopedArrayPointer<wchar_t> papersNames(new wchar_t[paperNameCount]);
paperNameCount = DeviceCapabilities(name, NULL, DC_PAPERS, papersNames.data(), NULL);
result.reserve(paperNameCount);
QScopedArrayPointer<POINT> paperSizes(new POINT[paperNameCount]);
paperNameCount = DeviceCapabilities(name, NULL, DC_PAPERSIZE, (wchar_t *)paperSizes.data(), NULL);
for (int i=0; i <(int)paperNameCount; i++)
result.push_back(qMakePair(QSizeF(paperSizes[i].x / 10, paperSizes[i].y / 10), papersNames[i]));
}
return result;
}
// Find the best-matching printer paper for size in millimeters.
static inline int findCustomPaperSize(const QSizeF &needlePt, const QString &printerName)
{
const QList<QPair<QSizeF, int> > sizes = printerPaperSizes(printerName);
const qreal nw = needlePt.width();
const qreal nh = needlePt.height();
for (int i = 0; i < sizes.size(); ++i) {
if (qAbs(nw - sizes.at(i).first.width()) <= 1 && qAbs(nh - sizes.at(i).first.height()) <= 1)
return sizes.at(i).second;
}
return -1;
}
static inline void setDevModePaperFlags(DEVMODE *devMode, bool custom)
{
if (custom) {
devMode->dmPaperSize = DMPAPER_USER;
devMode->dmFields |= DM_PAPERLENGTH | DM_PAPERWIDTH;
} else {
devMode->dmFields &= ~(DM_PAPERLENGTH | DM_PAPERWIDTH);
devMode->dmPaperLength = 0;
devMode->dmPaperWidth = 0;
}
}
QPrinter::PaperSize mapDevmodePaperSize(int s)
{
int i = 0;
@ -1293,6 +1341,7 @@ void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &
break;
d->devMode->dmPaperSize = mapPaperSizeDevmode(QPrinter::PaperSize(value.toInt()));
d->has_custom_paper_size = (QPrinter::PaperSize(value.toInt()) == QPrinter::Custom);
setDevModePaperFlags(d->devMode, d->has_custom_paper_size);
d->doReinit();
break;
case PPK_PaperName:
@ -1320,9 +1369,19 @@ void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &
wchar_t *papers = new wchar_t[size];
size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->name.utf16()),
NULL, DC_PAPERS, papers, NULL);
d->has_custom_paper_size = false;
d->devMode->dmPaperSize = papers[paperPos];
d->doReinit();
QScopedArrayPointer<POINT> paperSizes(new POINT[size]);
DWORD paperNameCount = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->name.utf16()), NULL, DC_PAPERSIZE, (wchar_t *)paperSizes.data(), NULL);
if (paperNameCount == size) {
const double multiplier = qt_multiplierForUnit(QPrinter::Millimeter, d->resolution);
d->paper_size = QSizeF((paperSizes[paperPos].x / 10.0) * multiplier, (paperSizes[paperPos].y / 10.0) * multiplier);
// Our sizes may not match the paper name's size exactly
// So we treat it as custom so we know the paper size is correct
d->has_custom_paper_size = true;
d->devMode->dmPaperSize = papers[paperPos];
setDevModePaperFlags(d->devMode, false);
d->doReinit();
}
delete [] papers;
}
}
@ -1373,6 +1432,7 @@ void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &
break;
d->has_custom_paper_size = false;
d->devMode->dmPaperSize = value.toInt();
setDevModePaperFlags(d->devMode, d->has_custom_paper_size);
d->doReinit();
break;
@ -1382,30 +1442,17 @@ void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &
d->paper_size = value.toSizeF();
if (!d->devMode)
break;
int orientation = d->devMode->dmOrientation;
DWORD needed = 0;
DWORD returned = 0;
if (!EnumForms(d->hPrinter, 1, 0, 0, &needed, &returned)) {
BYTE *forms = (BYTE *) malloc(needed);
if (EnumForms(d->hPrinter, 1, forms, needed, &needed, &returned)) {
for (DWORD i=0; i< returned; ++i) {
FORM_INFO_1 *formArray = reinterpret_cast<FORM_INFO_1 *>(forms);
// the form sizes are specified in 1000th of a mm,
// convert the size to Points
QSizeF size((formArray[i].Size.cx * 72/25.4)/1000.0,
(formArray[i].Size.cy * 72/25.4)/1000.0);
if (qAbs(d->paper_size.width() - size.width()) <= 2
&& qAbs(d->paper_size.height() - size.height()) <= 2)
{
d->devMode->dmPaperSize = i + 1;
break;
}
}
}
free(forms);
const QSizeF sizeMM = qt_SizeFromUnitToMillimeter(d->paper_size, QPrinter::Point, d->resolution);
const int match = findCustomPaperSize(sizeMM, d->name);
setDevModePaperFlags(d->devMode, (match >= 0) ? false : true);
if (match >= 0) {
d->devMode->dmPaperSize = match;
if (d->devMode->dmOrientation != DMORIENT_PORTRAIT)
qSwap(d->paper_size.rwidth(), d->paper_size.rheight());
} else {
d->devMode->dmPaperLength = qRound(sizeMM.height() * 10.0);
d->devMode->dmPaperWidth = qRound(sizeMM.width() * 10.0);
}
if (orientation != DMORIENT_PORTRAIT)
d->paper_size = QSizeF(d->paper_size.height(), d->paper_size.width());
break;
}
@ -1692,7 +1739,7 @@ QList<QPair<QString, QSizeF> > QWin32PrintEngine::supportedSizesWithNames(const
for (int i=0;i<(int)size;i++) {
wchar_t *paper = papers + (i * 64);
QString str = QString::fromWCharArray(paper, qwcsnlen(paper, 64));
paperSizes << qMakePair(str, QSizeF(points[i].x / 10, points[i].y / 10));
paperSizes << qMakePair(str, QSizeF(points[i].x / 10.0, points[i].y / 10.0));
}
delete [] points;
}
@ -1908,30 +1955,19 @@ static void draw_text_item_win(const QPointF &pos, const QTextItemInt &ti, HDC h
SelectObject(hdc, old_font);
}
void QWin32PrintEnginePrivate::updateCustomPaperSize()
{
uint paperSize = devMode->dmPaperSize;
const uint paperSize = devMode->dmPaperSize;
has_custom_paper_size = true;
if (paperSize > 0 && mapDevmodePaperSize(paperSize) == QPrinter::Custom) {
has_custom_paper_size = true;
DWORD needed = 0;
DWORD returned = 0;
if (!EnumForms(hPrinter, 1, 0, 0, &needed, &returned)) {
BYTE *forms = (BYTE *) malloc(needed);
if (EnumForms(hPrinter, 1, forms, needed, &needed, &returned)) {
if (paperSize <= returned) {
FORM_INFO_1 *formArray = (FORM_INFO_1 *) forms;
int width = formArray[paperSize - 1].Size.cx; // 1/1000 of a mm
int height = formArray[paperSize - 1].Size.cy; // 1/1000 of a mm
paper_size = QSizeF((width * 72 /25.4) / 1000.0, (height * 72 / 25.4) / 1000.0);
} else {
has_custom_paper_size = false;
}
const QList<QPair<QSizeF, int> > paperSizes = printerPaperSizes(name);
for (int i=0; i<paperSizes.size(); i++) {
if ((uint)paperSizes.at(i).second == paperSize) {
paper_size = paperSizes.at(paperSize).first;
has_custom_paper_size = false;
break;
}
free(forms);
}
} else {
has_custom_paper_size = false;
}
}

View File

@ -113,6 +113,8 @@ private slots:
void testCustomPageSizes();
void customPaperSizeAndMargins_data();
void customPaperSizeAndMargins();
void customPaperNameSettingBySize();
void customPaperNameSettingByName();
#if !defined(QT_NO_COMPLETER) && !defined(QT_NO_FILEDIALOG)
void printDialogCompleter();
#endif
@ -967,6 +969,13 @@ void tst_QPrinter::errorReporting()
painter.end();
}
static QByteArray msgSizeMismatch(const QSizeF &actual, const QSizeF &expected)
{
QString result;
QDebug(&result) << "Paper size mismatch" << actual << "!=" << expected;
return result.toLocal8Bit();
}
void tst_QPrinter::testCustomPageSizes()
{
QPrinter p;
@ -975,12 +984,16 @@ void tst_QPrinter::testCustomPageSizes()
p.setPaperSize(customSize, QPrinter::Inch);
QSizeF paperSize = p.paperSize(QPrinter::Inch);
QCOMPARE(paperSize, customSize);
// Due to the different calculations, the sizes may be off by a fraction so we have to check it manually
// instead of relying on QSizeF comparison
QVERIFY2(sqrt(pow(paperSize.width() - customSize.width(), 2.0) + pow(paperSize.height() - customSize.height(), 2.0)) < 0.01,
msgSizeMismatch(paperSize, customSize));
QPrinter p2(QPrinter::HighResolution);
p2.setPaperSize(customSize, QPrinter::Inch);
paperSize = p.paperSize(QPrinter::Inch);
QCOMPARE(paperSize, customSize);
QVERIFY2(sqrt(pow(paperSize.width() - customSize.width(), 2.0) + pow(paperSize.height() - customSize.height(), 2.0)) < 0.01,
msgSizeMismatch(paperSize, customSize));
}
void tst_QPrinter::customPaperSizeAndMargins_data()
@ -1193,6 +1206,68 @@ void tst_QPrinter::testPageMetrics()
QCOMPARE(printer.pageSizeMM(), QSizeF(widthMMf, heightMMf));
}
void tst_QPrinter::customPaperNameSettingBySize()
{
#ifndef Q_OS_WIN
QSKIP("Currently this triggers a problem on non Windows platforms, this will be fixed separately - QTBUG-34521");
#endif
QPrinter printer(QPrinter::HighResolution);
QPrinterInfo info(printer);
QList<QPair<QString, QSizeF> > sizes = info.supportedSizesWithNames();
if (sizes.size() == 0)
QSKIP("No printers installed on this machine");
for (int i=0; i<sizes.size(); i++) {
printer.setPaperSize(sizes.at(i).second, QPrinter::Millimeter);
QCOMPARE(sizes.at(i).second, printer.paperSize(QPrinter::Millimeter));
// Some printers have the same size under different names which can cause a problem for the test
// So we iterate up to the current position to check
QSizeF paperSize = sizes.at(i).second;
QString paperName = printer.paperName();
bool paperNameFound = (sizes.at(i).first == paperName);
if (!paperNameFound) {
for (int j=0; j<i; j++) {
if (sizes.at(j).second == paperSize && sizes.at(j).first == paperName) {
paperNameFound = true;
break;
}
}
}
// Fail with the original values
if (!paperNameFound)
QCOMPARE(sizes.at(i).first, printer.paperName());
}
// Check setting a custom size after setting a standard one works
QSizeF customSize(200, 200);
printer.setPaperSize(customSize, QPrinter::Millimeter);
QCOMPARE(printer.paperSize(QPrinter::Millimeter), customSize);
QCOMPARE(printer.paperSize(), QPrinter::Custom);
// Finally check setting a standard size after a custom one works
printer.setPaperSize(sizes.at(0).second, QPrinter::Millimeter);
QCOMPARE(printer.paperName(), sizes.at(0).first);
QCOMPARE(printer.paperSize(QPrinter::Millimeter), sizes.at(0).second);
}
void tst_QPrinter::customPaperNameSettingByName()
{
#ifndef Q_OS_WIN
QSKIP("Currently this triggers a problem on non Windows platforms, this will be fixed separately - QTBUG-34521");
#endif
QPrinter printer(QPrinter::HighResolution);
QPrinterInfo info(printer);
QList<QPair<QString, QSizeF> > sizes = info.supportedSizesWithNames();
if (sizes.size() == 0)
QSKIP("No printers installed on this machine");
for (int i=0; i<sizes.size(); i++) {
printer.setPaperName(sizes.at(i).first);
QCOMPARE(sizes.at(i).first, printer.paperName());
QSizeF paperSize = printer.paperSize(QPrinter::Millimeter);
QVERIFY2(sqrt(pow(sizes.at(i).second.width() - paperSize.width(), 2.0) + pow(sizes.at(i).second.height() - paperSize.height(), 2.0)) < 0.01,
msgSizeMismatch(sizes.at(i).second, paperSize));
}
}
#endif // QT_NO_PRINTER
QTEST_MAIN(tst_QPrinter)