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