|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 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 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
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 |
|
55 QT_BEGIN_NAMESPACE |
|
56 |
|
57 extern int qt_defaultDpi(); |
|
58 |
|
59 #ifndef QT_NO_PRINTER |
|
60 |
|
61 extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size); |
|
62 |
|
63 /* also adds a space at the end of the number */ |
|
64 const 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 * 1000000); |
|
82 if (ifrac == 1000000) { |
|
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 = 100000; |
|
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 |
|
116 const 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 |
|
141 namespace 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); |
|
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 |
|
273 QByteArray 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 |
|
335 QByteArray 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 |
|
349 QByteArray 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 |
|
372 static 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 |
|
525 QByteArray 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 |
|
534 static 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 |
|
571 QByteArray 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 ¤t_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 ¤t_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 |
|
717 static 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 |
|
728 static 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 |
|
736 static 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 |
|
753 QPdf::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 |
|
766 void 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 |
|
797 void 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 |
|
807 QByteArray 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 |
|
863 const 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 |
|
879 const 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 |
|
898 static 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 |
|
906 QPdf::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 |
|
913 const char *QPdf::paperSizeToString(QPrinter::PaperSize paperSize) |
|
914 { |
|
915 return psToStr[paperSize]; |
|
916 } |
|
917 |
|
918 |
|
919 QByteArray 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 |
|
939 QPdfBaseEngine::QPdfBaseEngine(QPdfBaseEnginePrivate &dd, PaintEngineFeatures f) |
|
940 : QAlphaPaintEngine(dd, f) |
|
941 { |
|
942 Q_D(QPdfBaseEngine); |
|
943 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) |
|
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 |
|
969 void 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 |
|
982 void 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 |
|
995 void 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 |
|
1030 void 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 |
|
1072 void 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 |
|
1105 void 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 |
|
1142 void 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 |
|
1202 void 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 |
|
1243 extern QPainterPath qt_regionToPath(const QRegion ®ion); |
|
1244 |
|
1245 void 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 |
|
1293 void 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 |
|
1349 bool 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 |
|
1360 int 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 |
|
1399 void 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 |
|
1486 QVariant 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) |
|
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 |
|
1592 QPdfBaseEnginePrivate::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 |
|
1614 bool 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 |
|
1629 bool 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 |
|
1642 static 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 |
|
1663 bool 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) |
|
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 #endif |
|
1687 #ifndef QT_NO_LPR |
|
1688 } else { |
|
1689 QString pr; |
|
1690 if (!printerName.isEmpty()) |
|
1691 pr = printerName; |
|
1692 int fds[2]; |
|
1693 if (qt_safe_pipe(fds) != 0) { |
|
1694 qWarning("QPdfPrinter: Could not open pipe to print"); |
|
1695 return false; |
|
1696 } |
|
1697 |
|
1698 pid_t pid = fork(); |
|
1699 if (pid == 0) { // child process |
|
1700 // if possible, exit quickly, so the actual lp/lpr |
|
1701 // becomes a child of init, and ::waitpid() is |
|
1702 // guaranteed not to wait. |
|
1703 if (fork() > 0) { |
|
1704 closeAllOpenFds(); |
|
1705 |
|
1706 // try to replace this process with "true" - this prevents |
|
1707 // global destructors from being called (that could possibly |
|
1708 // do wrong things to the parent process) |
|
1709 (void)execlp("true", "true", (char *)0); |
|
1710 (void)execl("/bin/true", "true", (char *)0); |
|
1711 (void)execl("/usr/bin/true", "true", (char *)0); |
|
1712 ::_exit(0); |
|
1713 } |
|
1714 qt_safe_dup2(fds[0], 0, 0); |
|
1715 |
|
1716 closeAllOpenFds(); |
|
1717 |
|
1718 if (!printProgram.isEmpty()) { |
|
1719 if (!selectionOption.isEmpty()) |
|
1720 pr.prepend(selectionOption); |
|
1721 else |
|
1722 pr.prepend(QLatin1String("-P")); |
|
1723 (void)execlp(printProgram.toLocal8Bit().data(), printProgram.toLocal8Bit().data(), |
|
1724 pr.toLocal8Bit().data(), (char *)0); |
|
1725 } else { |
|
1726 // if no print program has been specified, be smart |
|
1727 // about the option string too. |
|
1728 QList<QByteArray> lprhack; |
|
1729 QList<QByteArray> lphack; |
|
1730 QByteArray media; |
|
1731 if (!pr.isEmpty() || !selectionOption.isEmpty()) { |
|
1732 if (!selectionOption.isEmpty()) { |
|
1733 QStringList list = selectionOption.split(QLatin1Char(' ')); |
|
1734 for (int i = 0; i < list.size(); ++i) |
|
1735 lprhack.append(list.at(i).toLocal8Bit()); |
|
1736 lphack = lprhack; |
|
1737 } else { |
|
1738 lprhack.append("-P"); |
|
1739 lphack.append("-d"); |
|
1740 } |
|
1741 lprhack.append(pr.toLocal8Bit()); |
|
1742 lphack.append(pr.toLocal8Bit()); |
|
1743 } |
|
1744 lphack.append("-s"); |
|
1745 |
|
1746 char ** lpargs = new char *[lphack.size()+6]; |
|
1747 char lp[] = "lp"; |
|
1748 lpargs[0] = lp; |
|
1749 int i; |
|
1750 for (i = 0; i < lphack.size(); ++i) |
|
1751 lpargs[i+1] = (char *)lphack.at(i).constData(); |
|
1752 #ifndef Q_OS_OSF |
|
1753 if (QPdf::paperSizeToString(paperSize)) { |
|
1754 char dash_o[] = "-o"; |
|
1755 lpargs[++i] = dash_o; |
|
1756 lpargs[++i] = const_cast<char *>(QPdf::paperSizeToString(paperSize)); |
|
1757 lpargs[++i] = dash_o; |
|
1758 media = "media="; |
|
1759 media += QPdf::paperSizeToString(paperSize); |
|
1760 lpargs[++i] = media.data(); |
|
1761 } |
|
1762 #endif |
|
1763 lpargs[++i] = 0; |
|
1764 char **lprargs = new char *[lprhack.size()+2]; |
|
1765 char lpr[] = "lpr"; |
|
1766 lprargs[0] = lpr; |
|
1767 for (int i = 0; i < lprhack.size(); ++i) |
|
1768 lprargs[i+1] = (char *)lprhack[i].constData(); |
|
1769 lprargs[lprhack.size() + 1] = 0; |
|
1770 (void)execvp("lp", lpargs); |
|
1771 (void)execvp("lpr", lprargs); |
|
1772 (void)execv("/bin/lp", lpargs); |
|
1773 (void)execv("/bin/lpr", lprargs); |
|
1774 (void)execv("/usr/bin/lp", lpargs); |
|
1775 (void)execv("/usr/bin/lpr", lprargs); |
|
1776 |
|
1777 delete []lpargs; |
|
1778 delete []lprargs; |
|
1779 } |
|
1780 // if we couldn't exec anything, close the fd, |
|
1781 // wait for a second so the parent process (the |
|
1782 // child of the GUI process) has exited. then |
|
1783 // exit. |
|
1784 QT_CLOSE(0); |
|
1785 (void)::sleep(1); |
|
1786 ::_exit(0); |
|
1787 } |
|
1788 // parent process |
|
1789 QT_CLOSE(fds[0]); |
|
1790 fd = fds[1]; |
|
1791 (void)qt_safe_waitpid(pid, 0, 0); |
|
1792 |
|
1793 if (fd < 0) |
|
1794 return false; |
|
1795 |
|
1796 outDevice = new QFile(); |
|
1797 static_cast<QFile *>(outDevice)->open(fd, QIODevice::WriteOnly); |
|
1798 #endif |
|
1799 } |
|
1800 |
|
1801 return true; |
|
1802 } |
|
1803 |
|
1804 void QPdfBaseEnginePrivate::closePrintDevice() |
|
1805 { |
|
1806 if (!outDevice) |
|
1807 return; |
|
1808 outDevice->close(); |
|
1809 if (fd >= 0) |
|
1810 #if defined(Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400 |
|
1811 ::_close(fd); |
|
1812 #else |
|
1813 ::close(fd); |
|
1814 #endif |
|
1815 fd = -1; |
|
1816 delete outDevice; |
|
1817 outDevice = 0; |
|
1818 |
|
1819 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) |
|
1820 if (!cupsTempFile.isEmpty()) { |
|
1821 QString tempFile = cupsTempFile; |
|
1822 cupsTempFile.clear(); |
|
1823 QCUPSSupport cups; |
|
1824 |
|
1825 // Set up print options. |
|
1826 QByteArray prnName; |
|
1827 QList<QPair<QByteArray, QByteArray> > options; |
|
1828 QVector<cups_option_t> cupsOptStruct; |
|
1829 |
|
1830 if (!printerName.isEmpty()) { |
|
1831 prnName = printerName.toLocal8Bit(); |
|
1832 } else { |
|
1833 QPrinterInfo def = QPrinterInfo::defaultPrinter(); |
|
1834 if (def.isNull()) { |
|
1835 qWarning("Could not determine printer to print to"); |
|
1836 QFile::remove(tempFile); |
|
1837 return; |
|
1838 } |
|
1839 prnName = def.printerName().toLocal8Bit(); |
|
1840 } |
|
1841 |
|
1842 if (!cupsStringPageSize.isEmpty()) { |
|
1843 options.append(QPair<QByteArray, QByteArray>("media", cupsStringPageSize.toLocal8Bit())); |
|
1844 } |
|
1845 |
|
1846 if (copies > 1) { |
|
1847 options.append(QPair<QByteArray, QByteArray>("copies", QString::number(copies).toLocal8Bit())); |
|
1848 } |
|
1849 |
|
1850 if (collate) { |
|
1851 options.append(QPair<QByteArray, QByteArray>("Collate", "True")); |
|
1852 } |
|
1853 |
|
1854 if (duplex != QPrinter::DuplexNone) { |
|
1855 switch(duplex) { |
|
1856 case QPrinter::DuplexNone: break; |
|
1857 case QPrinter::DuplexAuto: |
|
1858 if (orientation == QPrinter::Portrait) |
|
1859 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge")); |
|
1860 else |
|
1861 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge")); |
|
1862 break; |
|
1863 case QPrinter::DuplexLongSide: |
|
1864 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge")); |
|
1865 break; |
|
1866 case QPrinter::DuplexShortSide: |
|
1867 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge")); |
|
1868 break; |
|
1869 } |
|
1870 } |
|
1871 |
|
1872 if (QCUPSSupport::cupsVersion() >= 10300 && orientation == QPrinter::Landscape) { |
|
1873 options.append(QPair<QByteArray, QByteArray>("landscape", "")); |
|
1874 } |
|
1875 |
|
1876 QStringList::const_iterator it = cupsOptions.constBegin(); |
|
1877 while (it != cupsOptions.constEnd()) { |
|
1878 options.append(QPair<QByteArray, QByteArray>((*it).toLocal8Bit(), (*(it+1)).toLocal8Bit())); |
|
1879 it += 2; |
|
1880 } |
|
1881 |
|
1882 for (int c = 0; c < options.size(); ++c) { |
|
1883 cups_option_t opt; |
|
1884 opt.name = options[c].first.data(); |
|
1885 opt.value = options[c].second.data(); |
|
1886 cupsOptStruct.append(opt); |
|
1887 } |
|
1888 |
|
1889 // Print the file. |
|
1890 cups_option_t* optPtr = cupsOptStruct.size() ? &cupsOptStruct.first() : 0; |
|
1891 cups.printFile(prnName.constData(), tempFile.toLocal8Bit().constData(), |
|
1892 title.toLocal8Bit().constData(), cupsOptStruct.size(), optPtr); |
|
1893 |
|
1894 QFile::remove(tempFile); |
|
1895 } |
|
1896 #endif |
|
1897 } |
|
1898 |
|
1899 QPdfBaseEnginePrivate::~QPdfBaseEnginePrivate() |
|
1900 { |
|
1901 qDeleteAll(fonts); |
|
1902 delete currentPage; |
|
1903 } |
|
1904 |
|
1905 void QPdfBaseEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti) |
|
1906 { |
|
1907 Q_Q(QPdfBaseEngine); |
|
1908 |
|
1909 QFontEngine *fe = ti.fontEngine; |
|
1910 |
|
1911 QFontEngine::FaceId face_id = fe->faceId(); |
|
1912 bool noEmbed = false; |
|
1913 if (face_id.filename.isEmpty() |
|
1914 || (!postscript && ((fe->fsType & 0x200) /* bitmap embedding only */ |
|
1915 || (fe->fsType == 2) /* no embedding allowed */))) { |
|
1916 *currentPage << "Q\n"; |
|
1917 q->QPaintEngine::drawTextItem(p, ti); |
|
1918 *currentPage << "q\n"; |
|
1919 if (face_id.filename.isEmpty()) |
|
1920 return; |
|
1921 noEmbed = true; |
|
1922 } |
|
1923 |
|
1924 QFontSubset *font = fonts.value(face_id, 0); |
|
1925 if (!font) { |
|
1926 font = new QFontSubset(fe, requestObject()); |
|
1927 font->noEmbed = noEmbed; |
|
1928 } |
|
1929 fonts.insert(face_id, font); |
|
1930 |
|
1931 if (!currentPage->fonts.contains(font->object_id)) |
|
1932 currentPage->fonts.append(font->object_id); |
|
1933 |
|
1934 qreal size = ti.fontEngine->fontDef.pixelSize; |
|
1935 #ifdef Q_WS_WIN |
|
1936 if (ti.fontEngine->type() == QFontEngine::Win) { |
|
1937 QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine); |
|
1938 size = fe->tm.tmHeight; |
|
1939 } |
|
1940 #endif |
|
1941 |
|
1942 QVarLengthArray<glyph_t> glyphs; |
|
1943 QVarLengthArray<QFixedPoint> positions; |
|
1944 QTransform m = QTransform::fromTranslate(p.x(), p.y()); |
|
1945 ti.fontEngine->getGlyphPositions(ti.glyphs, m, ti.flags, |
|
1946 glyphs, positions); |
|
1947 if (glyphs.size() == 0) |
|
1948 return; |
|
1949 int synthesized = ti.fontEngine->synthesized(); |
|
1950 qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.; |
|
1951 |
|
1952 *currentPage << "BT\n" |
|
1953 << "/F" << font->object_id << size << "Tf " |
|
1954 << stretch << (synthesized & QFontEngine::SynthesizedItalic |
|
1955 ? "0 .3 -1 0 0 Tm\n" |
|
1956 : "0 0 -1 0 0 Tm\n"); |
|
1957 |
|
1958 |
|
1959 #if 0 |
|
1960 // #### implement actual text for complex languages |
|
1961 const unsigned short *logClusters = ti.logClusters; |
|
1962 int pos = 0; |
|
1963 do { |
|
1964 int end = pos + 1; |
|
1965 while (end < ti.num_chars && logClusters[end] == logClusters[pos]) |
|
1966 ++end; |
|
1967 *currentPage << "/Span << /ActualText <FEFF"; |
|
1968 for (int i = pos; i < end; ++i) { |
|
1969 s << toHex((ushort)ti.chars[i].unicode(), buf); |
|
1970 } |
|
1971 *currentPage << "> >>\n" |
|
1972 "BDC\n" |
|
1973 "<"; |
|
1974 int ge = end == ti.num_chars ? ti.num_glyphs : logClusters[end]; |
|
1975 for (int gs = logClusters[pos]; gs < ge; ++gs) |
|
1976 *currentPage << toHex((ushort)ti.glyphs[gs].glyph, buf); |
|
1977 *currentPage << "> Tj\n" |
|
1978 "EMC\n"; |
|
1979 pos = end; |
|
1980 } while (pos < ti.num_chars); |
|
1981 #else |
|
1982 qreal last_x = 0.; |
|
1983 qreal last_y = 0.; |
|
1984 for (int i = 0; i < glyphs.size(); ++i) { |
|
1985 qreal x = positions[i].x.toReal(); |
|
1986 qreal y = positions[i].y.toReal(); |
|
1987 if (synthesized & QFontEngine::SynthesizedItalic) |
|
1988 x += .3*y; |
|
1989 x /= stretch; |
|
1990 char buf[5]; |
|
1991 int g = font->addGlyph(glyphs[i]); |
|
1992 *currentPage << x - last_x << last_y - y << "Td <" |
|
1993 << QPdf::toHex((ushort)g, buf) << "> Tj\n"; |
|
1994 last_x = x; |
|
1995 last_y = y; |
|
1996 } |
|
1997 if (synthesized & QFontEngine::SynthesizedBold) { |
|
1998 *currentPage << stretch << (synthesized & QFontEngine::SynthesizedItalic |
|
1999 ? "0 .3 -1 0 0 Tm\n" |
|
2000 : "0 0 -1 0 0 Tm\n"); |
|
2001 *currentPage << "/Span << /ActualText <> >> BDC\n"; |
|
2002 last_x = 0.5*fe->lineThickness().toReal(); |
|
2003 last_y = 0.; |
|
2004 for (int i = 0; i < glyphs.size(); ++i) { |
|
2005 qreal x = positions[i].x.toReal(); |
|
2006 qreal y = positions[i].y.toReal(); |
|
2007 if (synthesized & QFontEngine::SynthesizedItalic) |
|
2008 x += .3*y; |
|
2009 x /= stretch; |
|
2010 char buf[5]; |
|
2011 int g = font->addGlyph(glyphs[i]); |
|
2012 *currentPage << x - last_x << last_y - y << "Td <" |
|
2013 << QPdf::toHex((ushort)g, buf) << "> Tj\n"; |
|
2014 last_x = x; |
|
2015 last_y = y; |
|
2016 } |
|
2017 *currentPage << "EMC\n"; |
|
2018 } |
|
2019 #endif |
|
2020 |
|
2021 *currentPage << "ET\n"; |
|
2022 } |
|
2023 |
|
2024 QRect QPdfBaseEnginePrivate::paperRect() const |
|
2025 { |
|
2026 int w; |
|
2027 int h; |
|
2028 if (paperSize == QPrinter::Custom) { |
|
2029 w = qRound(customPaperSize.width()*resolution/72.); |
|
2030 h = qRound(customPaperSize.height()*resolution/72.); |
|
2031 } else { |
|
2032 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) |
|
2033 if (QCUPSSupport::isAvailable() && !cupsPaperRect.isNull()) { |
|
2034 QRect r = cupsPaperRect; |
|
2035 w = r.width(); |
|
2036 h = r.height(); |
|
2037 } else |
|
2038 #endif |
|
2039 { |
|
2040 QPdf::PaperSize s = QPdf::paperSize(paperSize); |
|
2041 w = s.width; |
|
2042 h = s.height; |
|
2043 } |
|
2044 w = qRound(w*resolution/72.); |
|
2045 h = qRound(h*resolution/72.); |
|
2046 } |
|
2047 if (orientation == QPrinter::Portrait) |
|
2048 return QRect(0, 0, w, h); |
|
2049 else |
|
2050 return QRect(0, 0, h, w); |
|
2051 } |
|
2052 |
|
2053 QRect QPdfBaseEnginePrivate::pageRect() const |
|
2054 { |
|
2055 if(fullPage) |
|
2056 return paperRect(); |
|
2057 |
|
2058 QRect r; |
|
2059 |
|
2060 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) |
|
2061 if (!hasCustomPageMargins && QCUPSSupport::isAvailable() && !cupsPageRect.isNull()) { |
|
2062 r = cupsPageRect; |
|
2063 if (r == cupsPaperRect) { |
|
2064 // if cups doesn't define any margins, give it at least approx 3.5 mm |
|
2065 r = QRect(10, 10, r.width() - 20, r.height() - 20); |
|
2066 } |
|
2067 } else |
|
2068 #endif |
|
2069 { |
|
2070 QPdf::PaperSize s; |
|
2071 if (paperSize == QPrinter::Custom) { |
|
2072 s.width = qRound(customPaperSize.width()); |
|
2073 s.height = qRound(customPaperSize.height()); |
|
2074 } else { |
|
2075 s = QPdf::paperSize(paperSize); |
|
2076 } |
|
2077 if (hasCustomPageMargins) |
|
2078 r = QRect(0, 0, s.width, s.height); |
|
2079 else |
|
2080 r = QRect(72/3, 72/3, s.width - 2*72/3, s.height - 2*72/3); |
|
2081 } |
|
2082 |
|
2083 int x = qRound(r.left()*resolution/72.); |
|
2084 int y = qRound(r.top()*resolution/72.); |
|
2085 int w = qRound(r.width()*resolution/72.); |
|
2086 int h = qRound(r.height()*resolution/72.); |
|
2087 if (orientation == QPrinter::Portrait) |
|
2088 r = QRect(x, y, w, h); |
|
2089 else |
|
2090 r = QRect(y, x, h, w); |
|
2091 |
|
2092 if (hasCustomPageMargins) { |
|
2093 r.adjust(qRound(leftMargin*(resolution/72.)), |
|
2094 qRound(topMargin*(resolution/72.)), |
|
2095 -qRound(rightMargin*(resolution/72.)), |
|
2096 -qRound(bottomMargin*(resolution/72.))); |
|
2097 } |
|
2098 return r; |
|
2099 } |
|
2100 |
|
2101 #endif |
|
2102 |
|
2103 QT_END_NAMESPACE |