1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #ifndef QT_NO_PRINTER
43 
44 #include "qprinter_p.h"
45 #include "qprintengine_win_p.h"
46 
47 #include <limits.h>
48 
49 #include <private/qfont_p.h>
50 #include <private/qfontengine_p.h>
51 #include <private/qpainter_p.h>
52 
53 #include <qbitmap.h>
54 #include <qdebug.h>
55 #include <qvector.h>
56 #include <qpicture.h>
57 #include <private/qpicture_p.h>
58 
59 QT_BEGIN_NAMESPACE
60 
61 extern QPainterPath qt_regionToPath(const QRegion &region);
62 
63 // #define QT_DEBUG_DRAW
64 
65 static void draw_text_item_win(const QPointF &_pos, const QTextItemInt &ti, HDC hdc,
66                                bool convertToText, const QTransform &xform, const QPointF &topLeft);
67 
68 static const struct {
69     int winSizeName;
70     QPrinter::PaperSize qtSizeName;
71 } dmMapping[] = {
72     { DMPAPER_LETTER,             QPrinter::Letter },
73     { DMPAPER_LETTERSMALL,        QPrinter::Letter },
74     { DMPAPER_TABLOID,            QPrinter::Tabloid },
75     { DMPAPER_LEDGER,             QPrinter::Ledger },
76     { DMPAPER_LEGAL,              QPrinter::Legal },
77     { DMPAPER_EXECUTIVE,          QPrinter::Executive },
78     { DMPAPER_A3,                 QPrinter::A3 },
79     { DMPAPER_A4,                 QPrinter::A4 },
80     { DMPAPER_A4SMALL,            QPrinter::A4 },
81     { DMPAPER_A5,                 QPrinter::A5 },
82     { DMPAPER_B4,                 QPrinter::B4 },
83     { DMPAPER_B5,                 QPrinter::B5 },
84     { DMPAPER_FOLIO,              QPrinter::Folio },
85     { DMPAPER_ENV_10,             QPrinter::Comm10E },
86     { DMPAPER_ENV_DL,             QPrinter::DLE },
87     { DMPAPER_ENV_C3,             QPrinter::C5E },
88     { DMPAPER_LETTER_EXTRA,       QPrinter::Letter },
89     { DMPAPER_LEGAL_EXTRA,        QPrinter::Legal },
90     { DMPAPER_TABLOID_EXTRA,      QPrinter::Tabloid },
91     { DMPAPER_A4_EXTRA,           QPrinter::A4},
92     { DMPAPER_LETTER_TRANSVERSE,  QPrinter::Letter},
93     { DMPAPER_A4_TRANSVERSE,      QPrinter::A4},
94     { DMPAPER_LETTER_EXTRA_TRANSVERSE, QPrinter::Letter },
95     { DMPAPER_A_PLUS,             QPrinter::A4 },
96     { DMPAPER_B_PLUS,             QPrinter::A3 },
97     { DMPAPER_LETTER_PLUS,        QPrinter::Letter },
98     { DMPAPER_A4_PLUS,            QPrinter::A4 },
99     { DMPAPER_A5_TRANSVERSE,      QPrinter::A5 },
100     { DMPAPER_B5_TRANSVERSE,      QPrinter::B5 },
101     { DMPAPER_A3_EXTRA,           QPrinter::A3 },
102     { DMPAPER_A5_EXTRA,           QPrinter::A5 },
103     { DMPAPER_B5_EXTRA,           QPrinter::B5 },
104     { DMPAPER_A2,                 QPrinter::A2 },
105     { DMPAPER_A3_TRANSVERSE,      QPrinter::A3 },
106     { DMPAPER_A3_EXTRA_TRANSVERSE,QPrinter::A3 },
107     { 0, QPrinter::Custom }
108 };
109 
mapDevmodePaperSize(int s)110 QPrinter::PaperSize mapDevmodePaperSize(int s)
111 {
112     int i = 0;
113     while ((dmMapping[i].winSizeName > 0) && (dmMapping[i].winSizeName != s))
114         i++;
115     return dmMapping[i].qtSizeName;
116 }
117 
mapPaperSizeDevmode(QPrinter::PaperSize s)118 static int mapPaperSizeDevmode(QPrinter::PaperSize s)
119 {
120     int i = 0;
121  while ((dmMapping[i].winSizeName > 0) && (dmMapping[i].qtSizeName != s))
122         i++;
123     return dmMapping[i].winSizeName;
124 }
125 
126 static const struct {
127     int winSourceName;
128     QPrinter::PaperSource qtSourceName;
129 }  sources[] = {
130     { DMBIN_ONLYONE,        QPrinter::OnlyOne },
131     { DMBIN_LOWER,          QPrinter::Lower },
132     { DMBIN_MIDDLE,         QPrinter::Middle },
133     { DMBIN_MANUAL,         QPrinter::Manual },
134     { DMBIN_ENVELOPE,       QPrinter::Envelope },
135     { DMBIN_ENVMANUAL,      QPrinter::EnvelopeManual },
136     { DMBIN_AUTO,           QPrinter::Auto },
137     { DMBIN_TRACTOR,        QPrinter::Tractor },
138     { DMBIN_SMALLFMT,       QPrinter::SmallFormat },
139     { DMBIN_LARGEFMT,       QPrinter::LargeFormat },
140     { DMBIN_LARGECAPACITY,  QPrinter::LargeCapacity },
141     { DMBIN_CASSETTE,       QPrinter::Cassette },
142     { DMBIN_FORMSOURCE,     QPrinter::FormSource },
143     { 0, (QPrinter::PaperSource) -1 }
144 };
145 
mapDevmodePaperSource(int s)146 static QPrinter::PaperSource mapDevmodePaperSource(int s)
147 {
148     int i = 0;
149     while ((sources[i].winSourceName > 0) && (sources[i].winSourceName != s))
150         i++;
151     return sources[i].winSourceName ? sources[i].qtSourceName : (QPrinter::PaperSource) s;
152 }
153 
mapPaperSourceDevmode(QPrinter::PaperSource s)154 static int mapPaperSourceDevmode(QPrinter::PaperSource s)
155 {
156     int i = 0;
157     while ((sources[i].qtSourceName >= 0) && (sources[i].qtSourceName != s))
158         i++;
159     return sources[i].winSourceName ? sources[i].winSourceName : s;
160 }
161 
QWin32PrintEngine(QPrinter::PrinterMode mode)162 QWin32PrintEngine::QWin32PrintEngine(QPrinter::PrinterMode mode)
163     : QAlphaPaintEngine(*(new QWin32PrintEnginePrivate),
164                    PaintEngineFeatures(PrimitiveTransform
165                                        | PixmapTransform
166                                        | PerspectiveTransform
167                                        | PainterPaths
168                                        | Antialiasing
169                                        | PaintOutsidePaintEvent))
170 {
171     Q_D(QWin32PrintEngine);
172     d->docName = QLatin1String("document1");
173     d->mode = mode;
174     d->queryDefault();
175     d->initialize();
176 }
177 
begin(QPaintDevice * pdev)178 bool QWin32PrintEngine::begin(QPaintDevice *pdev)
179 {
180     Q_D(QWin32PrintEngine);
181 
182     QAlphaPaintEngine::begin(pdev);
183     if (!continueCall())
184         return true;
185 
186     if (d->reinit) {
187        d->resetDC();
188        d->reinit = false;
189     }
190 
191     // ### set default colors and stuff...
192 
193     bool ok = d->state == QPrinter::Idle;
194 
195     if (!d->hdc)
196         return false;
197 
198     // Assign the FILE: to get the query...
199     if (d->printToFile && d->fileName.isEmpty())
200         d->fileName = d->port;
201 
202     d->devMode->dmCopies = d->num_copies;
203 
204     DOCINFO di;
205     memset(&di, 0, sizeof(DOCINFO));
206     di.cbSize = sizeof(DOCINFO);
207     di.lpszDocName = reinterpret_cast<const wchar_t *>(d->docName.utf16());
208     if (d->printToFile && !d->fileName.isEmpty())
209         di.lpszOutput = reinterpret_cast<const wchar_t *>(d->fileName.utf16());
210     if (ok && StartDoc(d->hdc, &di) == SP_ERROR) {
211         qErrnoWarning("QWin32PrintEngine::begin: StartDoc failed");
212         ok = false;
213     }
214 
215     if (StartPage(d->hdc) <= 0) {
216         qErrnoWarning("QWin32PrintEngine::begin: StartPage failed");
217         ok = false;
218     }
219 
220     if (!ok) {
221         d->state = QPrinter::Idle;
222     } else {
223         d->state = QPrinter::Active;
224     }
225 
226     d->matrix = QTransform();
227     d->has_pen = true;
228     d->pen = QColor(Qt::black);
229     d->has_brush = false;
230 
231     d->complex_xform = false;
232 
233     updateMatrix(d->matrix);
234 
235     if (!ok)
236         cleanUp();
237 
238     return ok;
239 }
240 
end()241 bool QWin32PrintEngine::end()
242 {
243     Q_D(QWin32PrintEngine);
244 
245     if (d->hdc) {
246         if (d->state == QPrinter::Aborted) {
247             cleanUp();
248             AbortDoc(d->hdc);
249             return true;
250         }
251     }
252 
253     QAlphaPaintEngine::end();
254     if (!continueCall())
255         return true;
256 
257     if (d->hdc) {
258         EndPage(d->hdc);                 // end; printing done
259         EndDoc(d->hdc);
260     }
261 
262     d->state = QPrinter::Idle;
263     d->reinit = true;
264     return true;
265 }
266 
newPage()267 bool QWin32PrintEngine::newPage()
268 {
269     Q_D(QWin32PrintEngine);
270     Q_ASSERT(isActive());
271 
272     Q_ASSERT(d->hdc);
273 
274     flushAndInit();
275 
276     bool transparent = GetBkMode(d->hdc) == TRANSPARENT;
277 
278     if (!EndPage(d->hdc)) {
279         qErrnoWarning("QWin32PrintEngine::newPage: EndPage failed");
280         return false;
281     }
282 
283     if (d->reinit) {
284         if (!d->resetDC()) {
285             qErrnoWarning("QWin32PrintEngine::newPage: ResetDC failed");
286             return false;
287         }
288         d->reinit = false;
289     }
290 
291     if (!StartPage(d->hdc)) {
292         qErrnoWarning("Win32PrintEngine::newPage: StartPage failed");
293         return false;
294     }
295 
296     SetTextAlign(d->hdc, TA_BASELINE);
297     if (transparent)
298         SetBkMode(d->hdc, TRANSPARENT);
299 
300     // ###
301     return true;
302 
303     bool success = false;
304     if (d->hdc && d->state == QPrinter::Active) {
305         if (EndPage(d->hdc) != SP_ERROR) {
306             // reinitialize the DC before StartPage if needed,
307             // because resetdc is disabled between calls to the StartPage and EndPage functions
308             // (see StartPage documentation in the Platform SDK:Windows GDI)
309 //          state = PST_ACTIVEDOC;
310 //          reinit();
311 //          state = PST_ACTIVE;
312             // start the new page now
313             if (d->reinit) {
314                 if (!d->resetDC())
315                     qErrnoWarning("QWin32PrintEngine::newPage(), ResetDC failed (2)");
316                 d->reinit = false;
317             }
318             success = (StartPage(d->hdc) != SP_ERROR);
319         }
320         if (!success) {
321             d->state = QPrinter::Aborted;
322             return false;
323         }
324     }
325     return true;
326 }
327 
abort()328 bool QWin32PrintEngine::abort()
329 {
330     // do nothing loop.
331     return false;
332 }
333 
drawTextItem(const QPointF & p,const QTextItem & textItem)334 void QWin32PrintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
335 {
336     Q_D(const QWin32PrintEngine);
337 
338     QAlphaPaintEngine::drawTextItem(p, textItem);
339     if (!continueCall())
340         return;
341 
342     const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
343     QRgb brushColor = state->pen().brush().color().rgb();
344     bool fallBack = state->pen().brush().style() != Qt::SolidPattern
345                     || qAlpha(brushColor) != 0xff
346                     || d->txop >= QTransform::TxProject
347                     || ti.fontEngine->type() != QFontEngine::Win;
348 
349 
350     if (!fallBack) {
351         QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
352 
353         // Try selecting the font to see if we get a substitution font
354         SelectObject(d->hdc, fe->hfont);
355 
356         if (GetDeviceCaps(d->hdc, TECHNOLOGY) != DT_CHARSTREAM) {
357             wchar_t n[64];
358             GetTextFace(d->hdc, 64, n);
359             fallBack = QString::fromWCharArray(n)
360                     != QString::fromWCharArray(fe->logfont.lfFaceName);
361         }
362     }
363 
364 
365     if (fallBack) {
366         QPaintEngine::drawTextItem(p, textItem);
367         return ;
368     }
369 
370     // We only want to convert the glyphs to text if the entire string is compatible with ASCII
371     // and if we actually have access to the chars.
372     bool convertToText = ti.chars != 0;
373     for (int i=0;  i < ti.num_chars; ++i) {
374         if (ti.chars[i].unicode() >= 0x80) {
375             convertToText = false;
376             break;
377         }
378 
379         if (ti.logClusters[i] != i) {
380             convertToText = false;
381             break;
382         }
383     }
384 
385     COLORREF cf = RGB(qRed(brushColor), qGreen(brushColor), qBlue(brushColor));
386     SelectObject(d->hdc, CreateSolidBrush(cf));
387     SelectObject(d->hdc, CreatePen(PS_SOLID, 1, cf));
388     SetTextColor(d->hdc, cf);
389 
390     draw_text_item_win(p, ti, d->hdc, convertToText, d->matrix, d->devPaperRect.topLeft());
391     DeleteObject(SelectObject(d->hdc,GetStockObject(HOLLOW_BRUSH)));
392     DeleteObject(SelectObject(d->hdc,GetStockObject(BLACK_PEN)));
393 }
394 
mmToInches(double mm)395 static inline qreal mmToInches(double mm)
396 {
397     return mm*0.039370147;
398 }
399 
inchesToMM(double in)400 static inline qreal inchesToMM(double in)
401 {
402     return in/0.039370147;
403 }
404 
metric(QPaintDevice::PaintDeviceMetric m) const405 int QWin32PrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const
406 {
407     Q_D(const QWin32PrintEngine);
408 
409     if (!d->hdc)
410         return 0;
411 
412     int val;
413     int res = d->resolution;
414 
415     switch (m) {
416     case QPaintDevice::PdmWidth:
417         if (d->has_custom_paper_size) {
418             val =  qRound(d->paper_size.width() * res / 72.0);
419         } else {
420             int logPixelsX = GetDeviceCaps(d->hdc, LOGPIXELSX);
421             if (logPixelsX == 0) {
422                 qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
423                         "might be a driver problem");
424                 logPixelsX = 600; // Reasonable default
425             }
426             val = res
427                   * GetDeviceCaps(d->hdc, d->fullPage ? PHYSICALWIDTH : HORZRES)
428                   / logPixelsX;
429         }
430         if (d->pageMarginsSet)
431             val -= int(mmToInches((d->previousDialogMargins.left() +
432                                    d->previousDialogMargins.width()) / 100.0) * res);
433         break;
434     case QPaintDevice::PdmHeight:
435         if (d->has_custom_paper_size) {
436             val = qRound(d->paper_size.height() * res / 72.0);
437         } else {
438             int logPixelsY = GetDeviceCaps(d->hdc, LOGPIXELSY);
439             if (logPixelsY == 0) {
440                 qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
441                         "might be a driver problem");
442                 logPixelsY = 600; // Reasonable default
443             }
444             val = res
445                   * GetDeviceCaps(d->hdc, d->fullPage ? PHYSICALHEIGHT : VERTRES)
446                   / logPixelsY;
447         }
448         if (d->pageMarginsSet)
449             val -= int(mmToInches((d->previousDialogMargins.top() +
450                                    d->previousDialogMargins.height()) / 100.0) * res);
451         break;
452     case QPaintDevice::PdmDpiX:
453         val = res;
454         break;
455     case QPaintDevice::PdmDpiY:
456         val = res;
457         break;
458     case QPaintDevice::PdmPhysicalDpiX:
459         val = GetDeviceCaps(d->hdc, LOGPIXELSX);
460         break;
461     case QPaintDevice::PdmPhysicalDpiY:
462         val = GetDeviceCaps(d->hdc, LOGPIXELSY);
463         break;
464     case QPaintDevice::PdmWidthMM:
465         if (d->has_custom_paper_size) {
466             val = qRound(d->paper_size.width()*25.4/72);
467         } else {
468             if (!d->fullPage) {
469                 val = GetDeviceCaps(d->hdc, HORZSIZE);
470             } else {
471                 float wi = 25.4 * GetDeviceCaps(d->hdc, PHYSICALWIDTH);
472                 int logPixelsX = GetDeviceCaps(d->hdc,  LOGPIXELSX);
473                 if (logPixelsX == 0) {
474                     qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
475                             "might be a driver problem");
476                     logPixelsX = 600; // Reasonable default
477                 }
478                 val = qRound(wi / logPixelsX);
479             }
480         }
481         if (d->pageMarginsSet)
482             val -= (d->previousDialogMargins.left() +
483                     d->previousDialogMargins.width()) / 100.0;
484         break;
485     case QPaintDevice::PdmHeightMM:
486         if (d->has_custom_paper_size) {
487             val = qRound(d->paper_size.height()*25.4/72);
488         } else {
489             if (!d->fullPage) {
490                 val = GetDeviceCaps(d->hdc, VERTSIZE);
491             } else {
492                 float hi = 25.4 * GetDeviceCaps(d->hdc, PHYSICALHEIGHT);
493                 int logPixelsY = GetDeviceCaps(d->hdc,  LOGPIXELSY);
494                 if (logPixelsY == 0) {
495                     qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
496                             "might be a driver problem");
497                     logPixelsY = 600; // Reasonable default
498                 }
499                 val = qRound(hi / logPixelsY);
500             }
501         }
502         if (d->pageMarginsSet)
503             val -= (d->previousDialogMargins.top() +
504                     d->previousDialogMargins.height()) / 100.0;
505         break;
506     case QPaintDevice::PdmNumColors:
507         {
508             int bpp = GetDeviceCaps(d->hdc, BITSPIXEL);
509             if(bpp==32)
510                 val = INT_MAX;
511             else if(bpp<=8)
512                 val = GetDeviceCaps(d->hdc, NUMCOLORS);
513             else
514                 val = 1 << (bpp * GetDeviceCaps(d->hdc, PLANES));
515         }
516         break;
517     case QPaintDevice::PdmDepth:
518         val = GetDeviceCaps(d->hdc, PLANES);
519         break;
520     default:
521         qWarning("QPrinter::metric: Invalid metric command");
522         return 0;
523     }
524     return val;
525 }
526 
updateState(const QPaintEngineState & state)527 void QWin32PrintEngine::updateState(const QPaintEngineState &state)
528 {
529     Q_D(QWin32PrintEngine);
530 
531     QAlphaPaintEngine::updateState(state);
532     if (!continueCall())
533         return;
534 
535     if (state.state() & DirtyTransform) {
536         updateMatrix(state.transform());
537     }
538 
539     if (state.state() & DirtyPen) {
540         d->pen = state.pen();
541         d->has_pen = d->pen.style() != Qt::NoPen && d->pen.isSolid();
542     }
543 
544     if (state.state() & DirtyBrush) {
545         QBrush brush = state.brush();
546         d->has_brush = brush.style() == Qt::SolidPattern;
547         d->brush_color = brush.color();
548     }
549 
550     if (state.state() & DirtyClipEnabled) {
551         if (state.isClipEnabled())
552             updateClipPath(painter()->clipPath(), Qt::ReplaceClip);
553         else
554             updateClipPath(QPainterPath(), Qt::NoClip);
555     }
556 
557     if (state.state() & DirtyClipPath) {
558         updateClipPath(state.clipPath(), state.clipOperation());
559     }
560 
561     if (state.state() & DirtyClipRegion) {
562         QRegion clipRegion = state.clipRegion();
563         QPainterPath clipPath = qt_regionToPath(clipRegion);
564         updateClipPath(clipPath, state.clipOperation());
565     }
566 }
567 
updateClipPath(const QPainterPath & clipPath,Qt::ClipOperation op)568 void QWin32PrintEngine::updateClipPath(const QPainterPath &clipPath, Qt::ClipOperation op)
569 {
570     Q_D(QWin32PrintEngine);
571 
572     bool doclip = true;
573     if (op == Qt::NoClip) {
574         SelectClipRgn(d->hdc, 0);
575         doclip = false;
576     }
577 
578     if (doclip) {
579         QPainterPath xformed = clipPath * d->matrix;
580 
581         if (xformed.isEmpty()) {
582             QRegion empty(-0x1000000, -0x1000000, 1, 1);
583             SelectClipRgn(d->hdc, empty.handle());
584         } else {
585             d->composeGdiPath(xformed);
586             const int ops[] = {
587                 -1,         // Qt::NoClip, covered above
588                 RGN_COPY,   // Qt::ReplaceClip
589                 RGN_AND,    // Qt::IntersectClip
590                 RGN_OR      // Qt::UniteClip
591             };
592             Q_ASSERT(op > 0 && unsigned(op) <= sizeof(ops) / sizeof(int));
593             SelectClipPath(d->hdc, ops[op]);
594         }
595     }
596 
597     QPainterPath aclip = qt_regionToPath(alphaClipping());
598     if (!aclip.isEmpty()) {
599         QTransform tx(d->stretch_x, 0, 0, d->stretch_y, d->origin_x, d->origin_y);
600         d->composeGdiPath(tx.map(aclip));
601         SelectClipPath(d->hdc, RGN_DIFF);
602     }
603 }
604 
updateMatrix(const QTransform & m)605 void QWin32PrintEngine::updateMatrix(const QTransform &m)
606 {
607     Q_D(QWin32PrintEngine);
608 
609     QTransform stretch(d->stretch_x, 0, 0, d->stretch_y, d->origin_x, d->origin_y);
610     d->painterMatrix = m;
611     d->matrix = d->painterMatrix * stretch;
612     d->txop = d->matrix.type();
613     d->complex_xform = (d->txop > QTransform::TxScale);
614 }
615 
drawPixmap(const QRectF & targetRect,const QPixmap & originalPixmap,const QRectF & sourceRect)616 void QWin32PrintEngine::drawPixmap(const QRectF &targetRect,
617                                    const QPixmap &originalPixmap,
618                                    const QRectF &sourceRect)
619 {
620     Q_D(QWin32PrintEngine);
621 
622     QAlphaPaintEngine::drawPixmap(targetRect, originalPixmap, sourceRect);
623     if (!continueCall())
624         return;
625 
626     const int tileSize = 2048;
627 
628     QRectF r = targetRect;
629     QRectF sr = sourceRect;
630 
631     QPixmap pixmap = originalPixmap;
632     if (sr.size() != pixmap.size()) {
633         pixmap = pixmap.copy(sr.toRect());
634     }
635 
636     qreal scaleX = 1.0f;
637     qreal scaleY = 1.0f;
638 
639     QTransform scaleMatrix = QTransform::fromScale(r.width() / pixmap.width(), r.height() / pixmap.height());
640     QTransform adapted = QPixmap::trueMatrix(d->painterMatrix * scaleMatrix,
641                                              pixmap.width(), pixmap.height());
642 
643     qreal xform_offset_x = adapted.dx();
644     qreal xform_offset_y = adapted.dy();
645 
646     if (d->complex_xform) {
647         pixmap = pixmap.transformed(adapted);
648         scaleX = d->stretch_x;
649         scaleY = d->stretch_y;
650     } else {
651         scaleX = d->stretch_x * (r.width() / pixmap.width()) * d->painterMatrix.m11();
652         scaleY = d->stretch_y * (r.height() / pixmap.height()) * d->painterMatrix.m22();
653     }
654 
655     QPointF topLeft = r.topLeft() * d->painterMatrix;
656     int tx = int(topLeft.x() * d->stretch_x + d->origin_x);
657     int ty = int(topLeft.y() * d->stretch_y + d->origin_y);
658     int tw = qAbs(int(pixmap.width() * scaleX));
659     int th = qAbs(int(pixmap.height() * scaleY));
660 
661     xform_offset_x *= d->stretch_x;
662     xform_offset_y *= d->stretch_y;
663 
664     int dc_state = SaveDC(d->hdc);
665 
666     int tilesw = pixmap.width() / tileSize;
667     int tilesh = pixmap.height() / tileSize;
668     ++tilesw;
669     ++tilesh;
670 
671     int txinc = tileSize*scaleX;
672     int tyinc = tileSize*scaleY;
673 
674     for (int y = 0; y < tilesh; ++y) {
675         int tposy = ty + (y * tyinc);
676         int imgh = tileSize;
677         int height = tyinc;
678         if (y == (tilesh - 1)) {
679             imgh = pixmap.height() - (y * tileSize);
680             height = (th - (y * tyinc));
681         }
682         for (int x = 0; x < tilesw; ++x) {
683             int tposx = tx + (x * txinc);
684             int imgw = tileSize;
685             int width = txinc;
686             if (x == (tilesw - 1)) {
687                 imgw = pixmap.width() - (x * tileSize);
688                 width = (tw - (x * txinc));
689             }
690 
691             QPixmap p = pixmap.copy(tileSize * x, tileSize * y, imgw, imgh);
692             HBITMAP hbitmap = p.toWinHBITMAP(QPixmap::NoAlpha);
693             HDC display_dc = GetDC(0);
694             HDC hbitmap_hdc = CreateCompatibleDC(display_dc);
695             HGDIOBJ null_bitmap = SelectObject(hbitmap_hdc, hbitmap);
696 
697             ReleaseDC(0, display_dc);
698 
699             if (!StretchBlt(d->hdc, qRound(tposx - xform_offset_x), qRound(tposy - xform_offset_y), width, height,
700                             hbitmap_hdc, 0, 0, p.width(), p.height(), SRCCOPY))
701                 qErrnoWarning("QWin32PrintEngine::drawPixmap, StretchBlt failed");
702 
703             SelectObject(hbitmap_hdc, null_bitmap);
704             DeleteObject(hbitmap);
705             DeleteDC(hbitmap_hdc);
706         }
707     }
708 
709     RestoreDC(d->hdc, dc_state);
710 }
711 
712 
drawTiledPixmap(const QRectF & r,const QPixmap & pm,const QPointF & pos)713 void QWin32PrintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &pos)
714 {
715     Q_D(QWin32PrintEngine);
716 
717     QAlphaPaintEngine::drawTiledPixmap(r, pm, pos);
718     if (!continueCall())
719         return;
720 
721     if (d->complex_xform || !pos.isNull()) {
722         QPaintEngine::drawTiledPixmap(r, pm, pos);
723     } else {
724         int dc_state = SaveDC(d->hdc);
725 
726         HDC display_dc = GetDC(0);
727         HBITMAP hbitmap = pm.toWinHBITMAP(QPixmap::NoAlpha);
728         HDC hbitmap_hdc = CreateCompatibleDC(display_dc);
729         HGDIOBJ null_bitmap = SelectObject(hbitmap_hdc, hbitmap);
730 
731         ReleaseDC(0, display_dc);
732 
733         QRectF trect = d->painterMatrix.mapRect(r);
734         int tx = int(trect.left() * d->stretch_x + d->origin_x);
735         int ty = int(trect.top() * d->stretch_y + d->origin_y);
736 
737         int xtiles = int(trect.width() / pm.width()) + 1;
738         int ytiles = int(trect.height() / pm.height()) + 1;
739         int xinc = int(pm.width() * d->stretch_x);
740         int yinc = int(pm.height() * d->stretch_y);
741 
742         for (int y = 0; y < ytiles; ++y) {
743             int ity = ty + (yinc * y);
744             int ith = pm.height();
745             if (y == (ytiles - 1)) {
746                 ith = int(trect.height() - (pm.height() * y));
747             }
748 
749             for (int x = 0; x < xtiles; ++x) {
750                 int itx = tx + (xinc * x);
751                 int itw = pm.width();
752                 if (x == (xtiles - 1)) {
753                     itw = int(trect.width() - (pm.width() * x));
754                 }
755 
756                 if (!StretchBlt(d->hdc, itx, ity, int(itw * d->stretch_x), int(ith * d->stretch_y),
757                                 hbitmap_hdc, 0, 0, itw, ith, SRCCOPY))
758                     qErrnoWarning("QWin32PrintEngine::drawPixmap, StretchBlt failed");
759 
760             }
761         }
762 
763         SelectObject(hbitmap_hdc, null_bitmap);
764         DeleteObject(hbitmap);
765         DeleteDC(hbitmap_hdc);
766 
767         RestoreDC(d->hdc, dc_state);
768     }
769 }
770 
771 
composeGdiPath(const QPainterPath & path)772 void QWin32PrintEnginePrivate::composeGdiPath(const QPainterPath &path)
773 {
774     if (!BeginPath(hdc))
775         qErrnoWarning("QWin32PrintEnginePrivate::drawPath: BeginPath failed");
776 
777     // Drawing the subpaths
778     int start = -1;
779     for (int i=0; i<path.elementCount(); ++i) {
780         const QPainterPath::Element &elm = path.elementAt(i);
781         switch (elm.type) {
782         case QPainterPath::MoveToElement:
783             if (start >= 0
784                 && path.elementAt(start).x == path.elementAt(i-1).x
785                 && path.elementAt(start).y == path.elementAt(i-1).y)
786                 CloseFigure(hdc);
787             start = i;
788             MoveToEx(hdc, qRound(elm.x), qRound(elm.y), 0);
789             break;
790         case QPainterPath::LineToElement:
791             LineTo(hdc, qRound(elm.x), qRound(elm.y));
792             break;
793         case QPainterPath::CurveToElement: {
794             POINT pts[3] = {
795                 { qRound(elm.x), qRound(elm.y) },
796                 { qRound(path.elementAt(i+1).x), qRound(path.elementAt(i+1).y) },
797                 { qRound(path.elementAt(i+2).x), qRound(path.elementAt(i+2).y) }
798             };
799             i+=2;
800             PolyBezierTo(hdc, pts, 3);
801             break;
802         }
803         default:
804             qFatal("QWin32PaintEngine::drawPath: Unhandled type: %d", elm.type);
805         }
806     }
807 
808     if (start >= 0
809         && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x
810         && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y)
811         CloseFigure(hdc);
812 
813     if (!EndPath(hdc))
814         qErrnoWarning("QWin32PaintEngine::drawPath: EndPath failed");
815 
816     SetPolyFillMode(hdc, path.fillRule() == Qt::WindingFill ? WINDING : ALTERNATE);
817 }
818 
819 
fillPath_dev(const QPainterPath & path,const QColor & color)820 void QWin32PrintEnginePrivate::fillPath_dev(const QPainterPath &path, const QColor &color)
821 {
822 #ifdef QT_DEBUG_DRAW
823     qDebug() << " --- QWin32PrintEnginePrivate::fillPath() bound:" << path.boundingRect() << color;
824 #endif
825 
826     composeGdiPath(path);
827 
828     HBRUSH brush = CreateSolidBrush(RGB(color.red(), color.green(), color.blue()));
829     HGDIOBJ old_brush = SelectObject(hdc, brush);
830     FillPath(hdc);
831     DeleteObject(SelectObject(hdc, old_brush));
832 }
833 
strokePath_dev(const QPainterPath & path,const QColor & color,qreal penWidth)834 void QWin32PrintEnginePrivate::strokePath_dev(const QPainterPath &path, const QColor &color, qreal penWidth)
835 {
836     composeGdiPath(path);
837     LOGBRUSH brush;
838     brush.lbStyle = BS_SOLID;
839     brush.lbColor = RGB(color.red(), color.green(), color.blue());
840     DWORD capStyle = PS_ENDCAP_SQUARE;
841     DWORD joinStyle = PS_JOIN_BEVEL;
842     if (pen.capStyle() == Qt::FlatCap)
843         capStyle = PS_ENDCAP_FLAT;
844     else if (pen.capStyle() == Qt::RoundCap)
845         capStyle = PS_ENDCAP_ROUND;
846 
847     if (pen.joinStyle() == Qt::MiterJoin)
848         joinStyle = PS_JOIN_MITER;
849     else if (pen.joinStyle() == Qt::RoundJoin)
850         joinStyle = PS_JOIN_ROUND;
851 
852     HPEN pen = ExtCreatePen(((penWidth == 0) ? PS_COSMETIC : PS_GEOMETRIC)
853                             | PS_SOLID | capStyle | joinStyle,
854                             (penWidth == 0) ? 1 : penWidth, &brush, 0, 0);
855 
856     HGDIOBJ old_pen = SelectObject(hdc, pen);
857     StrokePath(hdc);
858     DeleteObject(SelectObject(hdc, old_pen));
859 }
860 
861 
fillPath(const QPainterPath & path,const QColor & color)862 void QWin32PrintEnginePrivate::fillPath(const QPainterPath &path, const QColor &color)
863 {
864     fillPath_dev(path * matrix, color);
865 }
866 
strokePath(const QPainterPath & path,const QColor & color)867 void QWin32PrintEnginePrivate::strokePath(const QPainterPath &path, const QColor &color)
868 {
869     QPainterPathStroker stroker;
870     if (pen.style() == Qt::CustomDashLine) {
871         stroker.setDashPattern(pen.dashPattern());
872         stroker.setDashOffset(pen.dashOffset());
873     } else {
874         stroker.setDashPattern(pen.style());
875     }
876     stroker.setCapStyle(pen.capStyle());
877     stroker.setJoinStyle(pen.joinStyle());
878     stroker.setMiterLimit(pen.miterLimit());
879 
880     QPainterPath stroke;
881     qreal width = pen.widthF();
882     if (pen.style() == Qt::SolidLine && (pen.isCosmetic() || matrix.type() < QTransform::TxScale)) {
883         strokePath_dev(path * matrix, color, width);
884     } else {
885         stroker.setWidth(width);
886         if (pen.isCosmetic()) {
887             stroke = stroker.createStroke(path * matrix);
888         } else {
889             stroke = stroker.createStroke(path) * painterMatrix;
890             QTransform stretch(stretch_x, 0, 0, stretch_y, origin_x, origin_y);
891             stroke = stroke * stretch;
892         }
893 
894         if (stroke.isEmpty())
895             return;
896 
897         fillPath_dev(stroke, color);
898     }
899 }
900 
901 
drawPath(const QPainterPath & path)902 void QWin32PrintEngine::drawPath(const QPainterPath &path)
903 {
904 #ifdef QT_DEBUG_DRAW
905     qDebug() << " - QWin32PrintEngine::drawPath(), bounds: " << path.boundingRect();
906 #endif
907 
908     Q_D(QWin32PrintEngine);
909 
910     QAlphaPaintEngine::drawPath(path);
911     if (!continueCall())
912         return;
913 
914     if (d->has_brush)
915         d->fillPath(path, d->brush_color);
916 
917     if (d->has_pen)
918         d->strokePath(path, d->pen.color());
919 }
920 
921 
drawPolygon(const QPointF * points,int pointCount,PolygonDrawMode mode)922 void QWin32PrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
923 {
924 #ifdef QT_DEBUG_DRAW
925     qDebug() << " - QWin32PrintEngine::drawPolygon(), pointCount: " << pointCount;
926 #endif
927 
928     QAlphaPaintEngine::drawPolygon(points, pointCount, mode);
929     if (!continueCall())
930         return;
931 
932     Q_ASSERT(pointCount > 1);
933 
934     QPainterPath path(points[0]);
935 
936     for (int i=1; i<pointCount; ++i) {
937         path.lineTo(points[i]);
938     }
939 
940     Q_D(QWin32PrintEngine);
941 
942     bool has_brush = d->has_brush;
943 
944     if (mode == PolylineMode)
945         d->has_brush = false; // No brush for polylines
946     else
947         path.closeSubpath(); // polygons are should always be closed.
948 
949     drawPath(path);
950     d->has_brush = has_brush;
951 }
952 
queryDefault()953 void QWin32PrintEnginePrivate::queryDefault()
954 {
955     /* Read the default printer name, driver and port with the intuitive function
956      * Strings "windows" and "device" are specified in the MSDN under EnumPrinters()
957      */
958     QString noPrinters(QLatin1String("qt_no_printers"));
959     wchar_t buffer[256];
960     GetProfileString(L"windows", L"device",
961                      reinterpret_cast<const wchar_t *>(noPrinters.utf16()),
962                      buffer, 256);
963     QString output = QString::fromWCharArray(buffer);
964     if (output.isEmpty() || output == noPrinters) // no printers
965         return;
966 
967     QStringList info = output.split(QLatin1Char(','));
968     int infoSize = info.size();
969     if (infoSize > 0) {
970         if (name.isEmpty())
971             name = info.at(0);
972         if (program.isEmpty() && infoSize > 1)
973             program = info.at(1);
974         if (port.isEmpty() && infoSize > 2)
975             port = info.at(2);
976     }
977 }
978 
~QWin32PrintEnginePrivate()979 QWin32PrintEnginePrivate::~QWin32PrintEnginePrivate()
980 {
981     if (hdc)
982         release();
983 }
984 
initialize()985 void QWin32PrintEnginePrivate::initialize()
986 {
987     if (hdc)
988         release();
989     Q_ASSERT(!hPrinter);
990     Q_ASSERT(!hdc);
991     Q_ASSERT(!devMode);
992     Q_ASSERT(!pInfo);
993 
994     if (name.isEmpty())
995         return;
996 
997     txop = QTransform::TxNone;
998 
999     bool ok = OpenPrinter((LPWSTR)name.utf16(), (LPHANDLE)&hPrinter, 0);
1000     if (!ok) {
1001         qErrnoWarning("QWin32PrintEngine::initialize: OpenPrinter failed");
1002         return;
1003     }
1004 
1005     // Fetch the PRINTER_INFO_2 with DEVMODE data containing the
1006     // printer settings.
1007     DWORD infoSize, numBytes;
1008     GetPrinter(hPrinter, 2, NULL, 0, &infoSize);
1009     hMem = GlobalAlloc(GHND, infoSize);
1010     pInfo = (PRINTER_INFO_2*) GlobalLock(hMem);
1011     ok = GetPrinter(hPrinter, 2, (LPBYTE)pInfo, infoSize, &numBytes);
1012 
1013     if (!ok) {
1014         qErrnoWarning("QWin32PrintEngine::initialize: GetPrinter failed");
1015         GlobalUnlock(pInfo);
1016         GlobalFree(hMem);
1017         ClosePrinter(hPrinter);
1018         pInfo = 0;
1019         hMem = 0;
1020         hPrinter = 0;
1021         return;
1022     }
1023 
1024     devMode = pInfo->pDevMode;
1025     hdc = CreateDC(reinterpret_cast<const wchar_t *>(program.utf16()),
1026                    reinterpret_cast<const wchar_t *>(name.utf16()), 0, devMode);
1027 
1028     Q_ASSERT(hPrinter);
1029     Q_ASSERT(pInfo);
1030 
1031     if (devMode) {
1032         num_copies = devMode->dmCopies;
1033     }
1034 
1035     initHDC();
1036 
1037 #ifdef QT_DEBUG_DRAW
1038     qDebug() << "QWin32PrintEngine::initialize()" << endl
1039              << " - paperRect" << devPaperRect << endl
1040              << " - pageRect" << devPageRect << endl
1041              << " - stretch_x" << stretch_x << endl
1042              << " - stretch_y" << stretch_y << endl
1043              << " - origin_x" << origin_x << endl
1044              << " - origin_y" << origin_y << endl;
1045 #endif
1046 }
1047 
initHDC()1048 void QWin32PrintEnginePrivate::initHDC()
1049 {
1050     Q_ASSERT(hdc);
1051 
1052     HDC display_dc = GetDC(0);
1053     dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
1054     dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
1055     dpi_display = GetDeviceCaps(display_dc, LOGPIXELSY);
1056     ReleaseDC(0, display_dc);
1057     if (dpi_display == 0) {
1058         qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
1059                 "might be a driver problem");
1060         dpi_display = 96; // Reasonable default
1061     }
1062 
1063     switch(mode) {
1064     case QPrinter::ScreenResolution:
1065         resolution = dpi_display;
1066         stretch_x = dpi_x / double(dpi_display);
1067         stretch_y = dpi_y / double(dpi_display);
1068         break;
1069     case QPrinter::PrinterResolution:
1070     case QPrinter::HighResolution:
1071         resolution = dpi_y;
1072         stretch_x = 1;
1073         stretch_y = 1;
1074         break;
1075     default:
1076         break;
1077     }
1078 
1079     initDevRects();
1080 }
1081 
initDevRects()1082 void QWin32PrintEnginePrivate::initDevRects()
1083 {
1084     devPaperRect = QRect(0, 0,
1085                          GetDeviceCaps(hdc, PHYSICALWIDTH),
1086                          GetDeviceCaps(hdc, PHYSICALHEIGHT));
1087     devPhysicalPageRect = QRect(GetDeviceCaps(hdc, PHYSICALOFFSETX),
1088                                 GetDeviceCaps(hdc, PHYSICALOFFSETY),
1089                                 GetDeviceCaps(hdc, HORZRES),
1090                                 GetDeviceCaps(hdc, VERTRES));
1091     if (!pageMarginsSet)
1092         devPageRect = devPhysicalPageRect;
1093     else
1094         devPageRect = devPaperRect.adjusted(qRound(mmToInches(previousDialogMargins.left() / 100.0) * dpi_x),
1095                                             qRound(mmToInches(previousDialogMargins.top() / 100.0) * dpi_y),
1096                                             -qRound(mmToInches(previousDialogMargins.width() / 100.0) * dpi_x),
1097                                             -qRound(mmToInches(previousDialogMargins.height() / 100.0) * dpi_y));
1098     updateOrigin();
1099 }
1100 
setPageMargins(int marginLeft,int marginTop,int marginRight,int marginBottom)1101 void QWin32PrintEnginePrivate::setPageMargins(int marginLeft, int marginTop, int marginRight, int marginBottom)
1102 {
1103     pageMarginsSet = true;
1104     previousDialogMargins = QRect(marginLeft, marginTop, marginRight, marginBottom);
1105 
1106     devPageRect = devPaperRect.adjusted(qRound(mmToInches(marginLeft / 100.0) * dpi_x),
1107                                         qRound(mmToInches(marginTop / 100.0) * dpi_y),
1108                                         - qRound(mmToInches(marginRight / 100.0) * dpi_x),
1109                                         - qRound(mmToInches(marginBottom / 100.0) * dpi_y));
1110     updateOrigin();
1111 }
1112 
getPageMargins() const1113 QRect QWin32PrintEnginePrivate::getPageMargins() const
1114 {
1115     if (pageMarginsSet)
1116         return previousDialogMargins;
1117     else
1118         return QRect(qRound(inchesToMM(devPhysicalPageRect.left()) * 100.0 / dpi_x),
1119                      qRound(inchesToMM(devPhysicalPageRect.top()) * 100.0 / dpi_y),
1120                      qRound(inchesToMM(devPaperRect.right() - devPhysicalPageRect.right()) * 100.0 / dpi_x),
1121                      qRound(inchesToMM(devPaperRect.bottom() - devPhysicalPageRect.bottom()) * 100.0 / dpi_y));
1122 }
1123 
release()1124 void QWin32PrintEnginePrivate::release()
1125 {
1126     if (hdc == 0)
1127         return;
1128 
1129     if (globalDevMode) { // Devmode comes from print dialog
1130         GlobalUnlock(globalDevMode);
1131     } else {            // Devmode comes from initialize...
1132         // devMode is a part of the same memory block as pInfo so one free is enough...
1133         GlobalUnlock(hMem);
1134         GlobalFree(hMem);
1135     }
1136     if (hPrinter)
1137         ClosePrinter(hPrinter);
1138     DeleteDC(hdc);
1139 
1140     hdc = 0;
1141     hPrinter = 0;
1142     pInfo = 0;
1143     hMem = 0;
1144     devMode = 0;
1145 }
1146 
queryResolutions() const1147 QList<QVariant> QWin32PrintEnginePrivate::queryResolutions() const
1148 {
1149     // Read the supported resolutions of the printer.
1150     QList<QVariant> list;
1151 
1152     DWORD numRes = DeviceCapabilities(reinterpret_cast<const wchar_t *>(name.utf16()),
1153                                       reinterpret_cast<const wchar_t *>(port.utf16()),
1154                                       DC_ENUMRESOLUTIONS, 0, 0);
1155     if (numRes == (DWORD)-1)
1156         return list;
1157 
1158     LONG *enumRes = (LONG*)malloc(numRes * 2 * sizeof(LONG));
1159     DWORD errRes = DeviceCapabilities(reinterpret_cast<const wchar_t *>(name.utf16()),
1160                                       reinterpret_cast<const wchar_t *>(port.utf16()),
1161                                       DC_ENUMRESOLUTIONS, (LPWSTR)enumRes, 0);
1162 
1163     if (errRes == (DWORD)-1) {
1164         qErrnoWarning("QWin32PrintEngine::queryResolutions: DeviceCapabilities failed");
1165         return list;
1166     }
1167 
1168     for (uint i=0; i<numRes; ++i)
1169         list.append(int(enumRes[i * 2]));
1170 
1171     return list;
1172 }
1173 
doReinit()1174 void QWin32PrintEnginePrivate::doReinit()
1175 {
1176     if (state == QPrinter::Active) {
1177         reinit = true;
1178     } else {
1179         resetDC();
1180         initDevRects();
1181         reinit = false;
1182     }
1183 }
1184 
updateOrigin()1185 void QWin32PrintEnginePrivate::updateOrigin()
1186 {
1187     if (fullPage) {
1188         // subtract physical margins to make (0,0) absolute top corner of paper
1189         // then add user defined margins
1190         origin_x = -devPhysicalPageRect.x();
1191         origin_y = -devPhysicalPageRect.y();
1192         if (pageMarginsSet) {
1193             origin_x += devPageRect.left();
1194             origin_y += devPageRect.top();
1195         }
1196     } else {
1197         origin_x = 0;
1198         origin_y = 0;
1199         if (pageMarginsSet) {
1200             origin_x = devPageRect.left() - devPhysicalPageRect.x();
1201             origin_y = devPageRect.top() - devPhysicalPageRect.y();
1202         }
1203     }
1204 }
1205 
setProperty(PrintEnginePropertyKey key,const QVariant & value)1206 void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
1207 {
1208     Q_D(QWin32PrintEngine);
1209     switch (key) {
1210     case PPK_CollateCopies:
1211         {
1212             if (!d->devMode)
1213                 break;
1214             d->devMode->dmCollate = value.toBool() ? DMCOLLATE_TRUE : DMCOLLATE_FALSE;
1215             d->doReinit();
1216         }
1217         break;
1218 
1219     case PPK_ColorMode:
1220         {
1221             if (!d->devMode)
1222                 break;
1223             d->devMode->dmColor = (value.toInt() == QPrinter::Color) ? DMCOLOR_COLOR : DMCOLOR_MONOCHROME;
1224             d->doReinit();
1225         }
1226         break;
1227 
1228     case PPK_Creator:
1229 
1230         break;
1231 
1232     case PPK_DocumentName:
1233         if (isActive()) {
1234             qWarning("QWin32PrintEngine: Cannot change document name while printing is active");
1235             return;
1236         }
1237         d->docName = value.toString();
1238         break;
1239 
1240     case PPK_FullPage:
1241         d->fullPage = value.toBool();
1242         d->updateOrigin();
1243         break;
1244 
1245     case PPK_CopyCount: // fallthrough
1246     case PPK_NumberOfCopies:
1247         if (!d->devMode)
1248             break;
1249         d->num_copies = value.toInt();
1250         d->devMode->dmCopies = d->num_copies;
1251         d->doReinit();
1252         break;
1253 
1254     case PPK_Orientation:
1255         {
1256             if (!d->devMode)
1257                 break;
1258             int orientation = value.toInt() == QPrinter::Landscape ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
1259             int old_orientation = d->devMode->dmOrientation;
1260             d->devMode->dmOrientation = orientation;
1261             if (d->has_custom_paper_size && old_orientation != orientation)
1262                 d->paper_size = QSizeF(d->paper_size.height(), d->paper_size.width());
1263             d->doReinit();
1264         }
1265         break;
1266 
1267     case PPK_OutputFileName:
1268         if (isActive()) {
1269             qWarning("QWin32PrintEngine: Cannot change filename while printing");
1270         } else {
1271             d->fileName = value.toString();
1272             d->printToFile = !value.toString().isEmpty();
1273         }
1274         break;
1275 
1276     case PPK_PaperSize:
1277         if (!d->devMode)
1278             break;
1279         d->devMode->dmPaperSize = mapPaperSizeDevmode(QPrinter::PaperSize(value.toInt()));
1280         d->has_custom_paper_size = (QPrinter::PaperSize(value.toInt()) == QPrinter::Custom);
1281         d->doReinit();
1282         break;
1283 
1284     case PPK_PaperSource:
1285         {
1286             if (!d->devMode)
1287                 break;
1288             int dmMapped = DMBIN_AUTO;
1289 
1290             QList<QVariant> v = property(PPK_PaperSources).toList();
1291             if (v.contains(value))
1292                 dmMapped = mapPaperSourceDevmode(QPrinter::PaperSource(value.toInt()));
1293 
1294             d->devMode->dmDefaultSource = dmMapped;
1295             d->doReinit();
1296         }
1297         break;
1298 
1299     case PPK_PrinterName:
1300         d->name = value.toString();
1301         if(d->name.isEmpty())
1302             d->queryDefault();
1303         d->initialize();
1304         break;
1305 
1306     case PPK_Resolution:
1307         {
1308             d->resolution = value.toInt();
1309 
1310             d->stretch_x = d->dpi_x / double(d->resolution);
1311             d->stretch_y = d->dpi_y / double(d->resolution);
1312         }
1313         break;
1314 
1315     case PPK_SelectionOption:
1316 
1317         break;
1318 
1319     case PPK_SupportedResolutions:
1320 
1321         break;
1322 
1323 
1324     case PPK_WindowsPageSize:
1325         if (!d->devMode)
1326             break;
1327         d->has_custom_paper_size = false;
1328         d->devMode->dmPaperSize = value.toInt();
1329         d->doReinit();
1330         break;
1331 
1332     case PPK_CustomPaperSize:
1333     {
1334         d->has_custom_paper_size = true;
1335         d->paper_size = value.toSizeF();
1336         if (!d->devMode)
1337             break;
1338         int orientation = d->devMode->dmOrientation;
1339         DWORD needed = 0;
1340         DWORD returned = 0;
1341         if (!EnumForms(d->hPrinter, 1, 0, 0, &needed, &returned)) {
1342             BYTE *forms = (BYTE *) malloc(needed);
1343             if (EnumForms(d->hPrinter, 1, forms, needed, &needed, &returned)) {
1344                 for (DWORD i=0; i< returned; ++i) {
1345                     FORM_INFO_1 *formArray = reinterpret_cast<FORM_INFO_1 *>(forms);
1346                     // the form sizes are specified in 1000th of a mm,
1347                     // convert the size to Points
1348                     QSizeF size((formArray[i].Size.cx * 72/25.4)/1000.0,
1349                                 (formArray[i].Size.cy * 72/25.4)/1000.0);
1350                     if (qAbs(d->paper_size.width() - size.width()) <= 2
1351                         && qAbs(d->paper_size.height() - size.height()) <= 2)
1352                     {
1353                         d->devMode->dmPaperSize = i + 1;
1354                         break;
1355                     }
1356                 }
1357             }
1358             free(forms);
1359         }
1360         if (orientation != DMORIENT_PORTRAIT)
1361             d->paper_size = QSizeF(d->paper_size.height(), d->paper_size.width());
1362         break;
1363     }
1364 
1365     case PPK_PageMargins:
1366     {
1367         QList<QVariant> margins(value.toList());
1368         Q_ASSERT(margins.size() == 4);
1369         int left, top, right, bottom;
1370         // specified in 1/100 mm
1371         left = (margins.at(0).toReal()*25.4/72.0) * 100;
1372         top = (margins.at(1).toReal()*25.4/72.0) * 100;
1373         right = (margins.at(2).toReal()*25.4/72.0) * 100;
1374         bottom = (margins.at(3).toReal()*25.4/72.0) * 100;
1375         d->setPageMargins(left, top, right, bottom);
1376         break;
1377     }
1378     default:
1379         // Do nothing
1380         break;
1381     }
1382 }
1383 
property(PrintEnginePropertyKey key) const1384 QVariant QWin32PrintEngine::property(PrintEnginePropertyKey key) const
1385 {
1386     Q_D(const QWin32PrintEngine);
1387     QVariant value;
1388     switch (key) {
1389 
1390     case PPK_CollateCopies:
1391         value = false;
1392         break;
1393 
1394     case PPK_ColorMode:
1395         {
1396             if (!d->devMode) {
1397                 value = QPrinter::Color;
1398             } else {
1399                 value = (d->devMode->dmColor == DMCOLOR_COLOR) ? QPrinter::Color : QPrinter::GrayScale;
1400             }
1401         }
1402         break;
1403 
1404     case PPK_DocumentName:
1405         value = d->docName;
1406         break;
1407 
1408     case PPK_FullPage:
1409         value = d->fullPage;
1410         break;
1411 
1412     case PPK_CopyCount:
1413         value = d->num_copies;
1414         break;
1415 
1416     case PPK_SupportsMultipleCopies:
1417         value = true;
1418         break;
1419 
1420     case PPK_NumberOfCopies:
1421         value = 1;
1422         break;
1423 
1424     case PPK_Orientation:
1425         {
1426             if (!d->devMode) {
1427                 value = QPrinter::Portrait;
1428             } else {
1429                 value = (d->devMode->dmOrientation == DMORIENT_LANDSCAPE) ? QPrinter::Landscape : QPrinter::Portrait;
1430             }
1431         }
1432         break;
1433 
1434     case PPK_OutputFileName:
1435         value = d->fileName;
1436         break;
1437 
1438     case PPK_PageRect:
1439         if (d->has_custom_paper_size) {
1440             QRect rect(0, 0,
1441                        qRound(d->paper_size.width() * d->resolution / 72.0),
1442                        qRound(d->paper_size.height() * d->resolution / 72.0));
1443             if (d->pageMarginsSet) {
1444                 rect = rect.adjusted(qRound(mmToInches(d->previousDialogMargins.left()/100.0) * d->resolution),
1445                                      qRound(mmToInches(d->previousDialogMargins.top()/100.0) * d->resolution),
1446                                      -qRound(mmToInches(d->previousDialogMargins.width()/100.0) * d->resolution),
1447                                      -qRound(mmToInches(d->previousDialogMargins.height()/100.0) * d->resolution));
1448             }
1449             value = rect;
1450         } else {
1451             value = QTransform(1/d->stretch_x, 0, 0, 1/d->stretch_y, 0, 0)
1452                     .mapRect(d->fullPage ? d->devPhysicalPageRect : d->devPageRect);
1453         }
1454         break;
1455 
1456     case PPK_PaperSize:
1457         if (d->has_custom_paper_size) {
1458             value = QPrinter::Custom;
1459         } else {
1460             if (!d->devMode) {
1461                 value = QPrinter::A4;
1462             } else {
1463                 value = mapDevmodePaperSize(d->devMode->dmPaperSize);
1464             }
1465         }
1466         break;
1467 
1468     case PPK_PaperRect:
1469         if (d->has_custom_paper_size) {
1470             value = QRect(0, 0,
1471                           qRound(d->paper_size.width() * d->resolution / 72.0),
1472                           qRound(d->paper_size.height() * d->resolution / 72.0));
1473         } else {
1474             value = QTransform(1/d->stretch_x, 0, 0, 1/d->stretch_y, 0, 0).mapRect(d->devPaperRect);
1475         }
1476         break;
1477 
1478     case PPK_PaperSource:
1479         if (!d->devMode) {
1480             value = QPrinter::Auto;
1481         } else {
1482             value = mapDevmodePaperSource(d->devMode->dmDefaultSource);
1483         }
1484         break;
1485 
1486     case PPK_PrinterName:
1487         value = d->name;
1488         break;
1489 
1490     case PPK_Resolution:
1491         if (d->resolution || !d->name.isEmpty())
1492             value = d->resolution;
1493         break;
1494 
1495     case PPK_SupportedResolutions:
1496         value = d->queryResolutions();
1497         break;
1498 
1499     case PPK_WindowsPageSize:
1500         if (!d->devMode) {
1501             value = -1;
1502         } else {
1503             value = d->devMode->dmPaperSize;
1504         }
1505         break;
1506 
1507     case PPK_PaperSources:
1508         {
1509             int available = DeviceCapabilities((const wchar_t *)d->name.utf16(),
1510                                                (const wchar_t *)d->port.utf16(), DC_BINS, 0, d->devMode);
1511 
1512             if (available <= 0)
1513                 break;
1514 
1515             wchar_t *data = new wchar_t[available];
1516             int count = DeviceCapabilities((const wchar_t *)d->name.utf16(),
1517                                            (const wchar_t *)d->port.utf16(), DC_BINS, data, d->devMode);
1518 
1519             QList<QVariant> out;
1520             for (int i=0; i<count; ++i) {
1521                 QPrinter::PaperSource src = mapDevmodePaperSource(data[i]);
1522                 if (src != -1)
1523                     out << (int) src;
1524             }
1525             value = out;
1526 
1527             delete [] data;
1528         }
1529         break;
1530 
1531     case PPK_CustomPaperSize:
1532         value = d->paper_size;
1533         break;
1534 
1535     case PPK_PageMargins:
1536     {
1537         QList<QVariant> margins;
1538         QRect pageMargins(d->getPageMargins());
1539 
1540         // specified in 1/100 mm
1541         margins << (mmToInches(pageMargins.left()/100.0) * 72)
1542                 << (mmToInches(pageMargins.top()/100.0) * 72)
1543                 << (mmToInches(pageMargins.width()/100.0) * 72)
1544                 << (mmToInches(pageMargins.height()/100.0) * 72);
1545         value = margins;
1546         break;
1547     }
1548     default:
1549         // Do nothing
1550         break;
1551     }
1552     return value;
1553 }
1554 
printerState() const1555 QPrinter::PrinterState QWin32PrintEngine::printerState() const
1556 {
1557     return d_func()->state;
1558 }
1559 
getDC() const1560 HDC QWin32PrintEngine::getDC() const
1561 {
1562     return d_func()->hdc;
1563 }
1564 
releaseDC(HDC) const1565 void QWin32PrintEngine::releaseDC(HDC) const
1566 {
1567 
1568 }
1569 
createDevNames()1570 HGLOBAL *QWin32PrintEnginePrivate::createDevNames()
1571 {
1572     int size = sizeof(DEVNAMES)
1573                + program.length() * 2 + 2
1574                + name.length() * 2 + 2
1575                + port.length() * 2 + 2;
1576     HGLOBAL *hGlobal = (HGLOBAL *) GlobalAlloc(GMEM_MOVEABLE, size);
1577     DEVNAMES *dn = (DEVNAMES*) GlobalLock(hGlobal);
1578 
1579     dn->wDriverOffset = sizeof(DEVNAMES) / sizeof(wchar_t);
1580     dn->wDeviceOffset = dn->wDriverOffset + program.length() + 1;
1581     dn->wOutputOffset = dn->wDeviceOffset + name.length() + 1;
1582 
1583     memcpy((ushort*)dn + dn->wDriverOffset, program.utf16(), program.length() * 2 + 2);
1584     memcpy((ushort*)dn + dn->wDeviceOffset, name.utf16(), name.length() * 2 + 2);
1585     memcpy((ushort*)dn + dn->wOutputOffset, port.utf16(), port.length() * 2 + 2);
1586     dn->wDefault = 0;
1587 
1588     GlobalUnlock(hGlobal);
1589 
1590 //         printf("QPrintDialogWinPrivate::createDevNames()\n"
1591 //                " -> wDriverOffset: %d\n"
1592 //                " -> wDeviceOffset: %d\n"
1593 //                " -> wOutputOffset: %d\n",
1594 //                dn->wDriverOffset,
1595 //                dn->wDeviceOffset,
1596 //                dn->wOutputOffset);
1597 
1598 //         printf("QPrintDialogWinPrivate::createDevNames(): %s, %s, %s\n",
1599 //                QString::fromWCharArray((wchar_t*)(dn) + dn->wDriverOffset).latin1(),
1600 //                QString::fromWCharArray((wchar_t*)(dn) + dn->wDeviceOffset).latin1(),
1601 //                QString::fromWCharArray((wchar_t*)(dn) + dn->wOutputOffset).latin1());
1602 
1603     return hGlobal;
1604 }
1605 
readDevnames(HGLOBAL globalDevnames)1606 void QWin32PrintEnginePrivate::readDevnames(HGLOBAL globalDevnames)
1607 {
1608     if (globalDevnames) {
1609         DEVNAMES *dn = (DEVNAMES*) GlobalLock(globalDevnames);
1610         name = QString::fromWCharArray((wchar_t*)(dn) + dn->wDeviceOffset);
1611         port = QString::fromWCharArray((wchar_t*)(dn) + dn->wOutputOffset);
1612         program = QString::fromWCharArray((wchar_t*)(dn) + dn->wDriverOffset);
1613         GlobalUnlock(globalDevnames);
1614     }
1615 }
1616 
readDevmode(HGLOBAL globalDevmode)1617 void QWin32PrintEnginePrivate::readDevmode(HGLOBAL globalDevmode)
1618 {
1619     if (globalDevmode) {
1620         DEVMODE *dm = (DEVMODE*) GlobalLock(globalDevmode);
1621         release();
1622         globalDevMode = globalDevmode;
1623         devMode = dm;
1624         hdc = CreateDC(reinterpret_cast<const wchar_t *>(program.utf16()),
1625                        reinterpret_cast<const wchar_t *>(name.utf16()), 0, dm);
1626 
1627         num_copies = devMode->dmCopies;
1628         if (!OpenPrinter((wchar_t*)name.utf16(), &hPrinter, 0))
1629             qWarning("QPrinter: OpenPrinter() failed after reading DEVMODE.");
1630     }
1631 
1632     if (hdc)
1633         initHDC();
1634 }
1635 
draw_text_item_win(const QPointF & pos,const QTextItemInt & ti,HDC hdc,bool convertToText,const QTransform & xform,const QPointF & topLeft)1636 static void draw_text_item_win(const QPointF &pos, const QTextItemInt &ti, HDC hdc,
1637                                bool convertToText, const QTransform &xform, const QPointF &topLeft)
1638 {
1639     QFontEngine *fe = ti.fontEngine;
1640     QPointF baseline_pos = xform.inverted().map(xform.map(pos) - topLeft);
1641 
1642     SetTextAlign(hdc, TA_BASELINE);
1643     SetBkMode(hdc, TRANSPARENT);
1644 
1645     bool has_kerning = ti.f && ti.f->kerning();
1646     QFontEngineWin *winfe = (fe->type() == QFontEngine::Win) ? static_cast<QFontEngineWin *>(fe) : 0;
1647 
1648     HFONT hfont;
1649     bool ttf = false;
1650 
1651     if (winfe) {
1652         hfont = winfe->hfont;
1653         ttf = winfe->ttf;
1654     } else {
1655         hfont = (HFONT)GetStockObject(ANSI_VAR_FONT);
1656     }
1657 
1658     HGDIOBJ old_font = SelectObject(hdc, hfont);
1659     unsigned int options = (ttf && !convertToText) ? ETO_GLYPH_INDEX : 0;
1660     wchar_t *convertedGlyphs = (wchar_t *)ti.chars;
1661     QGlyphLayout glyphs = ti.glyphs;
1662 
1663     bool fast = !has_kerning && !(ti.flags & QTextItem::RightToLeft);
1664     for (int i = 0; fast && i < glyphs.numGlyphs; i++) {
1665         if (glyphs.offsets[i].x != 0 || glyphs.offsets[i].y != 0 || glyphs.justifications[i].space_18d6 != 0
1666             || glyphs.attributes[i].dontPrint) {
1667             fast = false;
1668             break;
1669         }
1670     }
1671 
1672 #if !defined(Q_OS_WINCE)
1673     // Scale, rotate and translate here.
1674     XFORM win_xform;
1675     win_xform.eM11 = xform.m11();
1676     win_xform.eM12 = xform.m12();
1677     win_xform.eM21 = xform.m21();
1678     win_xform.eM22 = xform.m22();
1679     win_xform.eDx = xform.dx();
1680     win_xform.eDy = xform.dy();
1681 
1682     SetGraphicsMode(hdc, GM_ADVANCED);
1683     SetWorldTransform(hdc, &win_xform);
1684 #endif
1685 
1686     if (fast) {
1687         // fast path
1688         QVarLengthArray<wchar_t> g(glyphs.numGlyphs);
1689         for (int i = 0; i < glyphs.numGlyphs; ++i)
1690             g[i] = glyphs.glyphs[i];
1691         ExtTextOut(hdc,
1692                    qRound(baseline_pos.x() + glyphs.offsets[0].x.toReal()),
1693                    qRound(baseline_pos.y() + glyphs.offsets[0].y.toReal()),
1694                    options, 0, convertToText ? convertedGlyphs : g.data(), glyphs.numGlyphs, 0);
1695     } else {
1696         QVarLengthArray<QFixedPoint> positions;
1697         QVarLengthArray<glyph_t> _glyphs;
1698 
1699         QTransform matrix = QTransform::fromTranslate(baseline_pos.x(), baseline_pos.y());
1700         ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags,
1701             _glyphs, positions);
1702         if (_glyphs.size() == 0) {
1703             SelectObject(hdc, old_font);
1704             return;
1705         }
1706 
1707         convertToText = convertToText && glyphs.numGlyphs == _glyphs.size();
1708         bool outputEntireItem = _glyphs.size() > 0;
1709 
1710         if (outputEntireItem) {
1711             options |= ETO_PDY;
1712             QVarLengthArray<INT> glyphDistances(_glyphs.size() * 2);
1713             QVarLengthArray<wchar_t> g(_glyphs.size());
1714             for (int i=0; i<_glyphs.size() - 1; ++i) {
1715                 glyphDistances[i * 2] = qRound(positions[i + 1].x) - qRound(positions[i].x);
1716                 glyphDistances[i * 2 + 1] = qRound(positions[i + 1].y) - qRound(positions[i].y);
1717                 g[i] = _glyphs[i];
1718             }
1719             glyphDistances[(_glyphs.size() - 1) * 2] = 0;
1720             glyphDistances[(_glyphs.size() - 1) * 2 + 1] = 0;
1721             g[_glyphs.size() - 1] = _glyphs[_glyphs.size() - 1];
1722             ExtTextOut(hdc, qRound(positions[0].x), qRound(positions[0].y), options, 0,
1723                        convertToText ? convertedGlyphs : g.data(), _glyphs.size(),
1724                        glyphDistances.data());
1725         } else {
1726             int i = 0;
1727             while(i < _glyphs.size()) {
1728                 wchar_t g = _glyphs[i];
1729 
1730                 ExtTextOut(hdc, qRound(positions[i].x),
1731                            qRound(positions[i].y), options, 0,
1732                            convertToText ? convertedGlyphs + i : &g, 1, 0);
1733                 ++i;
1734             }
1735         }
1736     }
1737 
1738 #if !defined(Q_OS_WINCE)
1739         win_xform.eM11 = win_xform.eM22 = 1.0;
1740         win_xform.eM12 = win_xform.eM21 = win_xform.eDx = win_xform.eDy = 0.0;
1741         SetWorldTransform(hdc, &win_xform);
1742 #endif
1743 
1744     SelectObject(hdc, old_font);
1745 }
1746 
1747 
updateCustomPaperSize()1748 void QWin32PrintEnginePrivate::updateCustomPaperSize()
1749 {
1750     uint paperSize = devMode->dmPaperSize;
1751     if (paperSize > 0 && mapDevmodePaperSize(paperSize) == QPrinter::Custom) {
1752         has_custom_paper_size = true;
1753         DWORD needed = 0;
1754         DWORD returned = 0;
1755         if (!EnumForms(hPrinter, 1, 0, 0, &needed, &returned)) {
1756             BYTE *forms = (BYTE *) malloc(needed);
1757             if (EnumForms(hPrinter, 1, forms, needed, &needed, &returned)) {
1758                 if (paperSize <= returned) {
1759                     FORM_INFO_1 *formArray = (FORM_INFO_1 *) forms;
1760                     int width = formArray[paperSize - 1].Size.cx; // 1/1000 of a mm
1761                     int height = formArray[paperSize - 1].Size.cy; // 1/1000 of a mm
1762                     paper_size = QSizeF((width * 72 /25.4) / 1000.0, (height * 72 / 25.4) / 1000.0);
1763                 } else {
1764                     has_custom_paper_size = false;
1765                 }
1766             }
1767             free(forms);
1768         }
1769     } else {
1770         has_custom_paper_size = false;
1771     }
1772 }
1773 
1774 QT_END_NAMESPACE
1775 
1776 #endif // QT_NO_PRINTER
1777