source: trunk/src/gui/painting/qprintengine_win.cpp@ 786

Last change on this file since 786 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

File size: 55.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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
59QT_BEGIN_NAMESPACE
60
61extern QPainterPath qt_regionToPath(const QRegion &region);
62
63// #define QT_DEBUG_DRAW
64
65static void draw_text_item_win(const QPointF &_pos, const QTextItemInt &ti, HDC hdc,
66 bool convertToText, const QTransform &xform, const QPointF &topLeft);
67
68static 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
110QPrinter::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
118static 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
126static 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
146static 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
154static 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
162QWin32PrintEngine::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
178bool 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
241bool 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
267bool 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
328bool QWin32PrintEngine::abort()
329{
330 // do nothing loop.
331 return false;
332}
333
334void 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 bool convertToText = true;
372 for (int i=0; i < ti.num_chars; ++i) {
373 if (ti.chars[i].unicode() >= 0x80) {
374 convertToText = false;
375 break;
376 }
377
378 if (ti.logClusters[i] != i) {
379 convertToText = false;
380 break;
381 }
382 }
383
384 COLORREF cf = RGB(qRed(brushColor), qGreen(brushColor), qBlue(brushColor));
385 SelectObject(d->hdc, CreateSolidBrush(cf));
386 SelectObject(d->hdc, CreatePen(PS_SOLID, 1, cf));
387 SetTextColor(d->hdc, cf);
388
389 draw_text_item_win(p, ti, d->hdc, convertToText, d->matrix, d->devPaperRect.topLeft());
390 DeleteObject(SelectObject(d->hdc,GetStockObject(HOLLOW_BRUSH)));
391 DeleteObject(SelectObject(d->hdc,GetStockObject(BLACK_PEN)));
392}
393
394static inline qreal mmToInches(double mm)
395{
396 return mm*0.039370147;
397}
398
399static inline qreal inchesToMM(double in)
400{
401 return in/0.039370147;
402}
403
404int QWin32PrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const
405{
406 Q_D(const QWin32PrintEngine);
407
408 if (!d->hdc)
409 return 0;
410
411 int val;
412 int res = d->resolution;
413
414 switch (m) {
415 case QPaintDevice::PdmWidth:
416 if (d->has_custom_paper_size) {
417 val = qRound(d->paper_size.width() * res / 72.0);
418 } else {
419 int logPixelsX = GetDeviceCaps(d->hdc, LOGPIXELSX);
420 if (logPixelsX == 0) {
421 qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
422 "might be a driver problem");
423 logPixelsX = 600; // Reasonable default
424 }
425 val = res
426 * GetDeviceCaps(d->hdc, d->fullPage ? PHYSICALWIDTH : HORZRES)
427 / logPixelsX;
428 }
429 if (d->pageMarginsSet)
430 val -= int(mmToInches((d->previousDialogMargins.left() +
431 d->previousDialogMargins.width()) / 100.0) * res);
432 break;
433 case QPaintDevice::PdmHeight:
434 if (d->has_custom_paper_size) {
435 val = qRound(d->paper_size.height() * res / 72.0);
436 } else {
437 int logPixelsY = GetDeviceCaps(d->hdc, LOGPIXELSY);
438 if (logPixelsY == 0) {
439 qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
440 "might be a driver problem");
441 logPixelsY = 600; // Reasonable default
442 }
443 val = res
444 * GetDeviceCaps(d->hdc, d->fullPage ? PHYSICALHEIGHT : VERTRES)
445 / logPixelsY;
446 }
447 if (d->pageMarginsSet)
448 val -= int(mmToInches((d->previousDialogMargins.top() +
449 d->previousDialogMargins.height()) / 100.0) * res);
450 break;
451 case QPaintDevice::PdmDpiX:
452 val = res;
453 break;
454 case QPaintDevice::PdmDpiY:
455 val = res;
456 break;
457 case QPaintDevice::PdmPhysicalDpiX:
458 val = GetDeviceCaps(d->hdc, LOGPIXELSX);
459 break;
460 case QPaintDevice::PdmPhysicalDpiY:
461 val = GetDeviceCaps(d->hdc, LOGPIXELSY);
462 break;
463 case QPaintDevice::PdmWidthMM:
464 if (d->has_custom_paper_size) {
465 val = qRound(d->paper_size.width()*25.4/72);
466 } else {
467 if (!d->fullPage) {
468 val = GetDeviceCaps(d->hdc, HORZSIZE);
469 } else {
470 float wi = 25.4 * GetDeviceCaps(d->hdc, PHYSICALWIDTH);
471 int logPixelsX = GetDeviceCaps(d->hdc, LOGPIXELSX);
472 if (logPixelsX == 0) {
473 qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
474 "might be a driver problem");
475 logPixelsX = 600; // Reasonable default
476 }
477 val = qRound(wi / logPixelsX);
478 }
479 }
480 if (d->pageMarginsSet)
481 val -= (d->previousDialogMargins.left() +
482 d->previousDialogMargins.width()) / 100.0;
483 break;
484 case QPaintDevice::PdmHeightMM:
485 if (d->has_custom_paper_size) {
486 val = qRound(d->paper_size.height()*25.4/72);
487 } else {
488 if (!d->fullPage) {
489 val = GetDeviceCaps(d->hdc, VERTSIZE);
490 } else {
491 float hi = 25.4 * GetDeviceCaps(d->hdc, PHYSICALHEIGHT);
492 int logPixelsY = GetDeviceCaps(d->hdc, LOGPIXELSY);
493 if (logPixelsY == 0) {
494 qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
495 "might be a driver problem");
496 logPixelsY = 600; // Reasonable default
497 }
498 val = qRound(hi / logPixelsY);
499 }
500 }
501 if (d->pageMarginsSet)
502 val -= (d->previousDialogMargins.top() +
503 d->previousDialogMargins.height()) / 100.0;
504 break;
505 case QPaintDevice::PdmNumColors:
506 {
507 int bpp = GetDeviceCaps(d->hdc, BITSPIXEL);
508 if(bpp==32)
509 val = INT_MAX;
510 else if(bpp<=8)
511 val = GetDeviceCaps(d->hdc, NUMCOLORS);
512 else
513 val = 1 << (bpp * GetDeviceCaps(d->hdc, PLANES));
514 }
515 break;
516 case QPaintDevice::PdmDepth:
517 val = GetDeviceCaps(d->hdc, PLANES);
518 break;
519 default:
520 qWarning("QPrinter::metric: Invalid metric command");
521 return 0;
522 }
523 return val;
524}
525
526void QWin32PrintEngine::updateState(const QPaintEngineState &state)
527{
528 Q_D(QWin32PrintEngine);
529
530 QAlphaPaintEngine::updateState(state);
531 if (!continueCall())
532 return;
533
534 if (state.state() & DirtyTransform) {
535 updateMatrix(state.transform());
536 }
537
538 if (state.state() & DirtyPen) {
539 d->pen = state.pen();
540 d->has_pen = d->pen.style() != Qt::NoPen && d->pen.isSolid();
541 }
542
543 if (state.state() & DirtyBrush) {
544 QBrush brush = state.brush();
545 d->has_brush = brush.style() == Qt::SolidPattern;
546 d->brush_color = brush.color();
547 }
548
549 if (state.state() & DirtyClipEnabled) {
550 if (state.isClipEnabled())
551 updateClipPath(painter()->clipPath(), Qt::ReplaceClip);
552 else
553 updateClipPath(QPainterPath(), Qt::NoClip);
554 }
555
556 if (state.state() & DirtyClipPath) {
557 updateClipPath(state.clipPath(), state.clipOperation());
558 }
559
560 if (state.state() & DirtyClipRegion) {
561 QRegion clipRegion = state.clipRegion();
562 QPainterPath clipPath = qt_regionToPath(clipRegion);
563 updateClipPath(clipPath, state.clipOperation());
564 }
565}
566
567void QWin32PrintEngine::updateClipPath(const QPainterPath &clipPath, Qt::ClipOperation op)
568{
569 Q_D(QWin32PrintEngine);
570
571 bool doclip = true;
572 if (op == Qt::NoClip) {
573 SelectClipRgn(d->hdc, 0);
574 doclip = false;
575 }
576
577 if (doclip) {
578 QPainterPath xformed = clipPath * d->matrix;
579
580 if (xformed.isEmpty()) {
581 QRegion empty(-0x1000000, -0x1000000, 1, 1);
582 SelectClipRgn(d->hdc, empty.handle());
583 } else {
584 d->composeGdiPath(xformed);
585 const int ops[] = {
586 -1, // Qt::NoClip, covered above
587 RGN_COPY, // Qt::ReplaceClip
588 RGN_AND, // Qt::IntersectClip
589 RGN_OR // Qt::UniteClip
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
604void 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
615void 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
712void 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
771void 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
819void 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
833void 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, &brush, 0, 0);
854
855 HGDIOBJ old_pen = SelectObject(hdc, pen);
856 StrokePath(hdc);
857 DeleteObject(SelectObject(hdc, old_pen));
858}
859
860
861void QWin32PrintEnginePrivate::fillPath(const QPainterPath &path, const QColor &color)
862{
863 fillPath_dev(path * matrix, color);
864}
865
866void 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
901void 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
921void 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
952void 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
978QWin32PrintEnginePrivate::~QWin32PrintEnginePrivate()
979{
980 if (hdc)
981 release();
982}
983
984void 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
1047void 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
1081void 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
1100void 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
1112QRect 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
1123void 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
1146QList<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
1173void QWin32PrintEnginePrivate::doReinit()
1174{
1175 if (state == QPrinter::Active) {
1176 reinit = true;
1177 } else {
1178 resetDC();
1179 initDevRects();
1180 reinit = false;
1181 }
1182}
1183
1184void 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
1205void 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_NumberOfCopies:
1245 if (!d->devMode)
1246 break;
1247 d->num_copies = value.toInt();
1248 d->devMode->dmCopies = d->num_copies;
1249 d->doReinit();
1250 break;
1251
1252 case PPK_Orientation:
1253 {
1254 if (!d->devMode)
1255 break;
1256 int orientation = value.toInt() == QPrinter::Landscape ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
1257 int old_orientation = d->devMode->dmOrientation;
1258 d->devMode->dmOrientation = orientation;
1259 if (d->has_custom_paper_size && old_orientation != orientation)
1260 d->paper_size = QSizeF(d->paper_size.height(), d->paper_size.width());
1261 d->doReinit();
1262 }
1263 break;
1264
1265 case PPK_OutputFileName:
1266 if (isActive()) {
1267 qWarning("QWin32PrintEngine: Cannot change filename while printing");
1268 } else {
1269 d->fileName = value.toString();
1270 d->printToFile = !value.toString().isEmpty();
1271 }
1272 break;
1273
1274 case PPK_PaperSize:
1275 if (!d->devMode)
1276 break;
1277 d->devMode->dmPaperSize = mapPaperSizeDevmode(QPrinter::PaperSize(value.toInt()));
1278 d->has_custom_paper_size = (QPrinter::PaperSize(value.toInt()) == QPrinter::Custom);
1279 d->doReinit();
1280 break;
1281
1282 case PPK_PaperSource:
1283 {
1284 if (!d->devMode)
1285 break;
1286 int dmMapped = DMBIN_AUTO;
1287
1288 QList<QVariant> v = property(PPK_PaperSources).toList();
1289 if (v.contains(value))
1290 dmMapped = mapPaperSourceDevmode(QPrinter::PaperSource(value.toInt()));
1291
1292 d->devMode->dmDefaultSource = dmMapped;
1293 d->doReinit();
1294 }
1295 break;
1296
1297 case PPK_PrinterName:
1298 d->name = value.toString();
1299 if(d->name.isEmpty())
1300 d->queryDefault();
1301 d->initialize();
1302 break;
1303
1304 case PPK_Resolution:
1305 {
1306 d->resolution = value.toInt();
1307
1308 d->stretch_x = d->dpi_x / double(d->resolution);
1309 d->stretch_y = d->dpi_y / double(d->resolution);
1310 }
1311 break;
1312
1313 case PPK_SelectionOption:
1314
1315 break;
1316
1317 case PPK_SupportedResolutions:
1318
1319 break;
1320
1321
1322 case PPK_WindowsPageSize:
1323 if (!d->devMode)
1324 break;
1325 d->has_custom_paper_size = false;
1326 d->devMode->dmPaperSize = value.toInt();
1327 d->doReinit();
1328 break;
1329
1330 case PPK_CustomPaperSize:
1331 {
1332 d->has_custom_paper_size = true;
1333 d->paper_size = value.toSizeF();
1334 if (!d->devMode)
1335 break;
1336 int orientation = d->devMode->dmOrientation;
1337 DWORD needed = 0;
1338 DWORD returned = 0;
1339 if (!EnumForms(d->hPrinter, 1, 0, 0, &needed, &returned)) {
1340 BYTE *forms = (BYTE *) malloc(needed);
1341 if (EnumForms(d->hPrinter, 1, forms, needed, &needed, &returned)) {
1342 for (DWORD i=0; i< returned; ++i) {
1343 FORM_INFO_1 *formArray = reinterpret_cast<FORM_INFO_1 *>(forms);
1344 // the form sizes are specified in 1000th of a mm,
1345 // convert the size to Points
1346 QSizeF size((formArray[i].Size.cx * 72/25.4)/1000.0,
1347 (formArray[i].Size.cy * 72/25.4)/1000.0);
1348 if (qAbs(d->paper_size.width() - size.width()) <= 2
1349 && qAbs(d->paper_size.height() - size.height()) <= 2)
1350 {
1351 d->devMode->dmPaperSize = i + 1;
1352 break;
1353 }
1354 }
1355 }
1356 free(forms);
1357 }
1358 if (orientation != DMORIENT_PORTRAIT)
1359 d->paper_size = QSizeF(d->paper_size.height(), d->paper_size.width());
1360 break;
1361 }
1362
1363 case PPK_PageMargins:
1364 {
1365 QList<QVariant> margins(value.toList());
1366 Q_ASSERT(margins.size() == 4);
1367 int left, top, right, bottom;
1368 // specified in 1/100 mm
1369 left = (margins.at(0).toReal()*25.4/72.0) * 100;
1370 top = (margins.at(1).toReal()*25.4/72.0) * 100;
1371 right = (margins.at(2).toReal()*25.4/72.0) * 100;
1372 bottom = (margins.at(3).toReal()*25.4/72.0) * 100;
1373 d->setPageMargins(left, top, right, bottom);
1374 break;
1375 }
1376 default:
1377 // Do nothing
1378 break;
1379 }
1380}
1381
1382QVariant QWin32PrintEngine::property(PrintEnginePropertyKey key) const
1383{
1384 Q_D(const QWin32PrintEngine);
1385 QVariant value;
1386 switch (key) {
1387
1388 case PPK_CollateCopies:
1389 value = false;
1390 break;
1391
1392 case PPK_ColorMode:
1393 {
1394 if (!d->devMode) {
1395 value = QPrinter::Color;
1396 } else {
1397 value = (d->devMode->dmColor == DMCOLOR_COLOR) ? QPrinter::Color : QPrinter::GrayScale;
1398 }
1399 }
1400 break;
1401
1402 case PPK_DocumentName:
1403 value = d->docName;
1404 break;
1405
1406 case PPK_FullPage:
1407 value = d->fullPage;
1408 break;
1409
1410 case PPK_NumberOfCopies:
1411 value = 1;
1412 break;
1413
1414 case PPK_Orientation:
1415 {
1416 if (!d->devMode) {
1417 value = QPrinter::Portrait;
1418 } else {
1419 value = (d->devMode->dmOrientation == DMORIENT_LANDSCAPE) ? QPrinter::Landscape : QPrinter::Portrait;
1420 }
1421 }
1422 break;
1423
1424 case PPK_OutputFileName:
1425 value = d->fileName;
1426 break;
1427
1428 case PPK_PageRect:
1429 if (d->has_custom_paper_size) {
1430 QRect rect(0, 0,
1431 qRound(d->paper_size.width() * d->resolution / 72.0),
1432 qRound(d->paper_size.height() * d->resolution / 72.0));
1433 if (d->pageMarginsSet) {
1434 rect = rect.adjusted(qRound(mmToInches(d->previousDialogMargins.left()/100.0) * d->resolution),
1435 qRound(mmToInches(d->previousDialogMargins.top()/100.0) * d->resolution),
1436 -qRound(mmToInches(d->previousDialogMargins.width()/100.0) * d->resolution),
1437 -qRound(mmToInches(d->previousDialogMargins.height()/100.0) * d->resolution));
1438 }
1439 value = rect;
1440 } else {
1441 value = QTransform(1/d->stretch_x, 0, 0, 1/d->stretch_y, 0, 0)
1442 .mapRect(d->fullPage ? d->devPhysicalPageRect : d->devPageRect);
1443 }
1444 break;
1445
1446 case PPK_PaperSize:
1447 if (d->has_custom_paper_size) {
1448 value = QPrinter::Custom;
1449 } else {
1450 if (!d->devMode) {
1451 value = QPrinter::A4;
1452 } else {
1453 value = mapDevmodePaperSize(d->devMode->dmPaperSize);
1454 }
1455 }
1456 break;
1457
1458 case PPK_PaperRect:
1459 if (d->has_custom_paper_size) {
1460 value = QRect(0, 0,
1461 qRound(d->paper_size.width() * d->resolution / 72.0),
1462 qRound(d->paper_size.height() * d->resolution / 72.0));
1463 } else {
1464 value = QTransform(1/d->stretch_x, 0, 0, 1/d->stretch_y, 0, 0).mapRect(d->devPaperRect);
1465 }
1466 break;
1467
1468 case PPK_PaperSource:
1469 if (!d->devMode) {
1470 value = QPrinter::Auto;
1471 } else {
1472 value = mapDevmodePaperSource(d->devMode->dmDefaultSource);
1473 }
1474 break;
1475
1476 case PPK_PrinterName:
1477 value = d->name;
1478 break;
1479
1480 case PPK_Resolution:
1481 if (d->resolution || !d->name.isEmpty())
1482 value = d->resolution;
1483 break;
1484
1485 case PPK_SupportedResolutions:
1486 value = d->queryResolutions();
1487 break;
1488
1489 case PPK_WindowsPageSize:
1490 if (!d->devMode) {
1491 value = -1;
1492 } else {
1493 value = d->devMode->dmPaperSize;
1494 }
1495 break;
1496
1497 case PPK_PaperSources:
1498 {
1499 int available = DeviceCapabilities((const wchar_t *)d->name.utf16(),
1500 (const wchar_t *)d->port.utf16(), DC_BINS, 0, d->devMode);
1501
1502 if (available <= 0)
1503 break;
1504
1505 wchar_t *data = new wchar_t[available];
1506 int count = DeviceCapabilities((const wchar_t *)d->name.utf16(),
1507 (const wchar_t *)d->port.utf16(), DC_BINS, data, d->devMode);
1508
1509 QList<QVariant> out;
1510 for (int i=0; i<count; ++i) {
1511 QPrinter::PaperSource src = mapDevmodePaperSource(data[i]);
1512 if (src != -1)
1513 out << (int) src;
1514 }
1515 value = out;
1516
1517 delete [] data;
1518 }
1519 break;
1520
1521 case PPK_CustomPaperSize:
1522 value = d->paper_size;
1523 break;
1524
1525 case PPK_PageMargins:
1526 {
1527 QList<QVariant> margins;
1528 QRect pageMargins(d->getPageMargins());
1529
1530 // specified in 1/100 mm
1531 margins << (mmToInches(pageMargins.left()/100.0) * 72)
1532 << (mmToInches(pageMargins.top()/100.0) * 72)
1533 << (mmToInches(pageMargins.width()/100.0) * 72)
1534 << (mmToInches(pageMargins.height()/100.0) * 72);
1535 value = margins;
1536 break;
1537 }
1538 default:
1539 // Do nothing
1540 break;
1541 }
1542 return value;
1543}
1544
1545QPrinter::PrinterState QWin32PrintEngine::printerState() const
1546{
1547 return d_func()->state;
1548}
1549
1550HDC QWin32PrintEngine::getDC() const
1551{
1552 return d_func()->hdc;
1553}
1554
1555void QWin32PrintEngine::releaseDC(HDC) const
1556{
1557
1558}
1559
1560HGLOBAL *QWin32PrintEnginePrivate::createDevNames()
1561{
1562 int size = sizeof(DEVNAMES)
1563 + program.length() * 2 + 2
1564 + name.length() * 2 + 2
1565 + port.length() * 2 + 2;
1566 HGLOBAL *hGlobal = (HGLOBAL *) GlobalAlloc(GMEM_MOVEABLE, size);
1567 DEVNAMES *dn = (DEVNAMES*) GlobalLock(hGlobal);
1568
1569 dn->wDriverOffset = sizeof(DEVNAMES) / sizeof(wchar_t);
1570 dn->wDeviceOffset = dn->wDriverOffset + program.length() + 1;
1571 dn->wOutputOffset = dn->wDeviceOffset + name.length() + 1;
1572
1573 memcpy((ushort*)dn + dn->wDriverOffset, program.utf16(), program.length() * 2 + 2);
1574 memcpy((ushort*)dn + dn->wDeviceOffset, name.utf16(), name.length() * 2 + 2);
1575 memcpy((ushort*)dn + dn->wOutputOffset, port.utf16(), port.length() * 2 + 2);
1576 dn->wDefault = 0;
1577
1578 GlobalUnlock(hGlobal);
1579
1580// printf("QPrintDialogWinPrivate::createDevNames()\n"
1581// " -> wDriverOffset: %d\n"
1582// " -> wDeviceOffset: %d\n"
1583// " -> wOutputOffset: %d\n",
1584// dn->wDriverOffset,
1585// dn->wDeviceOffset,
1586// dn->wOutputOffset);
1587
1588// printf("QPrintDialogWinPrivate::createDevNames(): %s, %s, %s\n",
1589// QString::fromWCharArray((wchar_t*)(dn) + dn->wDriverOffset).latin1(),
1590// QString::fromWCharArray((wchar_t*)(dn) + dn->wDeviceOffset).latin1(),
1591// QString::fromWCharArray((wchar_t*)(dn) + dn->wOutputOffset).latin1());
1592
1593 return hGlobal;
1594}
1595
1596void QWin32PrintEnginePrivate::readDevnames(HGLOBAL globalDevnames)
1597{
1598 if (globalDevnames) {
1599 DEVNAMES *dn = (DEVNAMES*) GlobalLock(globalDevnames);
1600 name = QString::fromWCharArray((wchar_t*)(dn) + dn->wDeviceOffset);
1601 port = QString::fromWCharArray((wchar_t*)(dn) + dn->wOutputOffset);
1602 program = QString::fromWCharArray((wchar_t*)(dn) + dn->wDriverOffset);
1603 GlobalUnlock(globalDevnames);
1604 }
1605}
1606
1607void QWin32PrintEnginePrivate::readDevmode(HGLOBAL globalDevmode)
1608{
1609 if (globalDevmode) {
1610 DEVMODE *dm = (DEVMODE*) GlobalLock(globalDevmode);
1611 release();
1612 globalDevMode = globalDevmode;
1613 devMode = dm;
1614 hdc = CreateDC(reinterpret_cast<const wchar_t *>(program.utf16()),
1615 reinterpret_cast<const wchar_t *>(name.utf16()), 0, dm);
1616
1617 num_copies = devMode->dmCopies;
1618 if (!OpenPrinter((wchar_t*)name.utf16(), &hPrinter, 0))
1619 qWarning("QPrinter: OpenPrinter() failed after reading DEVMODE.");
1620 }
1621
1622 if (hdc)
1623 initHDC();
1624}
1625
1626static void draw_text_item_win(const QPointF &pos, const QTextItemInt &ti, HDC hdc,
1627 bool convertToText, const QTransform &xform, const QPointF &topLeft)
1628{
1629 QFontEngine *fe = ti.fontEngine;
1630 QPointF baseline_pos = xform.inverted().map(xform.map(pos) - topLeft);
1631
1632 SetTextAlign(hdc, TA_BASELINE);
1633 SetBkMode(hdc, TRANSPARENT);
1634
1635 bool has_kerning = ti.f && ti.f->kerning();
1636 QFontEngineWin *winfe = (fe->type() == QFontEngine::Win) ? static_cast<QFontEngineWin *>(fe) : 0;
1637
1638 HFONT hfont;
1639 bool ttf = false;
1640
1641 if (winfe) {
1642 hfont = winfe->hfont;
1643 ttf = winfe->ttf;
1644 } else {
1645 hfont = (HFONT)GetStockObject(ANSI_VAR_FONT);
1646 }
1647
1648 HGDIOBJ old_font = SelectObject(hdc, hfont);
1649 unsigned int options = (ttf && !convertToText) ? ETO_GLYPH_INDEX : 0;
1650 wchar_t *convertedGlyphs = (wchar_t *)ti.chars;
1651 QGlyphLayout glyphs = ti.glyphs;
1652
1653 bool fast = !has_kerning && !(ti.flags & QTextItem::RightToLeft);
1654 for (int i = 0; fast && i < glyphs.numGlyphs; i++) {
1655 if (glyphs.offsets[i].x != 0 || glyphs.offsets[i].y != 0 || glyphs.justifications[i].space_18d6 != 0
1656 || glyphs.attributes[i].dontPrint) {
1657 fast = false;
1658 break;
1659 }
1660 }
1661
1662#if !defined(Q_OS_WINCE)
1663 // Scale, rotate and translate here.
1664 XFORM win_xform;
1665 win_xform.eM11 = xform.m11();
1666 win_xform.eM12 = xform.m12();
1667 win_xform.eM21 = xform.m21();
1668 win_xform.eM22 = xform.m22();
1669 win_xform.eDx = xform.dx();
1670 win_xform.eDy = xform.dy();
1671
1672 SetGraphicsMode(hdc, GM_ADVANCED);
1673 SetWorldTransform(hdc, &win_xform);
1674#endif
1675
1676 if (fast) {
1677 // fast path
1678 QVarLengthArray<wchar_t> g(glyphs.numGlyphs);
1679 for (int i = 0; i < glyphs.numGlyphs; ++i)
1680 g[i] = glyphs.glyphs[i];
1681 ExtTextOut(hdc,
1682 qRound(baseline_pos.x() + glyphs.offsets[0].x.toReal()),
1683 qRound(baseline_pos.y() + glyphs.offsets[0].y.toReal()),
1684 options, 0, convertToText ? convertedGlyphs : g.data(), glyphs.numGlyphs, 0);
1685 } else {
1686 QVarLengthArray<QFixedPoint> positions;
1687 QVarLengthArray<glyph_t> _glyphs;
1688
1689 QTransform matrix = QTransform::fromTranslate(baseline_pos.x(), baseline_pos.y());
1690 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags,
1691 _glyphs, positions);
1692 if (_glyphs.size() == 0) {
1693 SelectObject(hdc, old_font);
1694 return;
1695 }
1696
1697 convertToText = convertToText && glyphs.numGlyphs == _glyphs.size();
1698 bool outputEntireItem = _glyphs.size() > 0;
1699
1700 if (outputEntireItem) {
1701 options |= ETO_PDY;
1702 QVarLengthArray<INT> glyphDistances(_glyphs.size() * 2);
1703 QVarLengthArray<wchar_t> g(_glyphs.size());
1704 for (int i=0; i<_glyphs.size() - 1; ++i) {
1705 glyphDistances[i * 2] = qRound(positions[i + 1].x) - qRound(positions[i].x);
1706 glyphDistances[i * 2 + 1] = qRound(positions[i + 1].y) - qRound(positions[i].y);
1707 g[i] = _glyphs[i];
1708 }
1709 glyphDistances[(_glyphs.size() - 1) * 2] = 0;
1710 glyphDistances[(_glyphs.size() - 1) * 2 + 1] = 0;
1711 g[_glyphs.size() - 1] = _glyphs[_glyphs.size() - 1];
1712 ExtTextOut(hdc, qRound(positions[0].x), qRound(positions[0].y), options, 0,
1713 convertToText ? convertedGlyphs : g.data(), _glyphs.size(),
1714 glyphDistances.data());
1715 } else {
1716 int i = 0;
1717 while(i < _glyphs.size()) {
1718 wchar_t g = _glyphs[i];
1719
1720 ExtTextOut(hdc, qRound(positions[i].x),
1721 qRound(positions[i].y), options, 0,
1722 convertToText ? convertedGlyphs + i : &g, 1, 0);
1723 ++i;
1724 }
1725 }
1726 }
1727
1728#if !defined(Q_OS_WINCE)
1729 win_xform.eM11 = win_xform.eM22 = 1.0;
1730 win_xform.eM12 = win_xform.eM21 = win_xform.eDx = win_xform.eDy = 0.0;
1731 SetWorldTransform(hdc, &win_xform);
1732#endif
1733
1734 SelectObject(hdc, old_font);
1735}
1736
1737
1738void QWin32PrintEnginePrivate::updateCustomPaperSize()
1739{
1740 uint paperSize = devMode->dmPaperSize;
1741 if (paperSize > 0 && mapDevmodePaperSize(paperSize) == QPrinter::Custom) {
1742 has_custom_paper_size = true;
1743 DWORD needed = 0;
1744 DWORD returned = 0;
1745 if (!EnumForms(hPrinter, 1, 0, 0, &needed, &returned)) {
1746 BYTE *forms = (BYTE *) malloc(needed);
1747 if (EnumForms(hPrinter, 1, forms, needed, &needed, &returned)) {
1748 if (paperSize <= returned) {
1749 FORM_INFO_1 *formArray = (FORM_INFO_1 *) forms;
1750 int width = formArray[paperSize - 1].Size.cx; // 1/1000 of a mm
1751 int height = formArray[paperSize - 1].Size.cy; // 1/1000 of a mm
1752 paper_size = QSizeF((width * 72 /25.4) / 1000.0, (height * 72 / 25.4) / 1000.0);
1753 } else {
1754 has_custom_paper_size = false;
1755 }
1756 }
1757 free(forms);
1758 }
1759 } else {
1760 has_custom_paper_size = false;
1761 }
1762}
1763
1764QT_END_NAMESPACE
1765
1766#endif // QT_NO_PRINTER
Note: See TracBrowser for help on using the repository browser.