source: trunk/src/gui/painting/qpdf.cpp@ 651

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

trunk: Merged in qt 4.6.2 sources.

File size: 59.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** 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 qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41#include "qplatformdefs.h"
42#include <qdebug.h>
43#include "qpdf_p.h"
44#include <qfile.h>
45#include <qtemporaryfile.h>
46#include <private/qmath_p.h>
47#include "private/qcups_p.h"
48#include "qprinterinfo.h"
49#include <qnumeric.h>
50
51#ifdef Q_OS_UNIX
52#include "private/qcore_unix_p.h" // overrides QT_OPEN
53#endif
54
55QT_BEGIN_NAMESPACE
56
57extern int qt_defaultDpi();
58
59#ifndef QT_NO_PRINTER
60
61extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size);
62
63/* also adds a space at the end of the number */
64const char *qt_real_to_string(qreal val, char *buf) {
65 const char *ret = buf;
66
67 if (qIsNaN(val)) {
68 *(buf++) = '0';
69 *(buf++) = ' ';
70 *buf = 0;
71 return ret;
72 }
73
74 if (val < 0) {
75 *(buf++) = '-';
76 val = -val;
77 }
78 unsigned int ival = (unsigned int) val;
79 qreal frac = val - (qreal)ival;
80
81 int ifrac = (int)(frac * 1000000000);
82 if (ifrac == 1000000000) {
83 ++ival;
84 ifrac = 0;
85 }
86 char output[256];
87 int i = 0;
88 while (ival) {
89 output[i] = '0' + (ival % 10);
90 ++i;
91 ival /= 10;
92 }
93 int fact = 100000000;
94 if (i == 0) {
95 *(buf++) = '0';
96 } else {
97 while (i) {
98 *(buf++) = output[--i];
99 fact /= 10;
100 ifrac /= 10;
101 }
102 }
103
104 if (ifrac) {
105 *(buf++) = '.';
106 while (fact) {
107 *(buf++) = '0' + ((ifrac/fact) % 10);
108 fact /= 10;
109 }
110 }
111 *(buf++) = ' ';
112 *buf = 0;
113 return ret;
114}
115
116const char *qt_int_to_string(int val, char *buf) {
117 const char *ret = buf;
118 if (val < 0) {
119 *(buf++) = '-';
120 val = -val;
121 }
122 char output[256];
123 int i = 0;
124 while (val) {
125 output[i] = '0' + (val % 10);
126 ++i;
127 val /= 10;
128 }
129 if (i == 0) {
130 *(buf++) = '0';
131 } else {
132 while (i)
133 *(buf++) = output[--i];
134 }
135 *(buf++) = ' ';
136 *buf = 0;
137 return ret;
138}
139
140
141namespace QPdf {
142 ByteStream::ByteStream(QByteArray *byteArray, bool fileBacking)
143 : dev(new QBuffer(byteArray)),
144 fileBackingEnabled(fileBacking),
145 fileBackingActive(false),
146 handleDirty(false)
147 {
148 dev->open(QIODevice::ReadWrite | QIODevice::Append);
149 }
150
151 ByteStream::ByteStream(bool fileBacking)
152 : dev(new QBuffer(&ba)),
153 fileBackingEnabled(fileBacking),
154 fileBackingActive(false),
155 handleDirty(false)
156 {
157 dev->open(QIODevice::ReadWrite);
158 }
159
160 ByteStream::~ByteStream()
161 {
162 delete dev;
163 }
164
165 ByteStream &ByteStream::operator <<(char chr)
166 {
167 if (handleDirty) prepareBuffer();
168 dev->write(&chr, 1);
169 return *this;
170 }
171
172 ByteStream &ByteStream::operator <<(const char *str)
173 {
174 if (handleDirty) prepareBuffer();
175 dev->write(str, strlen(str));
176 return *this;
177 }
178
179 ByteStream &ByteStream::operator <<(const QByteArray &str)
180 {
181 if (handleDirty) prepareBuffer();
182 dev->write(str);
183 return *this;
184 }
185
186 ByteStream &ByteStream::operator <<(const ByteStream &src)
187 {
188 Q_ASSERT(!src.dev->isSequential());
189 if (handleDirty) prepareBuffer();
190 // We do play nice here, even though it looks ugly.
191 // We save the position and restore it afterwards.
192 ByteStream &s = const_cast<ByteStream&>(src);
193 qint64 pos = s.dev->pos();
194 s.dev->reset();
195 while (!s.dev->atEnd()) {
196 QByteArray buf = s.dev->read(chunkSize());
197 dev->write(buf);
198 }
199 s.dev->seek(pos);
200 return *this;
201 }
202
203 ByteStream &ByteStream::operator <<(qreal val) {
204 char buf[256];
205 qt_real_to_string(val, buf);
206 *this << buf;
207 return *this;
208 }
209
210 ByteStream &ByteStream::operator <<(int val) {
211 char buf[256];
212 qt_int_to_string(val, buf);
213 *this << buf;
214 return *this;
215 }
216
217 ByteStream &ByteStream::operator <<(const QPointF &p) {
218 char buf[256];
219 qt_real_to_string(p.x(), buf);
220 *this << buf;
221 qt_real_to_string(p.y(), buf);
222 *this << buf;
223 return *this;
224 }
225
226 QIODevice *ByteStream::stream()
227 {
228 dev->reset();
229 handleDirty = true;
230 return dev;
231 }
232
233 void ByteStream::clear()
234 {
235 dev->open(QIODevice::ReadWrite | QIODevice::Truncate);
236 }
237
238 void ByteStream::constructor_helper(QByteArray *ba)
239 {
240 delete dev;
241 dev = new QBuffer(ba);
242 dev->open(QIODevice::ReadWrite);
243 }
244
245 void ByteStream::prepareBuffer()
246 {
247 Q_ASSERT(!dev->isSequential());
248 qint64 size = dev->size();
249 if (fileBackingEnabled && !fileBackingActive
250 && size > maxMemorySize()) {
251 // Switch to file backing.
252 QTemporaryFile *newFile = new QTemporaryFile;
253 newFile->open();
254 dev->reset();
255 while (!dev->atEnd()) {
256 QByteArray buf = dev->read(chunkSize());
257 newFile->write(buf);
258 }
259 delete dev;
260 dev = newFile;
261 ba.clear();
262 fileBackingActive = true;
263 }
264 if (dev->pos() != size) {
265 dev->seek(size);
266 handleDirty = false;
267 }
268 }
269}
270
271#define QT_PATH_ELEMENT(elm)
272
273QByteArray QPdf::generatePath(const QPainterPath &path, const QTransform &matrix, PathFlags flags)
274{
275 QByteArray result;
276 if (!path.elementCount())
277 return result;
278
279 ByteStream s(&result);
280
281 int start = -1;
282 for (int i = 0; i < path.elementCount(); ++i) {
283 const QPainterPath::Element &elm = path.elementAt(i);
284 switch (elm.type) {
285 case QPainterPath::MoveToElement:
286 if (start >= 0
287 && path.elementAt(start).x == path.elementAt(i-1).x
288 && path.elementAt(start).y == path.elementAt(i-1).y)
289 s << "h\n";
290 s << matrix.map(QPointF(elm.x, elm.y)) << "m\n";
291 start = i;
292 break;
293 case QPainterPath::LineToElement:
294 s << matrix.map(QPointF(elm.x, elm.y)) << "l\n";
295 break;
296 case QPainterPath::CurveToElement:
297 Q_ASSERT(path.elementAt(i+1).type == QPainterPath::CurveToDataElement);
298 Q_ASSERT(path.elementAt(i+2).type == QPainterPath::CurveToDataElement);
299 s << matrix.map(QPointF(elm.x, elm.y))
300 << matrix.map(QPointF(path.elementAt(i+1).x, path.elementAt(i+1).y))
301 << matrix.map(QPointF(path.elementAt(i+2).x, path.elementAt(i+2).y))
302 << "c\n";
303 i += 2;
304 break;
305 default:
306 qFatal("QPdf::generatePath(), unhandled type: %d", elm.type);
307 }
308 }
309 if (start >= 0
310 && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x
311 && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y)
312 s << "h\n";
313
314 Qt::FillRule fillRule = path.fillRule();
315
316 const char *op = "";
317 switch (flags) {
318 case ClipPath:
319 op = (fillRule == Qt::WindingFill) ? "W n\n" : "W* n\n";
320 break;
321 case FillPath:
322 op = (fillRule == Qt::WindingFill) ? "f\n" : "f*\n";
323 break;
324 case StrokePath:
325 op = "S\n";
326 break;
327 case FillAndStrokePath:
328 op = (fillRule == Qt::WindingFill) ? "B\n" : "B*\n";
329 break;
330 }
331 s << op;
332 return result;
333}
334
335QByteArray QPdf::generateMatrix(const QTransform &matrix)
336{
337 QByteArray result;
338 ByteStream s(&result);
339 s << matrix.m11()
340 << matrix.m12()
341 << matrix.m21()
342 << matrix.m22()
343 << matrix.dx()
344 << matrix.dy()
345 << "cm\n";
346 return result;
347}
348
349QByteArray QPdf::generateDashes(const QPen &pen)
350{
351 QByteArray result;
352 ByteStream s(&result);
353 s << '[';
354
355 QVector<qreal> dasharray = pen.dashPattern();
356 qreal w = pen.widthF();
357 if (w < 0.001)
358 w = 1;
359 for (int i = 0; i < dasharray.size(); ++i) {
360 qreal dw = dasharray.at(i)*w;
361 if (dw < 0.0001) dw = 0.0001;
362 s << dw;
363 }
364 s << ']';
365 //qDebug() << "dasharray: pen has" << dasharray;
366 //qDebug() << " => " << result;
367 return result;
368}
369
370
371
372static const char* pattern_for_brush[] = {
373 0, // NoBrush
374 0, // SolidPattern
375 "0 J\n"
376 "6 w\n"
377 "[] 0 d\n"
378 "4 0 m\n"
379 "4 8 l\n"
380 "0 4 m\n"
381 "8 4 l\n"
382 "S\n", // Dense1Pattern
383
384 "0 J\n"
385 "2 w\n"
386 "[6 2] 1 d\n"
387 "0 0 m\n"
388 "0 8 l\n"
389 "8 0 m\n"
390 "8 8 l\n"
391 "S\n"
392 "[] 0 d\n"
393 "2 0 m\n"
394 "2 8 l\n"
395 "6 0 m\n"
396 "6 8 l\n"
397 "S\n"
398 "[6 2] -3 d\n"
399 "4 0 m\n"
400 "4 8 l\n"
401 "S\n", // Dense2Pattern
402
403 "0 J\n"
404 "2 w\n"
405 "[6 2] 1 d\n"
406 "0 0 m\n"
407 "0 8 l\n"
408 "8 0 m\n"
409 "8 8 l\n"
410 "S\n"
411 "[2 2] -1 d\n"
412 "2 0 m\n"
413 "2 8 l\n"
414 "6 0 m\n"
415 "6 8 l\n"
416 "S\n"
417 "[6 2] -3 d\n"
418 "4 0 m\n"
419 "4 8 l\n"
420 "S\n", // Dense3Pattern
421
422 "0 J\n"
423 "2 w\n"
424 "[2 2] 1 d\n"
425 "0 0 m\n"
426 "0 8 l\n"
427 "8 0 m\n"
428 "8 8 l\n"
429 "S\n"
430 "[2 2] -1 d\n"
431 "2 0 m\n"
432 "2 8 l\n"
433 "6 0 m\n"
434 "6 8 l\n"
435 "S\n"
436 "[2 2] 1 d\n"
437 "4 0 m\n"
438 "4 8 l\n"
439 "S\n", // Dense4Pattern
440
441 "0 J\n"
442 "2 w\n"
443 "[2 6] -1 d\n"
444 "0 0 m\n"
445 "0 8 l\n"
446 "8 0 m\n"
447 "8 8 l\n"
448 "S\n"
449 "[2 2] 1 d\n"
450 "2 0 m\n"
451 "2 8 l\n"
452 "6 0 m\n"
453 "6 8 l\n"
454 "S\n"
455 "[2 6] 3 d\n"
456 "4 0 m\n"
457 "4 8 l\n"
458 "S\n", // Dense5Pattern
459
460 "0 J\n"
461 "2 w\n"
462 "[2 6] -1 d\n"
463 "0 0 m\n"
464 "0 8 l\n"
465 "8 0 m\n"
466 "8 8 l\n"
467 "S\n"
468 "[2 6] 3 d\n"
469 "4 0 m\n"
470 "4 8 l\n"
471 "S\n", // Dense6Pattern
472
473 "0 J\n"
474 "2 w\n"
475 "[2 6] -1 d\n"
476 "0 0 m\n"
477 "0 8 l\n"
478 "8 0 m\n"
479 "8 8 l\n"
480 "S\n", // Dense7Pattern
481
482 "1 w\n"
483 "0 4 m\n"
484 "8 4 l\n"
485 "S\n", // HorPattern
486
487 "1 w\n"
488 "4 0 m\n"
489 "4 8 l\n"
490 "S\n", // VerPattern
491
492 "1 w\n"
493 "4 0 m\n"
494 "4 8 l\n"
495 "0 4 m\n"
496 "8 4 l\n"
497 "S\n", // CrossPattern
498
499 "1 w\n"
500 "-1 5 m\n"
501 "5 -1 l\n"
502 "3 9 m\n"
503 "9 3 l\n"
504 "S\n", // BDiagPattern
505
506 "1 w\n"
507 "-1 3 m\n"
508 "5 9 l\n"
509 "3 -1 m\n"
510 "9 5 l\n"
511 "S\n", // FDiagPattern
512
513 "1 w\n"
514 "-1 3 m\n"
515 "5 9 l\n"
516 "3 -1 m\n"
517 "9 5 l\n"
518 "-1 5 m\n"
519 "5 -1 l\n"
520 "3 9 m\n"
521 "9 3 l\n"
522 "S\n", // DiagCrossPattern
523};
524
525QByteArray QPdf::patternForBrush(const QBrush &b)
526{
527 int style = b.style();
528 if (style > Qt::DiagCrossPattern)
529 return QByteArray();
530 return pattern_for_brush[style];
531}
532
533#ifdef USE_NATIVE_GRADIENTS
534static void writeTriangleLine(uchar *&data, int xpos, int ypos, int xoff, int yoff, uint rgb, uchar flag, bool alpha)
535{
536 data[0] = flag;
537 data[1] = (uchar)(xpos >> 16);
538 data[2] = (uchar)(xpos >> 8);
539 data[3] = (uchar)(xpos >> 0);
540 data[4] = (uchar)(ypos >> 16);
541 data[5] = (uchar)(ypos >> 8);
542 data[6] = (uchar)(ypos >> 0);
543 data += 7;
544 if (alpha) {
545 *data++ = (uchar)qAlpha(rgb);
546 } else {
547 *data++ = (uchar)qRed(rgb);
548 *data++ = (uchar)qGreen(rgb);
549 *data++ = (uchar)qBlue(rgb);
550 }
551 xpos += xoff;
552 ypos += yoff;
553 data[0] = flag;
554 data[1] = (uchar)(xpos >> 16);
555 data[2] = (uchar)(xpos >> 8);
556 data[3] = (uchar)(xpos >> 0);
557 data[4] = (uchar)(ypos >> 16);
558 data[5] = (uchar)(ypos >> 8);
559 data[6] = (uchar)(ypos >> 0);
560 data += 7;
561 if (alpha) {
562 *data++ = (uchar)qAlpha(rgb);
563 } else {
564 *data++ = (uchar)qRed(rgb);
565 *data++ = (uchar)qGreen(rgb);
566 *data++ = (uchar)qBlue(rgb);
567 }
568}
569
570
571QByteArray QPdf::generateLinearGradientShader(const QLinearGradient *gradient, const QPointF *page_rect, bool alpha)
572{
573 // generate list of triangles with colors
574 QPointF start = gradient->start();
575 QPointF stop = gradient->finalStop();
576 QGradientStops stops = gradient->stops();
577 QPointF offset = stop - start;
578 QGradient::Spread spread = gradient->spread();
579
580 if (gradient->spread() == QGradient::ReflectSpread) {
581 offset *= 2;
582 for (int i = stops.size() - 2; i >= 0; --i) {
583 QGradientStop stop = stops.at(i);
584 stop.first = 2. - stop.first;
585 stops.append(stop);
586 }
587 for (int i = 0 ; i < stops.size(); ++i)
588 stops[i].first /= 2.;
589 }
590
591 QPointF orthogonal(offset.y(), -offset.x());
592 qreal length = offset.x()*offset.x() + offset.y()*offset.y();
593
594 // find the max and min values in offset and orth direction that are needed to cover
595 // the whole page
596 int off_min = INT_MAX;
597 int off_max = INT_MIN;
598 qreal ort_min = INT_MAX;
599 qreal ort_max = INT_MIN;
600 for (int i = 0; i < 4; ++i) {
601 qreal off = ((page_rect[i].x() - start.x()) * offset.x() + (page_rect[i].y() - start.y()) * offset.y())/length;
602 qreal ort = ((page_rect[i].x() - start.x()) * orthogonal.x() + (page_rect[i].y() - start.y()) * orthogonal.y())/length;
603 off_min = qMin(off_min, qFloor(off));
604 off_max = qMax(off_max, qCeil(off));
605 ort_min = qMin(ort_min, ort);
606 ort_max = qMax(ort_max, ort);
607 }
608 ort_min -= 1;
609 ort_max += 1;
610
611 start += off_min * offset + ort_min * orthogonal;
612 orthogonal *= (ort_max - ort_min);
613 int num = off_max - off_min;
614
615 QPointF gradient_rect[4] = { start,
616 start + orthogonal,
617 start + num*offset,
618 start + num*offset + orthogonal };
619 qreal xmin = gradient_rect[0].x();
620 qreal xmax = gradient_rect[0].x();
621 qreal ymin = gradient_rect[0].y();
622 qreal ymax = gradient_rect[0].y();
623 for (int i = 1; i < 4; ++i) {
624 xmin = qMin(xmin, gradient_rect[i].x());
625 xmax = qMax(xmax, gradient_rect[i].x());
626 ymin = qMin(ymin, gradient_rect[i].y());
627 ymax = qMax(ymax, gradient_rect[i].y());
628 }
629 xmin -= 1000;
630 xmax += 1000;
631 ymin -= 1000;
632 ymax += 1000;
633 start -= QPointF(xmin, ymin);
634 qreal factor_x = qreal(1<<24)/(xmax - xmin);
635 qreal factor_y = qreal(1<<24)/(ymax - ymin);
636 int xoff = (int)(orthogonal.x()*factor_x);
637 int yoff = (int)(orthogonal.y()*factor_y);
638
639 QByteArray triangles;
640 triangles.resize(spread == QGradient::PadSpread ? 20*(stops.size()+2) : 20*num*stops.size());
641 uchar *data = (uchar *) triangles.data();
642 if (spread == QGradient::PadSpread) {
643 if (off_min > 0 || off_max < 1) {
644 // linear gradient outside of page
645 const QGradientStop &current_stop = off_min > 0 ? stops.at(stops.size()-1) : stops.at(0);
646 uint rgb = current_stop.second.rgba();
647 int xpos = (int)(start.x()*factor_x);
648 int ypos = (int)(start.y()*factor_y);
649 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, 0, alpha);
650 start += num*offset;
651 xpos = (int)(start.x()*factor_x);
652 ypos = (int)(start.y()*factor_y);
653 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, 1, alpha);
654 } else {
655 int flag = 0;
656 if (off_min < 0) {
657 uint rgb = stops.at(0).second.rgba();
658 int xpos = (int)(start.x()*factor_x);
659 int ypos = (int)(start.y()*factor_y);
660 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
661 start -= off_min*offset;
662 flag = 1;
663 }
664 for (int s = 0; s < stops.size(); ++s) {
665 const QGradientStop &current_stop = stops.at(s);
666 uint rgb = current_stop.second.rgba();
667 int xpos = (int)(start.x()*factor_x);
668 int ypos = (int)(start.y()*factor_y);
669 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
670 if (s < stops.size()-1)
671 start += offset*(stops.at(s+1).first - stops.at(s).first);
672 flag = 1;
673 }
674 if (off_max > 1) {
675 start += (off_max - 1)*offset;
676 uint rgb = stops.at(stops.size()-1).second.rgba();
677 int xpos = (int)(start.x()*factor_x);
678 int ypos = (int)(start.y()*factor_y);
679 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
680 }
681 }
682 } else {
683 for (int i = 0; i < num; ++i) {
684 uchar flag = 0;
685 for (int s = 0; s < stops.size(); ++s) {
686 uint rgb = stops.at(s).second.rgba();
687 int xpos = (int)(start.x()*factor_x);
688 int ypos = (int)(start.y()*factor_y);
689 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
690 if (s < stops.size()-1)
691 start += offset*(stops.at(s+1).first - stops.at(s).first);
692 flag = 1;
693 }
694 }
695 }
696 triangles.resize((char *)data - triangles.constData());
697
698 QByteArray shader;
699 QPdf::ByteStream s(&shader);
700 s << "<<\n"
701 "/ShadingType 4\n"
702 "/ColorSpace " << (alpha ? "/DeviceGray\n" : "/DeviceRGB\n") <<
703 "/AntiAlias true\n"
704 "/BitsPerCoordinate 24\n"
705 "/BitsPerComponent 8\n"
706 "/BitsPerFlag 8\n"
707 "/Decode [" << xmin << xmax << ymin << ymax << (alpha ? "0 1]\n" : "0 1 0 1 0 1]\n") <<
708 "/AntiAlias true\n"
709 "/Length " << triangles.length() << "\n"
710 ">>\n"
711 "stream\n" << triangles << "endstream\n"
712 "endobj\n";
713 return shader;
714}
715#endif
716
717static void moveToHook(qfixed x, qfixed y, void *data)
718{
719 QPdf::Stroker *t = (QPdf::Stroker *)data;
720 if (!t->first)
721 *t->stream << "h\n";
722 if (!t->cosmeticPen)
723 t->matrix.map(x, y, &x, &y);
724 *t->stream << x << y << "m\n";
725 t->first = false;
726}
727
728static void lineToHook(qfixed x, qfixed y, void *data)
729{
730 QPdf::Stroker *t = (QPdf::Stroker *)data;
731 if (!t->cosmeticPen)
732 t->matrix.map(x, y, &x, &y);
733 *t->stream << x << y << "l\n";
734}
735
736static void cubicToHook(qfixed c1x, qfixed c1y,
737 qfixed c2x, qfixed c2y,
738 qfixed ex, qfixed ey,
739 void *data)
740{
741 QPdf::Stroker *t = (QPdf::Stroker *)data;
742 if (!t->cosmeticPen) {
743 t->matrix.map(c1x, c1y, &c1x, &c1y);
744 t->matrix.map(c2x, c2y, &c2x, &c2y);
745 t->matrix.map(ex, ey, &ex, &ey);
746 }
747 *t->stream << c1x << c1y
748 << c2x << c2y
749 << ex << ey
750 << "c\n";
751}
752
753QPdf::Stroker::Stroker()
754 : stream(0),
755 first(true),
756 dashStroker(&basicStroker)
757{
758 stroker = &basicStroker;
759 basicStroker.setMoveToHook(moveToHook);
760 basicStroker.setLineToHook(lineToHook);
761 basicStroker.setCubicToHook(cubicToHook);
762 cosmeticPen = true;
763 basicStroker.setStrokeWidth(.1);
764}
765
766void QPdf::Stroker::setPen(const QPen &pen)
767{
768 if (pen.style() == Qt::NoPen) {
769 stroker = 0;
770 return;
771 }
772 qreal w = pen.widthF();
773 bool zeroWidth = w < 0.0001;
774 cosmeticPen = pen.isCosmetic();
775 if (zeroWidth)
776 w = .1;
777
778 basicStroker.setStrokeWidth(w);
779 basicStroker.setCapStyle(pen.capStyle());
780 basicStroker.setJoinStyle(pen.joinStyle());
781 basicStroker.setMiterLimit(pen.miterLimit());
782
783 QVector<qreal> dashpattern = pen.dashPattern();
784 if (zeroWidth) {
785 for (int i = 0; i < dashpattern.size(); ++i)
786 dashpattern[i] *= 10.;
787 }
788 if (!dashpattern.isEmpty()) {
789 dashStroker.setDashPattern(dashpattern);
790 dashStroker.setDashOffset(pen.dashOffset());
791 stroker = &dashStroker;
792 } else {
793 stroker = &basicStroker;
794 }
795}
796
797void QPdf::Stroker::strokePath(const QPainterPath &path)
798{
799 if (!stroker)
800 return;
801 first = true;
802
803 stroker->strokePath(path, this, cosmeticPen ? matrix : QTransform());
804 *stream << "h f\n";
805}
806
807QByteArray QPdf::ascii85Encode(const QByteArray &input)
808{
809 int isize = input.size()/4*4;
810 QByteArray output;
811 output.resize(input.size()*5/4+7);
812 char *out = output.data();
813 const uchar *in = (const uchar *)input.constData();
814 for (int i = 0; i < isize; i += 4) {
815 uint val = (((uint)in[i])<<24) + (((uint)in[i+1])<<16) + (((uint)in[i+2])<<8) + (uint)in[i+3];
816 if (val == 0) {
817 *out = 'z';
818 ++out;
819 } else {
820 char base[5];
821 base[4] = val % 85;
822 val /= 85;
823 base[3] = val % 85;
824 val /= 85;
825 base[2] = val % 85;
826 val /= 85;
827 base[1] = val % 85;
828 val /= 85;
829 base[0] = val % 85;
830 *(out++) = base[0] + '!';
831 *(out++) = base[1] + '!';
832 *(out++) = base[2] + '!';
833 *(out++) = base[3] + '!';
834 *(out++) = base[4] + '!';
835 }
836 }
837 //write the last few bytes
838 int remaining = input.size() - isize;
839 if (remaining) {
840 uint val = 0;
841 for (int i = isize; i < input.size(); ++i)
842 val = (val << 8) + in[i];
843 val <<= 8*(4-remaining);
844 char base[5];
845 base[4] = val % 85;
846 val /= 85;
847 base[3] = val % 85;
848 val /= 85;
849 base[2] = val % 85;
850 val /= 85;
851 base[1] = val % 85;
852 val /= 85;
853 base[0] = val % 85;
854 for (int i = 0; i < remaining+1; ++i)
855 *(out++) = base[i] + '!';
856 }
857 *(out++) = '~';
858 *(out++) = '>';
859 output.resize(out-output.data());
860 return output;
861}
862
863const char *QPdf::toHex(ushort u, char *buffer)
864{
865 int i = 3;
866 while (i >= 0) {
867 ushort hex = (u & 0x000f);
868 if (hex < 0x0a)
869 buffer[i] = '0'+hex;
870 else
871 buffer[i] = 'A'+(hex-0x0a);
872 u = u >> 4;
873 i--;
874 }
875 buffer[4] = '\0';
876 return buffer;
877}
878
879const char *QPdf::toHex(uchar u, char *buffer)
880{
881 int i = 1;
882 while (i >= 0) {
883 ushort hex = (u & 0x000f);
884 if (hex < 0x0a)
885 buffer[i] = '0'+hex;
886 else
887 buffer[i] = 'A'+(hex-0x0a);
888 u = u >> 4;
889 i--;
890 }
891 buffer[2] = '\0';
892 return buffer;
893}
894
895#define Q_MM(n) int((n * 720 + 127) / 254)
896#define Q_IN(n) int(n * 72)
897
898static const char * const psToStr[QPrinter::NPaperSize+1] =
899{
900 "A4", "B5", "Letter", "Legal", "Executive",
901 "A0", "A1", "A2", "A3", "A5", "A6", "A7", "A8", "A9", "B0", "B1",
902 "B10", "B2", "B3", "B4", "B6", "B7", "B8", "B9", "C5E", "Comm10E",
903 "DLE", "Folio", "Ledger", "Tabloid", 0
904};
905
906QPdf::PaperSize QPdf::paperSize(QPrinter::PaperSize paperSize)
907{
908 QSizeF s = qt_paperSizeToQSizeF(paperSize);
909 PaperSize p = { Q_MM(s.width()), Q_MM(s.height()) };
910 return p;
911}
912
913const char *QPdf::paperSizeToString(QPrinter::PaperSize paperSize)
914{
915 return psToStr[paperSize];
916}
917
918
919QByteArray QPdf::stripSpecialCharacters(const QByteArray &string)
920{
921 QByteArray s = string;
922 s.replace(' ', "");
923 s.replace('(', "");
924 s.replace(')', "");
925 s.replace('<', "");
926 s.replace('>', "");
927 s.replace('[', "");
928 s.replace(']', "");
929 s.replace('{', "");
930 s.replace('}', "");
931 s.replace('/', "");
932 s.replace('%', "");
933 return s;
934}
935
936
937// -------------------------- base engine, shared code between PS and PDF -----------------------
938
939QPdfBaseEngine::QPdfBaseEngine(QPdfBaseEnginePrivate &dd, PaintEngineFeatures f)
940 : QAlphaPaintEngine(dd, f)
941{
942 Q_D(QPdfBaseEngine);
943#if !defined(QT_NO_CUPS) && (!defined(QT_NO_LIBRARY) || defined(Q_WS_PM))
944 if (QCUPSSupport::isAvailable()) {
945 QCUPSSupport cups;
946 const cups_dest_t* printers = cups.availablePrinters();
947 int prnCount = cups.availablePrintersCount();
948
949 for (int i = 0; i < prnCount; ++i) {
950 if (printers[i].is_default) {
951 d->printerName = QString::fromLocal8Bit(printers[i].name);
952 break;
953 }
954 }
955
956 } else
957#endif
958 {
959 d->printerName = QString::fromLocal8Bit(qgetenv("PRINTER"));
960 if (d->printerName.isEmpty())
961 d->printerName = QString::fromLocal8Bit(qgetenv("LPDEST"));
962 if (d->printerName.isEmpty())
963 d->printerName = QString::fromLocal8Bit(qgetenv("NPRINTER"));
964 if (d->printerName.isEmpty())
965 d->printerName = QString::fromLocal8Bit(qgetenv("NGPRINTER"));
966 }
967}
968
969void QPdfBaseEngine::drawPoints (const QPointF *points, int pointCount)
970{
971 if (!points)
972 return;
973
974 QPainterPath p;
975 for (int i=0; i!=pointCount;++i) {
976 p.moveTo(points[i]);
977 p.lineTo(points[i] + QPointF(0, 0.001));
978 }
979 drawPath(p);
980}
981
982void QPdfBaseEngine::drawLines (const QLineF *lines, int lineCount)
983{
984 if (!lines)
985 return;
986
987 QPainterPath p;
988 for (int i=0; i!=lineCount;++i) {
989 p.moveTo(lines[i].p1());
990 p.lineTo(lines[i].p2());
991 }
992 drawPath(p);
993}
994
995void QPdfBaseEngine::drawRects (const QRectF *rects, int rectCount)
996{
997 if (!rects)
998 return;
999
1000 Q_D(QPdfBaseEngine);
1001 if (d->useAlphaEngine) {
1002 QAlphaPaintEngine::drawRects(rects, rectCount);
1003 if (!continueCall())
1004 return;
1005 }
1006
1007 if (d->clipEnabled && d->allClipped)
1008 return;
1009 if (!d->hasPen && !d->hasBrush)
1010 return;
1011
1012 QBrush penBrush = d->pen.brush();
1013 if (d->simplePen || !d->hasPen) {
1014 // draw strokes natively in this case for better output
1015 if(!d->simplePen && !d->stroker.matrix.isIdentity())
1016 *d->currentPage << "q\n" << QPdf::generateMatrix(d->stroker.matrix);
1017 for (int i = 0; i < rectCount; ++i)
1018 *d->currentPage << rects[i].x() << rects[i].y() << rects[i].width() << rects[i].height() << "re\n";
1019 *d->currentPage << (d->hasPen ? (d->hasBrush ? "B\n" : "S\n") : "f\n");
1020 if(!d->simplePen && !d->stroker.matrix.isIdentity())
1021 *d->currentPage << "Q\n";
1022 } else {
1023 QPainterPath p;
1024 for (int i=0; i!=rectCount; ++i)
1025 p.addRect(rects[i]);
1026 drawPath(p);
1027 }
1028}
1029
1030void QPdfBaseEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1031{
1032 Q_D(QPdfBaseEngine);
1033
1034 if (d->useAlphaEngine) {
1035 QAlphaPaintEngine::drawPolygon(points, pointCount, mode);
1036 if (!continueCall())
1037 return;
1038 }
1039
1040 if (!points || !pointCount)
1041 return;
1042
1043 bool hb = d->hasBrush;
1044 QPainterPath p;
1045
1046 switch(mode) {
1047 case OddEvenMode:
1048 p.setFillRule(Qt::OddEvenFill);
1049 break;
1050 case ConvexMode:
1051 case WindingMode:
1052 p.setFillRule(Qt::WindingFill);
1053 break;
1054 case PolylineMode:
1055 d->hasBrush = false;
1056 break;
1057 default:
1058 break;
1059 }
1060
1061 p.moveTo(points[0]);
1062 for (int i = 1; i < pointCount; ++i)
1063 p.lineTo(points[i]);
1064
1065 if (mode != PolylineMode)
1066 p.closeSubpath();
1067 drawPath(p);
1068
1069 d->hasBrush = hb;
1070}
1071
1072void QPdfBaseEngine::drawPath (const QPainterPath &p)
1073{
1074 Q_D(QPdfBaseEngine);
1075
1076 if (d->useAlphaEngine) {
1077 QAlphaPaintEngine::drawPath(p);
1078 if (!continueCall())
1079 return;
1080 }
1081
1082 if (d->clipEnabled && d->allClipped)
1083 return;
1084 if (!d->hasPen && !d->hasBrush)
1085 return;
1086
1087 if (d->simplePen) {
1088 // draw strokes natively in this case for better output
1089 *d->currentPage << QPdf::generatePath(p, QTransform(), d->hasBrush ? QPdf::FillAndStrokePath : QPdf::StrokePath);
1090 } else {
1091 if (d->hasBrush)
1092 *d->currentPage << QPdf::generatePath(p, d->stroker.matrix, QPdf::FillPath);
1093 if (d->hasPen) {
1094 *d->currentPage << "q\n";
1095 QBrush b = d->brush;
1096 d->brush = d->pen.brush();
1097 setBrush();
1098 d->stroker.strokePath(p);
1099 *d->currentPage << "Q\n";
1100 d->brush = b;
1101 }
1102 }
1103}
1104
1105void QPdfBaseEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
1106{
1107 Q_D(QPdfBaseEngine);
1108
1109 if (d->useAlphaEngine) {
1110 QAlphaPaintEngine::drawTextItem(p, textItem);
1111 if (!continueCall())
1112 return;
1113 }
1114
1115 if (!d->hasPen || (d->clipEnabled && d->allClipped))
1116 return;
1117
1118 if (d->stroker.matrix.type() >= QTransform::TxProject) {
1119 QPaintEngine::drawTextItem(p, textItem);
1120 return;
1121 }
1122
1123 *d->currentPage << "q\n";
1124 if(!d->simplePen)
1125 *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
1126
1127 bool hp = d->hasPen;
1128 d->hasPen = false;
1129 QBrush b = d->brush;
1130 d->brush = d->pen.brush();
1131 setBrush();
1132
1133 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
1134 Q_ASSERT(ti.fontEngine->type() != QFontEngine::Multi);
1135 d->drawTextItem(p, ti);
1136 d->hasPen = hp;
1137 d->brush = b;
1138 *d->currentPage << "Q\n";
1139}
1140
1141
1142void QPdfBaseEngine::updateState(const QPaintEngineState &state)
1143{
1144 Q_D(QPdfBaseEngine);
1145
1146 if (d->useAlphaEngine) {
1147 QAlphaPaintEngine::updateState(state);
1148 if (!continueCall())
1149 return;
1150 }
1151
1152 QPaintEngine::DirtyFlags flags = state.state();
1153
1154 if (flags & DirtyTransform)
1155 d->stroker.matrix = state.transform();
1156
1157 if (flags & DirtyPen) {
1158 d->pen = state.pen();
1159 d->hasPen = d->pen.style() != Qt::NoPen;
1160 d->stroker.setPen(d->pen);
1161 QBrush penBrush = d->pen.brush();
1162 bool oldSimple = d->simplePen;
1163 d->simplePen = (d->hasPen && (penBrush.style() == Qt::SolidPattern) && penBrush.isOpaque());
1164 if (oldSimple != d->simplePen)
1165 flags |= DirtyTransform;
1166 }
1167 if (flags & DirtyBrush) {
1168 d->brush = state.brush();
1169 d->hasBrush = d->brush.style() != Qt::NoBrush;
1170 }
1171 if (flags & DirtyBrushOrigin) {
1172 d->brushOrigin = state.brushOrigin();
1173 flags |= DirtyBrush;
1174 }
1175 if (flags & DirtyOpacity)
1176 d->opacity = state.opacity();
1177
1178 bool ce = d->clipEnabled;
1179 if (flags & DirtyClipPath) {
1180 d->clipEnabled = true;
1181 updateClipPath(state.clipPath(), state.clipOperation());
1182 } else if (flags & DirtyClipRegion) {
1183 d->clipEnabled = true;
1184 QPainterPath path;
1185 QVector<QRect> rects = state.clipRegion().rects();
1186 for (int i = 0; i < rects.size(); ++i)
1187 path.addRect(rects.at(i));
1188 updateClipPath(path, state.clipOperation());
1189 flags |= DirtyClipPath;
1190 } else if (flags & DirtyClipEnabled) {
1191 d->clipEnabled = state.isClipEnabled();
1192 }
1193
1194 if (ce != d->clipEnabled)
1195 flags |= DirtyClipPath;
1196 else if (!d->clipEnabled)
1197 flags &= ~DirtyClipPath;
1198
1199 setupGraphicsState(flags);
1200}
1201
1202void QPdfBaseEngine::setupGraphicsState(QPaintEngine::DirtyFlags flags)
1203{
1204 Q_D(QPdfBaseEngine);
1205 if (flags & DirtyClipPath)
1206 flags |= DirtyTransform|DirtyPen|DirtyBrush;
1207
1208 if (flags & DirtyTransform) {
1209 *d->currentPage << "Q\n";
1210 flags |= DirtyPen|DirtyBrush;
1211 }
1212
1213 if (flags & DirtyClipPath) {
1214 *d->currentPage << "Q q\n";
1215
1216 d->allClipped = false;
1217 if (d->clipEnabled && !d->clips.isEmpty()) {
1218 for (int i = 0; i < d->clips.size(); ++i) {
1219 if (d->clips.at(i).isEmpty()) {
1220 d->allClipped = true;
1221 break;
1222 }
1223 }
1224 if (!d->allClipped) {
1225 for (int i = 0; i < d->clips.size(); ++i) {
1226 *d->currentPage << QPdf::generatePath(d->clips.at(i), QTransform(), QPdf::ClipPath);
1227 }
1228 }
1229 }
1230 }
1231
1232 if (flags & DirtyTransform) {
1233 *d->currentPage << "q\n";
1234 if (d->simplePen && !d->stroker.matrix.isIdentity())
1235 *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
1236 }
1237 if (flags & DirtyBrush)
1238 setBrush();
1239 if (d->simplePen && (flags & DirtyPen))
1240 setPen();
1241}
1242
1243extern QPainterPath qt_regionToPath(const QRegion &region);
1244
1245void QPdfBaseEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
1246{
1247 Q_D(QPdfBaseEngine);
1248 QPainterPath path = d->stroker.matrix.map(p);
1249 //qDebug() << "updateClipPath: " << d->stroker.matrix << p.boundingRect() << path.boundingRect() << op;
1250
1251 if (op == Qt::NoClip) {
1252 d->clipEnabled = false;
1253 d->clips.clear();
1254 } else if (op == Qt::ReplaceClip) {
1255 d->clips.clear();
1256 d->clips.append(path);
1257 } else if (op == Qt::IntersectClip) {
1258 d->clips.append(path);
1259 } else { // UniteClip
1260 // ask the painter for the current clipping path. that's the easiest solution
1261 path = painter()->clipPath();
1262 path = d->stroker.matrix.map(path);
1263 d->clips.clear();
1264 d->clips.append(path);
1265 }
1266
1267 if (d->useAlphaEngine) {
1268 // if we have an alpha region, we have to subtract that from the
1269 // any existing clip region since that region will be filled in
1270 // later with images
1271 QPainterPath alphaClip = qt_regionToPath(alphaClipping());
1272 if (!alphaClip.isEmpty()) {
1273 if (!d->clipEnabled) {
1274 QRect r = d->fullPage ? d->paperRect() : d->pageRect();
1275 QPainterPath dev;
1276 dev.addRect(QRect(0, 0, r.width(), r.height()));
1277 if (path.isEmpty())
1278 path = dev;
1279 else
1280 path = path.intersected(dev);
1281 d->clipEnabled = true;
1282 } else {
1283 path = painter()->clipPath();
1284 path = d->stroker.matrix.map(path);
1285 }
1286 path = path.subtracted(alphaClip);
1287 d->clips.clear();
1288 d->clips.append(path);
1289 }
1290 }
1291}
1292
1293void QPdfBaseEngine::setPen()
1294{
1295 Q_D(QPdfBaseEngine);
1296 if (d->pen.style() == Qt::NoPen)
1297 return;
1298 QBrush b = d->pen.brush();
1299 Q_ASSERT(b.style() == Qt::SolidPattern && b.isOpaque());
1300
1301 QColor rgba = b.color();
1302 if (d->colorMode == QPrinter::GrayScale) {
1303 qreal gray = qGray(rgba.rgba())/255.;
1304 *d->currentPage << gray << gray << gray;
1305 } else {
1306 *d->currentPage << rgba.redF()
1307 << rgba.greenF()
1308 << rgba.blueF();
1309 }
1310 *d->currentPage << "SCN\n";
1311
1312 *d->currentPage << d->pen.widthF() << "w ";
1313
1314 int pdfCapStyle = 0;
1315 switch(d->pen.capStyle()) {
1316 case Qt::FlatCap:
1317 pdfCapStyle = 0;
1318 break;
1319 case Qt::SquareCap:
1320 pdfCapStyle = 2;
1321 break;
1322 case Qt::RoundCap:
1323 pdfCapStyle = 1;
1324 break;
1325 default:
1326 break;
1327 }
1328 *d->currentPage << pdfCapStyle << "J ";
1329
1330 int pdfJoinStyle = 0;
1331 switch(d->pen.joinStyle()) {
1332 case Qt::MiterJoin:
1333 pdfJoinStyle = 0;
1334 break;
1335 case Qt::BevelJoin:
1336 pdfJoinStyle = 2;
1337 break;
1338 case Qt::RoundJoin:
1339 pdfJoinStyle = 1;
1340 break;
1341 default:
1342 break;
1343 }
1344 *d->currentPage << pdfJoinStyle << "j ";
1345
1346 *d->currentPage << QPdf::generateDashes(d->pen) << " 0 d\n";
1347}
1348
1349bool QPdfBaseEngine::newPage()
1350{
1351 Q_D(QPdfBaseEngine);
1352 setupGraphicsState(DirtyBrush|DirtyPen|DirtyClipPath);
1353 QFile *outfile = qobject_cast<QFile*> (d->outDevice);
1354 if (outfile && outfile->error() != QFile::NoError)
1355 return false;
1356 return true;
1357}
1358
1359
1360int QPdfBaseEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const
1361{
1362 Q_D(const QPdfBaseEngine);
1363 int val;
1364 QRect r = d->fullPage ? d->paperRect() : d->pageRect();
1365 switch (metricType) {
1366 case QPaintDevice::PdmWidth:
1367 val = r.width();
1368 break;
1369 case QPaintDevice::PdmHeight:
1370 val = r.height();
1371 break;
1372 case QPaintDevice::PdmDpiX:
1373 case QPaintDevice::PdmDpiY:
1374 val = d->resolution;
1375 break;
1376 case QPaintDevice::PdmPhysicalDpiX:
1377 case QPaintDevice::PdmPhysicalDpiY:
1378 val = 1200;
1379 break;
1380 case QPaintDevice::PdmWidthMM:
1381 val = qRound(r.width()*25.4/d->resolution);
1382 break;
1383 case QPaintDevice::PdmHeightMM:
1384 val = qRound(r.height()*25.4/d->resolution);
1385 break;
1386 case QPaintDevice::PdmNumColors:
1387 val = INT_MAX;
1388 break;
1389 case QPaintDevice::PdmDepth:
1390 val = 32;
1391 break;
1392 default:
1393 qWarning("QPrinter::metric: Invalid metric command");
1394 return 0;
1395 }
1396 return val;
1397}
1398
1399void QPdfBaseEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
1400{
1401 Q_D(QPdfBaseEngine);
1402 switch (key) {
1403 case PPK_CollateCopies:
1404 d->collate = value.toBool();
1405 break;
1406 case PPK_ColorMode:
1407 d->colorMode = QPrinter::ColorMode(value.toInt());
1408 break;
1409 case PPK_Creator:
1410 d->creator = value.toString();
1411 break;
1412 case PPK_DocumentName:
1413 d->title = value.toString();
1414 break;
1415 case PPK_FullPage:
1416 d->fullPage = value.toBool();
1417 break;
1418 case PPK_NumberOfCopies:
1419 d->copies = value.toInt();
1420 break;
1421 case PPK_Orientation:
1422 d->orientation = QPrinter::Orientation(value.toInt());
1423 break;
1424 case PPK_OutputFileName:
1425 d->outputFileName = value.toString();
1426 break;
1427 case PPK_PageOrder:
1428 d->pageOrder = QPrinter::PageOrder(value.toInt());
1429 break;
1430 case PPK_PaperSize:
1431 d->paperSize = QPrinter::PaperSize(value.toInt());
1432 break;
1433 case PPK_PaperSource:
1434 d->paperSource = QPrinter::PaperSource(value.toInt());
1435 break;
1436 case PPK_PrinterName:
1437 d->printerName = value.toString();
1438 break;
1439 case PPK_PrinterProgram:
1440 d->printProgram = value.toString();
1441 break;
1442 case PPK_Resolution:
1443 d->resolution = value.toInt();
1444 break;
1445 case PPK_SelectionOption:
1446 d->selectionOption = value.toString();
1447 break;
1448 case PPK_FontEmbedding:
1449 d->embedFonts = value.toBool();
1450 break;
1451 case PPK_Duplex:
1452 d->duplex = static_cast<QPrinter::DuplexMode> (value.toInt());
1453 break;
1454 case PPK_CupsPageRect:
1455 d->cupsPageRect = value.toRect();
1456 break;
1457 case PPK_CupsPaperRect:
1458 d->cupsPaperRect = value.toRect();
1459 break;
1460 case PPK_CupsOptions:
1461 d->cupsOptions = value.toStringList();
1462 break;
1463 case PPK_CupsStringPageSize:
1464 d->cupsStringPageSize = value.toString();
1465 break;
1466 case PPK_CustomPaperSize:
1467 d->paperSize = QPrinter::Custom;
1468 d->customPaperSize = value.toSizeF();
1469 break;
1470 case PPK_PageMargins:
1471 {
1472 QList<QVariant> margins(value.toList());
1473 Q_ASSERT(margins.size() == 4);
1474 d->leftMargin = margins.at(0).toReal();
1475 d->topMargin = margins.at(1).toReal();
1476 d->rightMargin = margins.at(2).toReal();
1477 d->bottomMargin = margins.at(3).toReal();
1478 d->hasCustomPageMargins = true;
1479 break;
1480 }
1481 default:
1482 break;
1483 }
1484}
1485
1486QVariant QPdfBaseEngine::property(PrintEnginePropertyKey key) const
1487{
1488 Q_D(const QPdfBaseEngine);
1489
1490 QVariant ret;
1491 switch (key) {
1492 case PPK_CollateCopies:
1493 ret = d->collate;
1494 break;
1495 case PPK_ColorMode:
1496 ret = d->colorMode;
1497 break;
1498 case PPK_Creator:
1499 ret = d->creator;
1500 break;
1501 case PPK_DocumentName:
1502 ret = d->title;
1503 break;
1504 case PPK_FullPage:
1505 ret = d->fullPage;
1506 break;
1507 case PPK_NumberOfCopies:
1508#if !defined(QT_NO_CUPS) && (!defined(QT_NO_LIBRARY) || defined(Q_WS_PM))
1509 if (QCUPSSupport::isAvailable())
1510 ret = 1;
1511 else
1512#endif
1513 ret = d->copies;
1514 break;
1515 case PPK_Orientation:
1516 ret = d->orientation;
1517 break;
1518 case PPK_OutputFileName:
1519 ret = d->outputFileName;
1520 break;
1521 case PPK_PageOrder:
1522 ret = d->pageOrder;
1523 break;
1524 case PPK_PaperSize:
1525 ret = d->paperSize;
1526 break;
1527 case PPK_PaperSource:
1528 ret = d->paperSource;
1529 break;
1530 case PPK_PrinterName:
1531 ret = d->printerName;
1532 break;
1533 case PPK_PrinterProgram:
1534 ret = d->printProgram;
1535 break;
1536 case PPK_Resolution:
1537 ret = d->resolution;
1538 break;
1539 case PPK_SupportedResolutions:
1540 ret = QList<QVariant>() << 72;
1541 break;
1542 case PPK_PaperRect:
1543 ret = d->paperRect();
1544 break;
1545 case PPK_PageRect:
1546 ret = d->pageRect();
1547 break;
1548 case PPK_SelectionOption:
1549 ret = d->selectionOption;
1550 break;
1551 case PPK_FontEmbedding:
1552 ret = d->embedFonts;
1553 break;
1554 case PPK_Duplex:
1555 ret = d->duplex;
1556 break;
1557 case PPK_CupsPageRect:
1558 ret = d->cupsPageRect;
1559 break;
1560 case PPK_CupsPaperRect:
1561 ret = d->cupsPaperRect;
1562 break;
1563 case PPK_CupsOptions:
1564 ret = d->cupsOptions;
1565 break;
1566 case PPK_CupsStringPageSize:
1567 ret = d->cupsStringPageSize;
1568 break;
1569 case PPK_CustomPaperSize:
1570 ret = d->customPaperSize;
1571 break;
1572 case PPK_PageMargins:
1573 {
1574 QList<QVariant> margins;
1575 if (d->hasCustomPageMargins) {
1576 margins << d->leftMargin << d->topMargin
1577 << d->rightMargin << d->bottomMargin;
1578 } else {
1579 const qreal defaultMargin = 10; // ~3.5 mm
1580 margins << defaultMargin << defaultMargin
1581 << defaultMargin << defaultMargin;
1582 }
1583 ret = margins;
1584 break;
1585 }
1586 default:
1587 break;
1588 }
1589 return ret;
1590}
1591
1592QPdfBaseEnginePrivate::QPdfBaseEnginePrivate(QPrinter::PrinterMode m)
1593 : clipEnabled(false), allClipped(false), hasPen(true), hasBrush(false), simplePen(false),
1594 useAlphaEngine(false),
1595 outDevice(0), fd(-1),
1596 duplex(QPrinter::DuplexNone), collate(false), fullPage(false), embedFonts(true), copies(1),
1597 pageOrder(QPrinter::FirstPageFirst), orientation(QPrinter::Portrait),
1598 paperSize(QPrinter::A4), colorMode(QPrinter::Color), paperSource(QPrinter::Auto),
1599 hasCustomPageMargins(false),
1600 leftMargin(0), topMargin(0), rightMargin(0), bottomMargin(0)
1601{
1602 resolution = 72;
1603 if (m == QPrinter::HighResolution)
1604 resolution = 1200;
1605 else if (m == QPrinter::ScreenResolution)
1606 resolution = qt_defaultDpi();
1607
1608 postscript = false;
1609 currentObject = 1;
1610 currentPage = 0;
1611 stroker.stream = 0;
1612}
1613
1614bool QPdfBaseEngine::begin(QPaintDevice *pdev)
1615{
1616 Q_D(QPdfBaseEngine);
1617 d->pdev = pdev;
1618
1619 d->postscript = false;
1620 d->currentObject = 1;
1621
1622 d->currentPage = new QPdfPage;
1623 d->stroker.stream = d->currentPage;
1624 d->opacity = 1.0;
1625
1626 return d->openPrintDevice();
1627}
1628
1629bool QPdfBaseEngine::end()
1630{
1631 Q_D(QPdfBaseEngine);
1632 qDeleteAll(d->fonts);
1633 d->fonts.clear();
1634 delete d->currentPage;
1635 d->currentPage = 0;
1636
1637 d->closePrintDevice();
1638 return true;
1639}
1640
1641#ifndef QT_NO_LPR
1642static void closeAllOpenFds()
1643{
1644 // hack time... getting the maximum number of open
1645 // files, if possible. if not we assume it's the
1646 // larger of 256 and the fd we got
1647 int i;
1648#if defined(_SC_OPEN_MAX)
1649 i = (int)sysconf(_SC_OPEN_MAX);
1650#elif defined(_POSIX_OPEN_MAX)
1651 i = (int)_POSIX_OPEN_MAX;
1652#elif defined(OPEN_MAX)
1653 i = (int)OPEN_MAX;
1654#else
1655 i = 256;
1656#endif
1657 // leave stdin/out/err untouched
1658 while(--i > 2)
1659 QT_CLOSE(i);
1660}
1661#endif
1662
1663bool QPdfBaseEnginePrivate::openPrintDevice()
1664{
1665 if(outDevice)
1666 return false;
1667
1668 if (!outputFileName.isEmpty()) {
1669 QFile *file = new QFile(outputFileName);
1670 if (! file->open(QFile::WriteOnly|QFile::Truncate)) {
1671 delete file;
1672 return false;
1673 }
1674 outDevice = file;
1675#if !defined(QT_NO_CUPS) && (!defined(QT_NO_LIBRARY) || defined(Q_WS_PM))
1676 } else if (QCUPSSupport::isAvailable()) {
1677 QCUPSSupport cups;
1678 QPair<int, QString> ret = cups.tempFd();
1679 if (ret.first < 0) {
1680 qWarning("QPdfPrinter: Could not open temporary file to print");
1681 return false;
1682 }
1683 cupsTempFile = ret.second;
1684 outDevice = new QFile();
1685 static_cast<QFile *>(outDevice)->open(ret.first, QIODevice::WriteOnly);
1686 // save fd to get the temporary file closed in closePrintDevice()
1687 fd = ret.first;
1688#endif
1689#ifndef QT_NO_LPR
1690 } else {
1691 QString pr;
1692 if (!printerName.isEmpty())
1693 pr = printerName;
1694 int fds[2];
1695 if (qt_safe_pipe(fds) != 0) {
1696 qWarning("QPdfPrinter: Could not open pipe to print");
1697 return false;
1698 }
1699
1700 pid_t pid = fork();
1701 if (pid == 0) { // child process
1702 // if possible, exit quickly, so the actual lp/lpr
1703 // becomes a child of init, and ::waitpid() is
1704 // guaranteed not to wait.
1705 if (fork() > 0) {
1706 closeAllOpenFds();
1707
1708 // try to replace this process with "true" - this prevents
1709 // global destructors from being called (that could possibly
1710 // do wrong things to the parent process)
1711 (void)execlp("true", "true", (char *)0);
1712 (void)execl("/bin/true", "true", (char *)0);
1713 (void)execl("/usr/bin/true", "true", (char *)0);
1714 ::_exit(0);
1715 }
1716 qt_safe_dup2(fds[0], 0, 0);
1717
1718 closeAllOpenFds();
1719
1720 if (!printProgram.isEmpty()) {
1721 if (!selectionOption.isEmpty())
1722 pr.prepend(selectionOption);
1723 else
1724 pr.prepend(QLatin1String("-P"));
1725 (void)execlp(printProgram.toLocal8Bit().data(), printProgram.toLocal8Bit().data(),
1726 pr.toLocal8Bit().data(), (char *)0);
1727 } else {
1728 // if no print program has been specified, be smart
1729 // about the option string too.
1730 QList<QByteArray> lprhack;
1731 QList<QByteArray> lphack;
1732 QByteArray media;
1733 if (!pr.isEmpty() || !selectionOption.isEmpty()) {
1734 if (!selectionOption.isEmpty()) {
1735 QStringList list = selectionOption.split(QLatin1Char(' '));
1736 for (int i = 0; i < list.size(); ++i)
1737 lprhack.append(list.at(i).toLocal8Bit());
1738 lphack = lprhack;
1739 } else {
1740 lprhack.append("-P");
1741 lphack.append("-d");
1742 }
1743 lprhack.append(pr.toLocal8Bit());
1744 lphack.append(pr.toLocal8Bit());
1745 }
1746 lphack.append("-s");
1747
1748 char ** lpargs = new char *[lphack.size()+6];
1749 char lp[] = "lp";
1750 lpargs[0] = lp;
1751 int i;
1752 for (i = 0; i < lphack.size(); ++i)
1753 lpargs[i+1] = (char *)lphack.at(i).constData();
1754#ifndef Q_OS_OSF
1755 if (QPdf::paperSizeToString(paperSize)) {
1756 char dash_o[] = "-o";
1757 lpargs[++i] = dash_o;
1758 lpargs[++i] = const_cast<char *>(QPdf::paperSizeToString(paperSize));
1759 lpargs[++i] = dash_o;
1760 media = "media=";
1761 media += QPdf::paperSizeToString(paperSize);
1762 lpargs[++i] = media.data();
1763 }
1764#endif
1765 lpargs[++i] = 0;
1766 char **lprargs = new char *[lprhack.size()+2];
1767 char lpr[] = "lpr";
1768 lprargs[0] = lpr;
1769 for (int i = 0; i < lprhack.size(); ++i)
1770 lprargs[i+1] = (char *)lprhack[i].constData();
1771 lprargs[lprhack.size() + 1] = 0;
1772 (void)execvp("lp", lpargs);
1773 (void)execvp("lpr", lprargs);
1774 (void)execv("/bin/lp", lpargs);
1775 (void)execv("/bin/lpr", lprargs);
1776 (void)execv("/usr/bin/lp", lpargs);
1777 (void)execv("/usr/bin/lpr", lprargs);
1778
1779 delete []lpargs;
1780 delete []lprargs;
1781 }
1782 // if we couldn't exec anything, close the fd,
1783 // wait for a second so the parent process (the
1784 // child of the GUI process) has exited. then
1785 // exit.
1786 QT_CLOSE(0);
1787 (void)::sleep(1);
1788 ::_exit(0);
1789 }
1790 // parent process
1791 QT_CLOSE(fds[0]);
1792 fd = fds[1];
1793 (void)qt_safe_waitpid(pid, 0, 0);
1794
1795 if (fd < 0)
1796 return false;
1797
1798 outDevice = new QFile();
1799 static_cast<QFile *>(outDevice)->open(fd, QIODevice::WriteOnly);
1800#endif
1801 }
1802
1803 return true;
1804}
1805
1806void QPdfBaseEnginePrivate::closePrintDevice()
1807{
1808 if (!outDevice)
1809 return;
1810 outDevice->close();
1811 if (fd >= 0)
1812#if defined(Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400
1813 ::_close(fd);
1814#else
1815 ::close(fd);
1816#endif
1817 fd = -1;
1818 delete outDevice;
1819 outDevice = 0;
1820
1821#if !defined(QT_NO_CUPS) && (!defined(QT_NO_LIBRARY) || defined(Q_WS_PM))
1822 if (!cupsTempFile.isEmpty()) {
1823 QString tempFile = cupsTempFile;
1824 cupsTempFile.clear();
1825 QCUPSSupport cups;
1826
1827 // Set up print options.
1828 QByteArray prnName;
1829 QList<QPair<QByteArray, QByteArray> > options;
1830 QVector<cups_option_t> cupsOptStruct;
1831
1832 if (!printerName.isEmpty()) {
1833 prnName = printerName.toLocal8Bit();
1834 } else {
1835 QPrinterInfo def = QPrinterInfo::defaultPrinter();
1836 if (def.isNull()) {
1837 qWarning("Could not determine printer to print to");
1838 QFile::remove(tempFile);
1839 return;
1840 }
1841 prnName = def.printerName().toLocal8Bit();
1842 }
1843
1844 if (!cupsStringPageSize.isEmpty()) {
1845 options.append(QPair<QByteArray, QByteArray>("media", cupsStringPageSize.toLocal8Bit()));
1846 }
1847
1848 if (copies > 1) {
1849 options.append(QPair<QByteArray, QByteArray>("copies", QString::number(copies).toLocal8Bit()));
1850 }
1851
1852 if (collate) {
1853 options.append(QPair<QByteArray, QByteArray>("Collate", "True"));
1854 }
1855
1856 if (duplex != QPrinter::DuplexNone) {
1857 switch(duplex) {
1858 case QPrinter::DuplexNone: break;
1859 case QPrinter::DuplexAuto:
1860 if (orientation == QPrinter::Portrait)
1861 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
1862 else
1863 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
1864 break;
1865 case QPrinter::DuplexLongSide:
1866 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
1867 break;
1868 case QPrinter::DuplexShortSide:
1869 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
1870 break;
1871 }
1872 }
1873
1874 if (QCUPSSupport::cupsVersion() >= 10300 && orientation == QPrinter::Landscape) {
1875 options.append(QPair<QByteArray, QByteArray>("landscape", ""));
1876 }
1877
1878 QStringList::const_iterator it = cupsOptions.constBegin();
1879 while (it != cupsOptions.constEnd()) {
1880 options.append(QPair<QByteArray, QByteArray>((*it).toLocal8Bit(), (*(it+1)).toLocal8Bit()));
1881 it += 2;
1882 }
1883
1884 for (int c = 0; c < options.size(); ++c) {
1885 cups_option_t opt;
1886 opt.name = options[c].first.data();
1887 opt.value = options[c].second.data();
1888 cupsOptStruct.append(opt);
1889 }
1890
1891 // Print the file.
1892 cups_option_t* optPtr = cupsOptStruct.size() ? &cupsOptStruct.first() : 0;
1893 cups.printFile(prnName.constData(), tempFile.toLocal8Bit().constData(),
1894 title.toLocal8Bit().constData(), cupsOptStruct.size(), optPtr);
1895
1896 QFile::remove(tempFile);
1897 }
1898#endif
1899}
1900
1901QPdfBaseEnginePrivate::~QPdfBaseEnginePrivate()
1902{
1903 qDeleteAll(fonts);
1904 delete currentPage;
1905}
1906
1907void QPdfBaseEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
1908{
1909 Q_Q(QPdfBaseEngine);
1910
1911 QFontEngine *fe = ti.fontEngine;
1912
1913 QFontEngine::FaceId face_id = fe->faceId();
1914 bool noEmbed = false;
1915 if (face_id.filename.isEmpty()
1916 || (!postscript && ((fe->fsType & 0x200) /* bitmap embedding only */
1917 || (fe->fsType == 2) /* no embedding allowed */))) {
1918 *currentPage << "Q\n";
1919 q->QPaintEngine::drawTextItem(p, ti);
1920 *currentPage << "q\n";
1921 if (face_id.filename.isEmpty())
1922 return;
1923 noEmbed = true;
1924 }
1925
1926 QFontSubset *font = fonts.value(face_id, 0);
1927 if (!font) {
1928 font = new QFontSubset(fe, requestObject());
1929 font->noEmbed = noEmbed;
1930 }
1931 fonts.insert(face_id, font);
1932
1933 if (!currentPage->fonts.contains(font->object_id))
1934 currentPage->fonts.append(font->object_id);
1935
1936 qreal size = ti.fontEngine->fontDef.pixelSize;
1937#ifdef Q_WS_WIN
1938 if (ti.fontEngine->type() == QFontEngine::Win) {
1939 QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
1940 size = fe->tm.tmHeight;
1941 }
1942#endif
1943
1944 QVarLengthArray<glyph_t> glyphs;
1945 QVarLengthArray<QFixedPoint> positions;
1946 QTransform m = QTransform::fromTranslate(p.x(), p.y());
1947 ti.fontEngine->getGlyphPositions(ti.glyphs, m, ti.flags,
1948 glyphs, positions);
1949 if (glyphs.size() == 0)
1950 return;
1951 int synthesized = ti.fontEngine->synthesized();
1952 qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
1953
1954 *currentPage << "BT\n"
1955 << "/F" << font->object_id << size << "Tf "
1956 << stretch << (synthesized & QFontEngine::SynthesizedItalic
1957 ? "0 .3 -1 0 0 Tm\n"
1958 : "0 0 -1 0 0 Tm\n");
1959
1960
1961#if 0
1962 // #### implement actual text for complex languages
1963 const unsigned short *logClusters = ti.logClusters;
1964 int pos = 0;
1965 do {
1966 int end = pos + 1;
1967 while (end < ti.num_chars && logClusters[end] == logClusters[pos])
1968 ++end;
1969 *currentPage << "/Span << /ActualText <FEFF";
1970 for (int i = pos; i < end; ++i) {
1971 s << toHex((ushort)ti.chars[i].unicode(), buf);
1972 }
1973 *currentPage << "> >>\n"
1974 "BDC\n"
1975 "<";
1976 int ge = end == ti.num_chars ? ti.num_glyphs : logClusters[end];
1977 for (int gs = logClusters[pos]; gs < ge; ++gs)
1978 *currentPage << toHex((ushort)ti.glyphs[gs].glyph, buf);
1979 *currentPage << "> Tj\n"
1980 "EMC\n";
1981 pos = end;
1982 } while (pos < ti.num_chars);
1983#else
1984 qreal last_x = 0.;
1985 qreal last_y = 0.;
1986 for (int i = 0; i < glyphs.size(); ++i) {
1987 qreal x = positions[i].x.toReal();
1988 qreal y = positions[i].y.toReal();
1989 if (synthesized & QFontEngine::SynthesizedItalic)
1990 x += .3*y;
1991 x /= stretch;
1992 char buf[5];
1993 int g = font->addGlyph(glyphs[i]);
1994 *currentPage << x - last_x << last_y - y << "Td <"
1995 << QPdf::toHex((ushort)g, buf) << "> Tj\n";
1996 last_x = x;
1997 last_y = y;
1998 }
1999 if (synthesized & QFontEngine::SynthesizedBold) {
2000 *currentPage << stretch << (synthesized & QFontEngine::SynthesizedItalic
2001 ? "0 .3 -1 0 0 Tm\n"
2002 : "0 0 -1 0 0 Tm\n");
2003 *currentPage << "/Span << /ActualText <> >> BDC\n";
2004 last_x = 0.5*fe->lineThickness().toReal();
2005 last_y = 0.;
2006 for (int i = 0; i < glyphs.size(); ++i) {
2007 qreal x = positions[i].x.toReal();
2008 qreal y = positions[i].y.toReal();
2009 if (synthesized & QFontEngine::SynthesizedItalic)
2010 x += .3*y;
2011 x /= stretch;
2012 char buf[5];
2013 int g = font->addGlyph(glyphs[i]);
2014 *currentPage << x - last_x << last_y - y << "Td <"
2015 << QPdf::toHex((ushort)g, buf) << "> Tj\n";
2016 last_x = x;
2017 last_y = y;
2018 }
2019 *currentPage << "EMC\n";
2020 }
2021#endif
2022
2023 *currentPage << "ET\n";
2024}
2025
2026QRect QPdfBaseEnginePrivate::paperRect() const
2027{
2028 int w;
2029 int h;
2030 if (paperSize == QPrinter::Custom) {
2031 w = qRound(customPaperSize.width()*resolution/72.);
2032 h = qRound(customPaperSize.height()*resolution/72.);
2033 } else {
2034#if !defined(QT_NO_CUPS) && (!defined(QT_NO_LIBRARY) || defined(Q_WS_PM))
2035 if (QCUPSSupport::isAvailable() && !cupsPaperRect.isNull()) {
2036 QRect r = cupsPaperRect;
2037 w = r.width();
2038 h = r.height();
2039 } else
2040#endif
2041 {
2042 QPdf::PaperSize s = QPdf::paperSize(paperSize);
2043 w = s.width;
2044 h = s.height;
2045 }
2046 w = qRound(w*resolution/72.);
2047 h = qRound(h*resolution/72.);
2048 }
2049 if (orientation == QPrinter::Portrait)
2050 return QRect(0, 0, w, h);
2051 else
2052 return QRect(0, 0, h, w);
2053}
2054
2055QRect QPdfBaseEnginePrivate::pageRect() const
2056{
2057 if(fullPage)
2058 return paperRect();
2059
2060 QRect r;
2061
2062#if !defined(QT_NO_CUPS) && (!defined(QT_NO_LIBRARY) || defined(Q_WS_PM))
2063 if (!hasCustomPageMargins && QCUPSSupport::isAvailable() && !cupsPageRect.isNull()) {
2064 r = cupsPageRect;
2065 if (r == cupsPaperRect) {
2066 // if cups doesn't define any margins, give it at least approx 3.5 mm
2067 r = QRect(10, 10, r.width() - 20, r.height() - 20);
2068 }
2069 } else
2070#endif
2071 {
2072 QPdf::PaperSize s;
2073 if (paperSize == QPrinter::Custom) {
2074 s.width = qRound(customPaperSize.width());
2075 s.height = qRound(customPaperSize.height());
2076 } else {
2077 s = QPdf::paperSize(paperSize);
2078 }
2079 if (hasCustomPageMargins)
2080 r = QRect(0, 0, s.width, s.height);
2081 else
2082 r = QRect(72/3, 72/3, s.width - 2*72/3, s.height - 2*72/3);
2083 }
2084
2085 int x = qRound(r.left()*resolution/72.);
2086 int y = qRound(r.top()*resolution/72.);
2087 int w = qRound(r.width()*resolution/72.);
2088 int h = qRound(r.height()*resolution/72.);
2089 if (orientation == QPrinter::Portrait)
2090 r = QRect(x, y, w, h);
2091 else
2092 r = QRect(y, x, h, w);
2093
2094 if (hasCustomPageMargins) {
2095 r.adjust(qRound(leftMargin*(resolution/72.)),
2096 qRound(topMargin*(resolution/72.)),
2097 -qRound(rightMargin*(resolution/72.)),
2098 -qRound(bottomMargin*(resolution/72.)));
2099 }
2100 return r;
2101}
2102
2103#endif
2104
2105QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.