Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qprintengine_win.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include <QtPrintSupport/qtprintsupportglobal.h>
6
7#ifndef QT_NO_PRINTER
8
10
11#include <limits.h>
12
13#include <private/qprinter_p.h>
14#include <private/qfont_p.h>
15#include <private/qfontengine_p.h>
16#include <private/qpainter_p.h>
17#if QT_CONFIG(directwrite)
18# include <private/qwindowsfontenginedirectwrite_p.h>
19#endif
20
21#include <qpa/qplatformprintplugin.h>
22#include <qpa/qplatformprintersupport.h>
23
24#include <qbitmap.h>
25#include <qdebug.h>
26#include <qlist.h>
27#include <qpicture.h>
28#include <qpa/qplatformpixmap.h>
29#include <private/qpicture_p.h>
30#include <private/qpixmap_raster_p.h>
31#include <QtCore/QMetaType>
32#include <QtCore/qt_windows.h>
33#include <QtGui/qpagelayout.h>
34#include <QtGui/private/qpixmap_win_p.h>
35
37
38extern QPainterPath qt_regionToPath(const QRegion &region);
39extern QMarginsF qt_convertMargins(const QMarginsF &margins, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits);
40
41// #define QT_DEBUG_DRAW
42// #define QT_DEBUG_METRICS
43
44static void draw_text_item_win(const QPointF &_pos, const QTextItemInt &ti, HDC hdc,
45 const QTransform &xform, const QPointF &topLeft);
46
47QWin32PrintEngine::QWin32PrintEngine(QPrinter::PrinterMode mode, const QString &deviceId)
48 : QAlphaPaintEngine(*(new QWin32PrintEnginePrivate),
49 PaintEngineFeatures(PrimitiveTransform
50 | PixmapTransform
51 | PerspectiveTransform
52 | PainterPaths
53 | Antialiasing
54 | PaintOutsidePaintEvent))
55{
56 Q_D(QWin32PrintEngine);
57 d->mode = mode;
58 QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get();
59 if (ps)
60 d->m_printDevice = ps->createPrintDevice(deviceId.isEmpty() ? ps->defaultPrintDeviceId() : deviceId);
61 d->m_pageLayout.setPageSize(d->m_printDevice.defaultPageSize());
62 d->initialize();
63}
64
65static QByteArray msgBeginFailed(const char *function, const DOCINFO &d)
66{
67 QString result;
68 QTextStream str(&result);
69 str << "QWin32PrintEngine::begin: " << function << " failed";
70 if (d.lpszDocName && d.lpszDocName[0])
71 str << ", document \"" << QString::fromWCharArray(d.lpszDocName) << '"';
72 if (d.lpszOutput && d.lpszOutput[0])
73 str << ", file \"" << QString::fromWCharArray(d.lpszOutput) << '"';
74 return std::move(result).toLocal8Bit();
75}
76
77bool QWin32PrintEngine::begin(QPaintDevice *pdev)
78{
79 Q_D(QWin32PrintEngine);
80
81 QAlphaPaintEngine::begin(pdev);
82 if (!continueCall())
83 return true;
84
85 if (d->reinit) {
86 d->resetDC();
87 d->reinit = false;
88 }
89
90 // ### set default colors and stuff...
91
92 bool ok = d->state == QPrinter::Idle;
93
94 if (!d->hdc)
95 return false;
96
97 d->devMode->dmCopies = d->num_copies;
98
99 DOCINFO di;
100 memset(&di, 0, sizeof(DOCINFO));
101 di.cbSize = sizeof(DOCINFO);
102 if (d->docName.isEmpty())
103 di.lpszDocName = L"document1";
104 else
105 di.lpszDocName = reinterpret_cast<const wchar_t *>(d->docName.utf16());
106 if (d->printToFile && !d->fileName.isEmpty())
107 di.lpszOutput = reinterpret_cast<const wchar_t *>(d->fileName.utf16());
108 if (d->printToFile)
109 di.lpszOutput = d->fileName.isEmpty() ? L"FILE:" : reinterpret_cast<const wchar_t *>(d->fileName.utf16());
110 if (ok && StartDoc(d->hdc, &di) == SP_ERROR) {
111 qErrnoWarning(msgBeginFailed("StartDoc", di));
112 ok = false;
113 }
114
115 if (StartPage(d->hdc) <= 0) {
116 qErrnoWarning(msgBeginFailed("StartPage", di));
117 ok = false;
118 }
119
120 if (!ok) {
121 d->state = QPrinter::Idle;
122 } else {
123 d->state = QPrinter::Active;
124 }
125
126 d->matrix = QTransform();
127 d->has_pen = true;
128 d->pen = QColor(Qt::black);
129 d->has_brush = false;
130
131 d->complex_xform = false;
132
133 updateMatrix(d->matrix);
134
135 if (!ok)
136 cleanUp();
137
138#ifdef QT_DEBUG_METRICS
139 qDebug("QWin32PrintEngine::begin()");
140 d->debugMetrics();
141#endif // QT_DEBUG_METRICS
142
143 return ok;
144}
145
146bool QWin32PrintEngine::end()
147{
148 Q_D(QWin32PrintEngine);
149
150 if (d->hdc) {
151 if (d->state == QPrinter::Aborted) {
152 cleanUp();
153 AbortDoc(d->hdc);
154 return true;
155 }
156 }
157
158 QAlphaPaintEngine::end();
159 if (!continueCall())
160 return true;
161
162 if (d->hdc) {
163 if (EndPage(d->hdc) <= 0) // end; printing done
164 qErrnoWarning("QWin32PrintEngine::end: EndPage failed (%p)", d->hdc);
165 if (EndDoc(d->hdc) <= 0)
166 qErrnoWarning("QWin32PrintEngine::end: EndDoc failed");
167 }
168
169 d->state = QPrinter::Idle;
170 d->reinit = true;
171 return true;
172}
173
174bool QWin32PrintEngine::newPage()
175{
176 Q_D(QWin32PrintEngine);
177 Q_ASSERT(isActive());
178
179 Q_ASSERT(d->hdc);
180
181 flushAndInit();
182
183 bool transparent = GetBkMode(d->hdc) == TRANSPARENT;
184
185 if (EndPage(d->hdc) <= 0) {
186 qErrnoWarning("QWin32PrintEngine::newPage: EndPage failed");
187 return false;
188 }
189
190 if (d->reinit) {
191 if (!d->resetDC())
192 return false;
193 d->reinit = false;
194 }
195
196 if (StartPage(d->hdc) <= 0) {
197 qErrnoWarning("Win32PrintEngine::newPage: StartPage failed");
198 return false;
199 }
200
201 SetTextAlign(d->hdc, TA_BASELINE);
202 if (transparent)
203 SetBkMode(d->hdc, TRANSPARENT);
204
205#ifdef QT_DEBUG_METRICS
206 qDebug("QWin32PrintEngine::newPage()");
207 d->debugMetrics();
208#endif // QT_DEBUG_METRICS
209
210 // ###
211 return true;
212
213 bool success = false;
214 if (d->hdc && d->state == QPrinter::Active) {
215 if (EndPage(d->hdc) > 0) {
216 // reinitialize the DC before StartPage if needed,
217 // because resetdc is disabled between calls to the StartPage and EndPage functions
218 // (see StartPage documentation in the Platform SDK:Windows GDI)
219// state = PST_ACTIVEDOC;
220// reinit();
221// state = PST_ACTIVE;
222 // start the new page now
223 if (d->reinit) {
224 if (!d->resetDC())
225 qErrnoWarning("QWin32PrintEngine::newPage(), ResetDC failed (2)");
226 d->reinit = false;
227 }
228 success = (StartPage(d->hdc) > 0);
229 if (!success)
230 qErrnoWarning("Win32PrintEngine::newPage: StartPage failed (2)");
231 }
232 if (!success) {
233 d->state = QPrinter::Aborted;
234 return false;
235 }
236 }
237 return true;
238}
239
240bool QWin32PrintEngine::abort()
241{
242 // do nothing loop.
243 return false;
244}
245
246void QWin32PrintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
247{
248 Q_D(const QWin32PrintEngine);
249
250 QAlphaPaintEngine::drawTextItem(p, textItem);
251 if (!continueCall())
252 return;
253
254 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
255 QRgb brushColor = state->pen().brush().color().rgb();
256 bool fallBack = state->pen().brush().style() != Qt::SolidPattern
257 || qAlpha(brushColor) != 0xff
258 || d->txop >= QTransform::TxProject
259 || !d->embed_fonts;
260
261 if (!fallBack) {
262 bool deleteFont = false;
263 HFONT hfont = nullptr;
264 if (ti.fontEngine->type() == QFontEngine::Win) {
265 hfont = static_cast<HFONT>(ti.fontEngine->handle());
266 }
267#if QT_CONFIG(directwrite)
268 else if (ti.fontEngine->type() == QFontEngine::DirectWrite) {
269 QWindowsFontEngineDirectWrite *fedw = static_cast<QWindowsFontEngineDirectWrite *>(ti.fontEngine);
270 hfont = fedw->createHFONT();
271 if (hfont)
272 deleteFont = true;
273 }
274#endif
275
276 if (hfont) {
277 // Try selecting the font to see if we get a substitution font
278 SelectObject(d->hdc, hfont);
279 if (GetDeviceCaps(d->hdc, TECHNOLOGY) != DT_CHARSTREAM) {
280 LOGFONT logFont;
281 GetObject(hfont, sizeof(LOGFONT), &logFont);
282
283 wchar_t n[64];
284 GetTextFace(d->hdc, 64, n);
285 fallBack = QString::fromWCharArray(n)
286 != QString::fromWCharArray(logFont.lfFaceName);
287
288 if (deleteFont)
289 DeleteObject(hfont);
290 }
291 } else {
292 fallBack = true;
293 }
294 }
295
296
297 if (fallBack) {
298 QPaintEngine::drawTextItem(p, textItem);
299 return ;
300 }
301
302 COLORREF cf = RGB(qRed(brushColor), qGreen(brushColor), qBlue(brushColor));
303 SelectObject(d->hdc, CreateSolidBrush(cf));
304 SelectObject(d->hdc, CreatePen(PS_SOLID, 1, cf));
305 SetTextColor(d->hdc, cf);
306
307 draw_text_item_win(p, ti, d->hdc, d->matrix, QPointF(0.0, 0.0));
308 DeleteObject(SelectObject(d->hdc,GetStockObject(HOLLOW_BRUSH)));
309 DeleteObject(SelectObject(d->hdc,GetStockObject(BLACK_PEN)));
310}
311
312int QWin32PrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const
313{
314 Q_D(const QWin32PrintEngine);
315
316 if (!d->hdc)
317 return 0;
318
319 int val;
320 int res = d->resolution;
321
322 switch (m) {
323 case QPaintDevice::PdmWidth:
324 val = d->m_paintRectPixels.width();
325#ifdef QT_DEBUG_METRICS
326 qDebug() << "QWin32PrintEngine::metric(PdmWidth) = " << val;
327 d->debugMetrics();
328#endif // QT_DEBUG_METRICS
329 break;
330 case QPaintDevice::PdmHeight:
331 val = d->m_paintRectPixels.height();
332#ifdef QT_DEBUG_METRICS
333 qDebug() << "QWin32PrintEngine::metric(PdmHeight) = " << val;
334 d->debugMetrics();
335#endif // QT_DEBUG_METRICS
336 break;
337 case QPaintDevice::PdmDpiX:
338 val = res;
339 break;
340 case QPaintDevice::PdmDpiY:
341 val = res;
342 break;
343 case QPaintDevice::PdmPhysicalDpiX:
344 val = GetDeviceCaps(d->hdc, LOGPIXELSX);
345 break;
346 case QPaintDevice::PdmPhysicalDpiY:
347 val = GetDeviceCaps(d->hdc, LOGPIXELSY);
348 break;
349 case QPaintDevice::PdmWidthMM:
350 val = d->m_paintSizeMM.width();
351#ifdef QT_DEBUG_METRICS
352 qDebug() << "QWin32PrintEngine::metric(PdmWidthMM) = " << val;
353 d->debugMetrics();
354#endif // QT_DEBUG_METRICS
355 break;
356 case QPaintDevice::PdmHeightMM:
357 val = d->m_paintSizeMM.height();
358#ifdef QT_DEBUG_METRICS
359 qDebug() << "QWin32PrintEngine::metric(PdmHeightMM) = " << val;
360 d->debugMetrics();
361#endif // QT_DEBUG_METRICS
362 break;
363 case QPaintDevice::PdmNumColors:
364 {
365 int bpp = GetDeviceCaps(d->hdc, BITSPIXEL);
366 if (bpp==32)
367 val = INT_MAX;
368 else if (bpp<=8)
369 val = GetDeviceCaps(d->hdc, NUMCOLORS);
370 else
371 val = 1 << (bpp * GetDeviceCaps(d->hdc, PLANES));
372 }
373 break;
374 case QPaintDevice::PdmDepth:
375 val = GetDeviceCaps(d->hdc, PLANES);
376 break;
377 case QPaintDevice::PdmDevicePixelRatio:
378 val = 1;
379 break;
380 case QPaintDevice::PdmDevicePixelRatioScaled:
381 val = 1 * QPaintDevice::devicePixelRatioFScale();
382 break;
383 default:
384 qWarning("QPrinter::metric: Invalid metric command");
385 return 0;
386 }
387 return val;
388}
389
390void QWin32PrintEngine::updateState(const QPaintEngineState &state)
391{
392 Q_D(QWin32PrintEngine);
393
394 QAlphaPaintEngine::updateState(state);
395 if (!continueCall())
396 return;
397
398 if (state.state() & DirtyTransform) {
399 updateMatrix(state.transform());
400 }
401
402 if (state.state() & DirtyPen) {
403 d->pen = state.pen();
404 d->has_pen = d->pen.style() != Qt::NoPen && d->pen.isSolid();
405 }
406
407 if (state.state() & DirtyBrush) {
408 QBrush brush = state.brush();
409 d->has_brush = brush.style() == Qt::SolidPattern;
410 d->brush_color = brush.color();
411 }
412
413 if (state.state() & DirtyClipEnabled) {
414 if (state.isClipEnabled())
415 updateClipPath(painter()->clipPath(), Qt::ReplaceClip);
416 else
417 updateClipPath(QPainterPath(), Qt::NoClip);
418 }
419
420 if (state.state() & DirtyClipPath) {
421 updateClipPath(state.clipPath(), state.clipOperation());
422 }
423
424 if (state.state() & DirtyClipRegion) {
425 QRegion clipRegion = state.clipRegion();
426 QPainterPath clipPath = qt_regionToPath(clipRegion);
427 updateClipPath(clipPath, state.clipOperation());
428 }
429}
430
431void QWin32PrintEngine::updateClipPath(const QPainterPath &clipPath, Qt::ClipOperation op)
432{
433 Q_D(QWin32PrintEngine);
434
435 bool doclip = true;
436 if (op == Qt::NoClip) {
437 SelectClipRgn(d->hdc, nullptr);
438 doclip = false;
439 }
440
441 if (doclip) {
442 QPainterPath xformed = clipPath * d->matrix;
443
444 if (xformed.isEmpty()) {
445// QRegion empty(-0x1000000, -0x1000000, 1, 1);
446 HRGN empty = CreateRectRgn(-0x1000000, -0x1000000, -0x0fffffff, -0x0ffffff);
447 SelectClipRgn(d->hdc, empty);
448 DeleteObject(empty);
449 } else {
450 d->composeGdiPath(xformed);
451 const int ops[] = {
452 -1, // Qt::NoClip, covered above
453 RGN_COPY, // Qt::ReplaceClip
454 RGN_AND, // Qt::IntersectClip
455 RGN_OR // Qt::UniteClip
456 };
457 Q_ASSERT(op > 0 && unsigned(op) <= sizeof(ops) / sizeof(int));
458 SelectClipPath(d->hdc, ops[op]);
459 }
460 }
461
462 QPainterPath aclip = qt_regionToPath(alphaClipping());
463 if (!aclip.isEmpty()) {
464 QTransform tx(d->stretch_x, 0, 0, d->stretch_y, d->origin_x, d->origin_y);
465 d->composeGdiPath(tx.map(aclip));
466 SelectClipPath(d->hdc, RGN_DIFF);
467 }
468}
469
470void QWin32PrintEngine::updateMatrix(const QTransform &m)
471{
472 Q_D(QWin32PrintEngine);
473
474 QTransform stretch(d->stretch_x, 0, 0, d->stretch_y, d->origin_x, d->origin_y);
475 d->painterMatrix = m;
476 d->matrix = d->painterMatrix * stretch;
477 d->txop = d->matrix.type();
478 d->complex_xform = (d->txop > QTransform::TxScale);
479}
480
487
488void QWin32PrintEngine::drawPixmap(const QRectF &targetRect,
489 const QPixmap &originalPixmap,
490 const QRectF &sourceRect)
491{
492 Q_D(QWin32PrintEngine);
493
494 QAlphaPaintEngine::drawPixmap(targetRect, originalPixmap, sourceRect);
495 if (!continueCall())
496 return;
497
498 const int tileSize = 2048;
499
500 QRectF r = targetRect;
501 QRectF sr = sourceRect;
502
503 QPixmap pixmap = originalPixmap;
504 if (sr.size() != pixmap.size()) {
505 pixmap = pixmap.copy(sr.toRect());
506 }
507
508 qreal scaleX = 1.0f;
509 qreal scaleY = 1.0f;
510
511 QTransform scaleMatrix = QTransform::fromScale(r.width() / pixmap.width(), r.height() / pixmap.height());
512 QTransform adapted = QPixmap::trueMatrix(d->painterMatrix * scaleMatrix,
513 pixmap.width(), pixmap.height());
514
515 qreal xform_offset_x = adapted.dx();
516 qreal xform_offset_y = adapted.dy();
517
518 if (d->complex_xform) {
519 pixmap = pixmap.transformed(adapted);
520 scaleX = d->stretch_x;
521 scaleY = d->stretch_y;
522 } else {
523 scaleX = d->stretch_x * (r.width() / pixmap.width()) * d->painterMatrix.m11();
524 scaleY = d->stretch_y * (r.height() / pixmap.height()) * d->painterMatrix.m22();
525 }
526
527 QPointF topLeft = r.topLeft() * d->painterMatrix;
528 int tx = int(topLeft.x() * d->stretch_x + d->origin_x);
529 int ty = int(topLeft.y() * d->stretch_y + d->origin_y);
530 int tw = qAbs(int(pixmap.width() * scaleX));
531 int th = qAbs(int(pixmap.height() * scaleY));
532
533 xform_offset_x *= d->stretch_x;
534 xform_offset_y *= d->stretch_y;
535
536 int dc_state = SaveDC(d->hdc);
537
538 int tilesw = pixmap.width() / tileSize;
539 int tilesh = pixmap.height() / tileSize;
540 ++tilesw;
541 ++tilesh;
542
543 int txinc = tileSize*scaleX;
544 int tyinc = tileSize*scaleY;
545
546 for (int y = 0; y < tilesh; ++y) {
547 int tposy = ty + (y * tyinc);
548 int imgh = tileSize;
549 int height = tyinc;
550 if (y == (tilesh - 1)) {
551 imgh = pixmap.height() - (y * tileSize);
552 height = (th - (y * tyinc));
553 }
554 for (int x = 0; x < tilesw; ++x) {
555 int tposx = tx + (x * txinc);
556 int imgw = tileSize;
557 int width = txinc;
558 if (x == (tilesw - 1)) {
559 imgw = pixmap.width() - (x * tileSize);
560 width = (tw - (x * txinc));
561 }
562
563
564 QImage img(QSize(imgw, imgh), QImage::Format_RGB32);
565 img.setDevicePixelRatio(pixmap.devicePixelRatio());
566 img.fill(Qt::white);
567 QPainter painter(&img);
568 painter.drawPixmap(0,0, pixmap, tileSize * x, tileSize * y, imgw, imgh);
569 QPixmap p = QPixmap::fromImage(img);
570
571 HBITMAP hbitmap = qt_pixmapToWinHBITMAP(p, HBitmapNoAlpha);
572 HDC hbitmap_hdc = CreateCompatibleDC(d->hdc);
573 HGDIOBJ null_bitmap = SelectObject(hbitmap_hdc, hbitmap);
574
575 if (!StretchBlt(d->hdc, qRound(tposx - xform_offset_x), qRound(tposy - xform_offset_y), width, height,
576 hbitmap_hdc, 0, 0, p.width(), p.height(), SRCCOPY))
577 qErrnoWarning("QWin32PrintEngine::drawPixmap, StretchBlt failed");
578
579 SelectObject(hbitmap_hdc, null_bitmap);
580 DeleteObject(hbitmap);
581 DeleteDC(hbitmap_hdc);
582 }
583 }
584
585 RestoreDC(d->hdc, dc_state);
586}
587
588
589void QWin32PrintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &pos)
590{
591 Q_D(QWin32PrintEngine);
592
593 QAlphaPaintEngine::drawTiledPixmap(r, pm, pos);
594 if (!continueCall())
595 return;
596
597 if (d->complex_xform || !pos.isNull()) {
598 QPaintEngine::drawTiledPixmap(r, pm, pos);
599 } else {
600 int dc_state = SaveDC(d->hdc);
601
602 HBITMAP hbitmap = qt_pixmapToWinHBITMAP(pm, HBitmapNoAlpha);
603 HDC hbitmap_hdc = CreateCompatibleDC(d->hdc);
604 HGDIOBJ null_bitmap = SelectObject(hbitmap_hdc, hbitmap);
605
606 QRectF trect = d->painterMatrix.mapRect(r);
607 int tx = int(trect.left() * d->stretch_x + d->origin_x);
608 int ty = int(trect.top() * d->stretch_y + d->origin_y);
609
610 int xtiles = int(trect.width() / pm.width()) + 1;
611 int ytiles = int(trect.height() / pm.height()) + 1;
612 int xinc = int(pm.width() * d->stretch_x);
613 int yinc = int(pm.height() * d->stretch_y);
614
615 for (int y = 0; y < ytiles; ++y) {
616 int ity = ty + (yinc * y);
617 int ith = pm.height();
618 if (y == (ytiles - 1)) {
619 ith = int(trect.height() - (pm.height() * y));
620 }
621
622 for (int x = 0; x < xtiles; ++x) {
623 int itx = tx + (xinc * x);
624 int itw = pm.width();
625 if (x == (xtiles - 1)) {
626 itw = int(trect.width() - (pm.width() * x));
627 }
628
629 if (!StretchBlt(d->hdc, itx, ity, int(itw * d->stretch_x), int(ith * d->stretch_y),
630 hbitmap_hdc, 0, 0, itw, ith, SRCCOPY))
631 qErrnoWarning("QWin32PrintEngine::drawPixmap, StretchBlt failed");
632
633 }
634 }
635
636 SelectObject(hbitmap_hdc, null_bitmap);
637 DeleteObject(hbitmap);
638 DeleteDC(hbitmap_hdc);
639
640 RestoreDC(d->hdc, dc_state);
641 }
642}
643
644
645void QWin32PrintEnginePrivate::composeGdiPath(const QPainterPath &path)
646{
647 if (!BeginPath(hdc))
648 qErrnoWarning("QWin32PrintEnginePrivate::drawPath: BeginPath failed");
649
650 // Drawing the subpaths
651 int start = -1;
652 for (int i=0; i<path.elementCount(); ++i) {
653 const QPainterPath::Element &elm = path.elementAt(i);
654 switch (elm.type) {
655 case QPainterPath::MoveToElement:
656 if (start >= 0
657 && path.elementAt(start).x == path.elementAt(i-1).x
658 && path.elementAt(start).y == path.elementAt(i-1).y)
659 CloseFigure(hdc);
660 start = i;
661 MoveToEx(hdc, qRound(elm.x), qRound(elm.y), 0);
662 break;
663 case QPainterPath::LineToElement:
664 LineTo(hdc, qRound(elm.x), qRound(elm.y));
665 break;
666 case QPainterPath::CurveToElement: {
667 POINT pts[3] = {
668 { qRound(elm.x), qRound(elm.y) },
669 { qRound(path.elementAt(i+1).x), qRound(path.elementAt(i+1).y) },
670 { qRound(path.elementAt(i+2).x), qRound(path.elementAt(i+2).y) }
671 };
672 i+=2;
673 PolyBezierTo(hdc, pts, 3);
674 break;
675 }
676 default:
677 qFatal("QWin32PaintEngine::drawPath: Unhandled type: %d", elm.type);
678 }
679 }
680
681 if (start >= 0
682 && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x
683 && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y)
684 CloseFigure(hdc);
685
686 if (!EndPath(hdc))
687 qErrnoWarning("QWin32PaintEngine::drawPath: EndPath failed");
688
689 SetPolyFillMode(hdc, path.fillRule() == Qt::WindingFill ? WINDING : ALTERNATE);
690}
691
692
693void QWin32PrintEnginePrivate::fillPath_dev(const QPainterPath &path, const QColor &color)
694{
695#ifdef QT_DEBUG_DRAW
696 qDebug() << " --- QWin32PrintEnginePrivate::fillPath() bound:" << path.boundingRect() << color;
697#endif
698
699 composeGdiPath(path);
700
701 HBRUSH brush = CreateSolidBrush(RGB(color.red(), color.green(), color.blue()));
702 HGDIOBJ old_brush = SelectObject(hdc, brush);
703 FillPath(hdc);
704 DeleteObject(SelectObject(hdc, old_brush));
705}
706
707void QWin32PrintEnginePrivate::strokePath_dev(const QPainterPath &path, const QColor &color, qreal penWidth)
708{
709 composeGdiPath(path);
710 LOGBRUSH brush;
711 brush.lbStyle = BS_SOLID;
712 brush.lbColor = RGB(color.red(), color.green(), color.blue());
713 DWORD capStyle = PS_ENDCAP_SQUARE;
714 DWORD joinStyle = PS_JOIN_BEVEL;
715 if (pen.capStyle() == Qt::FlatCap)
716 capStyle = PS_ENDCAP_FLAT;
717 else if (pen.capStyle() == Qt::RoundCap)
718 capStyle = PS_ENDCAP_ROUND;
719
720 if (pen.joinStyle() == Qt::MiterJoin)
721 joinStyle = PS_JOIN_MITER;
722 else if (pen.joinStyle() == Qt::RoundJoin)
723 joinStyle = PS_JOIN_ROUND;
724
725 HPEN pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | capStyle | joinStyle,
726 (penWidth == 0) ? 1 : penWidth, &brush, 0, nullptr);
727
728 HGDIOBJ old_pen = SelectObject(hdc, pen);
729 StrokePath(hdc);
730 DeleteObject(SelectObject(hdc, old_pen));
731}
732
733
734void QWin32PrintEnginePrivate::fillPath(const QPainterPath &path, const QColor &color)
735{
736 fillPath_dev(path * matrix, color);
737}
738
739void QWin32PrintEnginePrivate::strokePath(const QPainterPath &path, const QColor &color)
740{
741 QPainterPathStroker stroker;
742 if (pen.style() == Qt::CustomDashLine) {
743 stroker.setDashPattern(pen.dashPattern());
744 stroker.setDashOffset(pen.dashOffset());
745 } else {
746 stroker.setDashPattern(pen.style());
747 }
748 stroker.setCapStyle(pen.capStyle());
749 stroker.setJoinStyle(pen.joinStyle());
750 stroker.setMiterLimit(pen.miterLimit());
751
752 QPainterPath stroke;
753 qreal width = pen.widthF();
754 bool cosmetic = pen.isCosmetic();
755 if (pen.style() == Qt::SolidLine && (cosmetic || matrix.type() < QTransform::TxScale)) {
756 strokePath_dev(path * matrix, color, width);
757 } else {
758 stroker.setWidth(width);
759 if (cosmetic) {
760 stroke = stroker.createStroke(path * matrix);
761 } else {
762 stroke = stroker.createStroke(path) * painterMatrix;
763 QTransform stretch(stretch_x, 0, 0, stretch_y, origin_x, origin_y);
764 stroke = stroke * stretch;
765 }
766
767 if (stroke.isEmpty())
768 return;
769
770 fillPath_dev(stroke, color);
771 }
772}
773
774
775void QWin32PrintEngine::drawPath(const QPainterPath &path)
776{
777#ifdef QT_DEBUG_DRAW
778 qDebug() << " - QWin32PrintEngine::drawPath(), bounds: " << path.boundingRect();
779#endif
780
781 Q_D(QWin32PrintEngine);
782
783 QAlphaPaintEngine::drawPath(path);
784 if (!continueCall())
785 return;
786
787 if (d->has_brush)
788 d->fillPath(path, d->brush_color);
789
790 if (d->has_pen)
791 d->strokePath(path, d->pen.color());
792}
793
794
795void QWin32PrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
796{
797#ifdef QT_DEBUG_DRAW
798 qDebug() << " - QWin32PrintEngine::drawPolygon(), pointCount: " << pointCount;
799#endif
800
801 QAlphaPaintEngine::drawPolygon(points, pointCount, mode);
802 if (!continueCall())
803 return;
804
805 Q_ASSERT(pointCount > 1);
806
807 QPainterPath path(points[0]);
808
809 for (int i=1; i<pointCount; ++i) {
810 path.lineTo(points[i]);
811 }
812
813 Q_D(QWin32PrintEngine);
814
815 bool has_brush = d->has_brush;
816
817 if (mode == PolylineMode)
818 d->has_brush = false; // No brush for polylines
819 else
820 path.closeSubpath(); // polygons are should always be closed.
821
822 drawPath(path);
823 d->has_brush = has_brush;
824}
825
826QWin32PrintEnginePrivate::~QWin32PrintEnginePrivate()
827{
828 release();
829}
830
832{
833 release();
834
835 Q_ASSERT(!hPrinter);
836 Q_ASSERT(!hdc);
837 Q_ASSERT(!devMode);
838 Q_ASSERT(!pInfo);
839
840 if (!m_printDevice.isValid())
841 return;
842
843 txop = QTransform::TxNone;
844
845 QString printerName = m_printDevice.id();
846 bool ok = OpenPrinter(reinterpret_cast<LPWSTR>(const_cast<ushort *>(printerName.utf16())),
847 reinterpret_cast<LPHANDLE>(&hPrinter), nullptr);
848 if (!ok) {
849 qErrnoWarning("QWin32PrintEngine::initialize: OpenPrinter failed");
850 return;
851 }
852
853 // Fetch the PRINTER_INFO_2 with DEVMODE data containing the
854 // printer settings.
855 DWORD infoSize, numBytes;
856 GetPrinter(hPrinter, 2, nullptr, 0, &infoSize);
857 hMem = GlobalAlloc(GHND, infoSize);
858 pInfo = reinterpret_cast<PRINTER_INFO_2*>(GlobalLock(hMem));
859 ok = GetPrinter(hPrinter, 2, reinterpret_cast<LPBYTE>(pInfo), infoSize, &numBytes);
860
861 if (!ok) {
862 qErrnoWarning("QWin32PrintEngine::initialize: GetPrinter failed");
863 release();
864 return;
865 }
866
867 devMode = pInfo->pDevMode;
868
869 if (!devMode) {
870 // pInfo->pDevMode == NULL for some printers and passing NULL
871 // into CreateDC leads to the printer doing nothing. In addition,
872 // the framework assumes that devMode isn't NULL, such as in
873 // QWin32PrintEngine::begin() and QPageSetupDialog::exec()
874 // Attempt to get the DEVMODE a different way.
875
876 // Allocate the required buffer
877 auto *lpwPrinterName = reinterpret_cast<LPWSTR>(const_cast<ushort *>(printerName.utf16()));
878 LONG result = DocumentProperties(nullptr, hPrinter, lpwPrinterName,
879 nullptr, nullptr, 0);
880 devMode = reinterpret_cast<DEVMODE *>(malloc(result));
881 initializeDevMode(devMode);
882 ownsDevMode = true;
883
884 // Get the default DevMode
885 result = DocumentProperties(nullptr, hPrinter, lpwPrinterName,
886 devMode, nullptr, DM_OUT_BUFFER);
887 if (result != IDOK) {
888 qErrnoWarning("QWin32PrintEngine::initialize: Failed to obtain devMode");
889 free(devMode);
890 devMode = nullptr;
891 ownsDevMode = false;
892 }
893 }
894
895 hdc = CreateDC(nullptr, reinterpret_cast<LPCWSTR>(printerName.utf16()),
896 nullptr, devMode);
897
898 if (!hdc) {
899 qErrnoWarning("QWin32PrintEngine::initialize: CreateDC failed");
900 release();
901 return;
902 }
903
904 Q_ASSERT(hPrinter);
905 Q_ASSERT(pInfo);
906
907 initHDC();
908
909 if (devMode) {
910 num_copies = devMode->dmCopies;
911 devMode->dmCollate = DMCOLLATE_TRUE;
913 }
914
915#if defined QT_DEBUG_DRAW || defined QT_DEBUG_METRICS
916 qDebug("QWin32PrintEngine::initialize()");
917 debugMetrics();
918#endif // QT_DEBUG_DRAW || QT_DEBUG_METRICS
919}
920
921void QWin32PrintEnginePrivate::initializeDevMode(DEVMODE *devMode)
922{
923 memset(devMode, 0, sizeof(DEVMODE));
924 devMode->dmSize = sizeof(DEVMODE);
925 devMode->dmSpecVersion = DM_SPECVERSION;
926}
927
929{
930 Q_ASSERT(hdc);
931
932 HDC display_dc = GetDC(nullptr);
933 dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
934 dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
935 dpi_display = GetDeviceCaps(display_dc, LOGPIXELSY);
936 ReleaseDC(nullptr, display_dc);
937 if (dpi_display == 0) {
938 qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
939 "might be a driver problem");
940 dpi_display = 96; // Reasonable default
941 }
942
943 switch(mode) {
944 case QPrinter::ScreenResolution:
946 stretch_x = dpi_x / double(dpi_display);
947 stretch_y = dpi_y / double(dpi_display);
948 break;
949 case QPrinter::PrinterResolution:
950 case QPrinter::HighResolution:
952 stretch_x = 1;
953 stretch_y = 1;
954 break;
955 default:
956 break;
957 }
958
960}
961
963{
964 if (globalDevMode) { // Devmode comes from print dialog
965 GlobalUnlock(globalDevMode);
966 } else if (hMem) {
967 GlobalUnlock(hMem);
968 GlobalFree(hMem);
969 }
970 if (hPrinter)
971 ClosePrinter(hPrinter);
972 if (hdc)
973 DeleteDC(hdc);
974
975 // Check if devMode was allocated separately from pInfo / hMem.
976 if (ownsDevMode)
977 free(devMode);
978
979 hdc = nullptr;
980 hPrinter = nullptr;
981 pInfo = nullptr;
982 hMem = nullptr;
983 devMode = nullptr;
984 ownsDevMode = false;
985}
986
988{
989 if (state == QPrinter::Active) {
990 reinit = true;
991 } else {
992 resetDC();
993 reinit = false;
994 }
995}
996
998{
999 if (!hdc) {
1000 qWarning("ResetDC() called with null hdc.");
1001 return false;
1002 }
1003 const HDC oldHdc = hdc;
1004 const HDC hdc = ResetDC(oldHdc, devMode);
1005 if (!hdc) {
1006 const int lastError = GetLastError();
1007 qErrnoWarning(lastError, "ResetDC() on %p failed (%d)", oldHdc, lastError);
1008 }
1009 return hdc != 0;
1010}
1011
1012static int indexOfId(const QList<QPrint::InputSlot> &inputSlots, QPrint::InputSlotId id)
1013{
1014 for (int i = 0; i < inputSlots.size(); ++i) {
1015 if (inputSlots.at(i).id == id)
1016 return i;
1017 }
1018 return -1;
1019}
1020
1021static int indexOfWindowsId(const QList<QPrint::InputSlot> &inputSlots, int windowsId)
1022{
1023 for (int i = 0; i < inputSlots.size(); ++i) {
1024 if (inputSlots.at(i).windowsId == windowsId)
1025 return i;
1026 }
1027 return -1;
1028}
1029
1030void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
1031{
1032 Q_D(QWin32PrintEngine);
1033 switch (key) {
1034
1035 // The following keys are properties or derived values and so cannot be set
1036 case PPK_PageRect:
1037 break;
1038 case PPK_PaperRect:
1039 break;
1040 case PPK_PaperSources:
1041 break;
1042 case PPK_SupportsMultipleCopies:
1043 break;
1044 case PPK_SupportedResolutions:
1045 break;
1046
1047 // The following keys are settings that are unsupported by the Windows PrintEngine
1048 case PPK_CustomBase:
1049 break;
1050 case PPK_PageOrder:
1051 break;
1052 case PPK_PrinterProgram:
1053 break;
1054 case PPK_SelectionOption:
1055 break;
1056
1057 // The following keys are properties and settings that are supported by the Windows PrintEngine
1058 case PPK_FontEmbedding:
1059 d->embed_fonts = value.toBool();
1060 break;
1061
1062 case PPK_CollateCopies:
1063 {
1064 if (!d->devMode)
1065 break;
1066 d->devMode->dmCollate = value.toBool() ? DMCOLLATE_TRUE : DMCOLLATE_FALSE;
1067 d->devMode->dmFields |= DM_COLLATE;
1068 d->doReinit();
1069 }
1070 break;
1071
1072 case PPK_ColorMode:
1073 {
1074 if (!d->devMode)
1075 break;
1076 d->devMode->dmColor = (value.toInt() == QPrinter::Color) ? DMCOLOR_COLOR : DMCOLOR_MONOCHROME;
1077 d->devMode->dmFields |= DM_COLOR;
1078 d->doReinit();
1079 }
1080 break;
1081
1082 case PPK_Creator:
1083 d->m_creator = value.toString();
1084 break;
1085
1086 case PPK_DocumentName:
1087 if (isActive()) {
1088 qWarning("QWin32PrintEngine: Cannot change document name while printing is active");
1089 return;
1090 }
1091 d->docName = value.toString();
1092 break;
1093
1094 case PPK_Duplex: {
1095 if (!d->devMode)
1096 break;
1097 QPrint::DuplexMode mode = QPrint::DuplexMode(value.toInt());
1098 if (mode == property(PPK_Duplex).toInt() || !d->m_printDevice.supportedDuplexModes().contains(mode))
1099 break;
1100 switch (mode) {
1101 case QPrint::DuplexNone:
1102 d->devMode->dmDuplex = DMDUP_SIMPLEX;
1103 d->devMode->dmFields |= DM_DUPLEX;
1104 break;
1105 case QPrint::DuplexAuto:
1106 d->devMode->dmDuplex = d->m_pageLayout.orientation() == QPageLayout::Landscape ? DMDUP_HORIZONTAL : DMDUP_VERTICAL;
1107 d->devMode->dmFields |= DM_DUPLEX;
1108 break;
1109 case QPrint::DuplexLongSide:
1110 d->devMode->dmDuplex = DMDUP_VERTICAL;
1111 d->devMode->dmFields |= DM_DUPLEX;
1112 break;
1113 case QPrint::DuplexShortSide:
1114 d->devMode->dmDuplex = DMDUP_HORIZONTAL;
1115 d->devMode->dmFields |= DM_DUPLEX;
1116 break;
1117 default:
1118 // Don't change
1119 break;
1120 }
1121 d->doReinit();
1122 break;
1123 }
1124
1125 case PPK_FullPage:
1126 if (value.toBool())
1127 d->m_pageLayout.setMode(QPageLayout::FullPageMode);
1128 else
1129 d->m_pageLayout.setMode(QPageLayout::StandardMode);
1130 d->updateMetrics();
1131#ifdef QT_DEBUG_METRICS
1132 qDebug() << "QWin32PrintEngine::setProperty(PPK_FullPage," << value.toBool() << + ")";
1133 d->debugMetrics();
1134#endif // QT_DEBUG_METRICS
1135 break;
1136
1137 case PPK_CopyCount:
1138 case PPK_NumberOfCopies:
1139 if (!d->devMode)
1140 break;
1141 d->num_copies = value.toInt();
1142 d->devMode->dmCopies = d->num_copies;
1143 d->devMode->dmFields |= DM_COPIES;
1144 d->doReinit();
1145 break;
1146
1147 case PPK_Orientation: {
1148 if (!d->devMode)
1149 break;
1150 QPageLayout::Orientation orientation = QPageLayout::Orientation(value.toInt());
1151 d->devMode->dmOrientation = orientation == QPageLayout::Landscape ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
1152 d->devMode->dmFields |= DM_ORIENTATION;
1153 d->m_pageLayout.setOrientation(orientation);
1154 d->doReinit();
1155 d->updateMetrics();
1156#ifdef QT_DEBUG_METRICS
1157 qDebug() << "QWin32PrintEngine::setProperty(PPK_Orientation," << orientation << ')';
1158 d->debugMetrics();
1159#endif // QT_DEBUG_METRICS
1160 break;
1161 }
1162
1163 case PPK_OutputFileName:
1164 if (isActive()) {
1165 qWarning("QWin32PrintEngine: Cannot change filename while printing");
1166 } else {
1167 d->fileName = value.toString();
1168 d->printToFile = !value.toString().isEmpty();
1169 }
1170 break;
1171
1172 case PPK_PageSize: {
1173 if (!d->devMode)
1174 break;
1175 const QPageSize pageSize = QPageSize(QPageSize::PageSizeId(value.toInt()));
1176 if (pageSize.isValid()) {
1177 d->setPageSize(pageSize);
1178 d->doReinit();
1179#ifdef QT_DEBUG_METRICS
1180 qDebug() << "QWin32PrintEngine::setProperty(PPK_PageSize," << value.toInt() << ')';
1181 d->debugMetrics();
1182#endif // QT_DEBUG_METRICS
1183 }
1184 break;
1185 }
1186
1187 case PPK_PaperName: {
1188 if (!d->devMode)
1189 break;
1190 // Get the named page size from the printer if supported
1191 const QPageSize pageSize = d->m_printDevice.supportedPageSize(value.toString());
1192 if (pageSize.isValid()) {
1193 d->setPageSize(pageSize);
1194 d->doReinit();
1195#ifdef QT_DEBUG_METRICS
1196 qDebug() << "QWin32PrintEngine::setProperty(PPK_PaperName," << value.toString() << ')';
1197 d->debugMetrics();
1198#endif // QT_DEBUG_METRICS
1199 }
1200 break;
1201 }
1202
1203 case PPK_PaperSource: {
1204 if (!d->devMode)
1205 break;
1206 const auto inputSlots = d->m_printDevice.supportedInputSlots();
1207 const int paperSource = value.toInt();
1208 const int index = paperSource >= DMBIN_USER ?
1209 indexOfWindowsId(inputSlots, paperSource) : indexOfId(inputSlots, QPrint::InputSlotId(paperSource));
1210 d->devMode->dmDefaultSource = index >= 0 ? inputSlots.at(index).windowsId : DMBIN_AUTO;
1211 d->doReinit();
1212 break;
1213 }
1214
1215 case PPK_PrinterName: {
1216 QString id = value.toString();
1217 QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get();
1218 if (!ps)
1219 return;
1220
1221 QVariant pageSize = QVariant::fromValue(d->m_pageLayout.pageSize());
1222 const bool isFullPage = (d->m_pageLayout.mode() == QPageLayout::FullPageMode);
1223 QVariant orientation = QVariant::fromValue(d->m_pageLayout.orientation());
1224 QVariant margins = QVariant::fromValue(
1225 std::pair<QMarginsF, QPageLayout::Unit>(d->m_pageLayout.margins(), d->m_pageLayout.units()));
1226 QPrintDevice printDevice = ps->createPrintDevice(id.isEmpty() ? ps->defaultPrintDeviceId() : id);
1227 if (printDevice.isValid()) {
1228 d->m_printDevice = printDevice;
1229 d->initialize();
1230 if (d->m_printDevice.supportedPageSize(pageSize.value<QPageSize>()).isValid())
1231 setProperty(PPK_QPageSize, pageSize);
1232 else
1233 setProperty(PPK_CustomPaperSize, pageSize.value<QPageSize>().size(QPageSize::Point));
1234 setProperty(PPK_FullPage, QVariant(isFullPage));
1235 setProperty(PPK_Orientation, orientation);
1236 setProperty(PPK_QPageMargins, margins);
1237 }
1238 break;
1239 }
1240
1241 case PPK_Resolution: {
1242 d->resolution = value.toInt();
1243 d->stretch_x = d->dpi_x / double(d->resolution);
1244 d->stretch_y = d->dpi_y / double(d->resolution);
1245 d->updateMetrics();
1246#ifdef QT_DEBUG_METRICS
1247 qDebug() << "QWin32PrintEngine::setProperty(PPK_Resolution," << value.toInt() << ')';
1248 d->debugMetrics();
1249#endif // QT_DEBUG_METRICS
1250 break;
1251 }
1252
1253 case PPK_WindowsPageSize: {
1254 if (!d->devMode)
1255 break;
1256 const QPageSize pageSize = QPageSize(QPageSize::id(value.toInt()));
1257 if (pageSize.isValid()) {
1258 d->setPageSize(pageSize);
1259 d->doReinit();
1260#ifdef QT_DEBUG_METRICS
1261 qDebug() << "QWin32PrintEngine::setProperty(PPK_WindowsPageSize," << value.toInt() << ')';
1262 d->debugMetrics();
1263#endif // QT_DEBUG_METRICS
1264 break;
1265 }
1266 break;
1267 }
1268
1269 case PPK_CustomPaperSize: {
1270 if (!d->devMode)
1271 break;
1272 const QPageSize pageSize = QPageSize(value.toSizeF(), QPageSize::Point);
1273 if (pageSize.isValid()) {
1274 d->setPageSize(pageSize);
1275 d->doReinit();
1276#ifdef QT_DEBUG_METRICS
1277 qDebug() << "QWin32PrintEngine::setProperty(PPK_CustomPaperSize," << value.toSizeF() << ')';
1278 d->debugMetrics();
1279#endif // QT_DEBUG_METRICS
1280 }
1281 break;
1282 }
1283
1284 case PPK_PageMargins: {
1285 QList<QVariant> margins(value.toList());
1286 Q_ASSERT(margins.size() == 4);
1287 d->m_pageLayout.setUnits(QPageLayout::Point);
1288 d->m_pageLayout.setMargins(QMarginsF(margins.at(0).toReal(), margins.at(1).toReal(),
1289 margins.at(2).toReal(), margins.at(3).toReal()),
1290 QPageLayout::OutOfBoundsPolicy::Clamp);
1291 d->updateMetrics();
1292#ifdef QT_DEBUG_METRICS
1293 qDebug() << "QWin32PrintEngine::setProperty(PPK_PageMargins," << margins << ')';
1294 d->debugMetrics();
1295#endif // QT_DEBUG_METRICS
1296 break;
1297 }
1298
1299 case PPK_QPageSize: {
1300 if (!d->devMode)
1301 break;
1302 // Get the page size from the printer if supported
1303 const QPageSize pageSize = value.value<QPageSize>();
1304 if (pageSize.isValid()) {
1305 d->setPageSize(pageSize);
1306 d->doReinit();
1307#ifdef QT_DEBUG_METRICS
1308 qDebug() << "QWin32PrintEngine::setProperty(PPK_QPageSize," << pageSize << ')';
1309 d->debugMetrics();
1310#endif // QT_DEBUG_METRICS
1311 }
1312 break;
1313 }
1314
1315 case PPK_QPageMargins: {
1316 auto pair = value.value<std::pair<QMarginsF, QPageLayout::Unit>>();
1317 d->m_pageLayout.setUnits(pair.second);
1318 d->m_pageLayout.setMargins(pair.first, QPageLayout::OutOfBoundsPolicy::Clamp);
1319 d->updateMetrics();
1320#ifdef QT_DEBUG_METRICS
1321 qDebug() << "QWin32PrintEngine::setProperty(PPK_QPageMargins," << pair.first << pair.second << ')';
1322 d->debugMetrics();
1323#endif // QT_DEBUG_METRICS
1324 break;
1325 }
1326
1327 case PPK_QPageLayout: {
1328 QPageLayout pageLayout = value.value<QPageLayout>();
1329 if (pageLayout.isValid() && d->m_printDevice.isValidPageLayout(pageLayout, d->resolution)) {
1330 setProperty(PPK_QPageSize, QVariant::fromValue(pageLayout.pageSize()));
1331 setProperty(PPK_FullPage, pageLayout.mode() == QPageLayout::FullPageMode);
1332 setProperty(PPK_Orientation, QVariant::fromValue(pageLayout.orientation()));
1333 d->m_pageLayout.setUnits(pageLayout.units());
1334 d->m_pageLayout.setMargins(pageLayout.margins(), QPageLayout::OutOfBoundsPolicy::Clamp);
1335 d->updateMetrics();
1336#ifdef QT_DEBUG_METRICS
1337 qDebug() << "QWin32PrintEngine::setProperty(PPK_QPageLayout," << pageLayout << ')';
1338 d->debugMetrics();
1339#endif // QT_DEBUG_METRICS
1340 }
1341 break;
1342 }
1343
1344 // No default so that compiler will complain if new keys added and not handled in this engine
1345 }
1346}
1347
1348QVariant QWin32PrintEngine::property(PrintEnginePropertyKey key) const
1349{
1350 Q_D(const QWin32PrintEngine);
1351 QVariant value;
1352 switch (key) {
1353
1354 // The following keys are settings that are unsupported by the Windows PrintEngine
1355 // Return sensible default values to ensure consistent behavior across platforms
1356 case PPK_PageOrder:
1357 value = QPrinter::FirstPageFirst;
1358 break;
1359 case PPK_PrinterProgram:
1360 value = QString();
1361 break;
1362 case PPK_SelectionOption:
1363 value = QString();
1364 break;
1365
1366 // The following keys are properties and settings that are supported by the Windows PrintEngine
1367 case PPK_FontEmbedding:
1368 value = d->embed_fonts;
1369 break;
1370
1371 case PPK_CollateCopies:
1372 if (!d->devMode)
1373 value = false;
1374 else
1375 value = d->devMode->dmCollate == DMCOLLATE_TRUE;
1376 break;
1377
1378 case PPK_ColorMode:
1379 {
1380 if (!d->devMode) {
1381 value = QPrinter::Color;
1382 } else {
1383 value = (d->devMode->dmColor == DMCOLOR_COLOR) ? QPrinter::Color : QPrinter::GrayScale;
1384 }
1385 }
1386 break;
1387
1388 case PPK_Creator:
1389 value = d->m_creator;
1390 break;
1391
1392 case PPK_DocumentName:
1393 value = d->docName;
1394 break;
1395
1396 case PPK_Duplex: {
1397 if (!d->devMode) {
1398 value = QPrinter::DuplexNone;
1399 } else {
1400 switch (d->devMode->dmDuplex) {
1401 case DMDUP_VERTICAL:
1402 value = QPrinter::DuplexLongSide;
1403 break;
1404 case DMDUP_HORIZONTAL:
1405 value = QPrinter::DuplexShortSide;
1406 break;
1407 case DMDUP_SIMPLEX:
1408 default:
1409 value = QPrinter::DuplexNone;
1410 break;
1411 }
1412 }
1413 break;
1414 }
1415
1416 case PPK_FullPage:
1417 value = d->m_pageLayout.mode() == QPageLayout::FullPageMode;
1418 break;
1419
1420 case PPK_CopyCount:
1421 value = d->num_copies;
1422 break;
1423
1424 case PPK_SupportsMultipleCopies:
1425 value = true;
1426 break;
1427
1428 case PPK_NumberOfCopies:
1429 value = 1;
1430 break;
1431
1432 case PPK_Orientation:
1433 value = d->m_pageLayout.orientation();
1434 break;
1435
1436 case PPK_OutputFileName:
1437 value = d->fileName;
1438 break;
1439
1440 case PPK_PageRect:
1441 // PageRect is returned in device pixels
1442 value = d->m_pageLayout.paintRectPixels(d->resolution);
1443 break;
1444
1445 case PPK_PageSize:
1446 value = d->m_pageLayout.pageSize().id();
1447 break;
1448
1449 case PPK_PaperRect:
1450 // PaperRect is returned in device pixels
1451 value = d->m_pageLayout.fullRectPixels(d->resolution);
1452 break;
1453
1454 case PPK_PaperName:
1455 value = d->m_pageLayout.pageSize().name();
1456 break;
1457
1458 case PPK_PaperSource:
1459 if (!d->devMode) {
1460 value = d->m_printDevice.defaultInputSlot().id;
1461 } else {
1462 if (d->devMode->dmDefaultSource >= DMBIN_USER) {
1463 value = int(d->devMode->dmDefaultSource);
1464 } else {
1465 const auto inputSlots = d->m_printDevice.supportedInputSlots();
1466 const int index = indexOfWindowsId(inputSlots, d->devMode->dmDefaultSource);
1467 value = index >= 0 ? inputSlots.at(index).id : QPrint::Auto;
1468 }
1469 }
1470 break;
1471
1472 case PPK_PrinterName:
1473 value = d->m_printDevice.id();
1474 break;
1475
1476 case PPK_Resolution:
1477 if (d->resolution || d->m_printDevice.isValid())
1478 value = d->resolution;
1479 break;
1480
1481 case PPK_SupportedResolutions: {
1482 QList<QVariant> list;
1483 const auto resolutions = d->m_printDevice.supportedResolutions();
1484 list.reserve(resolutions.size());
1485 for (int resolution : resolutions)
1486 list << resolution;
1487 value = list;
1488 break;
1489 }
1490
1491 case PPK_WindowsPageSize:
1492 value = d->m_pageLayout.pageSize().windowsId();
1493 break;
1494
1495 case PPK_PaperSources: {
1496 QList<QVariant> out;
1497 const auto inputSlots = d->m_printDevice.supportedInputSlots();
1498 out.reserve(inputSlots.size());
1499 for (const QPrint::InputSlot &inputSlot : inputSlots)
1500 out << QVariant(inputSlot.id == QPrint::CustomInputSlot ? inputSlot.windowsId : int(inputSlot.id));
1501 value = out;
1502 break;
1503 }
1504
1505 case PPK_CustomPaperSize:
1506 value = d->m_pageLayout.fullRectPoints().size();
1507 break;
1508
1509 case PPK_PageMargins: {
1510 QList<QVariant> list;
1511 QMarginsF margins = d->m_pageLayout.margins(QPageLayout::Point);
1512 list << margins.left() << margins.top() << margins.right() << margins.bottom();
1513 value = list;
1514 break;
1515 }
1516
1517 case PPK_QPageSize:
1518 value.setValue(d->m_pageLayout.pageSize());
1519 break;
1520
1521 case PPK_QPageMargins: {
1522 std::pair<QMarginsF, QPageLayout::Unit> pair(d->m_pageLayout.margins(), d->m_pageLayout.units());
1523 value.setValue(pair);
1524 break;
1525 }
1526
1527 case PPK_QPageLayout:
1528 value.setValue(d->m_pageLayout);
1529 break;
1530
1531 case PPK_CustomBase:
1532 break;
1533
1534 // No default so that compiler will complain if new keys added and not handled in this engine
1535 }
1536 return value;
1537}
1538
1539QPrinter::PrinterState QWin32PrintEngine::printerState() const
1540{
1541 return d_func()->state;
1542}
1543
1544HDC QWin32PrintEngine::getDC() const
1545{
1546 return d_func()->hdc;
1547}
1548
1549void QWin32PrintEngine::releaseDC(HDC) const
1550{
1551
1552}
1553
1554HGLOBAL *QWin32PrintEngine::createGlobalDevNames()
1555{
1556 Q_D(QWin32PrintEngine);
1557
1558 const size_t size = sizeof(DEVNAMES) + d->m_printDevice.id().length() * 2 + 2;
1559 auto hGlobal = reinterpret_cast<HGLOBAL *>(GlobalAlloc(GMEM_MOVEABLE, size));
1560 auto dn = reinterpret_cast<DEVNAMES*>(GlobalLock(hGlobal));
1561
1562 dn->wDriverOffset = 0;
1563 dn->wDeviceOffset = sizeof(DEVNAMES) / sizeof(wchar_t);
1564 dn->wOutputOffset = 0;
1565
1566 memcpy(reinterpret_cast<ushort*>(dn) + dn->wDeviceOffset,
1567 d->m_printDevice.id().utf16(), d->m_printDevice.id().length() * 2 + 2);
1568 dn->wDefault = 0;
1569
1570 GlobalUnlock(hGlobal);
1571 return hGlobal;
1572}
1573
1574void QWin32PrintEngine::setGlobalDevMode(HGLOBAL globalDevNames, HGLOBAL globalDevMode)
1575{
1576 Q_D(QWin32PrintEngine);
1577 if (globalDevNames) {
1578 auto dn = reinterpret_cast<DEVNAMES*>(GlobalLock(globalDevNames));
1579 const QString id =
1580 QString::fromWCharArray(reinterpret_cast<const wchar_t*>(dn) + dn->wDeviceOffset);
1581 QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get();
1582 if (ps)
1583 d->m_printDevice = ps->createPrintDevice(id.isEmpty() ? ps->defaultPrintDeviceId() : id);
1584 GlobalUnlock(globalDevNames);
1585 }
1586
1587 if (globalDevMode) {
1588 auto dm = reinterpret_cast<DEVMODE*>(GlobalLock(globalDevMode));
1589 d->release();
1590 d->globalDevMode = globalDevMode;
1591 if (d->ownsDevMode) {
1592 free(d->devMode);
1593 d->ownsDevMode = false;
1594 }
1595 d->devMode = dm;
1596 d->hdc = CreateDC(nullptr, reinterpret_cast<LPCWSTR>(d->m_printDevice.id().utf16()), nullptr, dm);
1597
1598 d->num_copies = d->devMode->dmCopies;
1599 d->updatePageLayout();
1600
1601 if (!OpenPrinter((wchar_t*)d->m_printDevice.id().utf16(), &d->hPrinter, 0))
1602 qWarning("QPrinter: OpenPrinter() failed after reading DEVMODE.");
1603 }
1604
1605 if (d->hdc)
1606 d->initHDC();
1607
1608#if defined QT_DEBUG_DRAW || defined QT_DEBUG_METRICS
1609 qDebug("QWin32PrintEngine::setGlobalDevMode()");
1610 d->debugMetrics();
1611#endif // QT_DEBUG_DRAW || QT_DEBUG_METRICS
1612}
1613
1614HGLOBAL QWin32PrintEngine::globalDevMode()
1615{
1616 Q_D(QWin32PrintEngine);
1617 return d->globalDevMode;
1618}
1619
1620void QWin32PrintEnginePrivate::setPageSize(const QPageSize &pageSize)
1621{
1622 if (!pageSize.isValid())
1623 return;
1624
1625 Q_ASSERT(devMode);
1626
1627 // Use the printer page size if supported
1628 const QPageSize printerPageSize = m_printDevice.supportedPageSize(pageSize);
1629 const QPageSize usePageSize = printerPageSize.isValid() ? printerPageSize : pageSize;
1630
1631 const QMarginsF printable = m_printDevice.printableMargins(usePageSize, m_pageLayout.orientation(), resolution);
1632 m_pageLayout.setPageSize(usePageSize, qt_convertMargins(printable, QPageLayout::Point, m_pageLayout.units()));
1633
1634 // Setup if Windows custom size, i.e. not a known Windows ID
1635 if (printerPageSize.isValid()) {
1636 has_custom_paper_size = false;
1637 devMode->dmPaperSize = m_pageLayout.pageSize().windowsId();
1638 devMode->dmFields &= ~(DM_PAPERLENGTH | DM_PAPERWIDTH);
1639 devMode->dmPaperWidth = 0;
1640 devMode->dmPaperLength = 0;
1641 } else {
1642 devMode->dmPaperSize = DMPAPER_USER;
1643 devMode->dmFields |= DM_PAPERLENGTH | DM_PAPERWIDTH;
1644 // Size in tenths of a millimeter
1645 const QSizeF sizeMM = m_pageLayout.pageSize().size(QPageSize::Millimeter);
1646 devMode->dmPaperWidth = qRound(sizeMM.width() * 10.0);
1647 devMode->dmPaperLength = qRound(sizeMM.height() * 10.0);
1648 }
1650}
1651
1652// Update the page layout after any changes made to devMode
1654{
1655 Q_ASSERT(devMode);
1656
1657 // Update orientation first as is needed to obtain printable margins when changing page size
1658 m_pageLayout.setOrientation(devMode->dmOrientation == DMORIENT_LANDSCAPE ? QPageLayout::Landscape : QPageLayout::Portrait);
1659 if (devMode->dmPaperSize >= DMPAPER_LAST) {
1660 // Is a custom size
1661 // Check if it is using the Postscript Custom Size first
1662 bool hasCustom = false;
1663 int feature = PSIDENT_GDICENTRIC;
1664 if (ExtEscape(hdc, POSTSCRIPT_IDENTIFY,
1665 sizeof(DWORD), reinterpret_cast<LPCSTR>(&feature), 0, 0) >= 0) {
1666 PSFEATURE_CUSTPAPER custPaper;
1667 feature = FEATURESETTING_CUSTPAPER;
1668 if (ExtEscape(hdc, GET_PS_FEATURESETTING, sizeof(INT), reinterpret_cast<LPCSTR>(&feature),
1669 sizeof(custPaper), reinterpret_cast<LPSTR>(&custPaper)) > 0) {
1670 // If orientation is 1 and width/height is 0 then it's not really custom
1671 if (!(custPaper.lOrientation == 1 && custPaper.lWidth == 0 && custPaper.lHeight == 0)) {
1672 if (custPaper.lOrientation == 0 || custPaper.lOrientation == 2)
1673 m_pageLayout.setOrientation(QPageLayout::Portrait);
1674 else
1675 m_pageLayout.setOrientation(QPageLayout::Landscape);
1676 QPageSize pageSize = QPageSize(QSizeF(custPaper.lWidth, custPaper.lHeight),
1677 QPageSize::Point);
1678 setPageSize(pageSize);
1679 hasCustom = true;
1680 }
1681 }
1682 }
1683 if (!hasCustom) {
1684 QPageSize pageSize = QPageSize(QSizeF(devMode->dmPaperWidth / 10.0f, devMode->dmPaperLength / 10.0f),
1685 QPageSize::Millimeter);
1686 setPageSize(pageSize);
1687 }
1688 } else {
1689 // Is a supported size
1690 setPageSize(QPageSize(QPageSize::id(devMode->dmPaperSize)));
1691 }
1693}
1694
1695// Update the cached page paint metrics whenever page layout is changed
1697{
1698 m_paintRectPixels = m_pageLayout.paintRectPixels(resolution);
1699 // Some print devices allow scaling, so that "virtual" page size != current paper size
1700 const int devWidth = GetDeviceCaps(hdc, PHYSICALWIDTH);
1701 const int devHeight = GetDeviceCaps(hdc, PHYSICALHEIGHT);
1702 const int pageWidth = m_pageLayout.fullRectPixels(dpi_x).width();
1703 const int pageHeight = m_pageLayout.fullRectPixels(dpi_y).height();
1704 const qreal pageScaleX = (devWidth && pageWidth) ? qreal(devWidth) / pageWidth : 1;
1705 const qreal pageScaleY = (devHeight && pageHeight) ? qreal(devHeight) / pageHeight : 1;
1706 m_paintRectPixels = QTransform::fromScale(pageScaleX, pageScaleY).mapRect(m_paintRectPixels);
1707
1708 QSizeF sizeMM = m_pageLayout.paintRect(QPageLayout::Millimeter).size();
1709 m_paintSizeMM = QSize(qRound(sizeMM.width()), qRound(sizeMM.height()));
1710 // Calculate the origin using the physical device pixels, not our paint pixels
1711 // Origin is defined as User Margins - Device Margins
1712 const bool isFullPage = (m_pageLayout.mode() == QPageLayout::FullPageMode);
1713 const QMarginsF margins = isFullPage ? QMarginsF() : (m_pageLayout.margins(QPageLayout::Millimeter) / 25.4);
1714 origin_x = qRound(pageScaleX * margins.left() * dpi_x) - GetDeviceCaps(hdc, PHYSICALOFFSETX);
1715 origin_y = qRound(pageScaleY * margins.top() * dpi_y) - GetDeviceCaps(hdc, PHYSICALOFFSETY);
1716}
1717
1719{
1720 qDebug() << " " << "m_pageLayout = " << m_pageLayout;
1721 qDebug() << " " << "m_paintRectPixels = " << m_paintRectPixels;
1722 qDebug() << " " << "m_paintSizeMM = " << m_paintSizeMM;
1723 qDebug() << " " << "resolution = " << resolution;
1724 qDebug() << " " << "stretch = " << stretch_x << stretch_y;
1725 qDebug() << " " << "origin = " << origin_x << origin_y;
1726 qDebug() << " " << "dpi = " << dpi_x << dpi_y;
1727 qDebug() << "";
1728}
1729
1730static void draw_text_item_win(const QPointF &pos, const QTextItemInt &ti, HDC hdc,
1731 const QTransform &xform, const QPointF &topLeft)
1732{
1733 QPointF baseline_pos = xform.inverted().map(xform.map(pos) - topLeft);
1734
1735 SetTextAlign(hdc, TA_BASELINE);
1736 SetBkMode(hdc, TRANSPARENT);
1737
1738 const bool has_kerning = ti.f && ti.f->kerning();
1739
1740 HFONT hfont = nullptr;
1741 bool deleteFont = false;
1742
1743 if (ti.fontEngine->type() == QFontEngine::Win) {
1744 if (ti.fontEngine->supportsTransformation(QTransform::fromScale(0.5, 0.5))) // is TrueType font?
1745 hfont = static_cast<HFONT>(ti.fontEngine->handle());
1746 }
1747#if QT_CONFIG(directwrite)
1748 else if (ti.fontEngine->type() == QFontEngine::DirectWrite) {
1749 QWindowsFontEngineDirectWrite *fedw = static_cast<QWindowsFontEngineDirectWrite *>(ti.fontEngine);
1750 hfont = fedw->createHFONT();
1751 if (hfont)
1752 deleteFont = true;
1753 }
1754#endif
1755
1756 if (!hfont)
1757 hfont = (HFONT)GetStockObject(ANSI_VAR_FONT);
1758
1759 HGDIOBJ old_font = SelectObject(hdc, hfont);
1760 unsigned int options = ETO_GLYPH_INDEX;
1761 QGlyphLayout glyphs = ti.glyphs;
1762
1763 bool fast = !has_kerning && !(ti.flags & QTextItem::RightToLeft);
1764 for (int i = 0; fast && i < glyphs.numGlyphs; i++) {
1765 if (glyphs.offsets[i].x != 0 || glyphs.offsets[i].y != 0 || glyphs.justifications[i].space_18d6 != 0
1766 || glyphs.attributes[i].dontPrint) {
1767 fast = false;
1768 break;
1769 }
1770 }
1771
1772 // Scale, rotate and translate here.
1773 XFORM win_xform;
1774 win_xform.eM11 = xform.m11();
1775 win_xform.eM12 = xform.m12();
1776 win_xform.eM21 = xform.m21();
1777 win_xform.eM22 = xform.m22();
1778 win_xform.eDx = xform.dx();
1779 win_xform.eDy = xform.dy();
1780
1781 SetGraphicsMode(hdc, GM_ADVANCED);
1782 SetWorldTransform(hdc, &win_xform);
1783
1784 if (fast) {
1785 // fast path
1786 QVarLengthArray<wchar_t> g(glyphs.numGlyphs);
1787 for (int i = 0; i < glyphs.numGlyphs; ++i)
1788 g[i] = glyphs.glyphs[i];
1789 ExtTextOut(hdc,
1790 qRound(baseline_pos.x() + glyphs.offsets[0].x.toReal()),
1791 qRound(baseline_pos.y() + glyphs.offsets[0].y.toReal()),
1792 options, 0, g.constData(), glyphs.numGlyphs, 0);
1793 } else {
1794 QVarLengthArray<QFixedPoint> positions;
1795 QVarLengthArray<glyph_t> _glyphs;
1796
1797 QTransform matrix = QTransform::fromTranslate(baseline_pos.x(), baseline_pos.y());
1798 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags,
1799 _glyphs, positions);
1800 if (_glyphs.isEmpty()) {
1801 SelectObject(hdc, old_font);
1802 return;
1803 }
1804
1805 options |= ETO_PDY;
1806 QVarLengthArray<INT> glyphDistances(_glyphs.size() * 2);
1807 QVarLengthArray<wchar_t> g(_glyphs.size());
1808 const int lastGlyph = _glyphs.size() - 1;
1809 for (int i = 0; i < lastGlyph; ++i) {
1810 glyphDistances[i * 2] = qRound(positions[i + 1].x) - qRound(positions[i].x);
1811 glyphDistances[i * 2 + 1] = qRound(positions[i + 1].y) - qRound(positions[i].y);
1812 g[i] = _glyphs[i];
1813 }
1814 glyphDistances[lastGlyph * 2] = 0;
1815 glyphDistances[lastGlyph * 2 + 1] = 0;
1816 g[lastGlyph] = _glyphs[lastGlyph];
1817 ExtTextOut(hdc, qRound(positions[0].x), qRound(positions[0].y), options, nullptr,
1818 g.constData(), _glyphs.size(),
1819 glyphDistances.data());
1820 }
1821
1822 win_xform.eM11 = win_xform.eM22 = 1.0;
1823 win_xform.eM12 = win_xform.eM21 = win_xform.eDx = win_xform.eDy = 0.0;
1824 SetWorldTransform(hdc, &win_xform);
1825
1826 SelectObject(hdc, old_font);
1827
1828 if (deleteFont)
1829 DeleteObject(hfont);
1830}
1831
1832QT_END_NAMESPACE
1833
1834#endif // QT_NO_PRINTER
void fillPath_dev(const QPainterPath &path, const QColor &color)
void strokePath_dev(const QPainterPath &path, const QColor &color, qreal width)
void composeGdiPath(const QPainterPath &path)
void strokePath(const QPainterPath &path, const QColor &color)
void setPageSize(const QPageSize &pageSize)
void fillPath(const QPainterPath &path, const QColor &color)
QMarginsF qt_convertMargins(const QMarginsF &margins, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits)
HBitmapFormat
@ HBitmapPremultipliedAlpha
@ HBitmapNoAlpha
@ HBitmapAlpha
static void draw_text_item_win(const QPointF &_pos, const QTextItemInt &ti, HDC hdc, const QTransform &xform, const QPointF &topLeft)
static int indexOfWindowsId(const QList< QPrint::InputSlot > &inputSlots, int windowsId)
QT_BEGIN_NAMESPACE QPainterPath qt_regionToPath(const QRegion &region)
Definition qregion.cpp:1010
static QByteArray msgBeginFailed(const char *function, const DOCINFO &d)
static int indexOfId(const QList< QPrint::InputSlot > &inputSlots, QPrint::InputSlotId id)