Replace 'i < len-1 && func(i+1)' by 'i+1 < len && func(i+1)'
[profile/ivi/qtbase.git] / src / gui / painting / qprintengine_win.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
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
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
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
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
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
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
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
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
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
328 bool QWin32PrintEngine::abort()
329 {
330     // do nothing loop.
331     return false;
332 }
333
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
395 static inline qreal mmToInches(double mm)
396 {
397     return mm*0.039370147;
398 }
399
400 static inline qreal inchesToMM(double in)
401 {
402     return in/0.039370147;
403 }
404
405 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
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
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             };
591             Q_ASSERT(op > 0 && unsigned(op) <= sizeof(ops) / sizeof(int));
592             SelectClipPath(d->hdc, ops[op]);
593         }
594     }
595
596     QPainterPath aclip = qt_regionToPath(alphaClipping());
597     if (!aclip.isEmpty()) {
598         QTransform tx(d->stretch_x, 0, 0, d->stretch_y, d->origin_x, d->origin_y);
599         d->composeGdiPath(tx.map(aclip));
600         SelectClipPath(d->hdc, RGN_DIFF);
601     }
602 }
603
604 void QWin32PrintEngine::updateMatrix(const QTransform &m)
605 {
606     Q_D(QWin32PrintEngine);
607
608     QTransform stretch(d->stretch_x, 0, 0, d->stretch_y, d->origin_x, d->origin_y);
609     d->painterMatrix = m;
610     d->matrix = d->painterMatrix * stretch;
611     d->txop = d->matrix.type();
612     d->complex_xform = (d->txop > QTransform::TxScale);
613 }
614
615 void QWin32PrintEngine::drawPixmap(const QRectF &targetRect,
616                                    const QPixmap &originalPixmap,
617                                    const QRectF &sourceRect)
618 {
619     Q_D(QWin32PrintEngine);
620
621     QAlphaPaintEngine::drawPixmap(targetRect, originalPixmap, sourceRect);
622     if (!continueCall())
623         return;
624
625     const int tileSize = 2048;
626
627     QRectF r = targetRect;
628     QRectF sr = sourceRect;
629
630     QPixmap pixmap = originalPixmap;
631     if (sr.size() != pixmap.size()) {
632         pixmap = pixmap.copy(sr.toRect());
633     }
634
635     qreal scaleX = 1.0f;
636     qreal scaleY = 1.0f;
637
638     QTransform scaleMatrix = QTransform::fromScale(r.width() / pixmap.width(), r.height() / pixmap.height());
639     QTransform adapted = QPixmap::trueMatrix(d->painterMatrix * scaleMatrix,
640                                              pixmap.width(), pixmap.height());
641
642     qreal xform_offset_x = adapted.dx();
643     qreal xform_offset_y = adapted.dy();
644
645     if (d->complex_xform) {
646         pixmap = pixmap.transformed(adapted);
647         scaleX = d->stretch_x;
648         scaleY = d->stretch_y;
649     } else {
650         scaleX = d->stretch_x * (r.width() / pixmap.width()) * d->painterMatrix.m11();
651         scaleY = d->stretch_y * (r.height() / pixmap.height()) * d->painterMatrix.m22();
652     }
653
654     QPointF topLeft = r.topLeft() * d->painterMatrix;
655     int tx = int(topLeft.x() * d->stretch_x + d->origin_x);
656     int ty = int(topLeft.y() * d->stretch_y + d->origin_y);
657     int tw = qAbs(int(pixmap.width() * scaleX));
658     int th = qAbs(int(pixmap.height() * scaleY));
659
660     xform_offset_x *= d->stretch_x;
661     xform_offset_y *= d->stretch_y;
662
663     int dc_state = SaveDC(d->hdc);
664
665     int tilesw = pixmap.width() / tileSize;
666     int tilesh = pixmap.height() / tileSize;
667     ++tilesw;
668     ++tilesh;
669
670     int txinc = tileSize*scaleX;
671     int tyinc = tileSize*scaleY;
672
673     for (int y = 0; y < tilesh; ++y) {
674         int tposy = ty + (y * tyinc);
675         int imgh = tileSize;
676         int height = tyinc;
677         if (y == (tilesh - 1)) {
678             imgh = pixmap.height() - (y * tileSize);
679             height = (th - (y * tyinc));
680         }
681         for (int x = 0; x < tilesw; ++x) {
682             int tposx = tx + (x * txinc);
683             int imgw = tileSize;
684             int width = txinc;
685             if (x == (tilesw - 1)) {
686                 imgw = pixmap.width() - (x * tileSize);
687                 width = (tw - (x * txinc));
688             }
689
690             QPixmap p = pixmap.copy(tileSize * x, tileSize * y, imgw, imgh);
691             HBITMAP hbitmap = p.toWinHBITMAP(QPixmap::NoAlpha);
692             HDC display_dc = GetDC(0);
693             HDC hbitmap_hdc = CreateCompatibleDC(display_dc);
694             HGDIOBJ null_bitmap = SelectObject(hbitmap_hdc, hbitmap);
695
696             ReleaseDC(0, display_dc);
697
698             if (!StretchBlt(d->hdc, qRound(tposx - xform_offset_x), qRound(tposy - xform_offset_y), width, height,
699                             hbitmap_hdc, 0, 0, p.width(), p.height(), SRCCOPY))
700                 qErrnoWarning("QWin32PrintEngine::drawPixmap, StretchBlt failed");
701
702             SelectObject(hbitmap_hdc, null_bitmap);
703             DeleteObject(hbitmap);
704             DeleteDC(hbitmap_hdc);
705         }
706     }
707
708     RestoreDC(d->hdc, dc_state);
709 }
710
711
712 void QWin32PrintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &pos)
713 {
714     Q_D(QWin32PrintEngine);
715
716     QAlphaPaintEngine::drawTiledPixmap(r, pm, pos);
717     if (!continueCall())
718         return;
719
720     if (d->complex_xform || !pos.isNull()) {
721         QPaintEngine::drawTiledPixmap(r, pm, pos);
722     } else {
723         int dc_state = SaveDC(d->hdc);
724
725         HDC display_dc = GetDC(0);
726         HBITMAP hbitmap = pm.toWinHBITMAP(QPixmap::NoAlpha);
727         HDC hbitmap_hdc = CreateCompatibleDC(display_dc);
728         HGDIOBJ null_bitmap = SelectObject(hbitmap_hdc, hbitmap);
729
730         ReleaseDC(0, display_dc);
731
732         QRectF trect = d->painterMatrix.mapRect(r);
733         int tx = int(trect.left() * d->stretch_x + d->origin_x);
734         int ty = int(trect.top() * d->stretch_y + d->origin_y);
735
736         int xtiles = int(trect.width() / pm.width()) + 1;
737         int ytiles = int(trect.height() / pm.height()) + 1;
738         int xinc = int(pm.width() * d->stretch_x);
739         int yinc = int(pm.height() * d->stretch_y);
740
741         for (int y = 0; y < ytiles; ++y) {
742             int ity = ty + (yinc * y);
743             int ith = pm.height();
744             if (y == (ytiles - 1)) {
745                 ith = int(trect.height() - (pm.height() * y));
746             }
747
748             for (int x = 0; x < xtiles; ++x) {
749                 int itx = tx + (xinc * x);
750                 int itw = pm.width();
751                 if (x == (xtiles - 1)) {
752                     itw = int(trect.width() - (pm.width() * x));
753                 }
754
755                 if (!StretchBlt(d->hdc, itx, ity, int(itw * d->stretch_x), int(ith * d->stretch_y),
756                                 hbitmap_hdc, 0, 0, itw, ith, SRCCOPY))
757                     qErrnoWarning("QWin32PrintEngine::drawPixmap, StretchBlt failed");
758
759             }
760         }
761
762         SelectObject(hbitmap_hdc, null_bitmap);
763         DeleteObject(hbitmap);
764         DeleteDC(hbitmap_hdc);
765
766         RestoreDC(d->hdc, dc_state);
767     }
768 }
769
770
771 void QWin32PrintEnginePrivate::composeGdiPath(const QPainterPath &path)
772 {
773     if (!BeginPath(hdc))
774         qErrnoWarning("QWin32PrintEnginePrivate::drawPath: BeginPath failed");
775
776     // Drawing the subpaths
777     int start = -1;
778     for (int i=0; i<path.elementCount(); ++i) {
779         const QPainterPath::Element &elm = path.elementAt(i);
780         switch (elm.type) {
781         case QPainterPath::MoveToElement:
782             if (start >= 0
783                 && path.elementAt(start).x == path.elementAt(i-1).x
784                 && path.elementAt(start).y == path.elementAt(i-1).y)
785                 CloseFigure(hdc);
786             start = i;
787             MoveToEx(hdc, qRound(elm.x), qRound(elm.y), 0);
788             break;
789         case QPainterPath::LineToElement:
790             LineTo(hdc, qRound(elm.x), qRound(elm.y));
791             break;
792         case QPainterPath::CurveToElement: {
793             POINT pts[3] = {
794                 { qRound(elm.x), qRound(elm.y) },
795                 { qRound(path.elementAt(i+1).x), qRound(path.elementAt(i+1).y) },
796                 { qRound(path.elementAt(i+2).x), qRound(path.elementAt(i+2).y) }
797             };
798             i+=2;
799             PolyBezierTo(hdc, pts, 3);
800             break;
801         }
802         default:
803             qFatal("QWin32PaintEngine::drawPath: Unhandled type: %d", elm.type);
804         }
805     }
806
807     if (start >= 0
808         && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x
809         && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y)
810         CloseFigure(hdc);
811
812     if (!EndPath(hdc))
813         qErrnoWarning("QWin32PaintEngine::drawPath: EndPath failed");
814
815     SetPolyFillMode(hdc, path.fillRule() == Qt::WindingFill ? WINDING : ALTERNATE);
816 }
817
818
819 void QWin32PrintEnginePrivate::fillPath_dev(const QPainterPath &path, const QColor &color)
820 {
821 #ifdef QT_DEBUG_DRAW
822     qDebug() << " --- QWin32PrintEnginePrivate::fillPath() bound:" << path.boundingRect() << color;
823 #endif
824
825     composeGdiPath(path);
826
827     HBRUSH brush = CreateSolidBrush(RGB(color.red(), color.green(), color.blue()));
828     HGDIOBJ old_brush = SelectObject(hdc, brush);
829     FillPath(hdc);
830     DeleteObject(SelectObject(hdc, old_brush));
831 }
832
833 void QWin32PrintEnginePrivate::strokePath_dev(const QPainterPath &path, const QColor &color, qreal penWidth)
834 {
835     composeGdiPath(path);
836     LOGBRUSH brush;
837     brush.lbStyle = BS_SOLID;
838     brush.lbColor = RGB(color.red(), color.green(), color.blue());
839     DWORD capStyle = PS_ENDCAP_SQUARE;
840     DWORD joinStyle = PS_JOIN_BEVEL;
841     if (pen.capStyle() == Qt::FlatCap)
842         capStyle = PS_ENDCAP_FLAT;
843     else if (pen.capStyle() == Qt::RoundCap)
844         capStyle = PS_ENDCAP_ROUND;
845
846     if (pen.joinStyle() == Qt::MiterJoin)
847         joinStyle = PS_JOIN_MITER;
848     else if (pen.joinStyle() == Qt::RoundJoin)
849         joinStyle = PS_JOIN_ROUND;
850
851     HPEN pen = ExtCreatePen(((penWidth == 0) ? PS_COSMETIC : PS_GEOMETRIC)
852                             | PS_SOLID | capStyle | joinStyle,
853                             (penWidth == 0) ? 1 : penWidth, &brush, 0, 0);
854
855     HGDIOBJ old_pen = SelectObject(hdc, pen);
856     StrokePath(hdc);
857     DeleteObject(SelectObject(hdc, old_pen));
858 }
859
860
861 void QWin32PrintEnginePrivate::fillPath(const QPainterPath &path, const QColor &color)
862 {
863     fillPath_dev(path * matrix, color);
864 }
865
866 void QWin32PrintEnginePrivate::strokePath(const QPainterPath &path, const QColor &color)
867 {
868     QPainterPathStroker stroker;
869     if (pen.style() == Qt::CustomDashLine) {
870         stroker.setDashPattern(pen.dashPattern());
871         stroker.setDashOffset(pen.dashOffset());
872     } else {
873         stroker.setDashPattern(pen.style());
874     }
875     stroker.setCapStyle(pen.capStyle());
876     stroker.setJoinStyle(pen.joinStyle());
877     stroker.setMiterLimit(pen.miterLimit());
878
879     QPainterPath stroke;
880     qreal width = pen.widthF();
881     if (pen.style() == Qt::SolidLine && (pen.isCosmetic() || matrix.type() < QTransform::TxScale)) {
882         strokePath_dev(path * matrix, color, width);
883     } else {
884         stroker.setWidth(width);
885         if (pen.isCosmetic()) {
886             stroke = stroker.createStroke(path * matrix);
887         } else {
888             stroke = stroker.createStroke(path) * painterMatrix;
889             QTransform stretch(stretch_x, 0, 0, stretch_y, origin_x, origin_y);
890             stroke = stroke * stretch;
891         }
892
893         if (stroke.isEmpty())
894             return;
895
896         fillPath_dev(stroke, color);
897     }
898 }
899
900
901 void QWin32PrintEngine::drawPath(const QPainterPath &path)
902 {
903 #ifdef QT_DEBUG_DRAW
904     qDebug() << " - QWin32PrintEngine::drawPath(), bounds: " << path.boundingRect();
905 #endif
906
907     Q_D(QWin32PrintEngine);
908
909     QAlphaPaintEngine::drawPath(path);
910     if (!continueCall())
911         return;
912
913     if (d->has_brush)
914         d->fillPath(path, d->brush_color);
915
916     if (d->has_pen)
917         d->strokePath(path, d->pen.color());
918 }
919
920
921 void QWin32PrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
922 {
923 #ifdef QT_DEBUG_DRAW
924     qDebug() << " - QWin32PrintEngine::drawPolygon(), pointCount: " << pointCount;
925 #endif
926
927     QAlphaPaintEngine::drawPolygon(points, pointCount, mode);
928     if (!continueCall())
929         return;
930
931     Q_ASSERT(pointCount > 1);
932
933     QPainterPath path(points[0]);
934
935     for (int i=1; i<pointCount; ++i) {
936         path.lineTo(points[i]);
937     }
938
939     Q_D(QWin32PrintEngine);
940
941     bool has_brush = d->has_brush;
942
943     if (mode == PolylineMode)
944         d->has_brush = false; // No brush for polylines
945     else
946         path.closeSubpath(); // polygons are should always be closed.
947
948     drawPath(path);
949     d->has_brush = has_brush;
950 }
951
952 void QWin32PrintEnginePrivate::queryDefault()
953 {
954     /* Read the default printer name, driver and port with the intuitive function
955      * Strings "windows" and "device" are specified in the MSDN under EnumPrinters()
956      */
957     QString noPrinters(QLatin1String("qt_no_printers"));
958     wchar_t buffer[256];
959     GetProfileString(L"windows", L"device",
960                      reinterpret_cast<const wchar_t *>(noPrinters.utf16()),
961                      buffer, 256);
962     QString output = QString::fromWCharArray(buffer);
963     if (output.isEmpty() || output == noPrinters) // no printers
964         return;
965
966     QStringList info = output.split(QLatin1Char(','));
967     int infoSize = info.size();
968     if (infoSize > 0) {
969         if (name.isEmpty())
970             name = info.at(0);
971         if (program.isEmpty() && infoSize > 1)
972             program = info.at(1);
973         if (port.isEmpty() && infoSize > 2)
974             port = info.at(2);
975     }
976 }
977
978 QWin32PrintEnginePrivate::~QWin32PrintEnginePrivate()
979 {
980     if (hdc)
981         release();
982 }
983
984 void QWin32PrintEnginePrivate::initialize()
985 {
986     if (hdc)
987         release();
988     Q_ASSERT(!hPrinter);
989     Q_ASSERT(!hdc);
990     Q_ASSERT(!devMode);
991     Q_ASSERT(!pInfo);
992
993     if (name.isEmpty())
994         return;
995
996     txop = QTransform::TxNone;
997
998     bool ok = OpenPrinter((LPWSTR)name.utf16(), (LPHANDLE)&hPrinter, 0);
999     if (!ok) {
1000         qErrnoWarning("QWin32PrintEngine::initialize: OpenPrinter failed");
1001         return;
1002     }
1003
1004     // Fetch the PRINTER_INFO_2 with DEVMODE data containing the
1005     // printer settings.
1006     DWORD infoSize, numBytes;
1007     GetPrinter(hPrinter, 2, NULL, 0, &infoSize);
1008     hMem = GlobalAlloc(GHND, infoSize);
1009     pInfo = (PRINTER_INFO_2*) GlobalLock(hMem);
1010     ok = GetPrinter(hPrinter, 2, (LPBYTE)pInfo, infoSize, &numBytes);
1011
1012     if (!ok) {
1013         qErrnoWarning("QWin32PrintEngine::initialize: GetPrinter failed");
1014         GlobalUnlock(pInfo);
1015         GlobalFree(hMem);
1016         ClosePrinter(hPrinter);
1017         pInfo = 0;
1018         hMem = 0;
1019         hPrinter = 0;
1020         return;
1021     }
1022
1023     devMode = pInfo->pDevMode;
1024     hdc = CreateDC(reinterpret_cast<const wchar_t *>(program.utf16()),
1025                    reinterpret_cast<const wchar_t *>(name.utf16()), 0, devMode);
1026
1027     Q_ASSERT(hPrinter);
1028     Q_ASSERT(pInfo);
1029
1030     if (devMode) {
1031         num_copies = devMode->dmCopies;
1032     }
1033
1034     initHDC();
1035
1036 #ifdef QT_DEBUG_DRAW
1037     qDebug() << "QWin32PrintEngine::initialize()" << endl
1038              << " - paperRect" << devPaperRect << endl
1039              << " - pageRect" << devPageRect << endl
1040              << " - stretch_x" << stretch_x << endl
1041              << " - stretch_y" << stretch_y << endl
1042              << " - origin_x" << origin_x << endl
1043              << " - origin_y" << origin_y << endl;
1044 #endif
1045 }
1046
1047 void QWin32PrintEnginePrivate::initHDC()
1048 {
1049     Q_ASSERT(hdc);
1050
1051     HDC display_dc = GetDC(0);
1052     dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
1053     dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
1054     dpi_display = GetDeviceCaps(display_dc, LOGPIXELSY);
1055     ReleaseDC(0, display_dc);
1056     if (dpi_display == 0) {
1057         qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
1058                 "might be a driver problem");
1059         dpi_display = 96; // Reasonable default
1060     }
1061
1062     switch(mode) {
1063     case QPrinter::ScreenResolution:
1064         resolution = dpi_display;
1065         stretch_x = dpi_x / double(dpi_display);
1066         stretch_y = dpi_y / double(dpi_display);
1067         break;
1068     case QPrinter::PrinterResolution:
1069     case QPrinter::HighResolution:
1070         resolution = dpi_y;
1071         stretch_x = 1;
1072         stretch_y = 1;
1073         break;
1074     default:
1075         break;
1076     }
1077
1078     initDevRects();
1079 }
1080
1081 void QWin32PrintEnginePrivate::initDevRects()
1082 {
1083     devPaperRect = QRect(0, 0,
1084                          GetDeviceCaps(hdc, PHYSICALWIDTH),
1085                          GetDeviceCaps(hdc, PHYSICALHEIGHT));
1086     devPhysicalPageRect = QRect(GetDeviceCaps(hdc, PHYSICALOFFSETX),
1087                                 GetDeviceCaps(hdc, PHYSICALOFFSETY),
1088                                 GetDeviceCaps(hdc, HORZRES),
1089                                 GetDeviceCaps(hdc, VERTRES));
1090     if (!pageMarginsSet)
1091         devPageRect = devPhysicalPageRect;
1092     else
1093         devPageRect = devPaperRect.adjusted(qRound(mmToInches(previousDialogMargins.left() / 100.0) * dpi_x),
1094                                             qRound(mmToInches(previousDialogMargins.top() / 100.0) * dpi_y),
1095                                             -qRound(mmToInches(previousDialogMargins.width() / 100.0) * dpi_x),
1096                                             -qRound(mmToInches(previousDialogMargins.height() / 100.0) * dpi_y));
1097     updateOrigin();
1098 }
1099
1100 void QWin32PrintEnginePrivate::setPageMargins(int marginLeft, int marginTop, int marginRight, int marginBottom)
1101 {
1102     pageMarginsSet = true;
1103     previousDialogMargins = QRect(marginLeft, marginTop, marginRight, marginBottom);
1104
1105     devPageRect = devPaperRect.adjusted(qRound(mmToInches(marginLeft / 100.0) * dpi_x),
1106                                         qRound(mmToInches(marginTop / 100.0) * dpi_y),
1107                                         - qRound(mmToInches(marginRight / 100.0) * dpi_x),
1108                                         - qRound(mmToInches(marginBottom / 100.0) * dpi_y));
1109     updateOrigin();
1110 }
1111
1112 QRect QWin32PrintEnginePrivate::getPageMargins() const
1113 {
1114     if (pageMarginsSet)
1115         return previousDialogMargins;
1116     else
1117         return QRect(qRound(inchesToMM(devPhysicalPageRect.left()) * 100.0 / dpi_x),
1118                      qRound(inchesToMM(devPhysicalPageRect.top()) * 100.0 / dpi_y),
1119                      qRound(inchesToMM(devPaperRect.right() - devPhysicalPageRect.right()) * 100.0 / dpi_x),
1120                      qRound(inchesToMM(devPaperRect.bottom() - devPhysicalPageRect.bottom()) * 100.0 / dpi_y));
1121 }
1122
1123 void QWin32PrintEnginePrivate::release()
1124 {
1125     if (hdc == 0)
1126         return;
1127
1128     if (globalDevMode) { // Devmode comes from print dialog
1129         GlobalUnlock(globalDevMode);
1130     } else {            // Devmode comes from initialize...
1131         // devMode is a part of the same memory block as pInfo so one free is enough...
1132         GlobalUnlock(hMem);
1133         GlobalFree(hMem);
1134     }
1135     if (hPrinter)
1136         ClosePrinter(hPrinter);
1137     DeleteDC(hdc);
1138
1139     hdc = 0;
1140     hPrinter = 0;
1141     pInfo = 0;
1142     hMem = 0;
1143     devMode = 0;
1144 }
1145
1146 QList<QVariant> QWin32PrintEnginePrivate::queryResolutions() const
1147 {
1148     // Read the supported resolutions of the printer.
1149     QList<QVariant> list;
1150
1151     DWORD numRes = DeviceCapabilities(reinterpret_cast<const wchar_t *>(name.utf16()),
1152                                       reinterpret_cast<const wchar_t *>(port.utf16()),
1153                                       DC_ENUMRESOLUTIONS, 0, 0);
1154     if (numRes == (DWORD)-1)
1155         return list;
1156
1157     LONG *enumRes = (LONG*)malloc(numRes * 2 * sizeof(LONG));
1158     DWORD errRes = DeviceCapabilities(reinterpret_cast<const wchar_t *>(name.utf16()),
1159                                       reinterpret_cast<const wchar_t *>(port.utf16()),
1160                                       DC_ENUMRESOLUTIONS, (LPWSTR)enumRes, 0);
1161
1162     if (errRes == (DWORD)-1) {
1163         qErrnoWarning("QWin32PrintEngine::queryResolutions: DeviceCapabilities failed");
1164         return list;
1165     }
1166
1167     for (uint i=0; i<numRes; ++i)
1168         list.append(int(enumRes[i * 2]));
1169
1170     return list;
1171 }
1172
1173 void QWin32PrintEnginePrivate::doReinit()
1174 {
1175     if (state == QPrinter::Active) {
1176         reinit = true;
1177     } else {
1178         resetDC();
1179         initDevRects();
1180         reinit = false;
1181     }
1182 }
1183
1184 void QWin32PrintEnginePrivate::updateOrigin()
1185 {
1186     if (fullPage) {
1187         // subtract physical margins to make (0,0) absolute top corner of paper
1188         // then add user defined margins
1189         origin_x = -devPhysicalPageRect.x();
1190         origin_y = -devPhysicalPageRect.y();
1191         if (pageMarginsSet) {
1192             origin_x += devPageRect.left();
1193             origin_y += devPageRect.top();
1194         }
1195     } else {
1196         origin_x = 0;
1197         origin_y = 0;
1198         if (pageMarginsSet) {
1199             origin_x = devPageRect.left() - devPhysicalPageRect.x();
1200             origin_y = devPageRect.top() - devPhysicalPageRect.y();
1201         }
1202     }
1203 }
1204
1205 void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
1206 {
1207     Q_D(QWin32PrintEngine);
1208     switch (key) {
1209     case PPK_CollateCopies:
1210         {
1211             if (!d->devMode)
1212                 break;
1213             d->devMode->dmCollate = value.toBool() ? DMCOLLATE_TRUE : DMCOLLATE_FALSE;
1214             d->doReinit();
1215         }
1216         break;
1217
1218     case PPK_ColorMode:
1219         {
1220             if (!d->devMode)
1221                 break;
1222             d->devMode->dmColor = (value.toInt() == QPrinter::Color) ? DMCOLOR_COLOR : DMCOLOR_MONOCHROME;
1223             d->doReinit();
1224         }
1225         break;
1226
1227     case PPK_Creator:
1228
1229         break;
1230
1231     case PPK_DocumentName:
1232         if (isActive()) {
1233             qWarning("QWin32PrintEngine: Cannot change document name while printing is active");
1234             return;
1235         }
1236         d->docName = value.toString();
1237         break;
1238
1239     case PPK_FullPage:
1240         d->fullPage = value.toBool();
1241         d->updateOrigin();
1242         break;
1243
1244     case PPK_CopyCount: // fallthrough
1245     case PPK_NumberOfCopies:
1246         if (!d->devMode)
1247             break;
1248         d->num_copies = value.toInt();
1249         d->devMode->dmCopies = d->num_copies;
1250         d->doReinit();
1251         break;
1252
1253     case PPK_Orientation:
1254         {
1255             if (!d->devMode)
1256                 break;
1257             int orientation = value.toInt() == QPrinter::Landscape ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
1258             int old_orientation = d->devMode->dmOrientation;
1259             d->devMode->dmOrientation = orientation;
1260             if (d->has_custom_paper_size && old_orientation != orientation)
1261                 d->paper_size = QSizeF(d->paper_size.height(), d->paper_size.width());
1262             d->doReinit();
1263         }
1264         break;
1265
1266     case PPK_OutputFileName:
1267         if (isActive()) {
1268             qWarning("QWin32PrintEngine: Cannot change filename while printing");
1269         } else {
1270             d->fileName = value.toString();
1271             d->printToFile = !value.toString().isEmpty();
1272         }
1273         break;
1274
1275     case PPK_PaperSize:
1276         if (!d->devMode)
1277             break;
1278         d->devMode->dmPaperSize = mapPaperSizeDevmode(QPrinter::PaperSize(value.toInt()));
1279         d->has_custom_paper_size = (QPrinter::PaperSize(value.toInt()) == QPrinter::Custom);
1280         d->doReinit();
1281         break;
1282
1283     case PPK_PaperSource:
1284         {
1285             if (!d->devMode)
1286                 break;
1287             int dmMapped = DMBIN_AUTO;
1288
1289             QList<QVariant> v = property(PPK_PaperSources).toList();
1290             if (v.contains(value))
1291                 dmMapped = mapPaperSourceDevmode(QPrinter::PaperSource(value.toInt()));
1292
1293             d->devMode->dmDefaultSource = dmMapped;
1294             d->doReinit();
1295         }
1296         break;
1297
1298     case PPK_PrinterName:
1299         d->name = value.toString();
1300         if(d->name.isEmpty())
1301             d->queryDefault();
1302         d->initialize();
1303         break;
1304
1305     case PPK_Resolution:
1306         {
1307             d->resolution = value.toInt();
1308
1309             d->stretch_x = d->dpi_x / double(d->resolution);
1310             d->stretch_y = d->dpi_y / double(d->resolution);
1311         }
1312         break;
1313
1314     case PPK_SelectionOption:
1315
1316         break;
1317
1318     case PPK_SupportedResolutions:
1319
1320         break;
1321
1322
1323     case PPK_WindowsPageSize:
1324         if (!d->devMode)
1325             break;
1326         d->has_custom_paper_size = false;
1327         d->devMode->dmPaperSize = value.toInt();
1328         d->doReinit();
1329         break;
1330
1331     case PPK_CustomPaperSize:
1332     {
1333         d->has_custom_paper_size = true;
1334         d->paper_size = value.toSizeF();
1335         if (!d->devMode)
1336             break;
1337         int orientation = d->devMode->dmOrientation;
1338         DWORD needed = 0;
1339         DWORD returned = 0;
1340         if (!EnumForms(d->hPrinter, 1, 0, 0, &needed, &returned)) {
1341             BYTE *forms = (BYTE *) malloc(needed);
1342             if (EnumForms(d->hPrinter, 1, forms, needed, &needed, &returned)) {
1343                 for (DWORD i=0; i< returned; ++i) {
1344                     FORM_INFO_1 *formArray = reinterpret_cast<FORM_INFO_1 *>(forms);
1345                     // the form sizes are specified in 1000th of a mm,
1346                     // convert the size to Points
1347                     QSizeF size((formArray[i].Size.cx * 72/25.4)/1000.0,
1348                                 (formArray[i].Size.cy * 72/25.4)/1000.0);
1349                     if (qAbs(d->paper_size.width() - size.width()) <= 2
1350                         && qAbs(d->paper_size.height() - size.height()) <= 2)
1351                     {
1352                         d->devMode->dmPaperSize = i + 1;
1353                         break;
1354                     }
1355                 }
1356             }
1357             free(forms);
1358         }
1359         if (orientation != DMORIENT_PORTRAIT)
1360             d->paper_size = QSizeF(d->paper_size.height(), d->paper_size.width());
1361         break;
1362     }
1363
1364     case PPK_PageMargins:
1365     {
1366         QList<QVariant> margins(value.toList());
1367         Q_ASSERT(margins.size() == 4);
1368         int left, top, right, bottom;
1369         // specified in 1/100 mm
1370         left = (margins.at(0).toReal()*25.4/72.0) * 100;
1371         top = (margins.at(1).toReal()*25.4/72.0) * 100;
1372         right = (margins.at(2).toReal()*25.4/72.0) * 100;
1373         bottom = (margins.at(3).toReal()*25.4/72.0) * 100;
1374         d->setPageMargins(left, top, right, bottom);
1375         break;
1376     }
1377     default:
1378         // Do nothing
1379         break;
1380     }
1381 }
1382
1383 QVariant QWin32PrintEngine::property(PrintEnginePropertyKey key) const
1384 {
1385     Q_D(const QWin32PrintEngine);
1386     QVariant value;
1387     switch (key) {
1388
1389     case PPK_CollateCopies:
1390         value = false;
1391         break;
1392
1393     case PPK_ColorMode:
1394         {
1395             if (!d->devMode) {
1396                 value = QPrinter::Color;
1397             } else {
1398                 value = (d->devMode->dmColor == DMCOLOR_COLOR) ? QPrinter::Color : QPrinter::GrayScale;
1399             }
1400         }
1401         break;
1402
1403     case PPK_DocumentName:
1404         value = d->docName;
1405         break;
1406
1407     case PPK_FullPage:
1408         value = d->fullPage;
1409         break;
1410
1411     case PPK_CopyCount:
1412         value = d->num_copies;
1413         break;
1414
1415     case PPK_SupportsMultipleCopies:
1416         value = true;
1417         break;
1418
1419     case PPK_NumberOfCopies:
1420         value = 1;
1421         break;
1422
1423     case PPK_Orientation:
1424         {
1425             if (!d->devMode) {
1426                 value = QPrinter::Portrait;
1427             } else {
1428                 value = (d->devMode->dmOrientation == DMORIENT_LANDSCAPE) ? QPrinter::Landscape : QPrinter::Portrait;
1429             }
1430         }
1431         break;
1432
1433     case PPK_OutputFileName:
1434         value = d->fileName;
1435         break;
1436
1437     case PPK_PageRect:
1438         if (d->has_custom_paper_size) {
1439             QRect rect(0, 0,
1440                        qRound(d->paper_size.width() * d->resolution / 72.0),
1441                        qRound(d->paper_size.height() * d->resolution / 72.0));
1442             if (d->pageMarginsSet) {
1443                 rect = rect.adjusted(qRound(mmToInches(d->previousDialogMargins.left()/100.0) * d->resolution),
1444                                      qRound(mmToInches(d->previousDialogMargins.top()/100.0) * d->resolution),
1445                                      -qRound(mmToInches(d->previousDialogMargins.width()/100.0) * d->resolution),
1446                                      -qRound(mmToInches(d->previousDialogMargins.height()/100.0) * d->resolution));
1447             }
1448             value = rect;
1449         } else {
1450             value = QTransform(1/d->stretch_x, 0, 0, 1/d->stretch_y, 0, 0)
1451                     .mapRect(d->fullPage ? d->devPhysicalPageRect : d->devPageRect);
1452         }
1453         break;
1454
1455     case PPK_PaperSize:
1456         if (d->has_custom_paper_size) {
1457             value = QPrinter::Custom;
1458         } else {
1459             if (!d->devMode) {
1460                 value = QPrinter::A4;
1461             } else {
1462                 value = mapDevmodePaperSize(d->devMode->dmPaperSize);
1463             }
1464         }
1465         break;
1466
1467     case PPK_PaperRect:
1468         if (d->has_custom_paper_size) {
1469             value = QRect(0, 0,
1470                           qRound(d->paper_size.width() * d->resolution / 72.0),
1471                           qRound(d->paper_size.height() * d->resolution / 72.0));
1472         } else {
1473             value = QTransform(1/d->stretch_x, 0, 0, 1/d->stretch_y, 0, 0).mapRect(d->devPaperRect);
1474         }
1475         break;
1476
1477     case PPK_PaperSource:
1478         if (!d->devMode) {
1479             value = QPrinter::Auto;
1480         } else {
1481             value = mapDevmodePaperSource(d->devMode->dmDefaultSource);
1482         }
1483         break;
1484
1485     case PPK_PrinterName:
1486         value = d->name;
1487         break;
1488
1489     case PPK_Resolution:
1490         if (d->resolution || !d->name.isEmpty())
1491             value = d->resolution;
1492         break;
1493
1494     case PPK_SupportedResolutions:
1495         value = d->queryResolutions();
1496         break;
1497
1498     case PPK_WindowsPageSize:
1499         if (!d->devMode) {
1500             value = -1;
1501         } else {
1502             value = d->devMode->dmPaperSize;
1503         }
1504         break;
1505
1506     case PPK_PaperSources:
1507         {
1508             int available = DeviceCapabilities((const wchar_t *)d->name.utf16(),
1509                                                (const wchar_t *)d->port.utf16(), DC_BINS, 0, d->devMode);
1510
1511             if (available <= 0)
1512                 break;
1513
1514             wchar_t *data = new wchar_t[available];
1515             int count = DeviceCapabilities((const wchar_t *)d->name.utf16(),
1516                                            (const wchar_t *)d->port.utf16(), DC_BINS, data, d->devMode);
1517
1518             QList<QVariant> out;
1519             for (int i=0; i<count; ++i) {
1520                 QPrinter::PaperSource src = mapDevmodePaperSource(data[i]);
1521                 if (src != -1)
1522                     out << (int) src;
1523             }
1524             value = out;
1525
1526             delete [] data;
1527         }
1528         break;
1529
1530     case PPK_CustomPaperSize:
1531         value = d->paper_size;
1532         break;
1533
1534     case PPK_PageMargins:
1535     {
1536         QList<QVariant> margins;
1537         QRect pageMargins(d->getPageMargins());
1538
1539         // specified in 1/100 mm
1540         margins << (mmToInches(pageMargins.left()/100.0) * 72)
1541                 << (mmToInches(pageMargins.top()/100.0) * 72)
1542                 << (mmToInches(pageMargins.width()/100.0) * 72)
1543                 << (mmToInches(pageMargins.height()/100.0) * 72);
1544         value = margins;
1545         break;
1546     }
1547     default:
1548         // Do nothing
1549         break;
1550     }
1551     return value;
1552 }
1553
1554 QPrinter::PrinterState QWin32PrintEngine::printerState() const
1555 {
1556     return d_func()->state;
1557 }
1558
1559 HDC QWin32PrintEngine::getDC() const
1560 {
1561     return d_func()->hdc;
1562 }
1563
1564 void QWin32PrintEngine::releaseDC(HDC) const
1565 {
1566
1567 }
1568
1569 HGLOBAL *QWin32PrintEnginePrivate::createDevNames()
1570 {
1571     int size = sizeof(DEVNAMES)
1572                + program.length() * 2 + 2
1573                + name.length() * 2 + 2
1574                + port.length() * 2 + 2;
1575     HGLOBAL *hGlobal = (HGLOBAL *) GlobalAlloc(GMEM_MOVEABLE, size);
1576     DEVNAMES *dn = (DEVNAMES*) GlobalLock(hGlobal);
1577
1578     dn->wDriverOffset = sizeof(DEVNAMES) / sizeof(wchar_t);
1579     dn->wDeviceOffset = dn->wDriverOffset + program.length() + 1;
1580     dn->wOutputOffset = dn->wDeviceOffset + name.length() + 1;
1581
1582     memcpy((ushort*)dn + dn->wDriverOffset, program.utf16(), program.length() * 2 + 2);
1583     memcpy((ushort*)dn + dn->wDeviceOffset, name.utf16(), name.length() * 2 + 2);
1584     memcpy((ushort*)dn + dn->wOutputOffset, port.utf16(), port.length() * 2 + 2);
1585     dn->wDefault = 0;
1586
1587     GlobalUnlock(hGlobal);
1588
1589 //         printf("QPrintDialogWinPrivate::createDevNames()\n"
1590 //                " -> wDriverOffset: %d\n"
1591 //                " -> wDeviceOffset: %d\n"
1592 //                " -> wOutputOffset: %d\n",
1593 //                dn->wDriverOffset,
1594 //                dn->wDeviceOffset,
1595 //                dn->wOutputOffset);
1596
1597 //         printf("QPrintDialogWinPrivate::createDevNames(): %s, %s, %s\n",
1598 //                QString::fromWCharArray((wchar_t*)(dn) + dn->wDriverOffset).latin1(),
1599 //                QString::fromWCharArray((wchar_t*)(dn) + dn->wDeviceOffset).latin1(),
1600 //                QString::fromWCharArray((wchar_t*)(dn) + dn->wOutputOffset).latin1());
1601
1602     return hGlobal;
1603 }
1604
1605 void QWin32PrintEnginePrivate::readDevnames(HGLOBAL globalDevnames)
1606 {
1607     if (globalDevnames) {
1608         DEVNAMES *dn = (DEVNAMES*) GlobalLock(globalDevnames);
1609         name = QString::fromWCharArray((wchar_t*)(dn) + dn->wDeviceOffset);
1610         port = QString::fromWCharArray((wchar_t*)(dn) + dn->wOutputOffset);
1611         program = QString::fromWCharArray((wchar_t*)(dn) + dn->wDriverOffset);
1612         GlobalUnlock(globalDevnames);
1613     }
1614 }
1615
1616 void QWin32PrintEnginePrivate::readDevmode(HGLOBAL globalDevmode)
1617 {
1618     if (globalDevmode) {
1619         DEVMODE *dm = (DEVMODE*) GlobalLock(globalDevmode);
1620         release();
1621         globalDevMode = globalDevmode;
1622         devMode = dm;
1623         hdc = CreateDC(reinterpret_cast<const wchar_t *>(program.utf16()),
1624                        reinterpret_cast<const wchar_t *>(name.utf16()), 0, dm);
1625
1626         num_copies = devMode->dmCopies;
1627         if (!OpenPrinter((wchar_t*)name.utf16(), &hPrinter, 0))
1628             qWarning("QPrinter: OpenPrinter() failed after reading DEVMODE.");
1629     }
1630
1631     if (hdc)
1632         initHDC();
1633 }
1634
1635 static void draw_text_item_win(const QPointF &pos, const QTextItemInt &ti, HDC hdc,
1636                                bool convertToText, const QTransform &xform, const QPointF &topLeft)
1637 {
1638     QFontEngine *fe = ti.fontEngine;
1639     QPointF baseline_pos = xform.inverted().map(xform.map(pos) - topLeft);
1640
1641     SetTextAlign(hdc, TA_BASELINE);
1642     SetBkMode(hdc, TRANSPARENT);
1643
1644     bool has_kerning = ti.f && ti.f->kerning();
1645     QFontEngineWin *winfe = (fe->type() == QFontEngine::Win) ? static_cast<QFontEngineWin *>(fe) : 0;
1646
1647     HFONT hfont;
1648     bool ttf = false;
1649
1650     if (winfe) {
1651         hfont = winfe->hfont;
1652         ttf = winfe->ttf;
1653     } else {
1654         hfont = (HFONT)GetStockObject(ANSI_VAR_FONT);
1655     }
1656
1657     HGDIOBJ old_font = SelectObject(hdc, hfont);
1658     unsigned int options = (ttf && !convertToText) ? ETO_GLYPH_INDEX : 0;
1659     wchar_t *convertedGlyphs = (wchar_t *)ti.chars;
1660     QGlyphLayout glyphs = ti.glyphs;
1661
1662     bool fast = !has_kerning && !(ti.flags & QTextItem::RightToLeft);
1663     for (int i = 0; fast && i < glyphs.numGlyphs; i++) {
1664         if (glyphs.offsets[i].x != 0 || glyphs.offsets[i].y != 0 || glyphs.justifications[i].space_18d6 != 0
1665             || glyphs.attributes[i].dontPrint) {
1666             fast = false;
1667             break;
1668         }
1669     }
1670
1671 #if !defined(Q_OS_WINCE)
1672     // Scale, rotate and translate here.
1673     XFORM win_xform;
1674     win_xform.eM11 = xform.m11();
1675     win_xform.eM12 = xform.m12();
1676     win_xform.eM21 = xform.m21();
1677     win_xform.eM22 = xform.m22();
1678     win_xform.eDx = xform.dx();
1679     win_xform.eDy = xform.dy();
1680
1681     SetGraphicsMode(hdc, GM_ADVANCED);
1682     SetWorldTransform(hdc, &win_xform);
1683 #endif
1684
1685     if (fast) {
1686         // fast path
1687         QVarLengthArray<wchar_t> g(glyphs.numGlyphs);
1688         for (int i = 0; i < glyphs.numGlyphs; ++i)
1689             g[i] = glyphs.glyphs[i];
1690         ExtTextOut(hdc,
1691                    qRound(baseline_pos.x() + glyphs.offsets[0].x.toReal()),
1692                    qRound(baseline_pos.y() + glyphs.offsets[0].y.toReal()),
1693                    options, 0, convertToText ? convertedGlyphs : g.data(), glyphs.numGlyphs, 0);
1694     } else {
1695         QVarLengthArray<QFixedPoint> positions;
1696         QVarLengthArray<glyph_t> _glyphs;
1697
1698         QTransform matrix = QTransform::fromTranslate(baseline_pos.x(), baseline_pos.y());
1699         ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags,
1700             _glyphs, positions);
1701         if (_glyphs.size() == 0) {
1702             SelectObject(hdc, old_font);
1703             return;
1704         }
1705
1706         convertToText = convertToText && glyphs.numGlyphs == _glyphs.size();
1707         bool outputEntireItem = _glyphs.size() > 0;
1708
1709         if (outputEntireItem) {
1710             options |= ETO_PDY;
1711             QVarLengthArray<INT> glyphDistances(_glyphs.size() * 2);
1712             QVarLengthArray<wchar_t> g(_glyphs.size());
1713             for (int i=0; i<_glyphs.size() - 1; ++i) {
1714                 glyphDistances[i * 2] = qRound(positions[i + 1].x) - qRound(positions[i].x);
1715                 glyphDistances[i * 2 + 1] = qRound(positions[i + 1].y) - qRound(positions[i].y);
1716                 g[i] = _glyphs[i];
1717             }
1718             glyphDistances[(_glyphs.size() - 1) * 2] = 0;
1719             glyphDistances[(_glyphs.size() - 1) * 2 + 1] = 0;
1720             g[_glyphs.size() - 1] = _glyphs[_glyphs.size() - 1];
1721             ExtTextOut(hdc, qRound(positions[0].x), qRound(positions[0].y), options, 0,
1722                        convertToText ? convertedGlyphs : g.data(), _glyphs.size(),
1723                        glyphDistances.data());
1724         } else {
1725             int i = 0;
1726             while(i < _glyphs.size()) {
1727                 wchar_t g = _glyphs[i];
1728
1729                 ExtTextOut(hdc, qRound(positions[i].x),
1730                            qRound(positions[i].y), options, 0,
1731                            convertToText ? convertedGlyphs + i : &g, 1, 0);
1732                 ++i;
1733             }
1734         }
1735     }
1736
1737 #if !defined(Q_OS_WINCE)
1738         win_xform.eM11 = win_xform.eM22 = 1.0;
1739         win_xform.eM12 = win_xform.eM21 = win_xform.eDx = win_xform.eDy = 0.0;
1740         SetWorldTransform(hdc, &win_xform);
1741 #endif
1742
1743     SelectObject(hdc, old_font);
1744 }
1745
1746
1747 void QWin32PrintEnginePrivate::updateCustomPaperSize()
1748 {
1749     uint paperSize = devMode->dmPaperSize;
1750     if (paperSize > 0 && mapDevmodePaperSize(paperSize) == QPrinter::Custom) {
1751         has_custom_paper_size = true;
1752         DWORD needed = 0;
1753         DWORD returned = 0;
1754         if (!EnumForms(hPrinter, 1, 0, 0, &needed, &returned)) {
1755             BYTE *forms = (BYTE *) malloc(needed);
1756             if (EnumForms(hPrinter, 1, forms, needed, &needed, &returned)) {
1757                 if (paperSize <= returned) {
1758                     FORM_INFO_1 *formArray = (FORM_INFO_1 *) forms;
1759                     int width = formArray[paperSize - 1].Size.cx; // 1/1000 of a mm
1760                     int height = formArray[paperSize - 1].Size.cy; // 1/1000 of a mm
1761                     paper_size = QSizeF((width * 72 /25.4) / 1000.0, (height * 72 / 25.4) / 1000.0);
1762                 } else {
1763                     has_custom_paper_size = false;
1764                 }
1765             }
1766             free(forms);
1767         }
1768     } else {
1769         has_custom_paper_size = false;
1770     }
1771 }
1772
1773 QT_END_NAMESPACE
1774
1775 #endif // QT_NO_PRINTER