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