1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
41 #include "qplatformdefs.h"
45 #include <qtemporaryfile.h>
46 #include <private/qmath_p.h>
47 #include "private/qcups_p.h"
48 #include "qprinterinfo.h"
50 #include "private/qfont_p.h"
53 #include "private/qcore_unix_p.h" // overrides QT_OPEN
60 extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size);
62 /* also adds a space at the end of the number */
63 const char *qt_real_to_string(qreal val, char *buf) {
64 const char *ret = buf;
77 unsigned int ival = (unsigned int) val;
78 qreal frac = val - (qreal)ival;
80 int ifrac = (int)(frac * 1000000000);
81 if (ifrac == 1000000000) {
88 output[i] = '0' + (ival % 10);
97 *(buf++) = output[--i];
106 *(buf++) = '0' + ((ifrac/fact) % 10);
115 const char *qt_int_to_string(int val, char *buf) {
116 const char *ret = buf;
124 output[i] = '0' + (val % 10);
132 *(buf++) = output[--i];
141 ByteStream::ByteStream(QByteArray *byteArray, bool fileBacking)
142 : dev(new QBuffer(byteArray)),
143 fileBackingEnabled(fileBacking),
144 fileBackingActive(false),
147 dev->open(QIODevice::ReadWrite | QIODevice::Append);
150 ByteStream::ByteStream(bool fileBacking)
151 : dev(new QBuffer(&ba)),
152 fileBackingEnabled(fileBacking),
153 fileBackingActive(false),
156 dev->open(QIODevice::ReadWrite);
159 ByteStream::~ByteStream()
164 ByteStream &ByteStream::operator <<(char chr)
166 if (handleDirty) prepareBuffer();
171 ByteStream &ByteStream::operator <<(const char *str)
173 if (handleDirty) prepareBuffer();
174 dev->write(str, strlen(str));
178 ByteStream &ByteStream::operator <<(const QByteArray &str)
180 if (handleDirty) prepareBuffer();
185 ByteStream &ByteStream::operator <<(const ByteStream &src)
187 Q_ASSERT(!src.dev->isSequential());
188 if (handleDirty) prepareBuffer();
189 // We do play nice here, even though it looks ugly.
190 // We save the position and restore it afterwards.
191 ByteStream &s = const_cast<ByteStream&>(src);
192 qint64 pos = s.dev->pos();
194 while (!s.dev->atEnd()) {
195 QByteArray buf = s.dev->read(chunkSize());
202 ByteStream &ByteStream::operator <<(qreal val) {
204 qt_real_to_string(val, buf);
209 ByteStream &ByteStream::operator <<(int val) {
211 qt_int_to_string(val, buf);
216 ByteStream &ByteStream::operator <<(const QPointF &p) {
218 qt_real_to_string(p.x(), buf);
220 qt_real_to_string(p.y(), buf);
225 QIODevice *ByteStream::stream()
232 void ByteStream::clear()
234 dev->open(QIODevice::ReadWrite | QIODevice::Truncate);
237 void ByteStream::constructor_helper(QByteArray *ba)
240 dev = new QBuffer(ba);
241 dev->open(QIODevice::ReadWrite);
244 void ByteStream::prepareBuffer()
246 Q_ASSERT(!dev->isSequential());
247 qint64 size = dev->size();
248 if (fileBackingEnabled && !fileBackingActive
249 && size > maxMemorySize()) {
250 // Switch to file backing.
251 QTemporaryFile *newFile = new QTemporaryFile;
254 while (!dev->atEnd()) {
255 QByteArray buf = dev->read(chunkSize());
261 fileBackingActive = true;
263 if (dev->pos() != size) {
270 #define QT_PATH_ELEMENT(elm)
272 QByteArray QPdf::generatePath(const QPainterPath &path, const QTransform &matrix, PathFlags flags)
275 if (!path.elementCount())
278 ByteStream s(&result);
281 for (int i = 0; i < path.elementCount(); ++i) {
282 const QPainterPath::Element &elm = path.elementAt(i);
284 case QPainterPath::MoveToElement:
286 && path.elementAt(start).x == path.elementAt(i-1).x
287 && path.elementAt(start).y == path.elementAt(i-1).y)
289 s << matrix.map(QPointF(elm.x, elm.y)) << "m\n";
292 case QPainterPath::LineToElement:
293 s << matrix.map(QPointF(elm.x, elm.y)) << "l\n";
295 case QPainterPath::CurveToElement:
296 Q_ASSERT(path.elementAt(i+1).type == QPainterPath::CurveToDataElement);
297 Q_ASSERT(path.elementAt(i+2).type == QPainterPath::CurveToDataElement);
298 s << matrix.map(QPointF(elm.x, elm.y))
299 << matrix.map(QPointF(path.elementAt(i+1).x, path.elementAt(i+1).y))
300 << matrix.map(QPointF(path.elementAt(i+2).x, path.elementAt(i+2).y))
305 qFatal("QPdf::generatePath(), unhandled type: %d", elm.type);
309 && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x
310 && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y)
313 Qt::FillRule fillRule = path.fillRule();
318 op = (fillRule == Qt::WindingFill) ? "W n\n" : "W* n\n";
321 op = (fillRule == Qt::WindingFill) ? "f\n" : "f*\n";
326 case FillAndStrokePath:
327 op = (fillRule == Qt::WindingFill) ? "B\n" : "B*\n";
334 QByteArray QPdf::generateMatrix(const QTransform &matrix)
337 ByteStream s(&result);
348 QByteArray QPdf::generateDashes(const QPen &pen)
351 ByteStream s(&result);
354 QVector<qreal> dasharray = pen.dashPattern();
355 qreal w = pen.widthF();
358 for (int i = 0; i < dasharray.size(); ++i) {
359 qreal dw = dasharray.at(i)*w;
360 if (dw < 0.0001) dw = 0.0001;
364 //qDebug() << "dasharray: pen has" << dasharray;
365 //qDebug() << " => " << result;
371 static const char* pattern_for_brush[] = {
381 "S\n", // Dense1Pattern
400 "S\n", // Dense2Pattern
419 "S\n", // Dense3Pattern
438 "S\n", // Dense4Pattern
457 "S\n", // Dense5Pattern
470 "S\n", // Dense6Pattern
479 "S\n", // Dense7Pattern
496 "S\n", // CrossPattern
503 "S\n", // BDiagPattern
510 "S\n", // FDiagPattern
521 "S\n", // DiagCrossPattern
524 QByteArray QPdf::patternForBrush(const QBrush &b)
526 int style = b.style();
527 if (style > Qt::DiagCrossPattern)
529 return pattern_for_brush[style];
532 #ifdef USE_NATIVE_GRADIENTS
533 static void writeTriangleLine(uchar *&data, int xpos, int ypos, int xoff, int yoff, uint rgb, uchar flag, bool alpha)
536 data[1] = (uchar)(xpos >> 16);
537 data[2] = (uchar)(xpos >> 8);
538 data[3] = (uchar)(xpos >> 0);
539 data[4] = (uchar)(ypos >> 16);
540 data[5] = (uchar)(ypos >> 8);
541 data[6] = (uchar)(ypos >> 0);
544 *data++ = (uchar)qAlpha(rgb);
546 *data++ = (uchar)qRed(rgb);
547 *data++ = (uchar)qGreen(rgb);
548 *data++ = (uchar)qBlue(rgb);
553 data[1] = (uchar)(xpos >> 16);
554 data[2] = (uchar)(xpos >> 8);
555 data[3] = (uchar)(xpos >> 0);
556 data[4] = (uchar)(ypos >> 16);
557 data[5] = (uchar)(ypos >> 8);
558 data[6] = (uchar)(ypos >> 0);
561 *data++ = (uchar)qAlpha(rgb);
563 *data++ = (uchar)qRed(rgb);
564 *data++ = (uchar)qGreen(rgb);
565 *data++ = (uchar)qBlue(rgb);
570 QByteArray QPdf::generateLinearGradientShader(const QLinearGradient *gradient, const QPointF *page_rect, bool alpha)
572 // generate list of triangles with colors
573 QPointF start = gradient->start();
574 QPointF stop = gradient->finalStop();
575 QGradientStops stops = gradient->stops();
576 QPointF offset = stop - start;
577 QGradient::Spread spread = gradient->spread();
579 if (gradient->spread() == QGradient::ReflectSpread) {
581 for (int i = stops.size() - 2; i >= 0; --i) {
582 QGradientStop stop = stops.at(i);
583 stop.first = 2. - stop.first;
586 for (int i = 0 ; i < stops.size(); ++i)
587 stops[i].first /= 2.;
590 QPointF orthogonal(offset.y(), -offset.x());
591 qreal length = offset.x()*offset.x() + offset.y()*offset.y();
593 // find the max and min values in offset and orth direction that are needed to cover
595 int off_min = INT_MAX;
596 int off_max = INT_MIN;
597 qreal ort_min = INT_MAX;
598 qreal ort_max = INT_MIN;
599 for (int i = 0; i < 4; ++i) {
600 qreal off = ((page_rect[i].x() - start.x()) * offset.x() + (page_rect[i].y() - start.y()) * offset.y())/length;
601 qreal ort = ((page_rect[i].x() - start.x()) * orthogonal.x() + (page_rect[i].y() - start.y()) * orthogonal.y())/length;
602 off_min = qMin(off_min, qFloor(off));
603 off_max = qMax(off_max, qCeil(off));
604 ort_min = qMin(ort_min, ort);
605 ort_max = qMax(ort_max, ort);
610 start += off_min * offset + ort_min * orthogonal;
611 orthogonal *= (ort_max - ort_min);
612 int num = off_max - off_min;
614 QPointF gradient_rect[4] = { start,
617 start + num*offset + orthogonal };
618 qreal xmin = gradient_rect[0].x();
619 qreal xmax = gradient_rect[0].x();
620 qreal ymin = gradient_rect[0].y();
621 qreal ymax = gradient_rect[0].y();
622 for (int i = 1; i < 4; ++i) {
623 xmin = qMin(xmin, gradient_rect[i].x());
624 xmax = qMax(xmax, gradient_rect[i].x());
625 ymin = qMin(ymin, gradient_rect[i].y());
626 ymax = qMax(ymax, gradient_rect[i].y());
632 start -= QPointF(xmin, ymin);
633 qreal factor_x = qreal(1<<24)/(xmax - xmin);
634 qreal factor_y = qreal(1<<24)/(ymax - ymin);
635 int xoff = (int)(orthogonal.x()*factor_x);
636 int yoff = (int)(orthogonal.y()*factor_y);
638 QByteArray triangles;
639 triangles.resize(spread == QGradient::PadSpread ? 20*(stops.size()+2) : 20*num*stops.size());
640 uchar *data = (uchar *) triangles.data();
641 if (spread == QGradient::PadSpread) {
642 if (off_min > 0 || off_max < 1) {
643 // linear gradient outside of page
644 const QGradientStop ¤t_stop = off_min > 0 ? stops.at(stops.size()-1) : stops.at(0);
645 uint rgb = current_stop.second.rgba();
646 int xpos = (int)(start.x()*factor_x);
647 int ypos = (int)(start.y()*factor_y);
648 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, 0, alpha);
650 xpos = (int)(start.x()*factor_x);
651 ypos = (int)(start.y()*factor_y);
652 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, 1, alpha);
656 uint rgb = stops.at(0).second.rgba();
657 int xpos = (int)(start.x()*factor_x);
658 int ypos = (int)(start.y()*factor_y);
659 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
660 start -= off_min*offset;
663 for (int s = 0; s < stops.size(); ++s) {
664 const QGradientStop ¤t_stop = stops.at(s);
665 uint rgb = current_stop.second.rgba();
666 int xpos = (int)(start.x()*factor_x);
667 int ypos = (int)(start.y()*factor_y);
668 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
669 if (s < stops.size()-1)
670 start += offset*(stops.at(s+1).first - stops.at(s).first);
674 start += (off_max - 1)*offset;
675 uint rgb = stops.at(stops.size()-1).second.rgba();
676 int xpos = (int)(start.x()*factor_x);
677 int ypos = (int)(start.y()*factor_y);
678 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
682 for (int i = 0; i < num; ++i) {
684 for (int s = 0; s < stops.size(); ++s) {
685 uint rgb = stops.at(s).second.rgba();
686 int xpos = (int)(start.x()*factor_x);
687 int ypos = (int)(start.y()*factor_y);
688 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
689 if (s < stops.size()-1)
690 start += offset*(stops.at(s+1).first - stops.at(s).first);
695 triangles.resize((char *)data - triangles.constData());
698 QPdf::ByteStream s(&shader);
701 "/ColorSpace " << (alpha ? "/DeviceGray\n" : "/DeviceRGB\n") <<
703 "/BitsPerCoordinate 24\n"
704 "/BitsPerComponent 8\n"
706 "/Decode [" << xmin << xmax << ymin << ymax << (alpha ? "0 1]\n" : "0 1 0 1 0 1]\n") <<
708 "/Length " << triangles.length() << "\n"
710 "stream\n" << triangles << "endstream\n"
716 static void moveToHook(qfixed x, qfixed y, void *data)
718 QPdf::Stroker *t = (QPdf::Stroker *)data;
722 t->matrix.map(x, y, &x, &y);
723 *t->stream << x << y << "m\n";
727 static void lineToHook(qfixed x, qfixed y, void *data)
729 QPdf::Stroker *t = (QPdf::Stroker *)data;
731 t->matrix.map(x, y, &x, &y);
732 *t->stream << x << y << "l\n";
735 static void cubicToHook(qfixed c1x, qfixed c1y,
736 qfixed c2x, qfixed c2y,
737 qfixed ex, qfixed ey,
740 QPdf::Stroker *t = (QPdf::Stroker *)data;
741 if (!t->cosmeticPen) {
742 t->matrix.map(c1x, c1y, &c1x, &c1y);
743 t->matrix.map(c2x, c2y, &c2x, &c2y);
744 t->matrix.map(ex, ey, &ex, &ey);
746 *t->stream << c1x << c1y
752 QPdf::Stroker::Stroker()
755 dashStroker(&basicStroker)
757 stroker = &basicStroker;
758 basicStroker.setMoveToHook(moveToHook);
759 basicStroker.setLineToHook(lineToHook);
760 basicStroker.setCubicToHook(cubicToHook);
762 basicStroker.setStrokeWidth(.1);
765 void QPdf::Stroker::setPen(const QPen &pen)
767 if (pen.style() == Qt::NoPen) {
771 qreal w = pen.widthF();
772 bool zeroWidth = w < 0.0001;
773 cosmeticPen = pen.isCosmetic();
777 basicStroker.setStrokeWidth(w);
778 basicStroker.setCapStyle(pen.capStyle());
779 basicStroker.setJoinStyle(pen.joinStyle());
780 basicStroker.setMiterLimit(pen.miterLimit());
782 QVector<qreal> dashpattern = pen.dashPattern();
784 for (int i = 0; i < dashpattern.size(); ++i)
785 dashpattern[i] *= 10.;
787 if (!dashpattern.isEmpty()) {
788 dashStroker.setDashPattern(dashpattern);
789 dashStroker.setDashOffset(pen.dashOffset());
790 stroker = &dashStroker;
792 stroker = &basicStroker;
796 void QPdf::Stroker::strokePath(const QPainterPath &path)
802 stroker->strokePath(path, this, cosmeticPen ? matrix : QTransform());
806 QByteArray QPdf::ascii85Encode(const QByteArray &input)
808 int isize = input.size()/4*4;
810 output.resize(input.size()*5/4+7);
811 char *out = output.data();
812 const uchar *in = (const uchar *)input.constData();
813 for (int i = 0; i < isize; i += 4) {
814 uint val = (((uint)in[i])<<24) + (((uint)in[i+1])<<16) + (((uint)in[i+2])<<8) + (uint)in[i+3];
829 *(out++) = base[0] + '!';
830 *(out++) = base[1] + '!';
831 *(out++) = base[2] + '!';
832 *(out++) = base[3] + '!';
833 *(out++) = base[4] + '!';
836 //write the last few bytes
837 int remaining = input.size() - isize;
840 for (int i = isize; i < input.size(); ++i)
841 val = (val << 8) + in[i];
842 val <<= 8*(4-remaining);
853 for (int i = 0; i < remaining+1; ++i)
854 *(out++) = base[i] + '!';
858 output.resize(out-output.data());
862 const char *QPdf::toHex(ushort u, char *buffer)
866 ushort hex = (u & 0x000f);
870 buffer[i] = 'A'+(hex-0x0a);
878 const char *QPdf::toHex(uchar u, char *buffer)
882 ushort hex = (u & 0x000f);
886 buffer[i] = 'A'+(hex-0x0a);
894 #define Q_MM(n) int((n * 720 + 127) / 254)
895 #define Q_IN(n) int(n * 72)
897 static const char * const psToStr[QPrinter::NPaperSize+1] =
899 "A4", "B5", "Letter", "Legal", "Executive",
900 "A0", "A1", "A2", "A3", "A5", "A6", "A7", "A8", "A9", "B0", "B1",
901 "B10", "B2", "B3", "B4", "B6", "B7", "B8", "B9", "C5E", "Comm10E",
902 "DLE", "Folio", "Ledger", "Tabloid", 0
905 QPdf::PaperSize QPdf::paperSize(QPrinter::PaperSize paperSize)
907 QSizeF s = qt_paperSizeToQSizeF(paperSize);
908 PaperSize p = { Q_MM(s.width()), Q_MM(s.height()) };
912 const char *QPdf::paperSizeToString(QPrinter::PaperSize paperSize)
914 return psToStr[paperSize];
917 // -------------------------- base engine, shared code between PS and PDF -----------------------
919 QPdfBaseEngine::QPdfBaseEngine(QPdfBaseEnginePrivate &dd, PaintEngineFeatures f)
920 : QAlphaPaintEngine(dd, f)
923 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
924 if (QCUPSSupport::isAvailable()) {
926 const cups_dest_t* printers = cups.availablePrinters();
927 int prnCount = cups.availablePrintersCount();
929 for (int i = 0; i < prnCount; ++i) {
930 if (printers[i].is_default) {
931 d->printerName = QString::fromLocal8Bit(printers[i].name);
939 d->printerName = QString::fromLocal8Bit(qgetenv("PRINTER"));
940 if (d->printerName.isEmpty())
941 d->printerName = QString::fromLocal8Bit(qgetenv("LPDEST"));
942 if (d->printerName.isEmpty())
943 d->printerName = QString::fromLocal8Bit(qgetenv("NPRINTER"));
944 if (d->printerName.isEmpty())
945 d->printerName = QString::fromLocal8Bit(qgetenv("NGPRINTER"));
949 void QPdfBaseEngine::drawPoints (const QPointF *points, int pointCount)
956 for (int i=0; i!=pointCount;++i) {
958 p.lineTo(points[i] + QPointF(0, 0.001));
961 bool hadBrush = d->hasBrush;
964 d->hasBrush = hadBrush;
967 void QPdfBaseEngine::drawLines (const QLineF *lines, int lineCount)
974 for (int i=0; i!=lineCount;++i) {
975 p.moveTo(lines[i].p1());
976 p.lineTo(lines[i].p2());
978 bool hadBrush = d->hasBrush;
981 d->hasBrush = hadBrush;
984 void QPdfBaseEngine::drawRects (const QRectF *rects, int rectCount)
990 if (d->useAlphaEngine) {
991 QAlphaPaintEngine::drawRects(rects, rectCount);
996 if (d->clipEnabled && d->allClipped)
998 if (!d->hasPen && !d->hasBrush)
1001 QBrush penBrush = d->pen.brush();
1002 if (d->simplePen || !d->hasPen) {
1003 // draw strokes natively in this case for better output
1004 if(!d->simplePen && !d->stroker.matrix.isIdentity())
1005 *d->currentPage << "q\n" << QPdf::generateMatrix(d->stroker.matrix);
1006 for (int i = 0; i < rectCount; ++i)
1007 *d->currentPage << rects[i].x() << rects[i].y() << rects[i].width() << rects[i].height() << "re\n";
1008 *d->currentPage << (d->hasPen ? (d->hasBrush ? "B\n" : "S\n") : "f\n");
1009 if(!d->simplePen && !d->stroker.matrix.isIdentity())
1010 *d->currentPage << "Q\n";
1013 for (int i=0; i!=rectCount; ++i)
1014 p.addRect(rects[i]);
1019 void QPdfBaseEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1021 Q_D(QPdfBaseEngine);
1023 if (d->useAlphaEngine) {
1024 QAlphaPaintEngine::drawPolygon(points, pointCount, mode);
1025 if (!continueCall())
1029 if (!points || !pointCount)
1032 bool hb = d->hasBrush;
1037 p.setFillRule(Qt::OddEvenFill);
1041 p.setFillRule(Qt::WindingFill);
1044 d->hasBrush = false;
1050 p.moveTo(points[0]);
1051 for (int i = 1; i < pointCount; ++i)
1052 p.lineTo(points[i]);
1054 if (mode != PolylineMode)
1061 void QPdfBaseEngine::drawPath (const QPainterPath &p)
1063 Q_D(QPdfBaseEngine);
1065 if (d->useAlphaEngine) {
1066 QAlphaPaintEngine::drawPath(p);
1067 if (!continueCall())
1071 if (d->clipEnabled && d->allClipped)
1073 if (!d->hasPen && !d->hasBrush)
1077 // draw strokes natively in this case for better output
1078 *d->currentPage << QPdf::generatePath(p, QTransform(), d->hasBrush ? QPdf::FillAndStrokePath : QPdf::StrokePath);
1081 *d->currentPage << QPdf::generatePath(p, d->stroker.matrix, QPdf::FillPath);
1083 *d->currentPage << "q\n";
1084 QBrush b = d->brush;
1085 d->brush = d->pen.brush();
1087 d->stroker.strokePath(p);
1088 *d->currentPage << "Q\n";
1094 void QPdfBaseEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
1096 Q_D(QPdfBaseEngine);
1098 if (d->useAlphaEngine) {
1099 QAlphaPaintEngine::drawTextItem(p, textItem);
1100 if (!continueCall())
1104 if (!d->hasPen || (d->clipEnabled && d->allClipped))
1107 if (d->stroker.matrix.type() >= QTransform::TxProject) {
1108 QPaintEngine::drawTextItem(p, textItem);
1112 *d->currentPage << "q\n";
1114 *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
1116 bool hp = d->hasPen;
1118 QBrush b = d->brush;
1119 d->brush = d->pen.brush();
1122 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
1123 Q_ASSERT(ti.fontEngine->type() != QFontEngine::Multi);
1124 d->drawTextItem(p, ti);
1127 *d->currentPage << "Q\n";
1131 void QPdfBaseEngine::updateState(const QPaintEngineState &state)
1133 Q_D(QPdfBaseEngine);
1135 if (d->useAlphaEngine) {
1136 QAlphaPaintEngine::updateState(state);
1137 if (!continueCall())
1141 QPaintEngine::DirtyFlags flags = state.state();
1143 if (flags & DirtyTransform)
1144 d->stroker.matrix = state.transform();
1146 if (flags & DirtyPen) {
1147 d->pen = state.pen();
1148 d->hasPen = d->pen.style() != Qt::NoPen;
1149 d->stroker.setPen(d->pen);
1150 QBrush penBrush = d->pen.brush();
1151 bool oldSimple = d->simplePen;
1152 d->simplePen = (d->hasPen && (penBrush.style() == Qt::SolidPattern) && penBrush.isOpaque());
1153 if (oldSimple != d->simplePen)
1154 flags |= DirtyTransform;
1156 if (flags & DirtyBrush) {
1157 d->brush = state.brush();
1158 if (d->brush.color().alpha() == 0 && d->brush.style() == Qt::SolidPattern)
1159 d->brush.setStyle(Qt::NoBrush);
1160 d->hasBrush = d->brush.style() != Qt::NoBrush;
1162 if (flags & DirtyBrushOrigin) {
1163 d->brushOrigin = state.brushOrigin();
1164 flags |= DirtyBrush;
1166 if (flags & DirtyOpacity)
1167 d->opacity = state.opacity();
1169 bool ce = d->clipEnabled;
1170 if (flags & DirtyClipPath) {
1171 d->clipEnabled = true;
1172 updateClipPath(state.clipPath(), state.clipOperation());
1173 } else if (flags & DirtyClipRegion) {
1174 d->clipEnabled = true;
1176 QVector<QRect> rects = state.clipRegion().rects();
1177 for (int i = 0; i < rects.size(); ++i)
1178 path.addRect(rects.at(i));
1179 updateClipPath(path, state.clipOperation());
1180 flags |= DirtyClipPath;
1181 } else if (flags & DirtyClipEnabled) {
1182 d->clipEnabled = state.isClipEnabled();
1185 if (ce != d->clipEnabled)
1186 flags |= DirtyClipPath;
1187 else if (!d->clipEnabled)
1188 flags &= ~DirtyClipPath;
1190 setupGraphicsState(flags);
1193 void QPdfBaseEngine::setupGraphicsState(QPaintEngine::DirtyFlags flags)
1195 Q_D(QPdfBaseEngine);
1196 if (flags & DirtyClipPath)
1197 flags |= DirtyTransform|DirtyPen|DirtyBrush;
1199 if (flags & DirtyTransform) {
1200 *d->currentPage << "Q\n";
1201 flags |= DirtyPen|DirtyBrush;
1204 if (flags & DirtyClipPath) {
1205 *d->currentPage << "Q q\n";
1207 d->allClipped = false;
1208 if (d->clipEnabled && !d->clips.isEmpty()) {
1209 for (int i = 0; i < d->clips.size(); ++i) {
1210 if (d->clips.at(i).isEmpty()) {
1211 d->allClipped = true;
1215 if (!d->allClipped) {
1216 for (int i = 0; i < d->clips.size(); ++i) {
1217 *d->currentPage << QPdf::generatePath(d->clips.at(i), QTransform(), QPdf::ClipPath);
1223 if (flags & DirtyTransform) {
1224 *d->currentPage << "q\n";
1225 if (d->simplePen && !d->stroker.matrix.isIdentity())
1226 *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
1228 if (flags & DirtyBrush)
1230 if (d->simplePen && (flags & DirtyPen))
1234 extern QPainterPath qt_regionToPath(const QRegion ®ion);
1236 void QPdfBaseEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
1238 Q_D(QPdfBaseEngine);
1239 QPainterPath path = d->stroker.matrix.map(p);
1240 //qDebug() << "updateClipPath: " << d->stroker.matrix << p.boundingRect() << path.boundingRect() << op;
1242 if (op == Qt::NoClip) {
1243 d->clipEnabled = false;
1245 } else if (op == Qt::ReplaceClip) {
1247 d->clips.append(path);
1248 } else if (op == Qt::IntersectClip) {
1249 d->clips.append(path);
1250 } else { // UniteClip
1251 // ask the painter for the current clipping path. that's the easiest solution
1252 path = painter()->clipPath();
1253 path = d->stroker.matrix.map(path);
1255 d->clips.append(path);
1258 if (d->useAlphaEngine) {
1259 // if we have an alpha region, we have to subtract that from the
1260 // any existing clip region since that region will be filled in
1261 // later with images
1262 QPainterPath alphaClip = qt_regionToPath(alphaClipping());
1263 if (!alphaClip.isEmpty()) {
1264 if (!d->clipEnabled) {
1265 QRect r = d->fullPage ? d->paperRect() : d->pageRect();
1267 dev.addRect(QRect(0, 0, r.width(), r.height()));
1271 path = path.intersected(dev);
1272 d->clipEnabled = true;
1274 path = painter()->clipPath();
1275 path = d->stroker.matrix.map(path);
1277 path = path.subtracted(alphaClip);
1279 d->clips.append(path);
1284 void QPdfBaseEngine::setPen()
1286 Q_D(QPdfBaseEngine);
1287 if (d->pen.style() == Qt::NoPen)
1289 QBrush b = d->pen.brush();
1290 Q_ASSERT(b.style() == Qt::SolidPattern && b.isOpaque());
1292 QColor rgba = b.color();
1293 if (d->colorMode == QPrinter::GrayScale) {
1294 qreal gray = qGray(rgba.rgba())/255.;
1295 *d->currentPage << gray << gray << gray;
1297 *d->currentPage << rgba.redF()
1301 *d->currentPage << "SCN\n";
1303 *d->currentPage << d->pen.widthF() << "w ";
1305 int pdfCapStyle = 0;
1306 switch(d->pen.capStyle()) {
1319 *d->currentPage << pdfCapStyle << "J ";
1321 int pdfJoinStyle = 0;
1322 switch(d->pen.joinStyle()) {
1335 *d->currentPage << pdfJoinStyle << "j ";
1337 *d->currentPage << QPdf::generateDashes(d->pen) << " 0 d\n";
1340 bool QPdfBaseEngine::newPage()
1342 Q_D(QPdfBaseEngine);
1343 setupGraphicsState(DirtyBrush|DirtyPen|DirtyClipPath);
1344 QFile *outfile = qobject_cast<QFile*> (d->outDevice);
1345 if (outfile && outfile->error() != QFile::NoError)
1351 int QPdfBaseEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const
1353 Q_D(const QPdfBaseEngine);
1355 QRect r = d->fullPage ? d->paperRect() : d->pageRect();
1356 switch (metricType) {
1357 case QPaintDevice::PdmWidth:
1360 case QPaintDevice::PdmHeight:
1363 case QPaintDevice::PdmDpiX:
1364 case QPaintDevice::PdmDpiY:
1365 val = d->resolution;
1367 case QPaintDevice::PdmPhysicalDpiX:
1368 case QPaintDevice::PdmPhysicalDpiY:
1371 case QPaintDevice::PdmWidthMM:
1372 val = qRound(r.width()*25.4/d->resolution);
1374 case QPaintDevice::PdmHeightMM:
1375 val = qRound(r.height()*25.4/d->resolution);
1377 case QPaintDevice::PdmNumColors:
1380 case QPaintDevice::PdmDepth:
1384 qWarning("QPrinter::metric: Invalid metric command");
1390 void QPdfBaseEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
1392 Q_D(QPdfBaseEngine);
1394 case PPK_CollateCopies:
1395 d->collate = value.toBool();
1398 d->colorMode = QPrinter::ColorMode(value.toInt());
1401 d->creator = value.toString();
1403 case PPK_DocumentName:
1404 d->title = value.toString();
1407 d->fullPage = value.toBool();
1409 case PPK_CopyCount: // fallthrough
1410 case PPK_NumberOfCopies:
1411 d->copies = value.toInt();
1413 case PPK_Orientation:
1414 d->orientation = QPrinter::Orientation(value.toInt());
1416 case PPK_OutputFileName:
1417 d->outputFileName = value.toString();
1420 d->pageOrder = QPrinter::PageOrder(value.toInt());
1423 d->paperSize = QPrinter::PaperSize(value.toInt());
1425 case PPK_PaperSource:
1426 d->paperSource = QPrinter::PaperSource(value.toInt());
1428 case PPK_PrinterName:
1429 d->printerName = value.toString();
1431 case PPK_PrinterProgram:
1432 d->printProgram = value.toString();
1434 case PPK_Resolution:
1435 d->resolution = value.toInt();
1437 case PPK_SelectionOption:
1438 d->selectionOption = value.toString();
1440 case PPK_FontEmbedding:
1441 d->embedFonts = value.toBool();
1444 d->duplex = static_cast<QPrinter::DuplexMode> (value.toInt());
1446 case PPK_CupsPageRect:
1447 d->cupsPageRect = value.toRect();
1449 case PPK_CupsPaperRect:
1450 d->cupsPaperRect = value.toRect();
1452 case PPK_CupsOptions:
1453 d->cupsOptions = value.toStringList();
1455 case PPK_CupsStringPageSize:
1456 d->cupsStringPageSize = value.toString();
1458 case PPK_CustomPaperSize:
1459 d->paperSize = QPrinter::Custom;
1460 d->customPaperSize = value.toSizeF();
1462 case PPK_PageMargins:
1464 QList<QVariant> margins(value.toList());
1465 Q_ASSERT(margins.size() == 4);
1466 d->leftMargin = margins.at(0).toReal();
1467 d->topMargin = margins.at(1).toReal();
1468 d->rightMargin = margins.at(2).toReal();
1469 d->bottomMargin = margins.at(3).toReal();
1470 d->hasCustomPageMargins = true;
1478 QVariant QPdfBaseEngine::property(PrintEnginePropertyKey key) const
1480 Q_D(const QPdfBaseEngine);
1484 case PPK_CollateCopies:
1493 case PPK_DocumentName:
1502 case PPK_SupportsMultipleCopies:
1503 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
1504 if (QCUPSSupport::isAvailable())
1510 case PPK_NumberOfCopies:
1511 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
1512 if (QCUPSSupport::isAvailable())
1518 case PPK_Orientation:
1519 ret = d->orientation;
1521 case PPK_OutputFileName:
1522 ret = d->outputFileName;
1530 case PPK_PaperSource:
1531 ret = d->paperSource;
1533 case PPK_PrinterName:
1534 ret = d->printerName;
1536 case PPK_PrinterProgram:
1537 ret = d->printProgram;
1539 case PPK_Resolution:
1540 ret = d->resolution;
1542 case PPK_SupportedResolutions:
1543 ret = QList<QVariant>() << 72;
1546 ret = d->paperRect();
1549 ret = d->pageRect();
1551 case PPK_SelectionOption:
1552 ret = d->selectionOption;
1554 case PPK_FontEmbedding:
1555 ret = d->embedFonts;
1560 case PPK_CupsPageRect:
1561 ret = d->cupsPageRect;
1563 case PPK_CupsPaperRect:
1564 ret = d->cupsPaperRect;
1566 case PPK_CupsOptions:
1567 ret = d->cupsOptions;
1569 case PPK_CupsStringPageSize:
1570 ret = d->cupsStringPageSize;
1572 case PPK_CustomPaperSize:
1573 ret = d->customPaperSize;
1575 case PPK_PageMargins:
1577 QList<QVariant> margins;
1578 if (d->hasCustomPageMargins) {
1579 margins << d->leftMargin << d->topMargin
1580 << d->rightMargin << d->bottomMargin;
1582 const qreal defaultMargin = 10; // ~3.5 mm
1583 margins << defaultMargin << defaultMargin
1584 << defaultMargin << defaultMargin;
1595 QPdfBaseEnginePrivate::QPdfBaseEnginePrivate(QPrinter::PrinterMode m)
1596 : clipEnabled(false), allClipped(false), hasPen(true), hasBrush(false), simplePen(false),
1597 useAlphaEngine(false),
1598 outDevice(0), fd(-1),
1599 duplex(QPrinter::DuplexNone), collate(false), fullPage(false), embedFonts(true), copies(1),
1600 pageOrder(QPrinter::FirstPageFirst), orientation(QPrinter::Portrait),
1601 paperSize(QPrinter::A4), colorMode(QPrinter::Color), paperSource(QPrinter::Auto),
1602 hasCustomPageMargins(false),
1603 leftMargin(0), topMargin(0), rightMargin(0), bottomMargin(0)
1606 if (m == QPrinter::HighResolution)
1608 else if (m == QPrinter::ScreenResolution)
1609 resolution = qt_defaultDpi();
1617 bool QPdfBaseEngine::begin(QPaintDevice *pdev)
1619 Q_D(QPdfBaseEngine);
1622 d->postscript = false;
1623 d->currentObject = 1;
1625 d->currentPage = new QPdfPage;
1626 d->stroker.stream = d->currentPage;
1629 return d->openPrintDevice();
1632 bool QPdfBaseEngine::end()
1634 Q_D(QPdfBaseEngine);
1635 qDeleteAll(d->fonts);
1637 delete d->currentPage;
1640 d->closePrintDevice();
1645 static void closeAllOpenFds()
1647 // hack time... getting the maximum number of open
1648 // files, if possible. if not we assume it's the
1649 // larger of 256 and the fd we got
1651 #if defined(_SC_OPEN_MAX)
1652 i = (int)sysconf(_SC_OPEN_MAX);
1653 #elif defined(_POSIX_OPEN_MAX)
1654 i = (int)_POSIX_OPEN_MAX;
1655 #elif defined(OPEN_MAX)
1660 // leave stdin/out/err untouched
1666 bool QPdfBaseEnginePrivate::openPrintDevice()
1671 if (!outputFileName.isEmpty()) {
1672 QFile *file = new QFile(outputFileName);
1673 if (! file->open(QFile::WriteOnly|QFile::Truncate)) {
1678 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
1679 } else if (QCUPSSupport::isAvailable()) {
1681 QPair<int, QString> ret = cups.tempFd();
1682 if (ret.first < 0) {
1683 qWarning("QPdfPrinter: Could not open temporary file to print");
1686 cupsTempFile = ret.second;
1687 outDevice = new QFile();
1688 static_cast<QFile *>(outDevice)->open(ret.first, QIODevice::WriteOnly);
1693 if (!printerName.isEmpty())
1696 if (qt_safe_pipe(fds) != 0) {
1697 qWarning("QPdfPrinter: Could not open pipe to print");
1702 if (pid == 0) { // child process
1703 // if possible, exit quickly, so the actual lp/lpr
1704 // becomes a child of init, and ::waitpid() is
1705 // guaranteed not to wait.
1709 // try to replace this process with "true" - this prevents
1710 // global destructors from being called (that could possibly
1711 // do wrong things to the parent process)
1712 (void)execlp("true", "true", (char *)0);
1713 (void)execl("/bin/true", "true", (char *)0);
1714 (void)execl("/usr/bin/true", "true", (char *)0);
1717 qt_safe_dup2(fds[0], 0, 0);
1721 if (!printProgram.isEmpty()) {
1722 if (!selectionOption.isEmpty())
1723 pr.prepend(selectionOption);
1725 pr.prepend(QLatin1String("-P"));
1726 (void)execlp(printProgram.toLocal8Bit().data(), printProgram.toLocal8Bit().data(),
1727 pr.toLocal8Bit().data(), (char *)0);
1729 // if no print program has been specified, be smart
1730 // about the option string too.
1731 QList<QByteArray> lprhack;
1732 QList<QByteArray> lphack;
1734 if (!pr.isEmpty() || !selectionOption.isEmpty()) {
1735 if (!selectionOption.isEmpty()) {
1736 QStringList list = selectionOption.split(QLatin1Char(' '));
1737 for (int i = 0; i < list.size(); ++i)
1738 lprhack.append(list.at(i).toLocal8Bit());
1741 lprhack.append("-P");
1742 lphack.append("-d");
1744 lprhack.append(pr.toLocal8Bit());
1745 lphack.append(pr.toLocal8Bit());
1747 lphack.append("-s");
1749 char ** lpargs = new char *[lphack.size()+6];
1753 for (i = 0; i < lphack.size(); ++i)
1754 lpargs[i+1] = (char *)lphack.at(i).constData();
1756 if (QPdf::paperSizeToString(paperSize)) {
1757 char dash_o[] = "-o";
1758 lpargs[++i] = dash_o;
1759 lpargs[++i] = const_cast<char *>(QPdf::paperSizeToString(paperSize));
1760 lpargs[++i] = dash_o;
1762 media += QPdf::paperSizeToString(paperSize);
1763 lpargs[++i] = media.data();
1767 char **lprargs = new char *[lprhack.size()+2];
1770 for (int i = 0; i < lprhack.size(); ++i)
1771 lprargs[i+1] = (char *)lprhack[i].constData();
1772 lprargs[lprhack.size() + 1] = 0;
1773 (void)execvp("lp", lpargs);
1774 (void)execvp("lpr", lprargs);
1775 (void)execv("/bin/lp", lpargs);
1776 (void)execv("/bin/lpr", lprargs);
1777 (void)execv("/usr/bin/lp", lpargs);
1778 (void)execv("/usr/bin/lpr", lprargs);
1783 // if we couldn't exec anything, close the fd,
1784 // wait for a second so the parent process (the
1785 // child of the GUI process) has exited. then
1794 (void)qt_safe_waitpid(pid, 0, 0);
1799 outDevice = new QFile();
1800 static_cast<QFile *>(outDevice)->open(fd, QIODevice::WriteOnly);
1807 void QPdfBaseEnginePrivate::closePrintDevice()
1813 #if defined(Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400
1822 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
1823 if (!cupsTempFile.isEmpty()) {
1824 QString tempFile = cupsTempFile;
1825 cupsTempFile.clear();
1828 // Set up print options.
1830 QList<QPair<QByteArray, QByteArray> > options;
1831 QVector<cups_option_t> cupsOptStruct;
1833 if (!printerName.isEmpty()) {
1834 prnName = printerName.toLocal8Bit();
1836 QPrinterInfo def = QPrinterInfo::defaultPrinter();
1838 qWarning("Could not determine printer to print to");
1839 QFile::remove(tempFile);
1842 prnName = def.printerName().toLocal8Bit();
1845 if (!cupsStringPageSize.isEmpty()) {
1846 options.append(QPair<QByteArray, QByteArray>("media", cupsStringPageSize.toLocal8Bit()));
1850 options.append(QPair<QByteArray, QByteArray>("copies", QString::number(copies).toLocal8Bit()));
1854 options.append(QPair<QByteArray, QByteArray>("Collate", "True"));
1857 if (duplex != QPrinter::DuplexNone) {
1859 case QPrinter::DuplexNone: break;
1860 case QPrinter::DuplexAuto:
1861 if (orientation == QPrinter::Portrait)
1862 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
1864 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
1866 case QPrinter::DuplexLongSide:
1867 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
1869 case QPrinter::DuplexShortSide:
1870 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
1875 if (QCUPSSupport::cupsVersion() >= 10300 && orientation == QPrinter::Landscape) {
1876 options.append(QPair<QByteArray, QByteArray>("landscape", ""));
1879 QStringList::const_iterator it = cupsOptions.constBegin();
1880 while (it != cupsOptions.constEnd()) {
1881 options.append(QPair<QByteArray, QByteArray>((*it).toLocal8Bit(), (*(it+1)).toLocal8Bit()));
1885 for (int c = 0; c < options.size(); ++c) {
1887 opt.name = options[c].first.data();
1888 opt.value = options[c].second.data();
1889 cupsOptStruct.append(opt);
1893 cups_option_t* optPtr = cupsOptStruct.size() ? &cupsOptStruct.first() : 0;
1894 cups.printFile(prnName.constData(), tempFile.toLocal8Bit().constData(),
1895 title.toLocal8Bit().constData(), cupsOptStruct.size(), optPtr);
1897 QFile::remove(tempFile);
1902 QPdfBaseEnginePrivate::~QPdfBaseEnginePrivate()
1908 void QPdfBaseEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
1910 Q_Q(QPdfBaseEngine);
1912 QFontEngine *fe = ti.fontEngine;
1914 QFontEngine::FaceId face_id = fe->faceId();
1915 bool noEmbed = false;
1916 if (face_id.filename.isEmpty()
1917 || (!postscript && ((fe->fsType & 0x200) /* bitmap embedding only */
1918 || (fe->fsType == 2) /* no embedding allowed */))) {
1919 *currentPage << "Q\n";
1920 q->QPaintEngine::drawTextItem(p, ti);
1921 *currentPage << "q\n";
1922 if (face_id.filename.isEmpty())
1927 QFontSubset *font = fonts.value(face_id, 0);
1929 font = new QFontSubset(fe, requestObject());
1930 font->noEmbed = noEmbed;
1932 fonts.insert(face_id, font);
1934 if (!currentPage->fonts.contains(font->object_id))
1935 currentPage->fonts.append(font->object_id);
1937 qreal size = ti.fontEngine->fontDef.pixelSize;
1939 if (ti.fontEngine->type() == QFontEngine::Win) {
1940 QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
1941 size = fe->tm.tmHeight;
1945 QVarLengthArray<glyph_t> glyphs;
1946 QVarLengthArray<QFixedPoint> positions;
1947 QTransform m = QTransform::fromTranslate(p.x(), p.y());
1948 ti.fontEngine->getGlyphPositions(ti.glyphs, m, ti.flags,
1950 if (glyphs.size() == 0)
1952 int synthesized = ti.fontEngine->synthesized();
1953 qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
1955 *currentPage << "BT\n"
1956 << "/F" << font->object_id << size << "Tf "
1957 << stretch << (synthesized & QFontEngine::SynthesizedItalic
1958 ? "0 .3 -1 0 0 Tm\n"
1959 : "0 0 -1 0 0 Tm\n");
1963 // #### implement actual text for complex languages
1964 const unsigned short *logClusters = ti.logClusters;
1968 while (end < ti.num_chars && logClusters[end] == logClusters[pos])
1970 *currentPage << "/Span << /ActualText <FEFF";
1971 for (int i = pos; i < end; ++i) {
1972 s << toHex((ushort)ti.chars[i].unicode(), buf);
1974 *currentPage << "> >>\n"
1977 int ge = end == ti.num_chars ? ti.num_glyphs : logClusters[end];
1978 for (int gs = logClusters[pos]; gs < ge; ++gs)
1979 *currentPage << toHex((ushort)ti.glyphs[gs].glyph, buf);
1980 *currentPage << "> Tj\n"
1983 } while (pos < ti.num_chars);
1987 for (int i = 0; i < glyphs.size(); ++i) {
1988 qreal x = positions[i].x.toReal();
1989 qreal y = positions[i].y.toReal();
1990 if (synthesized & QFontEngine::SynthesizedItalic)
1994 int g = font->addGlyph(glyphs[i]);
1995 *currentPage << x - last_x << last_y - y << "Td <"
1996 << QPdf::toHex((ushort)g, buf) << "> Tj\n";
2000 if (synthesized & QFontEngine::SynthesizedBold) {
2001 *currentPage << stretch << (synthesized & QFontEngine::SynthesizedItalic
2002 ? "0 .3 -1 0 0 Tm\n"
2003 : "0 0 -1 0 0 Tm\n");
2004 *currentPage << "/Span << /ActualText <> >> BDC\n";
2005 last_x = 0.5*fe->lineThickness().toReal();
2007 for (int i = 0; i < glyphs.size(); ++i) {
2008 qreal x = positions[i].x.toReal();
2009 qreal y = positions[i].y.toReal();
2010 if (synthesized & QFontEngine::SynthesizedItalic)
2014 int g = font->addGlyph(glyphs[i]);
2015 *currentPage << x - last_x << last_y - y << "Td <"
2016 << QPdf::toHex((ushort)g, buf) << "> Tj\n";
2020 *currentPage << "EMC\n";
2024 *currentPage << "ET\n";
2027 QRect QPdfBaseEnginePrivate::paperRect() const
2031 if (paperSize == QPrinter::Custom) {
2032 w = qRound(customPaperSize.width()*resolution/72.);
2033 h = qRound(customPaperSize.height()*resolution/72.);
2035 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
2036 if (QCUPSSupport::isAvailable() && !cupsPaperRect.isNull()) {
2037 QRect r = cupsPaperRect;
2043 QPdf::PaperSize s = QPdf::paperSize(paperSize);
2047 w = qRound(w*resolution/72.);
2048 h = qRound(h*resolution/72.);
2050 if (orientation == QPrinter::Portrait)
2051 return QRect(0, 0, w, h);
2053 return QRect(0, 0, h, w);
2056 QRect QPdfBaseEnginePrivate::pageRect() const
2063 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
2064 if (!hasCustomPageMargins && QCUPSSupport::isAvailable() && !cupsPageRect.isNull()) {
2066 if (r == cupsPaperRect) {
2067 // if cups doesn't define any margins, give it at least approx 3.5 mm
2068 r = QRect(10, 10, r.width() - 20, r.height() - 20);
2074 if (paperSize == QPrinter::Custom) {
2075 s.width = qRound(customPaperSize.width());
2076 s.height = qRound(customPaperSize.height());
2078 s = QPdf::paperSize(paperSize);
2080 if (hasCustomPageMargins)
2081 r = QRect(0, 0, s.width, s.height);
2083 r = QRect(72/3, 72/3, s.width - 2*72/3, s.height - 2*72/3);
2086 int x = qRound(r.left()*resolution/72.);
2087 int y = qRound(r.top()*resolution/72.);
2088 int w = qRound(r.width()*resolution/72.);
2089 int h = qRound(r.height()*resolution/72.);
2090 if (orientation == QPrinter::Portrait)
2091 r = QRect(x, y, w, h);
2093 r = QRect(y, x, h, w);
2095 if (hasCustomPageMargins) {
2096 r.adjust(qRound(leftMargin*(resolution/72.)),
2097 qRound(topMargin*(resolution/72.)),
2098 -qRound(rightMargin*(resolution/72.)),
2099 -qRound(bottomMargin*(resolution/72.)));