Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qpdf.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qpdf_p.h"
5
6#ifndef QT_NO_PDF
7
8#include "qplatformdefs.h"
9
10#include <private/qcmyk_p.h>
11#include <private/qfont_p.h>
12#include <private/qmath_p.h>
13#include <private/qpainter_p.h>
14
15#include <qbuffer.h>
16#include <qcryptographichash.h>
17#include <qdatetime.h>
18#include <qdebug.h>
19#include <qfile.h>
20#include <qimagewriter.h>
21#include <qnumeric.h>
22#include <qtemporaryfile.h>
23#include <quuid.h>
24
25#ifndef QT_NO_COMPRESS
26#include <zlib.h>
27#endif
28
29#ifdef QT_NO_COMPRESS
30static const bool do_compress = false;
31#else
32static const bool do_compress = true;
33#endif
34
35// might be helpful for smooth transforms of images
36// Can't use it though, as gs generates completely wrong images if this is true.
37static const bool interpolateImages = false;
38
39static void initResources()
40{
41 Q_INIT_RESOURCE(qpdf);
42}
43
45
46using namespace Qt::StringLiterals;
47
48constexpr QPaintEngine::PaintEngineFeatures qt_pdf_decide_features()
49{
50 QPaintEngine::PaintEngineFeatures f = QPaintEngine::AllFeatures;
55 return f;
56}
57
58extern bool qt_isExtendedRadialGradient(const QBrush &brush);
59
60// helper function to remove transparency from brush in PDF/A-1b mode
62{
63 if (brush.style() == Qt::SolidPattern) {
64 QColor color = brush.color();
65 if (color.alpha() != 255) {
66 color.setAlpha(255);
67 brush.setColor(color);
68 }
69
70 return;
71 }
72
74 brush = QBrush(Qt::black); // the safest we can do so far...
75 return;
76 }
77
78 if (brush.style() == Qt::LinearGradientPattern
80 || brush.style() == Qt::ConicalGradientPattern) {
81
82 QGradientStops stops = brush.gradient()->stops();
83 for (int i = 0; i < stops.size(); ++i) {
84 if (stops[i].second.alpha() != 255)
85 stops[i].second.setAlpha(255);
86 }
87
88 const_cast<QGradient*>(brush.gradient())->setStops(stops);
89 return;
90 }
91
92 if (brush.style() == Qt::TexturePattern) {
93 // handled inside QPdfEnginePrivate::addImage() already
94 return;
95 }
96}
97
98
99/* also adds a space at the end of the number */
100const char *qt_real_to_string(qreal val, char *buf) {
101 const char *ret = buf;
102
103 if (!qIsFinite(val) || std::abs(val) > std::numeric_limits<quint32>::max()) {
104 *(buf++) = '0';
105 *(buf++) = ' ';
106 *buf = 0;
107 return ret;
108 }
109
110 if (val < 0) {
111 *(buf++) = '-';
112 val = -val;
113 }
114 qreal frac = std::modf(val, &val);
115 quint32 ival(val);
116
117 int ifrac = (int)(frac * 1000000000);
118 if (ifrac == 1000000000) {
119 ++ival;
120 ifrac = 0;
121 }
122 char output[256];
123 int i = 0;
124 while (ival) {
125 output[i] = '0' + (ival % 10);
126 ++i;
127 ival /= 10;
128 }
129 int fact = 100000000;
130 if (i == 0) {
131 *(buf++) = '0';
132 } else {
133 while (i) {
134 *(buf++) = output[--i];
135 fact /= 10;
136 ifrac /= 10;
137 }
138 }
139
140 if (ifrac) {
141 *(buf++) = '.';
142 while (fact) {
143 *(buf++) = '0' + ((ifrac/fact) % 10);
144 fact /= 10;
145 }
146 }
147 *(buf++) = ' ';
148 *buf = 0;
149 return ret;
150}
151
152const char *qt_int_to_string(int val, char *buf) {
153 const char *ret = buf;
154 if (val < 0) {
155 *(buf++) = '-';
156 val = -val;
157 }
158 char output[256];
159 int i = 0;
160 while (val) {
161 output[i] = '0' + (val % 10);
162 ++i;
163 val /= 10;
164 }
165 if (i == 0) {
166 *(buf++) = '0';
167 } else {
168 while (i)
169 *(buf++) = output[--i];
170 }
171 *(buf++) = ' ';
172 *buf = 0;
173 return ret;
174}
175
176
177namespace QPdf {
178 ByteStream::ByteStream(QByteArray *byteArray, bool fileBacking)
179 : dev(new QBuffer(byteArray)),
180 fileBackingEnabled(fileBacking),
181 fileBackingActive(false),
182 handleDirty(false)
183 {
185 }
186
187 ByteStream::ByteStream(bool fileBacking)
188 : dev(new QBuffer(&ba)),
189 fileBackingEnabled(fileBacking),
190 fileBackingActive(false),
191 handleDirty(false)
192 {
194 }
195
197 {
198 delete dev;
199 }
200
202 {
203 if (handleDirty) prepareBuffer();
204 dev->write(&chr, 1);
205 return *this;
206 }
207
209 {
210 if (handleDirty) prepareBuffer();
211 dev->write(str, strlen(str));
212 return *this;
213 }
214
216 {
217 if (handleDirty) prepareBuffer();
218 dev->write(str);
219 return *this;
220 }
221
223 {
224 Q_ASSERT(!src.dev->isSequential());
225 if (handleDirty) prepareBuffer();
226 // We do play nice here, even though it looks ugly.
227 // We save the position and restore it afterwards.
228 ByteStream &s = const_cast<ByteStream&>(src);
229 qint64 pos = s.dev->pos();
230 s.dev->reset();
231 while (!s.dev->atEnd()) {
232 QByteArray buf = s.dev->read(chunkSize());
233 dev->write(buf);
234 }
235 s.dev->seek(pos);
236 return *this;
237 }
238
240 char buf[256];
242 *this << buf;
243 return *this;
244 }
245
247 char buf[256];
248 qt_int_to_string(val, buf);
249 *this << buf;
250 return *this;
251 }
252
254 char buf[256];
255 qt_real_to_string(p.x(), buf);
256 *this << buf;
257 qt_real_to_string(p.y(), buf);
258 *this << buf;
259 return *this;
260 }
261
263 {
264 dev->reset();
265 handleDirty = true;
266 return dev;
267 }
268
273
274 void ByteStream::prepareBuffer()
275 {
276 Q_ASSERT(!dev->isSequential());
277 qint64 size = dev->size();
278 if (fileBackingEnabled && !fileBackingActive
279 && size > maxMemorySize()) {
280 // Switch to file backing.
281 QTemporaryFile *newFile = new QTemporaryFile;
282 if (newFile->open()) {
283 dev->reset();
284 while (!dev->atEnd()) {
285 QByteArray buf = dev->read(chunkSize());
286 newFile->write(buf);
287 }
288 delete dev;
289 dev = newFile;
290 ba.clear();
291 fileBackingActive = true;
292 }
293 }
294 if (dev->pos() != size) {
295 dev->seek(size);
296 handleDirty = false;
297 }
298 }
299}
300
301#define QT_PATH_ELEMENT(elm)
302
304{
306 if (!path.elementCount())
307 return result;
308
310
311 int start = -1;
312 for (int i = 0; i < path.elementCount(); ++i) {
313 const QPainterPath::Element &elm = path.elementAt(i);
314 switch (elm.type) {
316 if (start >= 0
317 && path.elementAt(start).x == path.elementAt(i-1).x
318 && path.elementAt(start).y == path.elementAt(i-1).y)
319 s << "h\n";
320 s << matrix.map(QPointF(elm.x, elm.y)) << "m\n";
321 start = i;
322 break;
324 s << matrix.map(QPointF(elm.x, elm.y)) << "l\n";
325 break;
327 Q_ASSERT(path.elementAt(i+1).type == QPainterPath::CurveToDataElement);
328 Q_ASSERT(path.elementAt(i+2).type == QPainterPath::CurveToDataElement);
329 s << matrix.map(QPointF(elm.x, elm.y))
330 << matrix.map(QPointF(path.elementAt(i+1).x, path.elementAt(i+1).y))
331 << matrix.map(QPointF(path.elementAt(i+2).x, path.elementAt(i+2).y))
332 << "c\n";
333 i += 2;
334 break;
335 default:
336 qFatal("QPdf::generatePath(), unhandled type: %d", elm.type);
337 }
338 }
339 if (start >= 0
340 && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x
341 && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y)
342 s << "h\n";
343
344 Qt::FillRule fillRule = path.fillRule();
345
346 const char *op = "";
347 switch (flags) {
348 case ClipPath:
349 op = (fillRule == Qt::WindingFill) ? "W n\n" : "W* n\n";
350 break;
351 case FillPath:
352 op = (fillRule == Qt::WindingFill) ? "f\n" : "f*\n";
353 break;
354 case StrokePath:
355 op = "S\n";
356 break;
358 op = (fillRule == Qt::WindingFill) ? "B\n" : "B*\n";
359 break;
360 }
361 s << op;
362 return result;
363}
364
366{
369 s << matrix.m11()
370 << matrix.m12()
371 << matrix.m21()
372 << matrix.m22()
373 << matrix.dx()
374 << matrix.dy()
375 << "cm\n";
376 return result;
377}
378
380{
383 s << '[';
384
385 QList<qreal> dasharray = pen.dashPattern();
386 qreal w = pen.widthF();
387 if (w < 0.001)
388 w = 1;
389 for (int i = 0; i < dasharray.size(); ++i) {
390 qreal dw = dasharray.at(i)*w;
391 if (dw < 0.0001) dw = 0.0001;
392 s << dw;
393 }
394 s << ']';
395 s << pen.dashOffset() * w;
396 s << " d\n";
397 return result;
398}
399
400
401
402static const char* const pattern_for_brush[] = {
403 nullptr, // NoBrush
404 nullptr, // SolidPattern
405 "0 J\n"
406 "6 w\n"
407 "[] 0 d\n"
408 "4 0 m\n"
409 "4 8 l\n"
410 "0 4 m\n"
411 "8 4 l\n"
412 "S\n", // Dense1Pattern
413
414 "0 J\n"
415 "2 w\n"
416 "[6 2] 1 d\n"
417 "0 0 m\n"
418 "0 8 l\n"
419 "8 0 m\n"
420 "8 8 l\n"
421 "S\n"
422 "[] 0 d\n"
423 "2 0 m\n"
424 "2 8 l\n"
425 "6 0 m\n"
426 "6 8 l\n"
427 "S\n"
428 "[6 2] -3 d\n"
429 "4 0 m\n"
430 "4 8 l\n"
431 "S\n", // Dense2Pattern
432
433 "0 J\n"
434 "2 w\n"
435 "[6 2] 1 d\n"
436 "0 0 m\n"
437 "0 8 l\n"
438 "8 0 m\n"
439 "8 8 l\n"
440 "S\n"
441 "[2 2] -1 d\n"
442 "2 0 m\n"
443 "2 8 l\n"
444 "6 0 m\n"
445 "6 8 l\n"
446 "S\n"
447 "[6 2] -3 d\n"
448 "4 0 m\n"
449 "4 8 l\n"
450 "S\n", // Dense3Pattern
451
452 "0 J\n"
453 "2 w\n"
454 "[2 2] 1 d\n"
455 "0 0 m\n"
456 "0 8 l\n"
457 "8 0 m\n"
458 "8 8 l\n"
459 "S\n"
460 "[2 2] -1 d\n"
461 "2 0 m\n"
462 "2 8 l\n"
463 "6 0 m\n"
464 "6 8 l\n"
465 "S\n"
466 "[2 2] 1 d\n"
467 "4 0 m\n"
468 "4 8 l\n"
469 "S\n", // Dense4Pattern
470
471 "0 J\n"
472 "2 w\n"
473 "[2 6] -1 d\n"
474 "0 0 m\n"
475 "0 8 l\n"
476 "8 0 m\n"
477 "8 8 l\n"
478 "S\n"
479 "[2 2] 1 d\n"
480 "2 0 m\n"
481 "2 8 l\n"
482 "6 0 m\n"
483 "6 8 l\n"
484 "S\n"
485 "[2 6] 3 d\n"
486 "4 0 m\n"
487 "4 8 l\n"
488 "S\n", // Dense5Pattern
489
490 "0 J\n"
491 "2 w\n"
492 "[2 6] -1 d\n"
493 "0 0 m\n"
494 "0 8 l\n"
495 "8 0 m\n"
496 "8 8 l\n"
497 "S\n"
498 "[2 6] 3 d\n"
499 "4 0 m\n"
500 "4 8 l\n"
501 "S\n", // Dense6Pattern
502
503 "0 J\n"
504 "2 w\n"
505 "[2 6] -1 d\n"
506 "0 0 m\n"
507 "0 8 l\n"
508 "8 0 m\n"
509 "8 8 l\n"
510 "S\n", // Dense7Pattern
511
512 "1 w\n"
513 "0 4 m\n"
514 "8 4 l\n"
515 "S\n", // HorPattern
516
517 "1 w\n"
518 "4 0 m\n"
519 "4 8 l\n"
520 "S\n", // VerPattern
521
522 "1 w\n"
523 "4 0 m\n"
524 "4 8 l\n"
525 "0 4 m\n"
526 "8 4 l\n"
527 "S\n", // CrossPattern
528
529 "1 w\n"
530 "-1 5 m\n"
531 "5 -1 l\n"
532 "3 9 m\n"
533 "9 3 l\n"
534 "S\n", // BDiagPattern
535
536 "1 w\n"
537 "-1 3 m\n"
538 "5 9 l\n"
539 "3 -1 m\n"
540 "9 5 l\n"
541 "S\n", // FDiagPattern
542
543 "1 w\n"
544 "-1 3 m\n"
545 "5 9 l\n"
546 "3 -1 m\n"
547 "9 5 l\n"
548 "-1 5 m\n"
549 "5 -1 l\n"
550 "3 9 m\n"
551 "9 3 l\n"
552 "S\n", // DiagCrossPattern
553};
554
556{
557 int style = b.style();
558 if (style > Qt::DiagCrossPattern)
559 return QByteArray();
560 return pattern_for_brush[style];
561}
562
563
564static void moveToHook(qfixed x, qfixed y, void *data)
565{
567 if (!t->first)
568 *t->stream << "h\n";
569 if (!t->cosmeticPen)
570 t->matrix.map(x, y, &x, &y);
571 *t->stream << x << y << "m\n";
572 t->first = false;
573}
574
575static void lineToHook(qfixed x, qfixed y, void *data)
576{
578 if (!t->cosmeticPen)
579 t->matrix.map(x, y, &x, &y);
580 *t->stream << x << y << "l\n";
581}
582
583static void cubicToHook(qfixed c1x, qfixed c1y,
584 qfixed c2x, qfixed c2y,
585 qfixed ex, qfixed ey,
586 void *data)
587{
589 if (!t->cosmeticPen) {
590 t->matrix.map(c1x, c1y, &c1x, &c1y);
591 t->matrix.map(c2x, c2y, &c2x, &c2y);
592 t->matrix.map(ex, ey, &ex, &ey);
593 }
594 *t->stream << c1x << c1y
595 << c2x << c2y
596 << ex << ey
597 << "c\n";
598}
599
601 : stream(nullptr),
602 first(true),
603 dashStroker(&basicStroker)
604{
605 stroker = &basicStroker;
606 basicStroker.setMoveToHook(moveToHook);
607 basicStroker.setLineToHook(lineToHook);
608 basicStroker.setCubicToHook(cubicToHook);
609 cosmeticPen = true;
610 basicStroker.setStrokeWidth(.1);
611}
612
613void QPdf::Stroker::setPen(const QPen &pen, QPainter::RenderHints)
614{
615 if (pen.style() == Qt::NoPen) {
616 stroker = nullptr;
617 return;
618 }
619 qreal w = pen.widthF();
620 bool zeroWidth = w < 0.0001;
621 cosmeticPen = pen.isCosmetic();
622 if (zeroWidth)
623 w = .1;
624
625 basicStroker.setStrokeWidth(w);
626 basicStroker.setCapStyle(pen.capStyle());
627 basicStroker.setJoinStyle(pen.joinStyle());
628 basicStroker.setMiterLimit(pen.miterLimit());
629
630 QList<qreal> dashpattern = pen.dashPattern();
631 if (zeroWidth) {
632 for (int i = 0; i < dashpattern.size(); ++i)
633 dashpattern[i] *= 10.;
634 }
635 if (!dashpattern.isEmpty()) {
636 dashStroker.setDashPattern(dashpattern);
637 dashStroker.setDashOffset(pen.dashOffset());
638 stroker = &dashStroker;
639 } else {
640 stroker = &basicStroker;
641 }
642}
643
645{
646 if (!stroker)
647 return;
648 first = true;
649
650 stroker->strokePath(path, this, cosmeticPen ? matrix : QTransform());
651 *stream << "h f\n";
652}
653
655{
656 int isize = input.size()/4*4;
658 output.resize(input.size()*5/4+7);
659 char *out = output.data();
660 const uchar *in = (const uchar *)input.constData();
661 for (int i = 0; i < isize; i += 4) {
662 uint val = (((uint)in[i])<<24) + (((uint)in[i+1])<<16) + (((uint)in[i+2])<<8) + (uint)in[i+3];
663 if (val == 0) {
664 *out = 'z';
665 ++out;
666 } else {
667 char base[5];
668 base[4] = val % 85;
669 val /= 85;
670 base[3] = val % 85;
671 val /= 85;
672 base[2] = val % 85;
673 val /= 85;
674 base[1] = val % 85;
675 val /= 85;
676 base[0] = val % 85;
677 *(out++) = base[0] + '!';
678 *(out++) = base[1] + '!';
679 *(out++) = base[2] + '!';
680 *(out++) = base[3] + '!';
681 *(out++) = base[4] + '!';
682 }
683 }
684 //write the last few bytes
685 int remaining = input.size() - isize;
686 if (remaining) {
687 uint val = 0;
688 for (int i = isize; i < input.size(); ++i)
689 val = (val << 8) + in[i];
690 val <<= 8*(4-remaining);
691 char base[5];
692 base[4] = val % 85;
693 val /= 85;
694 base[3] = val % 85;
695 val /= 85;
696 base[2] = val % 85;
697 val /= 85;
698 base[1] = val % 85;
699 val /= 85;
700 base[0] = val % 85;
701 for (int i = 0; i < remaining+1; ++i)
702 *(out++) = base[i] + '!';
703 }
704 *(out++) = '~';
705 *(out++) = '>';
706 output.resize(out-output.data());
707 return output;
708}
709
710const char *QPdf::toHex(ushort u, char *buffer)
711{
712 int i = 3;
713 while (i >= 0) {
714 ushort hex = (u & 0x000f);
715 if (hex < 0x0a)
716 buffer[i] = '0'+hex;
717 else
718 buffer[i] = 'A'+(hex-0x0a);
719 u = u >> 4;
720 i--;
721 }
722 buffer[4] = '\0';
723 return buffer;
724}
725
726const char *QPdf::toHex(uchar u, char *buffer)
727{
728 int i = 1;
729 while (i >= 0) {
730 ushort hex = (u & 0x000f);
731 if (hex < 0x0a)
732 buffer[i] = '0'+hex;
733 else
734 buffer[i] = 'A'+(hex-0x0a);
735 u = u >> 4;
736 i--;
737 }
738 buffer[2] = '\0';
739 return buffer;
740}
741
742
744 : QPdf::ByteStream(true) // Enable file backing
745{
746}
747
748void QPdfPage::streamImage(int w, int h, uint object)
749{
750 *this << w << "0 0 " << -h << "0 " << h << "cm /Im" << object << " Do\n";
751 if (!images.contains(object))
752 images.append(object);
753}
754
755
760
765
767{
768 Q_D(QPdfEngine);
769 d->outputFileName = filename;
770}
771
772
773void QPdfEngine::drawPoints (const QPointF *points, int pointCount)
774{
775 if (!points)
776 return;
777
778 Q_D(QPdfEngine);
780 for (int i=0; i!=pointCount;++i) {
781 p.moveTo(points[i]);
782 p.lineTo(points[i] + QPointF(0, 0.001));
783 }
784
785 bool hadBrush = d->hasBrush;
786 d->hasBrush = false;
787 drawPath(p);
788 d->hasBrush = hadBrush;
789}
790
791void QPdfEngine::drawLines (const QLineF *lines, int lineCount)
792{
793 if (!lines)
794 return;
795
796 Q_D(QPdfEngine);
798 for (int i=0; i!=lineCount;++i) {
799 p.moveTo(lines[i].p1());
800 p.lineTo(lines[i].p2());
801 }
802 bool hadBrush = d->hasBrush;
803 d->hasBrush = false;
804 drawPath(p);
805 d->hasBrush = hadBrush;
806}
807
808void QPdfEngine::drawRects (const QRectF *rects, int rectCount)
809{
810 if (!rects)
811 return;
812
813 Q_D(QPdfEngine);
814
815 if (d->clipEnabled && d->allClipped)
816 return;
817 if (!d->hasPen && !d->hasBrush)
818 return;
819
820 if ((d->simplePen && !d->needsTransform) || !d->hasPen) {
821 // draw natively in this case for better output
822 if (!d->hasPen && d->needsTransform) // i.e. this is just a fillrect
823 *d->currentPage << "q\n" << QPdf::generateMatrix(d->stroker.matrix);
824 for (int i = 0; i < rectCount; ++i)
825 *d->currentPage << rects[i].x() << rects[i].y() << rects[i].width() << rects[i].height() << "re\n";
826 *d->currentPage << (d->hasPen ? (d->hasBrush ? "B\n" : "S\n") : "f\n");
827 if (!d->hasPen && d->needsTransform)
828 *d->currentPage << "Q\n";
829 } else {
831 for (int i=0; i!=rectCount; ++i)
832 p.addRect(rects[i]);
833 drawPath(p);
834 }
835}
836
838{
839 Q_D(QPdfEngine);
840
841 if (!points || !pointCount)
842 return;
843
844 bool hb = d->hasBrush;
846
847 switch(mode) {
848 case OddEvenMode:
850 break;
851 case ConvexMode:
852 case WindingMode:
853 p.setFillRule(Qt::WindingFill);
854 break;
855 case PolylineMode:
856 d->hasBrush = false;
857 break;
858 default:
859 break;
860 }
861
862 p.moveTo(points[0]);
863 for (int i = 1; i < pointCount; ++i)
864 p.lineTo(points[i]);
865
866 if (mode != PolylineMode)
867 p.closeSubpath();
868 drawPath(p);
869
870 d->hasBrush = hb;
871}
872
874{
875 Q_D(QPdfEngine);
876
877 if (d->clipEnabled && d->allClipped)
878 return;
879 if (!d->hasPen && !d->hasBrush)
880 return;
881
882 if (d->simplePen) {
883 // draw strokes natively in this case for better output
884 *d->currentPage << QPdf::generatePath(p, d->needsTransform ? d->stroker.matrix : QTransform(),
886 } else {
887 if (d->hasBrush)
888 *d->currentPage << QPdf::generatePath(p, d->stroker.matrix, QPdf::FillPath);
889 if (d->hasPen) {
890 *d->currentPage << "q\n";
891 QBrush b = d->brush;
892 d->brush = d->pen.brush();
893 setBrush();
894 d->stroker.strokePath(p);
895 *d->currentPage << "Q\n";
896 d->brush = b;
897 }
898 }
899}
900
901void QPdfEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QRectF &sr)
902{
903 if (sr.isEmpty() || rectangle.isEmpty() || pixmap.isNull())
904 return;
905 Q_D(QPdfEngine);
906
907 QBrush b = d->brush;
908
909 QRect sourceRect = sr.toRect();
910 QPixmap pm = sourceRect != pixmap.rect() ? pixmap.copy(sourceRect) : pixmap;
911 QImage image = pm.toImage();
912 bool bitmap = true;
914 const int object = d->addImage(image, &bitmap, lossless, pm.cacheKey());
915 if (object < 0)
916 return;
917
918 *d->currentPage << "q\n";
919
920 if ((d->pdfVersion != QPdfEngine::Version_A1b) && (d->opacity != 1.0)) {
921 int stateObject = d->addConstantAlphaObject(qRound(255 * d->opacity), qRound(255 * d->opacity));
922 if (stateObject)
923 *d->currentPage << "/GState" << stateObject << "gs\n";
924 else
925 *d->currentPage << "/GSa gs\n";
926 } else {
927 *d->currentPage << "/GSa gs\n";
928 }
929
930 *d->currentPage
931 << QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
932 rectangle.x(), rectangle.y()) * (!d->needsTransform ? QTransform() : d->stroker.matrix));
933 if (bitmap) {
934 // set current pen as d->brush
935 d->brush = d->pen.brush();
936 }
937 setBrush();
938 d->currentPage->streamImage(image.width(), image.height(), object);
939 *d->currentPage << "Q\n";
940
941 d->brush = b;
942}
943
944void QPdfEngine::drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags)
945{
946 if (sr.isEmpty() || rectangle.isEmpty() || image.isNull())
947 return;
948 Q_D(QPdfEngine);
949
950 QRect sourceRect = sr.toRect();
951 QImage im = sourceRect != image.rect() ? image.copy(sourceRect) : image;
952 bool bitmap = true;
954 const int object = d->addImage(im, &bitmap, lossless, im.cacheKey());
955 if (object < 0)
956 return;
957
958 *d->currentPage << "q\n";
959
960 if ((d->pdfVersion != QPdfEngine::Version_A1b) && (d->opacity != 1.0)) {
961 int stateObject = d->addConstantAlphaObject(qRound(255 * d->opacity), qRound(255 * d->opacity));
962 if (stateObject)
963 *d->currentPage << "/GState" << stateObject << "gs\n";
964 else
965 *d->currentPage << "/GSa gs\n";
966 } else {
967 *d->currentPage << "/GSa gs\n";
968 }
969
970 *d->currentPage
971 << QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
972 rectangle.x(), rectangle.y()) * (!d->needsTransform ? QTransform() : d->stroker.matrix));
973 setBrush();
974 d->currentPage->streamImage(im.width(), im.height(), object);
975 *d->currentPage << "Q\n";
976}
977
978void QPdfEngine::drawTiledPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QPointF &point)
979{
980 Q_D(QPdfEngine);
981
982 bool bitmap = (pixmap.depth() == 1);
983 QBrush b = d->brush;
984 QPointF bo = d->brushOrigin;
985 bool hp = d->hasPen;
986 d->hasPen = false;
987 bool hb = d->hasBrush;
988 d->hasBrush = true;
989
990 d->brush = QBrush(pixmap);
991 if (bitmap)
992 // #### fix bitmap case where we have a brush pen
993 d->brush.setColor(d->pen.color());
994
995 d->brushOrigin = -point;
996 *d->currentPage << "q\n";
997 setBrush();
998
999 drawRects(&rectangle, 1);
1000 *d->currentPage << "Q\n";
1001
1002 d->hasPen = hp;
1003 d->hasBrush = hb;
1004 d->brush = b;
1005 d->brushOrigin = bo;
1006}
1007
1008void QPdfEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
1009{
1010 Q_D(QPdfEngine);
1011
1012 if (!d->hasPen || (d->clipEnabled && d->allClipped))
1013 return;
1014
1015 if (d->stroker.matrix.type() >= QTransform::TxProject) {
1016 QPaintEngine::drawTextItem(p, textItem);
1017 return;
1018 }
1019
1020 *d->currentPage << "q\n";
1021 if (d->needsTransform)
1022 *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
1023
1024 bool hp = d->hasPen;
1025 d->hasPen = false;
1026 QBrush b = d->brush;
1027 d->brush = d->pen.brush();
1028 setBrush();
1029
1030 const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
1031 Q_ASSERT(ti.fontEngine->type() != QFontEngine::Multi);
1032 d->drawTextItem(p, ti);
1033 d->hasPen = hp;
1034 d->brush = b;
1035 *d->currentPage << "Q\n";
1036}
1037
1039{
1040 Q_D(QPdfEngine);
1041
1042 const uint annot = d->addXrefEntry(-1);
1043 const QByteArray urlascii = url.toEncoded();
1044 int len = urlascii.size();
1045 QVarLengthArray<char> url_esc;
1046 url_esc.reserve(len + 1);
1047 for (int j = 0; j < len; j++) {
1048 if (urlascii[j] == '(' || urlascii[j] == ')' || urlascii[j] == '\\')
1049 url_esc.append('\\');
1050 url_esc.append(urlascii[j]);
1051 }
1052 url_esc.append('\0');
1053
1054 char buf[256];
1055 const QRectF rr = d->pageMatrix().mapRect(r);
1056 d->xprintf("<<\n/Type /Annot\n/Subtype /Link\n");
1057
1058 if (d->pdfVersion == QPdfEngine::Version_A1b)
1059 d->xprintf("/F 4\n"); // enable print flag, disable all other
1060
1061 d->xprintf("/Rect [");
1062 d->xprintf("%s ", qt_real_to_string(rr.left(), buf));
1063 d->xprintf("%s ", qt_real_to_string(rr.top(), buf));
1064 d->xprintf("%s ", qt_real_to_string(rr.right(), buf));
1065 d->xprintf("%s", qt_real_to_string(rr.bottom(), buf));
1066 d->xprintf("]\n/Border [0 0 0]\n/A <<\n");
1067 d->xprintf("/Type /Action\n/S /URI\n/URI (%s)\n", url_esc.constData());
1068 d->xprintf(">>\n>>\n");
1069 d->xprintf("endobj\n");
1070 d->currentPage->annotations.append(annot);
1071}
1072
1074{
1075 Q_D(QPdfEngine);
1076
1077 QPaintEngine::DirtyFlags flags = state.state();
1078
1079 if (flags & DirtyHints)
1080 flags |= DirtyBrush;
1081
1082 if (flags & DirtyTransform)
1083 d->stroker.matrix = state.transform();
1084
1085 if (flags & DirtyPen) {
1086 if (d->pdfVersion == QPdfEngine::Version_A1b) {
1087 QPen pen = state.pen();
1088
1089 QColor penColor = pen.color();
1090 if (penColor.alpha() != 255)
1091 penColor.setAlpha(255);
1092 pen.setColor(penColor);
1093
1094 QBrush penBrush = pen.brush();
1096 pen.setBrush(penBrush);
1097
1098 d->pen = pen;
1099 } else {
1100 d->pen = state.pen();
1101 }
1102 d->hasPen = d->pen.style() != Qt::NoPen;
1103 bool oldCosmetic = d->stroker.cosmeticPen;
1104 d->stroker.setPen(d->pen, state.renderHints());
1105 QBrush penBrush = d->pen.brush();
1106 bool oldSimple = d->simplePen;
1107 d->simplePen = (d->hasPen && (penBrush.style() == Qt::SolidPattern) && penBrush.isOpaque() && d->opacity == 1.0);
1108 if (oldSimple != d->simplePen || oldCosmetic != d->stroker.cosmeticPen)
1110 } else if (flags & DirtyHints) {
1111 d->stroker.setPen(d->pen, state.renderHints());
1112 }
1113 if (flags & DirtyBrush) {
1114 if (d->pdfVersion == QPdfEngine::Version_A1b) {
1115 QBrush brush = state.brush();
1117 d->brush = brush;
1118 } else {
1119 d->brush = state.brush();
1120 }
1121 if (d->brush.color().alpha() == 0 && d->brush.style() == Qt::SolidPattern)
1122 d->brush.setStyle(Qt::NoBrush);
1123 d->hasBrush = d->brush.style() != Qt::NoBrush;
1124 }
1125 if (flags & DirtyBrushOrigin) {
1126 d->brushOrigin = state.brushOrigin();
1127 flags |= DirtyBrush;
1128 }
1129 if (flags & DirtyOpacity) {
1130 d->opacity = state.opacity();
1131 if (d->simplePen && d->opacity != 1.0) {
1132 d->simplePen = false;
1134 }
1135 }
1136
1137 bool ce = d->clipEnabled;
1138 if (flags & DirtyClipPath) {
1139 d->clipEnabled = true;
1140 updateClipPath(state.clipPath(), state.clipOperation());
1141 } else if (flags & DirtyClipRegion) {
1142 d->clipEnabled = true;
1144 for (const QRect &rect : state.clipRegion())
1145 path.addRect(rect);
1146 updateClipPath(path, state.clipOperation());
1148 } else if (flags & DirtyClipEnabled) {
1149 d->clipEnabled = state.isClipEnabled();
1150 }
1151
1152 if (ce != d->clipEnabled)
1154 else if (!d->clipEnabled)
1155 flags &= ~DirtyClipPath;
1156
1158}
1159
1160void QPdfEngine::setupGraphicsState(QPaintEngine::DirtyFlags flags)
1161{
1162 Q_D(QPdfEngine);
1163 if (flags & DirtyClipPath)
1165
1166 if (flags & DirtyTransform) {
1167 *d->currentPage << "Q\n";
1169 }
1170
1171 if (flags & DirtyClipPath) {
1172 *d->currentPage << "Q q\n";
1173
1174 d->allClipped = false;
1175 if (d->clipEnabled && !d->clips.isEmpty()) {
1176 for (int i = 0; i < d->clips.size(); ++i) {
1177 if (d->clips.at(i).isEmpty()) {
1178 d->allClipped = true;
1179 break;
1180 }
1181 }
1182 if (!d->allClipped) {
1183 for (int i = 0; i < d->clips.size(); ++i) {
1184 *d->currentPage << QPdf::generatePath(d->clips.at(i), QTransform(), QPdf::ClipPath);
1185 }
1186 }
1187 }
1188 }
1189
1190 if (flags & DirtyTransform) {
1191 *d->currentPage << "q\n";
1192 d->needsTransform = false;
1193 if (!d->stroker.matrix.isIdentity()) {
1194 if (d->simplePen && !d->stroker.cosmeticPen)
1195 *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
1196 else
1197 d->needsTransform = true; // I.e. page-wide xf not set, local xf needed
1198 }
1199 }
1200 if (flags & DirtyBrush)
1201 setBrush();
1202 if (d->simplePen && (flags & DirtyPen))
1203 setPen();
1204}
1205
1206extern QPainterPath qt_regionToPath(const QRegion &region);
1207
1208void QPdfEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
1209{
1210 Q_D(QPdfEngine);
1211 QPainterPath path = d->stroker.matrix.map(p);
1212 //qDebug() << "updateClipPath: " << d->stroker.matrix << p.boundingRect() << path.boundingRect() << op;
1213
1214 switch (op) {
1215 case Qt::NoClip:
1216 d->clipEnabled = false;
1217 d->clips.clear();
1218 break;
1219 case Qt::ReplaceClip:
1220 d->clips.clear();
1221 d->clips.append(path);
1222 break;
1223 case Qt::IntersectClip:
1224 d->clips.append(path);
1225 break;
1226 }
1227}
1228
1230{
1231 Q_D(QPdfEngine);
1232 if (d->pen.style() == Qt::NoPen)
1233 return;
1234 QBrush b = d->pen.brush();
1235 Q_ASSERT(b.style() == Qt::SolidPattern && b.isOpaque());
1236
1237 d->writeColor(QPdfEnginePrivate::ColorDomain::Stroking, b.color());
1238 *d->currentPage << "SCN\n";
1239 *d->currentPage << d->pen.widthF() << "w ";
1240
1241 int pdfCapStyle = 0;
1242 switch(d->pen.capStyle()) {
1243 case Qt::FlatCap:
1244 pdfCapStyle = 0;
1245 break;
1246 case Qt::SquareCap:
1247 pdfCapStyle = 2;
1248 break;
1249 case Qt::RoundCap:
1250 pdfCapStyle = 1;
1251 break;
1252 default:
1253 break;
1254 }
1255 *d->currentPage << pdfCapStyle << "J ";
1256
1257 int pdfJoinStyle = 0;
1258 switch(d->pen.joinStyle()) {
1259 case Qt::MiterJoin:
1260 case Qt::SvgMiterJoin:
1261 *d->currentPage << qMax(qreal(1.0), d->pen.miterLimit()) << "M ";
1262 pdfJoinStyle = 0;
1263 break;
1264 case Qt::BevelJoin:
1265 pdfJoinStyle = 2;
1266 break;
1267 case Qt::RoundJoin:
1268 pdfJoinStyle = 1;
1269 break;
1270 default:
1271 break;
1272 }
1273 *d->currentPage << pdfJoinStyle << "j ";
1274
1275 *d->currentPage << QPdf::generateDashes(d->pen);
1276}
1277
1278
1280{
1281 Q_D(QPdfEngine);
1282 Qt::BrushStyle style = d->brush.style();
1283 if (style == Qt::NoBrush)
1284 return;
1285
1286 bool specifyColor;
1287 int gStateObject = 0;
1288 int patternObject = d->addBrushPattern(d->stroker.matrix, &specifyColor, &gStateObject);
1289 if (!patternObject && !specifyColor)
1290 return;
1291
1292 const auto domain = patternObject ? QPdfEnginePrivate::ColorDomain::NonStrokingPattern
1293 : QPdfEnginePrivate::ColorDomain::NonStroking;
1294 d->writeColor(domain, specifyColor ? d->brush.color() : QColor());
1295 if (patternObject)
1296 *d->currentPage << "/Pat" << patternObject;
1297 *d->currentPage << "scn\n";
1298
1299 if (gStateObject)
1300 *d->currentPage << "/GState" << gStateObject << "gs\n";
1301 else
1302 *d->currentPage << "/GSa gs\n";
1303}
1304
1305
1307{
1308 Q_D(QPdfEngine);
1309 if (!isActive())
1310 return false;
1311 d->newPage();
1312
1314 QFile *outfile = qobject_cast<QFile*> (d->outDevice);
1315 if (outfile && outfile->error() != QFile::NoError)
1316 return false;
1317 return true;
1318}
1319
1324
1325void QPdfEngine::setResolution(int resolution)
1326{
1327 Q_D(QPdfEngine);
1328 d->resolution = resolution;
1329}
1330
1332{
1333 Q_D(const QPdfEngine);
1334 return d->resolution;
1335}
1336
1338{
1339 Q_D(QPdfEngine);
1340 d->pdfVersion = version;
1341}
1342
1344{
1345 Q_D(QPdfEngine);
1346 d->xmpDocumentMetadata = xmpMetadata;
1347}
1348
1350{
1351 Q_D(const QPdfEngine);
1352 return d->xmpDocumentMetadata;
1353}
1354
1356{
1357 Q_D(QPdfEngine);
1358 d->m_pageLayout = pageLayout;
1359}
1360
1362{
1363 Q_D(QPdfEngine);
1364 d->m_pageLayout.setPageSize(pageSize);
1365}
1366
1368{
1369 Q_D(QPdfEngine);
1370 d->m_pageLayout.setOrientation(orientation);
1371}
1372
1374{
1375 Q_D(QPdfEngine);
1376 d->m_pageLayout.setUnits(units);
1377 d->m_pageLayout.setMargins(margins);
1378}
1379
1381{
1382 Q_D(const QPdfEngine);
1383 return d->m_pageLayout;
1384}
1385
1386// Metrics are in Device Pixels
1388{
1389 Q_D(const QPdfEngine);
1390 int val;
1391 switch (metricType) {
1393 val = d->m_pageLayout.paintRectPixels(d->resolution).width();
1394 break;
1396 val = d->m_pageLayout.paintRectPixels(d->resolution).height();
1397 break;
1400 val = d->resolution;
1401 break;
1404 val = 1200;
1405 break;
1407 val = qRound(d->m_pageLayout.paintRect(QPageLayout::Millimeter).width());
1408 break;
1410 val = qRound(d->m_pageLayout.paintRect(QPageLayout::Millimeter).height());
1411 break;
1413 val = INT_MAX;
1414 break;
1416 val = 32;
1417 break;
1419 val = 1;
1420 break;
1423 break;
1424 default:
1425 qWarning("QPdfWriter::metric: Invalid metric command");
1426 return 0;
1427 }
1428 return val;
1429}
1430
1432 : clipEnabled(false), allClipped(false), hasPen(true), hasBrush(false), simplePen(false),
1433 needsTransform(false), pdfVersion(QPdfEngine::Version_1_4),
1434 colorModel(QPdfEngine::ColorModel::Auto),
1435 outDevice(nullptr), ownsDevice(false),
1436 embedFonts(true),
1437 m_pageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(10, 10, 10, 10))
1438{
1439 initResources();
1440 resolution = 1200;
1441 currentObject = 1;
1442 currentPage = nullptr;
1443 stroker.stream = nullptr;
1444
1445 streampos = 0;
1446
1447 stream = new QDataStream;
1448}
1449
1451{
1452 Q_D(QPdfEngine);
1453 d->pdev = pdev;
1454
1455 if (!d->outDevice) {
1456 if (!d->outputFileName.isEmpty()) {
1457 QFile *file = new QFile(d->outputFileName);
1459 delete file;
1460 return false;
1461 }
1462 d->outDevice = file;
1463 } else {
1464 return false;
1465 }
1466 d->ownsDevice = true;
1467 }
1468
1469 d->currentObject = 1;
1470
1471 d->currentPage = new QPdfPage;
1472 d->stroker.stream = d->currentPage;
1473 d->opacity = 1.0;
1474
1475 d->stream->setDevice(d->outDevice);
1476
1477 d->streampos = 0;
1478 d->hasPen = true;
1479 d->hasBrush = false;
1480 d->clipEnabled = false;
1481 d->allClipped = false;
1482
1483 d->xrefPositions.clear();
1484 d->pageRoot = 0;
1485 d->namesRoot = 0;
1486 d->destsRoot = 0;
1487 d->attachmentsRoot = 0;
1488 d->catalog = 0;
1489 d->info = 0;
1490 d->graphicsState = 0;
1491 d->patternColorSpaceRGB = 0;
1492 d->patternColorSpaceGrayscale = 0;
1493 d->patternColorSpaceCMYK = 0;
1494 d->simplePen = false;
1495 d->needsTransform = false;
1496
1497 d->pages.clear();
1498 d->imageCache.clear();
1499 d->alphaCache.clear();
1500
1501 setActive(true);
1502 d->writeHeader();
1503 newPage();
1504
1505 return true;
1506}
1507
1509{
1510 Q_D(QPdfEngine);
1511 d->writeTail();
1512
1513 d->stream->setDevice(nullptr);
1514
1515 qDeleteAll(d->fonts);
1516 d->fonts.clear();
1517 delete d->currentPage;
1518 d->currentPage = nullptr;
1519
1520 if (d->outDevice && d->ownsDevice) {
1521 d->outDevice->close();
1522 delete d->outDevice;
1523 d->outDevice = nullptr;
1524 }
1525
1526 d->destCache.clear();
1527 d->fileCache.clear();
1528
1529 setActive(false);
1530 return true;
1531}
1532
1534{
1535 Q_D(QPdfEngine);
1536 d->fileCache.push_back({fileName, data, mimeType});
1537}
1538
1540{
1542 delete currentPage;
1543 delete stream;
1544}
1545
1547{
1548 addXrefEntry(0,false);
1549
1550 // Keep in sync with QPdfEngine::PdfVersion!
1551 static const char mapping[][4] = {
1552 "1.4", // Version_1_4
1553 "1.4", // Version_A1b
1554 "1.6", // Version_1_6
1555 };
1556 static const size_t numMappings = sizeof mapping / sizeof *mapping;
1557 const char *verStr = mapping[size_t(pdfVersion) < numMappings ? pdfVersion : 0];
1558
1559 xprintf("%%PDF-%s\n", verStr);
1560 xprintf("%%\303\242\303\243\n");
1561
1562 writeInfo();
1563
1564 int metaDataObj = -1;
1565 int outputIntentObj = -1;
1566 if (pdfVersion == QPdfEngine::Version_A1b || !xmpDocumentMetadata.isEmpty()) {
1567 metaDataObj = writeXmpDcumentMetaData();
1568 }
1570 outputIntentObj = writeOutputIntent();
1571 }
1572
1573 catalog = addXrefEntry(-1);
1574 pageRoot = requestObject();
1575 namesRoot = requestObject();
1576
1577 // catalog
1578 {
1579 QByteArray catalog;
1580 QPdf::ByteStream s(&catalog);
1581 s << "<<\n"
1582 << "/Type /Catalog\n"
1583 << "/Pages " << pageRoot << "0 R\n"
1584 << "/Names " << namesRoot << "0 R\n";
1585
1586 if (pdfVersion == QPdfEngine::Version_A1b || !xmpDocumentMetadata.isEmpty())
1587 s << "/Metadata " << metaDataObj << "0 R\n";
1588
1590 s << "/OutputIntents [" << outputIntentObj << "0 R]\n";
1591
1592 s << ">>\n"
1593 << "endobj\n";
1594
1595 write(catalog);
1596 }
1597
1598 // graphics state
1599 graphicsState = addXrefEntry(-1);
1600 xprintf("<<\n"
1601 "/Type /ExtGState\n"
1602 "/SA true\n"
1603 "/SM 0.02\n"
1604 "/ca 1.0\n"
1605 "/CA 1.0\n"
1606 "/AIS false\n"
1607 "/SMask /None"
1608 ">>\n"
1609 "endobj\n");
1610
1611 // color spaces for pattern
1612 patternColorSpaceRGB = addXrefEntry(-1);
1613 xprintf("[/Pattern /DeviceRGB]\n"
1614 "endobj\n");
1615 patternColorSpaceGrayscale = addXrefEntry(-1);
1616 xprintf("[/Pattern /DeviceGray]\n"
1617 "endobj\n");
1618 patternColorSpaceCMYK = addXrefEntry(-1);
1619 xprintf("[/Pattern /DeviceCMYK]\n"
1620 "endobj\n");
1621}
1622
1623QPdfEngine::ColorModel QPdfEnginePrivate::colorModelForColor(const QColor &color) const
1624{
1625 switch (colorModel) {
1629 return colorModel;
1631 switch (color.spec()) {
1632 case QColor::Invalid:
1633 case QColor::Rgb:
1634 case QColor::Hsv:
1635 case QColor::Hsl:
1638 case QColor::Cmyk:
1640 }
1641
1642 break;
1643 }
1644
1645 Q_UNREACHABLE_RETURN(QPdfEngine::ColorModel::RGB);
1646}
1647
1648void QPdfEnginePrivate::writeColor(ColorDomain domain, const QColor &color)
1649{
1650 // Switch to the right colorspace.
1651 // For simplicity: do it even if it redundant (= already in that colorspace)
1652 const QPdfEngine::ColorModel actualColorModel = colorModelForColor(color);
1653
1654 switch (actualColorModel) {
1656 switch (domain) {
1657 case ColorDomain::Stroking:
1658 *currentPage << "/CSp CS\n"; break;
1659 case ColorDomain::NonStroking:
1660 *currentPage << "/CSp cs\n"; break;
1661 case ColorDomain::NonStrokingPattern:
1662 *currentPage << "/PCSp cs\n"; break;
1663 }
1664 break;
1666 switch (domain) {
1667 case ColorDomain::Stroking:
1668 *currentPage << "/CSpg CS\n"; break;
1669 case ColorDomain::NonStroking:
1670 *currentPage << "/CSpg cs\n"; break;
1671 case ColorDomain::NonStrokingPattern:
1672 *currentPage << "/PCSpg cs\n"; break;
1673 }
1674 break;
1676 switch (domain) {
1677 case ColorDomain::Stroking:
1678 *currentPage << "/CSpcmyk CS\n"; break;
1679 case ColorDomain::NonStroking:
1680 *currentPage << "/CSpcmyk cs\n"; break;
1681 case ColorDomain::NonStrokingPattern:
1682 *currentPage << "/PCSpcmyk cs\n"; break;
1683 }
1684 break;
1686 Q_UNREACHABLE_RETURN();
1687 }
1688
1689 // If we also have a color specified, write it out.
1690 if (!color.isValid())
1691 return;
1692
1693 switch (actualColorModel) {
1695 *currentPage << color.redF()
1696 << color.greenF()
1697 << color.blueF();
1698 break;
1700 const qreal gray = qGray(color.rgba()) / 255.;
1701 *currentPage << gray;
1702 break;
1703 }
1705 *currentPage << color.cyanF()
1706 << color.magentaF()
1707 << color.yellowF()
1708 << color.blackF();
1709 break;
1711 Q_UNREACHABLE_RETURN();
1712 }
1713}
1714
1715void QPdfEnginePrivate::writeInfo()
1716{
1717 info = addXrefEntry(-1);
1718 xprintf("<<\n/Title ");
1719 printString(title);
1720 xprintf("\n/Creator ");
1721 printString(creator);
1722 xprintf("\n/Producer ");
1723 printString(QString::fromLatin1("Qt " QT_VERSION_STR));
1725 QTime t = now.time();
1726 QDate d = now.date();
1727 xprintf("\n/CreationDate (D:%d%02d%02d%02d%02d%02d",
1728 d.year(),
1729 d.month(),
1730 d.day(),
1731 t.hour(),
1732 t.minute(),
1733 t.second());
1734 int offset = now.offsetFromUtc();
1735 int hours = (offset / 60) / 60;
1736 int mins = (offset / 60) % 60;
1737 if (offset < 0)
1738 xprintf("-%02d'%02d')\n", -hours, -mins);
1739 else if (offset > 0)
1740 xprintf("+%02d'%02d')\n", hours , mins);
1741 else
1742 xprintf("Z)\n");
1743 xprintf(">>\n"
1744 "endobj\n");
1745}
1746
1747int QPdfEnginePrivate::writeXmpDcumentMetaData()
1748{
1749 const int metaDataObj = addXrefEntry(-1);
1750 QByteArray metaDataContent;
1751
1752 if (xmpDocumentMetadata.isEmpty()) {
1753 const QString producer(QString::fromLatin1("Qt " QT_VERSION_STR));
1754
1756 const QDate date = now.date();
1757 const QTime time = now.time();
1758 const QString timeStr =
1759 QString::asprintf("%d-%02d-%02dT%02d:%02d:%02d",
1760 date.year(), date.month(), date.day(),
1761 time.hour(), time.minute(), time.second());
1762
1763 const int offset = now.offsetFromUtc();
1764 const int hours = (offset / 60) / 60;
1765 const int mins = (offset / 60) % 60;
1766 QString tzStr;
1767 if (offset < 0)
1768 tzStr = QString::asprintf("-%02d:%02d", -hours, -mins);
1769 else if (offset > 0)
1770 tzStr = QString::asprintf("+%02d:%02d", hours , mins);
1771 else
1772 tzStr = "Z"_L1;
1773
1774 const QString metaDataDate = timeStr + tzStr;
1775
1776 QFile metaDataFile(":/qpdf/qpdfa_metadata.xml"_L1);
1777 bool ok = metaDataFile.open(QIODevice::ReadOnly);
1778 Q_ASSERT(ok);
1779 metaDataContent = QString::fromUtf8(metaDataFile.readAll()).arg(producer.toHtmlEscaped(),
1782 metaDataDate).toUtf8();
1783 }
1784 else
1785 metaDataContent = xmpDocumentMetadata;
1786
1787 xprintf("<<\n"
1788 "/Type /Metadata /Subtype /XML\n"
1789 "/Length %d\n"
1790 ">>\n"
1791 "stream\n", metaDataContent.size());
1792 write(metaDataContent);
1793 xprintf("\nendstream\n"
1794 "endobj\n");
1795
1796 return metaDataObj;
1797}
1798
1799int QPdfEnginePrivate::writeOutputIntent()
1800{
1801 const int colorProfile = addXrefEntry(-1);
1802 {
1803 QFile colorProfileFile(":/qpdf/sRGB2014.icc"_L1);
1804 bool ok = colorProfileFile.open(QIODevice::ReadOnly);
1805 Q_ASSERT(ok);
1806 const QByteArray colorProfileData = colorProfileFile.readAll();
1807
1810 int length_object = requestObject();
1811
1812 s << "<<\n";
1813 s << "/N 3\n";
1814 s << "/Alternate /DeviceRGB\n";
1815 s << "/Length " << length_object << "0 R\n";
1816 s << "/Filter /FlateDecode\n";
1817 s << ">>\n";
1818 s << "stream\n";
1819 write(data);
1820 const int len = writeCompressed(colorProfileData);
1821 write("\nendstream\n"
1822 "endobj\n");
1823 addXrefEntry(length_object);
1824 xprintf("%d\n"
1825 "endobj\n", len);
1826 }
1827
1828 const int outputIntent = addXrefEntry(-1);
1829 {
1830 xprintf("<<\n");
1831 xprintf("/Type /OutputIntent\n");
1832 xprintf("/S/GTS_PDFA1\n");
1833 xprintf("/OutputConditionIdentifier (sRGB_IEC61966-2-1_black_scaled)\n");
1834 xprintf("/DestOutputProfile %d 0 R\n", colorProfile);
1835 xprintf("/Info(sRGB IEC61966 v2.1 with black scaling)\n");
1836 xprintf("/RegistryName(http://www.color.org)\n");
1837 xprintf(">>\n");
1838 xprintf("endobj\n");
1839 }
1840
1841 return outputIntent;
1842}
1843
1844void QPdfEnginePrivate::writePageRoot()
1845{
1846 addXrefEntry(pageRoot);
1847
1848 xprintf("<<\n"
1849 "/Type /Pages\n"
1850 "/Kids \n"
1851 "[\n");
1852 int size = pages.size();
1853 for (int i = 0; i < size; ++i)
1854 xprintf("%d 0 R\n", pages[i]);
1855 xprintf("]\n");
1856
1857 //xprintf("/Group <</S /Transparency /I true /K false>>\n");
1858 xprintf("/Count %d\n", pages.size());
1859
1860 xprintf("/ProcSet [/PDF /Text /ImageB /ImageC]\n"
1861 ">>\n"
1862 "endobj\n");
1863}
1864
1865void QPdfEnginePrivate::writeDestsRoot()
1866{
1867 if (destCache.isEmpty())
1868 return;
1869
1870 QHash<QString, int> destObjects;
1871 QByteArray xs, ys;
1872 for (const DestInfo &destInfo : std::as_const(destCache)) {
1873 int destObj = addXrefEntry(-1);
1874 xs.setNum(static_cast<double>(destInfo.coords.x()), 'f');
1875 ys.setNum(static_cast<double>(destInfo.coords.y()), 'f');
1876 xprintf("[%d 0 R /XYZ %s %s 0]\n", destInfo.pageObj, xs.constData(), ys.constData());
1877 xprintf("endobj\n");
1878 destObjects.insert(destInfo.anchor, destObj);
1879 }
1880
1881 // names
1882 destsRoot = addXrefEntry(-1);
1883 QStringList anchors = destObjects.keys();
1884 anchors.sort();
1885 xprintf("<<\n/Limits [");
1886 printString(anchors.constFirst());
1887 xprintf(" ");
1888 printString(anchors.constLast());
1889 xprintf("]\n/Names [\n");
1890 for (const QString &anchor : std::as_const(anchors)) {
1891 printString(anchor);
1892 xprintf(" %d 0 R\n", destObjects[anchor]);
1893 }
1894 xprintf("]\n>>\n"
1895 "endobj\n");
1896}
1897
1898void QPdfEnginePrivate::writeAttachmentRoot()
1899{
1900 if (fileCache.isEmpty())
1901 return;
1902
1903 QList<int> attachments;
1904 const int size = fileCache.size();
1905 for (int i = 0; i < size; ++i) {
1906 auto attachment = fileCache.at(i);
1907 const int attachmentID = addXrefEntry(-1);
1908 xprintf("<<\n");
1909 if (do_compress)
1910 xprintf("/Filter /FlateDecode\n");
1911
1912 const int lenobj = requestObject();
1913 xprintf("/Length %d 0 R\n", lenobj);
1914 int len = 0;
1915 xprintf(">>\nstream\n");
1916 len = writeCompressed(attachment.data);
1917 xprintf("\nendstream\n"
1918 "endobj\n");
1919 addXrefEntry(lenobj);
1920 xprintf("%d\n"
1921 "endobj\n", len);
1922
1923 attachments.push_back(addXrefEntry(-1));
1924 xprintf("<<\n"
1925 "/F (%s)", attachment.fileName.toLatin1().constData());
1926
1927 xprintf("\n/EF <</F %d 0 R>>\n"
1928 "/Type/Filespec\n"
1929 , attachmentID);
1930 if (!attachment.mimeType.isEmpty())
1931 xprintf("/Subtype/%s\n",
1932 attachment.mimeType.replace("/"_L1, "#2F"_L1).toLatin1().constData());
1933 xprintf(">>\nendobj\n");
1934 }
1935
1936 // names
1937 attachmentsRoot = addXrefEntry(-1);
1938 xprintf("<</Names[");
1939 for (int i = 0; i < size; ++i) {
1940 auto attachment = fileCache.at(i);
1941 printString(attachment.fileName);
1942 xprintf("%d 0 R\n", attachments.at(i));
1943 }
1944 xprintf("]>>\n"
1945 "endobj\n");
1946}
1947
1948void QPdfEnginePrivate::writeNamesRoot()
1949{
1950 addXrefEntry(namesRoot);
1951 xprintf("<<\n");
1952
1953 if (attachmentsRoot)
1954 xprintf("/EmbeddedFiles %d 0 R\n", attachmentsRoot);
1955
1956 if (destsRoot)
1957 xprintf("/Dests %d 0 R\n", destsRoot);
1958
1959 xprintf(">>\n");
1960 xprintf("endobj\n");
1961}
1962
1963void QPdfEnginePrivate::embedFont(QFontSubset *font)
1964{
1965 //qDebug() << "embedFont" << font->object_id;
1966 int fontObject = font->object_id;
1967 QByteArray fontData = font->toTruetype();
1968#ifdef FONT_DUMP
1969 static int i = 0;
1970 QString fileName("font%1.ttf");
1971 fileName = fileName.arg(i++);
1972 QFile ff(fileName);
1973 ff.open(QFile::WriteOnly);
1974 ff.write(fontData);
1975 ff.close();
1976#endif
1977
1978 int fontDescriptor = requestObject();
1979 int fontstream = requestObject();
1980 int cidfont = requestObject();
1981 int toUnicode = requestObject();
1982 int cidset = requestObject();
1983
1984 QFontEngine::Properties properties = font->fontEngine->properties();
1985 QByteArray postscriptName = properties.postscriptName.replace(' ', '_');
1986
1987 {
1988 qreal scale = 1000/properties.emSquare.toReal();
1989 addXrefEntry(fontDescriptor);
1990 QByteArray descriptor;
1991 QPdf::ByteStream s(&descriptor);
1992 s << "<< /Type /FontDescriptor\n"
1993 "/FontName /Q";
1994 int tag = fontDescriptor;
1995 for (int i = 0; i < 5; ++i) {
1996 s << (char)('A' + (tag % 26));
1997 tag /= 26;
1998 }
1999 s << '+' << postscriptName << "\n"
2000 "/Flags " << 4 << "\n"
2001 "/FontBBox ["
2002 << properties.boundingBox.x()*scale
2003 << -(properties.boundingBox.y() + properties.boundingBox.height())*scale
2004 << (properties.boundingBox.x() + properties.boundingBox.width())*scale
2005 << -properties.boundingBox.y()*scale << "]\n"
2006 "/ItalicAngle " << properties.italicAngle.toReal() << "\n"
2007 "/Ascent " << properties.ascent.toReal()*scale << "\n"
2008 "/Descent " << -properties.descent.toReal()*scale << "\n"
2009 "/CapHeight " << properties.capHeight.toReal()*scale << "\n"
2010 "/StemV " << properties.lineWidth.toReal()*scale << "\n"
2011 "/FontFile2 " << fontstream << "0 R\n"
2012 "/CIDSet " << cidset << "0 R\n"
2013 ">>\nendobj\n";
2014 write(descriptor);
2015 }
2016 {
2017 addXrefEntry(fontstream);
2020
2021 int length_object = requestObject();
2022 s << "<<\n"
2023 "/Length1 " << fontData.size() << "\n"
2024 "/Length " << length_object << "0 R\n";
2025 if (do_compress)
2026 s << "/Filter /FlateDecode\n";
2027 s << ">>\n"
2028 "stream\n";
2029 write(header);
2030 int len = writeCompressed(fontData);
2031 write("\nendstream\n"
2032 "endobj\n");
2033 addXrefEntry(length_object);
2034 xprintf("%d\n"
2035 "endobj\n", len);
2036 }
2037 {
2038 addXrefEntry(cidfont);
2039 QByteArray cid;
2040 QPdf::ByteStream s(&cid);
2041 s << "<< /Type /Font\n"
2042 "/Subtype /CIDFontType2\n"
2043 "/BaseFont /" << postscriptName << "\n"
2044 "/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >>\n"
2045 "/FontDescriptor " << fontDescriptor << "0 R\n"
2046 "/CIDToGIDMap /Identity\n"
2047 << font->widthArray() <<
2048 ">>\n"
2049 "endobj\n";
2050 write(cid);
2051 }
2052 {
2053 addXrefEntry(toUnicode);
2054 QByteArray touc = font->createToUnicodeMap();
2055 xprintf("<< /Length %d >>\n"
2056 "stream\n", touc.size());
2057 write(touc);
2058 write("\nendstream\n"
2059 "endobj\n");
2060 }
2061 {
2062 addXrefEntry(fontObject);
2065 s << "<< /Type /Font\n"
2066 "/Subtype /Type0\n"
2067 "/BaseFont /" << postscriptName << "\n"
2068 "/Encoding /Identity-H\n"
2069 "/DescendantFonts [" << cidfont << "0 R]\n"
2070 "/ToUnicode " << toUnicode << "0 R"
2071 ">>\n"
2072 "endobj\n";
2073 write(font);
2074 }
2075 {
2076 QByteArray cidSetStream(font->nGlyphs() / 8 + 1, 0);
2077 int byteCounter = 0;
2078 int bitCounter = 0;
2079 for (qsizetype i = 0; i < font->nGlyphs(); ++i) {
2080 cidSetStream.data()[byteCounter] |= (1 << (7 - bitCounter));
2081
2082 bitCounter++;
2083 if (bitCounter == 8) {
2084 bitCounter = 0;
2085 byteCounter++;
2086 }
2087 }
2088
2089 addXrefEntry(cidset);
2090 xprintf("<<\n");
2091 xprintf("/Length %d\n", cidSetStream.size());
2092 xprintf(">>\n");
2093 xprintf("stream\n");
2094 write(cidSetStream);
2095 xprintf("\nendstream\n");
2096 xprintf("endobj\n");
2097 }
2098}
2099
2100qreal QPdfEnginePrivate::calcUserUnit() const
2101{
2102 // PDF standards < 1.6 support max 200x200in pages (no UserUnit)
2104 return 1.0;
2105
2106 const int maxLen = qMax(currentPage->pageSize.width(), currentPage->pageSize.height());
2107 if (maxLen <= 14400)
2108 return 1.0; // for pages up to 200x200in (14400x14400 units) use default scaling
2109
2110 // for larger pages, rescale units so we can have up to 381x381km
2111 return qMin(maxLen / 14400.0, 75000.0);
2112}
2113
2114void QPdfEnginePrivate::writeFonts()
2115{
2117 embedFont(*it);
2118 delete *it;
2119 }
2120 fonts.clear();
2121}
2122
2123void QPdfEnginePrivate::writePage()
2124{
2125 if (pages.empty())
2126 return;
2127
2128 *currentPage << "Q Q\n";
2129
2130 uint pageStream = requestObject();
2131 uint pageStreamLength = requestObject();
2132 uint resources = requestObject();
2133 uint annots = requestObject();
2134
2135 qreal userUnit = calcUserUnit();
2136
2137 addXrefEntry(pages.constLast());
2138 xprintf("<<\n"
2139 "/Type /Page\n"
2140 "/Parent %d 0 R\n"
2141 "/Contents %d 0 R\n"
2142 "/Resources %d 0 R\n"
2143 "/Annots %d 0 R\n"
2144 "/MediaBox [0 0 %s %s]\n",
2145 pageRoot, pageStream, resources, annots,
2146 // make sure we use the pagesize from when we started the page, since the user may have changed it
2147 QByteArray::number(currentPage->pageSize.width() / userUnit, 'f').constData(),
2148 QByteArray::number(currentPage->pageSize.height() / userUnit, 'f').constData());
2149
2151 xprintf("/UserUnit %s\n", QByteArray::number(userUnit, 'f').constData());
2152
2153 xprintf(">>\n"
2154 "endobj\n");
2155
2156 addXrefEntry(resources);
2157 xprintf("<<\n"
2158 "/ColorSpace <<\n"
2159 "/PCSp %d 0 R\n"
2160 "/PCSpg %d 0 R\n"
2161 "/PCSpcmyk %d 0 R\n"
2162 "/CSp /DeviceRGB\n"
2163 "/CSpg /DeviceGray\n"
2164 "/CSpcmyk /DeviceCMYK\n"
2165 ">>\n"
2166 "/ExtGState <<\n"
2167 "/GSa %d 0 R\n",
2168 patternColorSpaceRGB,
2169 patternColorSpaceGrayscale,
2170 patternColorSpaceCMYK,
2171 graphicsState);
2172
2173 for (int i = 0; i < currentPage->graphicStates.size(); ++i)
2174 xprintf("/GState%d %d 0 R\n", currentPage->graphicStates.at(i), currentPage->graphicStates.at(i));
2175 xprintf(">>\n");
2176
2177 xprintf("/Pattern <<\n");
2178 for (int i = 0; i < currentPage->patterns.size(); ++i)
2179 xprintf("/Pat%d %d 0 R\n", currentPage->patterns.at(i), currentPage->patterns.at(i));
2180 xprintf(">>\n");
2181
2182 xprintf("/Font <<\n");
2183 for (int i = 0; i < currentPage->fonts.size();++i)
2184 xprintf("/F%d %d 0 R\n", currentPage->fonts[i], currentPage->fonts[i]);
2185 xprintf(">>\n");
2186
2187 xprintf("/XObject <<\n");
2188 for (int i = 0; i<currentPage->images.size(); ++i) {
2189 xprintf("/Im%d %d 0 R\n", currentPage->images.at(i), currentPage->images.at(i));
2190 }
2191 xprintf(">>\n");
2192
2193 xprintf(">>\n"
2194 "endobj\n");
2195
2196 addXrefEntry(annots);
2197 xprintf("[ ");
2198 for (int i = 0; i<currentPage->annotations.size(); ++i) {
2199 xprintf("%d 0 R ", currentPage->annotations.at(i));
2200 }
2201 xprintf("]\nendobj\n");
2202
2203 addXrefEntry(pageStream);
2204 xprintf("<<\n"
2205 "/Length %d 0 R\n", pageStreamLength); // object number for stream length object
2206 if (do_compress)
2207 xprintf("/Filter /FlateDecode\n");
2208
2209 xprintf(">>\n");
2210 xprintf("stream\n");
2211 QIODevice *content = currentPage->stream();
2212 int len = writeCompressed(content);
2213 xprintf("\nendstream\n"
2214 "endobj\n");
2215
2216 addXrefEntry(pageStreamLength);
2217 xprintf("%d\nendobj\n",len);
2218}
2219
2221{
2222 writePage();
2223 writeFonts();
2224 writePageRoot();
2225 writeDestsRoot();
2226 writeAttachmentRoot();
2227 writeNamesRoot();
2228
2229 addXrefEntry(xrefPositions.size(),false);
2230 xprintf("xref\n"
2231 "0 %d\n"
2232 "%010d 65535 f \n", xrefPositions.size()-1, xrefPositions[0]);
2233
2234 for (int i = 1; i < xrefPositions.size()-1; ++i)
2235 xprintf("%010d 00000 n \n", xrefPositions[i]);
2236
2237 {
2238 QByteArray trailer;
2239 QPdf::ByteStream s(&trailer);
2240
2241 s << "trailer\n"
2242 << "<<\n"
2243 << "/Size " << xrefPositions.size() - 1 << "\n"
2244 << "/Info " << info << "0 R\n"
2245 << "/Root " << catalog << "0 R\n";
2246
2248 const QString uniqueId = QUuid::createUuid().toString();
2249 const QByteArray fileIdentifier = QCryptographicHash::hash(uniqueId.toLatin1(), QCryptographicHash::Md5).toHex();
2250 s << "/ID [ <" << fileIdentifier << "> <" << fileIdentifier << "> ]\n";
2251 }
2252
2253 s << ">>\n"
2254 << "startxref\n" << xrefPositions.constLast() << "\n"
2255 << "%%EOF\n";
2256
2257 write(trailer);
2258 }
2259}
2260
2261int QPdfEnginePrivate::addXrefEntry(int object, bool printostr)
2262{
2263 if (object < 0)
2264 object = requestObject();
2265
2266 if (object>=xrefPositions.size())
2267 xrefPositions.resize(object+1);
2268
2269 xrefPositions[object] = streampos;
2270 if (printostr)
2271 xprintf("%d 0 obj\n",object);
2272
2273 return object;
2274}
2275
2276void QPdfEnginePrivate::printString(QStringView string)
2277{
2278 if (string.isEmpty()) {
2279 write("()");
2280 return;
2281 }
2282
2283 // The 'text string' type in PDF is encoded either as PDFDocEncoding, or
2284 // Unicode UTF-16 with a Unicode byte order mark as the first character
2285 // (0xfeff), with the high-order byte first.
2286 QByteArray array("(\xfe\xff");
2287 const char16_t *utf16 = string.utf16();
2288
2289 for (qsizetype i = 0; i < string.size(); ++i) {
2290 char part[2] = {char((*(utf16 + i)) >> 8), char((*(utf16 + i)) & 0xff)};
2291 for(int j=0; j < 2; ++j) {
2292 if (part[j] == '(' || part[j] == ')' || part[j] == '\\')
2293 array.append('\\');
2294 array.append(part[j]);
2295 }
2296 }
2297 array.append(')');
2298 write(array);
2299}
2300
2301
2302void QPdfEnginePrivate::xprintf(const char* fmt, ...)
2303{
2304 if (!stream)
2305 return;
2306
2307 const int msize = 10000;
2308 char buf[msize];
2309
2310 va_list args;
2311 va_start(args, fmt);
2312 int bufsize = qvsnprintf(buf, msize, fmt, args);
2313 va_end(args);
2314
2315 if (Q_LIKELY(bufsize < msize)) {
2316 stream->writeRawData(buf, bufsize);
2317 } else {
2318 // Fallback for abnormal cases
2319 QScopedArrayPointer<char> tmpbuf(new char[bufsize + 1]);
2320 va_start(args, fmt);
2321 bufsize = qvsnprintf(tmpbuf.data(), bufsize + 1, fmt, args);
2322 va_end(args);
2323 stream->writeRawData(tmpbuf.data(), bufsize);
2324 }
2325 streampos += bufsize;
2326}
2327
2328int QPdfEnginePrivate::writeCompressed(QIODevice *dev)
2329{
2330#ifndef QT_NO_COMPRESS
2331 if (do_compress) {
2332 int size = QPdfPage::chunkSize();
2333 int sum = 0;
2334 ::z_stream zStruct;
2335 zStruct.zalloc = Z_NULL;
2336 zStruct.zfree = Z_NULL;
2337 zStruct.opaque = Z_NULL;
2338 if (::deflateInit(&zStruct, Z_DEFAULT_COMPRESSION) != Z_OK) {
2339 qWarning("QPdfStream::writeCompressed: Error in deflateInit()");
2340 return sum;
2341 }
2342 zStruct.avail_in = 0;
2343 QByteArray in, out;
2344 out.resize(size);
2345 while (!dev->atEnd() || zStruct.avail_in != 0) {
2346 if (zStruct.avail_in == 0) {
2347 in = dev->read(size);
2348 zStruct.avail_in = in.size();
2349 zStruct.next_in = reinterpret_cast<unsigned char*>(in.data());
2350 if (in.size() <= 0) {
2351 qWarning("QPdfStream::writeCompressed: Error in read()");
2352 ::deflateEnd(&zStruct);
2353 return sum;
2354 }
2355 }
2356 zStruct.next_out = reinterpret_cast<unsigned char*>(out.data());
2357 zStruct.avail_out = out.size();
2358 if (::deflate(&zStruct, 0) != Z_OK) {
2359 qWarning("QPdfStream::writeCompressed: Error in deflate()");
2360 ::deflateEnd(&zStruct);
2361 return sum;
2362 }
2363 int written = out.size() - zStruct.avail_out;
2364 stream->writeRawData(out.constData(), written);
2365 streampos += written;
2366 sum += written;
2367 }
2368 int ret;
2369 do {
2370 zStruct.next_out = reinterpret_cast<unsigned char*>(out.data());
2371 zStruct.avail_out = out.size();
2372 ret = ::deflate(&zStruct, Z_FINISH);
2373 if (ret != Z_OK && ret != Z_STREAM_END) {
2374 qWarning("QPdfStream::writeCompressed: Error in deflate()");
2375 ::deflateEnd(&zStruct);
2376 return sum;
2377 }
2378 int written = out.size() - zStruct.avail_out;
2379 stream->writeRawData(out.constData(), written);
2380 streampos += written;
2381 sum += written;
2382 } while (ret == Z_OK);
2383
2384 ::deflateEnd(&zStruct);
2385
2386 return sum;
2387 } else
2388#endif
2389 {
2390 QByteArray arr;
2391 int sum = 0;
2392 while (!dev->atEnd()) {
2393 arr = dev->read(QPdfPage::chunkSize());
2394 stream->writeRawData(arr.constData(), arr.size());
2395 streampos += arr.size();
2396 sum += arr.size();
2397 }
2398 return sum;
2399 }
2400}
2401
2402int QPdfEnginePrivate::writeCompressed(const char *src, int len)
2403{
2404#ifndef QT_NO_COMPRESS
2405 if (do_compress) {
2406 const QByteArray data = qCompress(reinterpret_cast<const uchar *>(src), len);
2407 constexpr qsizetype HeaderSize = 4;
2408 if (!data.isNull()) {
2409 stream->writeRawData(data.data() + HeaderSize, data.size() - HeaderSize);
2410 len = data.size() - HeaderSize;
2411 } else {
2412 qWarning("QPdfStream::writeCompressed: Error in compress()");
2413 len = 0;
2414 }
2415 } else
2416#endif
2417 {
2418 stream->writeRawData(src,len);
2419 }
2420 streampos += len;
2421 return len;
2422}
2423
2424int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height, WriteImageOption option,
2425 int maskObject, int softMaskObject, bool dct, bool isMono)
2426{
2427 int image = addXrefEntry(-1);
2428 xprintf("<<\n"
2429 "/Type /XObject\n"
2430 "/Subtype /Image\n"
2431 "/Width %d\n"
2432 "/Height %d\n", width, height);
2433
2434 switch (option) {
2435 case WriteImageOption::Monochrome:
2436 if (!isMono) {
2437 xprintf("/ImageMask true\n"
2438 "/Decode [1 0]\n");
2439 } else {
2440 xprintf("/BitsPerComponent 1\n"
2441 "/ColorSpace /DeviceGray\n");
2442 }
2443 break;
2444 case WriteImageOption::Grayscale:
2445 xprintf("/BitsPerComponent 8\n"
2446 "/ColorSpace /DeviceGray\n");
2447 break;
2448 case WriteImageOption::RGB:
2449 xprintf("/BitsPerComponent 8\n"
2450 "/ColorSpace /DeviceRGB\n");
2451 break;
2452 case WriteImageOption::CMYK:
2453 xprintf("/BitsPerComponent 8\n"
2454 "/ColorSpace /DeviceCMYK\n");
2455 break;
2456 }
2457
2458 if (maskObject > 0)
2459 xprintf("/Mask %d 0 R\n", maskObject);
2460 if (softMaskObject > 0)
2461 xprintf("/SMask %d 0 R\n", softMaskObject);
2462
2463 int lenobj = requestObject();
2464 xprintf("/Length %d 0 R\n", lenobj);
2466 xprintf("/Interpolate true\n");
2467 int len = 0;
2468 if (dct) {
2469 //qDebug("DCT");
2470 xprintf("/Filter /DCTDecode\n>>\nstream\n");
2471 write(data);
2472 len = data.size();
2473 } else {
2474 if (do_compress)
2475 xprintf("/Filter /FlateDecode\n>>\nstream\n");
2476 else
2477 xprintf(">>\nstream\n");
2478 len = writeCompressed(data);
2479 }
2480 xprintf("\nendstream\n"
2481 "endobj\n");
2482 addXrefEntry(lenobj);
2483 xprintf("%d\n"
2484 "endobj\n", len);
2485 return image;
2486}
2487
2495
2496void QPdfEnginePrivate::ShadingFunctionResult::writeColorSpace(QPdf::ByteStream *stream) const
2497{
2498 *stream << "/ColorSpace ";
2499 switch (colorModel) {
2501 *stream << "/DeviceRGB\n"; break;
2503 *stream << "/DeviceGray\n"; break;
2505 *stream << "/DeviceCMYK\n"; break;
2507 Q_UNREACHABLE(); break;
2508 }
2509}
2510
2511QPdfEnginePrivate::ShadingFunctionResult
2512QPdfEnginePrivate::createShadingFunction(const QGradient *gradient, int from, int to, bool reflect, bool alpha)
2513{
2514 QGradientStops stops = gradient->stops();
2515 if (stops.isEmpty()) {
2516 stops << QGradientStop(0, Qt::black);
2517 stops << QGradientStop(1, Qt::white);
2518 }
2519 if (stops.at(0).first > 0)
2520 stops.prepend(QGradientStop(0, stops.at(0).second));
2521 if (stops.at(stops.size() - 1).first < 1)
2522 stops.append(QGradientStop(1, stops.at(stops.size() - 1).second));
2523
2524 // Color to use which colorspace to use
2525 const QColor referenceColor = stops.constFirst().second;
2526
2527 switch (colorModel) {
2531 break;
2533 // Make sure that all the stops have the same color spec
2534 // (we don't support anything else)
2535 const QColor::Spec referenceSpec = referenceColor.spec();
2536 bool warned = false;
2537 for (QGradientStop &stop : stops) {
2538 if (stop.second.spec() != referenceSpec) {
2539 if (!warned) {
2540 qWarning("QPdfEngine: unable to create a gradient between colors of different spec");
2541 warned = true;
2542 }
2543 stop.second = stop.second.convertTo(referenceSpec);
2544 }
2545 }
2546 break;
2547 }
2548 }
2549
2550 ShadingFunctionResult result;
2551 result.colorModel = colorModelForColor(referenceColor);
2552
2553 QList<int> functions;
2554 const int numStops = stops.size();
2555 functions.reserve(numStops - 1);
2556 for (int i = 0; i < numStops - 1; ++i) {
2557 int f = addXrefEntry(-1);
2560 s << "<<\n"
2561 "/FunctionType 2\n"
2562 "/Domain [0 1]\n"
2563 "/N 1\n";
2564 if (alpha) {
2565 s << "/C0 [" << stops.at(i).second.alphaF() << "]\n"
2566 "/C1 [" << stops.at(i + 1).second.alphaF() << "]\n";
2567 } else {
2568 switch (result.colorModel) {
2570 s << "/C0 [" << stops.at(i).second.redF() << stops.at(i).second.greenF() << stops.at(i).second.blueF() << "]\n"
2571 "/C1 [" << stops.at(i + 1).second.redF() << stops.at(i + 1).second.greenF() << stops.at(i + 1).second.blueF() << "]\n";
2572 break;
2574 s << "/C0 [" << (qGray(stops.at(i).second.rgba()) / 255.) << "]\n"
2575 "/C1 [" << (qGray(stops.at(i + 1).second.rgba()) / 255.) << "]\n";
2576 break;
2578 s << "/C0 [" << stops.at(i).second.cyanF()
2579 << stops.at(i).second.magentaF()
2580 << stops.at(i).second.yellowF()
2581 << stops.at(i).second.blackF() << "]\n"
2582 "/C1 [" << stops.at(i + 1).second.cyanF()
2583 << stops.at(i + 1).second.magentaF()
2584 << stops.at(i + 1).second.yellowF()
2585 << stops.at(i + 1).second.blackF() << "]\n";
2586 break;
2587
2589 Q_UNREACHABLE();
2590 break;
2591 }
2592
2593 }
2594 s << ">>\n"
2595 "endobj\n";
2596 write(data);
2597 functions << f;
2598 }
2599
2600 QList<QGradientBound> gradientBounds;
2601 gradientBounds.reserve((to - from) * (numStops - 1));
2602
2603 for (int step = from; step < to; ++step) {
2604 if (reflect && step % 2) {
2605 for (int i = numStops - 1; i > 0; --i) {
2607 b.start = step + 1 - qBound(qreal(0.), stops.at(i).first, qreal(1.));
2608 b.stop = step + 1 - qBound(qreal(0.), stops.at(i - 1).first, qreal(1.));
2609 b.function = functions.at(i - 1);
2610 b.reverse = true;
2611 gradientBounds << b;
2612 }
2613 } else {
2614 for (int i = 0; i < numStops - 1; ++i) {
2616 b.start = step + qBound(qreal(0.), stops.at(i).first, qreal(1.));
2617 b.stop = step + qBound(qreal(0.), stops.at(i + 1).first, qreal(1.));
2618 b.function = functions.at(i);
2619 b.reverse = false;
2620 gradientBounds << b;
2621 }
2622 }
2623 }
2624
2625 // normalize bounds to [0..1]
2626 qreal bstart = gradientBounds.at(0).start;
2627 qreal bend = gradientBounds.at(gradientBounds.size() - 1).stop;
2628 qreal norm = 1./(bend - bstart);
2629 for (int i = 0; i < gradientBounds.size(); ++i) {
2630 gradientBounds[i].start = (gradientBounds[i].start - bstart)*norm;
2631 gradientBounds[i].stop = (gradientBounds[i].stop - bstart)*norm;
2632 }
2633
2634 int function;
2635 if (gradientBounds.size() > 1) {
2636 function = addXrefEntry(-1);
2639 s << "<<\n"
2640 "/FunctionType 3\n"
2641 "/Domain [0 1]\n"
2642 "/Bounds [";
2643 for (int i = 1; i < gradientBounds.size(); ++i)
2644 s << gradientBounds.at(i).start;
2645 s << "]\n"
2646 "/Encode [";
2647 for (int i = 0; i < gradientBounds.size(); ++i)
2648 s << (gradientBounds.at(i).reverse ? "1 0 " : "0 1 ");
2649 s << "]\n"
2650 "/Functions [";
2651 for (int i = 0; i < gradientBounds.size(); ++i)
2652 s << gradientBounds.at(i).function << "0 R ";
2653 s << "]\n"
2654 ">>\n"
2655 "endobj\n";
2656 write(data);
2657 } else {
2658 function = functions.at(0);
2659 }
2660 result.function = function;
2661 return result;
2662}
2663
2664int QPdfEnginePrivate::generateLinearGradientShader(const QLinearGradient *gradient, const QTransform &matrix, bool alpha)
2665{
2666 QPointF start = gradient->start();
2667 QPointF stop = gradient->finalStop();
2668 QPointF offset = stop - start;
2670
2671 int from = 0;
2672 int to = 1;
2673 bool reflect = false;
2674 switch (gradient->spread()) {
2676 break;
2678 reflect = true;
2679 Q_FALLTHROUGH();
2681 // calculate required bounds
2683 QTransform inv = matrix.inverted();
2684 QPointF page_rect[4] = { inv.map(pageRect.topLeft()),
2685 inv.map(pageRect.topRight()),
2686 inv.map(pageRect.bottomLeft()),
2687 inv.map(pageRect.bottomRight()) };
2688
2689 qreal length = offset.x()*offset.x() + offset.y()*offset.y();
2690
2691 // find the max and min values in offset and orth direction that are needed to cover
2692 // the whole page
2693 from = INT_MAX;
2694 to = INT_MIN;
2695 for (int i = 0; i < 4; ++i) {
2696 qreal off = ((page_rect[i].x() - start.x()) * offset.x() + (page_rect[i].y() - start.y()) * offset.y())/length;
2697 from = qMin(from, qFloor(off));
2698 to = qMax(to, qCeil(off));
2699 }
2700
2701 stop = start + to * offset;
2702 start = start + from * offset;\
2703 break;
2704 }
2705 }
2706
2707 const auto shadingFunctionResult = createShadingFunction(gradient, from, to, reflect, alpha);
2708
2711 s << "<<\n"
2712 "/ShadingType 2\n";
2713
2714 if (alpha)
2715 s << "/ColorSpace /DeviceGray\n";
2716 else
2717 shadingFunctionResult.writeColorSpace(&s);
2718
2719 s << "/AntiAlias true\n"
2720 "/Coords [" << start.x() << start.y() << stop.x() << stop.y() << "]\n"
2721 "/Extend [true true]\n"
2722 "/Function " << shadingFunctionResult.function << "0 R\n"
2723 ">>\n"
2724 "endobj\n";
2725 int shaderObject = addXrefEntry(-1);
2726 write(shader);
2727 return shaderObject;
2728}
2729
2730int QPdfEnginePrivate::generateRadialGradientShader(const QRadialGradient *gradient, const QTransform &matrix, bool alpha)
2731{
2732 QPointF p1 = gradient->center();
2733 qreal r1 = gradient->centerRadius();
2734 QPointF p0 = gradient->focalPoint();
2735 qreal r0 = gradient->focalRadius();
2736
2738
2739 int from = 0;
2740 int to = 1;
2741 bool reflect = false;
2742 switch (gradient->spread()) {
2744 break;
2746 reflect = true;
2747 Q_FALLTHROUGH();
2749 Q_ASSERT(qFuzzyIsNull(r0)); // QPainter emulates if this is not 0
2750
2752 QTransform inv = matrix.inverted();
2753 QPointF page_rect[4] = { inv.map(pageRect.topLeft()),
2754 inv.map(pageRect.topRight()),
2755 inv.map(pageRect.bottomLeft()),
2756 inv.map(pageRect.bottomRight()) };
2757
2758 // increase to until the whole page fits into it
2759 bool done = false;
2760 while (!done) {
2761 QPointF center = QPointF(p0.x() + to*(p1.x() - p0.x()), p0.y() + to*(p1.y() - p0.y()));
2762 double radius = r0 + to*(r1 - r0);
2763 double r2 = radius*radius;
2764 done = true;
2765 for (int i = 0; i < 4; ++i) {
2766 QPointF off = page_rect[i] - center;
2767 if (off.x()*off.x() + off.y()*off.y() > r2) {
2768 ++to;
2769 done = false;
2770 break;
2771 }
2772 }
2773 }
2774 p1 = QPointF(p0.x() + to*(p1.x() - p0.x()), p0.y() + to*(p1.y() - p0.y()));
2775 r1 = r0 + to*(r1 - r0);
2776 break;
2777 }
2778 }
2779
2780 const auto shadingFunctionResult = createShadingFunction(gradient, from, to, reflect, alpha);
2781
2784 s << "<<\n"
2785 "/ShadingType 3\n";
2786
2787 if (alpha)
2788 s << "/ColorSpace /DeviceGray\n";
2789 else
2790 shadingFunctionResult.writeColorSpace(&s);
2791
2792 s << "/AntiAlias true\n"
2793 "/Domain [0 1]\n"
2794 "/Coords [" << p0.x() << p0.y() << r0 << p1.x() << p1.y() << r1 << "]\n"
2795 "/Extend [true true]\n"
2796 "/Function " << shadingFunctionResult.function << "0 R\n"
2797 ">>\n"
2798 "endobj\n";
2799 int shaderObject = addXrefEntry(-1);
2800 write(shader);
2801 return shaderObject;
2802}
2803
2804int QPdfEnginePrivate::generateGradientShader(const QGradient *gradient, const QTransform &matrix, bool alpha)
2805{
2806 switch (gradient->type()) {
2808 return generateLinearGradientShader(static_cast<const QLinearGradient *>(gradient), matrix, alpha);
2810 return generateRadialGradientShader(static_cast<const QRadialGradient *>(gradient), matrix, alpha);
2812 Q_UNIMPLEMENTED(); // ### Implement me!
2813 break;
2815 break;
2816 }
2817 return 0;
2818}
2819
2820int QPdfEnginePrivate::gradientBrush(const QBrush &b, const QTransform &matrix, int *gStateObject)
2821{
2822 const QGradient *gradient = b.gradient();
2823
2824 if (!gradient || gradient->coordinateMode() != QGradient::LogicalMode)
2825 return 0;
2826
2828
2829 QTransform m = b.transform() * matrix;
2830 int shaderObject = generateGradientShader(gradient, m);
2831
2834 s << "<<\n"
2835 "/Type /Pattern\n"
2836 "/PatternType 2\n"
2837 "/Shading " << shaderObject << "0 R\n"
2838 "/Matrix ["
2839 << m.m11()
2840 << m.m12()
2841 << m.m21()
2842 << m.m22()
2843 << m.dx()
2844 << m.dy() << "]\n";
2845 s << ">>\n"
2846 "endobj\n";
2847
2848 int patternObj = addXrefEntry(-1);
2849 write(str);
2850 currentPage->patterns.append(patternObj);
2851
2852 if (!b.isOpaque()) {
2853 bool ca = true;
2854 QGradientStops stops = gradient->stops();
2855 int a = stops.at(0).second.alpha();
2856 for (int i = 1; i < stops.size(); ++i) {
2857 if (stops.at(i).second.alpha() != a) {
2858 ca = false;
2859 break;
2860 }
2861 }
2862 if (ca) {
2863 *gStateObject = addConstantAlphaObject(stops.at(0).second.alpha());
2864 } else {
2865 int alphaShaderObject = generateGradientShader(gradient, m, true);
2866
2867 QByteArray content;
2868 QPdf::ByteStream c(&content);
2869 c << "/Shader" << alphaShaderObject << "sh\n";
2870
2873 f << "<<\n"
2874 "/Type /XObject\n"
2875 "/Subtype /Form\n"
2876 "/BBox [0 0 " << pageRect.width() << pageRect.height() << "]\n"
2877 "/Group <</S /Transparency >>\n"
2878 "/Resources <<\n"
2879 "/Shading << /Shader" << alphaShaderObject << alphaShaderObject << "0 R >>\n"
2880 ">>\n";
2881
2882 f << "/Length " << content.size() << "\n"
2883 ">>\n"
2884 "stream\n"
2885 << content
2886 << "\nendstream\n"
2887 "endobj\n";
2888
2889 int softMaskFormObject = addXrefEntry(-1);
2890 write(form);
2891 *gStateObject = addXrefEntry(-1);
2892 xprintf("<< /SMask << /S /Alpha /G %d 0 R >> >>\n"
2893 "endobj\n", softMaskFormObject);
2894 currentPage->graphicStates.append(*gStateObject);
2895 }
2896 }
2897
2898 return patternObj;
2899}
2900
2901int QPdfEnginePrivate::addConstantAlphaObject(int brushAlpha, int penAlpha)
2902{
2903 if (brushAlpha == 255 && penAlpha == 255)
2904 return 0;
2905 uint object = alphaCache.value(QPair<uint, uint>(brushAlpha, penAlpha), 0);
2906 if (!object) {
2907 object = addXrefEntry(-1);
2908 QByteArray alphaDef;
2909 QPdf::ByteStream s(&alphaDef);
2910 s << "<<\n/ca " << (brushAlpha/qreal(255.)) << '\n';
2911 s << "/CA " << (penAlpha/qreal(255.)) << "\n>>";
2912 xprintf("%s\nendobj\n", alphaDef.constData());
2913 alphaCache.insert(QPair<uint, uint>(brushAlpha, penAlpha), object);
2914 }
2915 if (currentPage->graphicStates.indexOf(object) < 0)
2917
2918 return object;
2919}
2920
2921
2922int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor, int *gStateObject)
2923{
2924 Q_Q(QPdfEngine);
2925
2926 int paintType = 2; // Uncolored tiling
2927 int w = 8;
2928 int h = 8;
2929
2930 *specifyColor = true;
2931 *gStateObject = 0;
2932
2933 const Qt::BrushStyle style = brush.style();
2934 const bool isCosmetic = style >= Qt::Dense1Pattern && style <= Qt::DiagCrossPattern
2935 && !q->painter()->testRenderHint(QPainter::NonCosmeticBrushPatterns);
2937 if (!isCosmetic)
2938 matrix = m;
2940 matrix = matrix * pageMatrix();
2941
2942 if (style == Qt::LinearGradientPattern || style == Qt::RadialGradientPattern) {// && style <= Qt::ConicalGradientPattern) {
2943 *specifyColor = false;
2944 return gradientBrush(brush, matrix, gStateObject);
2945 }
2946
2947 if (!isCosmetic)
2948 matrix = brush.transform() * matrix;
2949
2950 if ((!brush.isOpaque() && brush.style() < Qt::LinearGradientPattern) || opacity != 1.0)
2951 *gStateObject = addConstantAlphaObject(qRound(brush.color().alpha() * opacity),
2952 qRound(pen.color().alpha() * opacity));
2953
2954 int imageObject = -1;
2956 if (pattern.isEmpty()) {
2957 if (brush.style() != Qt::TexturePattern)
2958 return 0;
2959 QImage image = brush.textureImage();
2960 bool bitmap = true;
2961 const bool lossless = q->painter()->testRenderHint(QPainter::LosslessImageRendering);
2962 imageObject = addImage(image, &bitmap, lossless, image.cacheKey());
2963 if (imageObject != -1) {
2964 QImage::Format f = image.format();
2966 paintType = 1; // Colored tiling
2967 *specifyColor = false;
2968 }
2969 w = image.width();
2970 h = image.height();
2971 QTransform m(w, 0, 0, -h, 0, h);
2974 s << "/Im" << imageObject << " Do\n";
2975 }
2976 }
2977
2980 s << "<<\n"
2981 "/Type /Pattern\n"
2982 "/PatternType 1\n"
2983 "/PaintType " << paintType << "\n"
2984 "/TilingType 1\n"
2985 "/BBox [0 0 " << w << h << "]\n"
2986 "/XStep " << w << "\n"
2987 "/YStep " << h << "\n"
2988 "/Matrix ["
2989 << matrix.m11()
2990 << matrix.m12()
2991 << matrix.m21()
2992 << matrix.m22()
2993 << matrix.dx()
2994 << matrix.dy() << "]\n"
2995 "/Resources \n<< "; // open resource tree
2996 if (imageObject > 0) {
2997 s << "/XObject << /Im" << imageObject << ' ' << imageObject << "0 R >> ";
2998 }
2999 s << ">>\n"
3000 "/Length " << pattern.size() << "\n"
3001 ">>\n"
3002 "stream\n"
3003 << pattern
3004 << "\nendstream\n"
3005 "endobj\n";
3006
3007 int patternObj = addXrefEntry(-1);
3008 write(str);
3009 currentPage->patterns.append(patternObj);
3010 return patternObj;
3011}
3012
3013static inline bool is_monochrome(const QList<QRgb> &colorTable)
3014{
3015 return colorTable.size() == 2
3016 && colorTable.at(0) == QColor(Qt::black).rgba()
3017 && colorTable.at(1) == QColor(Qt::white).rgba()
3018 ;
3019}
3020
3024int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, bool lossless, qint64 serial_no)
3025{
3026 if (img.isNull())
3027 return -1;
3028
3029 int object = imageCache.value(serial_no);
3030 if (object)
3031 return object;
3032
3033 QImage image = img;
3034 QImage::Format format = image.format();
3036
3038 if (image.hasAlphaChannel()) {
3039 // transparent images are not allowed in PDF/A-1b, so we convert it to
3040 // a format without alpha channel first
3041
3042 QImage alphaLessImage(image.width(), image.height(), QImage::Format_RGB32);
3043 alphaLessImage.fill(Qt::white);
3044
3045 QPainter p(&alphaLessImage);
3046 p.drawImage(0, 0, image);
3047
3048 image = alphaLessImage;
3049 format = image.format();
3050 }
3051 }
3052
3053 if (image.depth() == 1 && *bitmap && is_monochrome(img.colorTable())) {
3055 image = image.convertToFormat(QImage::Format_Mono);
3057 } else {
3058 *bitmap = false;
3060 image = image.convertToFormat(QImage::Format_ARGB32);
3062 }
3063 }
3064
3065 int w = image.width();
3066 int h = image.height();
3067
3068 if (format == QImage::Format_Mono) {
3069 int bytesPerLine = (w + 7) >> 3;
3071 data.resize(bytesPerLine * h);
3072 char *rawdata = data.data();
3073 for (int y = 0; y < h; ++y) {
3074 memcpy(rawdata, image.constScanLine(y), bytesPerLine);
3075 rawdata += bytesPerLine;
3076 }
3077 object = writeImage(data, w, h, WriteImageOption::Monochrome, 0, 0, false, is_monochrome(img.colorTable()));
3078 } else {
3079 QByteArray softMaskData;
3080 bool dct = false;
3082 bool hasAlpha = false;
3083 bool hasMask = false;
3084
3085 if (QImageWriter::supportedImageFormats().contains("jpeg") && !grayscale && !lossless) {
3087 QImageWriter writer(&buffer, "jpeg");
3088 writer.setQuality(94);
3090 // PDFs require CMYK colors not to be inverted in the JPEG encoding
3091 writer.setSubType("CMYK");
3092 }
3093 writer.write(image);
3094 dct = true;
3095
3097 softMaskData.resize(w * h);
3098 uchar *sdata = (uchar *)softMaskData.data();
3099 for (int y = 0; y < h; ++y) {
3100 const QRgb *rgb = (const QRgb *)image.constScanLine(y);
3101 for (int x = 0; x < w; ++x) {
3102 uchar alpha = qAlpha(*rgb);
3103 *sdata++ = alpha;
3104 hasMask |= (alpha < 255);
3105 hasAlpha |= (alpha != 0 && alpha != 255);
3106 ++rgb;
3107 }
3108 }
3109 }
3110 } else {
3112 imageData.resize(grayscale ? w * h : w * h * 4);
3113 uchar *data = (uchar *)imageData.data();
3114 const qsizetype bytesPerLine = image.bytesPerLine();
3115 if (grayscale) {
3116 for (int y = 0; y < h; ++y) {
3117 const uint *cmyk = (const uint *)image.constScanLine(y);
3118 for (int x = 0; x < w; ++x)
3119 *data++ = qGray(QCmyk32::fromCmyk32(*cmyk++).toColor().rgba());
3120 }
3121 } else {
3122 for (int y = 0; y < h; ++y) {
3123 uchar *start = data + y * w * 4;
3124 memcpy(start, image.constScanLine(y), bytesPerLine);
3125 }
3126 }
3127 } else {
3128 imageData.resize(grayscale ? w * h : 3 * w * h);
3129 uchar *data = (uchar *)imageData.data();
3130 softMaskData.resize(w * h);
3131 uchar *sdata = (uchar *)softMaskData.data();
3132 for (int y = 0; y < h; ++y) {
3133 const QRgb *rgb = (const QRgb *)image.constScanLine(y);
3134 if (grayscale) {
3135 for (int x = 0; x < w; ++x) {
3136 *(data++) = qGray(*rgb);
3137 uchar alpha = qAlpha(*rgb);
3138 *sdata++ = alpha;
3139 hasMask |= (alpha < 255);
3140 hasAlpha |= (alpha != 0 && alpha != 255);
3141 ++rgb;
3142 }
3143 } else {
3144 for (int x = 0; x < w; ++x) {
3145 *(data++) = qRed(*rgb);
3146 *(data++) = qGreen(*rgb);
3147 *(data++) = qBlue(*rgb);
3148 uchar alpha = qAlpha(*rgb);
3149 *sdata++ = alpha;
3150 hasMask |= (alpha < 255);
3151 hasAlpha |= (alpha != 0 && alpha != 255);
3152 ++rgb;
3153 }
3154 }
3155 }
3156 }
3158 hasAlpha = hasMask = false;
3159 }
3160 int maskObject = 0;
3161 int softMaskObject = 0;
3162 if (hasAlpha) {
3163 softMaskObject = writeImage(softMaskData, w, h, WriteImageOption::Grayscale, 0, 0);
3164 } else if (hasMask) {
3165 // dither the soft mask to 1bit and add it. This also helps PDF viewers
3166 // without transparency support
3167 int bytesPerLine = (w + 7) >> 3;
3168 QByteArray mask(bytesPerLine * h, 0);
3169 uchar *mdata = (uchar *)mask.data();
3170 const uchar *sdata = (const uchar *)softMaskData.constData();
3171 for (int y = 0; y < h; ++y) {
3172 for (int x = 0; x < w; ++x) {
3173 if (*sdata)
3174 mdata[x>>3] |= (0x80 >> (x&7));
3175 ++sdata;
3176 }
3177 mdata += bytesPerLine;
3178 }
3179 maskObject = writeImage(mask, w, h, WriteImageOption::Monochrome, 0, 0);
3180 }
3181
3182 const WriteImageOption option = [&]() {
3183 if (grayscale)
3184 return WriteImageOption::Grayscale;
3186 return WriteImageOption::CMYK;
3187 return WriteImageOption::RGB;
3188 }();
3189
3190 object = writeImage(imageData, w, h, option,
3191 maskObject, softMaskObject, dct);
3192 }
3193 imageCache.insert(serial_no, object);
3194 return object;
3195}
3196
3198{
3199 Q_Q(QPdfEngine);
3200
3201 const bool isLink = ti.charFormat.hasProperty(QTextFormat::AnchorHref);
3202 const bool isAnchor = ti.charFormat.hasProperty(QTextFormat::AnchorName);
3203 if (isLink || isAnchor) {
3204 qreal size = ti.fontEngine->fontDef.pixelSize;
3205 int synthesized = ti.fontEngine->synthesized();
3206 qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
3207 Q_ASSERT(stretch > qreal(0));
3208
3209 QTransform trans;
3210 // Build text rendering matrix (Trm). We need it to map the text area to user
3211 // space units on the PDF page.
3212 trans = QTransform(size*stretch, 0, 0, size, 0, 0);
3213 // Apply text matrix (Tm).
3214 trans *= QTransform(1,0,0,-1,p.x(),p.y());
3215 // Apply page displacement (Identity for first page).
3216 trans *= stroker.matrix;
3217 // Apply Current Transformation Matrix (CTM)
3218 trans *= pageMatrix();
3219 qreal x1, y1, x2, y2;
3220 trans.map(0, 0, &x1, &y1);
3221 trans.map(ti.width.toReal()/size, (ti.ascent.toReal()-ti.descent.toReal())/size, &x2, &y2);
3222
3223 if (isLink) {
3224 uint annot = addXrefEntry(-1);
3225 QByteArray x1s, y1s, x2s, y2s;
3226 x1s.setNum(static_cast<double>(x1), 'f');
3227 y1s.setNum(static_cast<double>(y1), 'f');
3228 x2s.setNum(static_cast<double>(x2), 'f');
3229 y2s.setNum(static_cast<double>(y2), 'f');
3230 QByteArray rectData = x1s + ' ' + y1s + ' ' + x2s + ' ' + y2s;
3231 xprintf("<<\n/Type /Annot\n/Subtype /Link\n");
3232
3234 xprintf("/F 4\n"); // enable print flag, disable all other
3235
3236 xprintf("/Rect [");
3237 xprintf(rectData.constData());
3238#ifdef Q_DEBUG_PDF_LINKS
3239 xprintf("]\n/Border [16 16 1]\n");
3240#else
3241 xprintf("]\n/Border [0 0 0]\n");
3242#endif
3243 const QString link = ti.charFormat.anchorHref();
3244 const bool isInternal = link.startsWith(QLatin1Char('#'));
3245 if (!isInternal) {
3246 xprintf("/A <<\n");
3247 xprintf("/Type /Action\n/S /URI\n/URI (%s)\n", link.toLatin1().constData());
3248 xprintf(">>\n");
3249 } else {
3250 xprintf("/Dest ");
3251 printString(link.sliced(1));
3252 xprintf("\n");
3253 }
3254 xprintf(">>\n");
3255 xprintf("endobj\n");
3256
3257 if (!currentPage->annotations.contains(annot)) {
3259 }
3260 } else {
3261 const QString anchor = ti.charFormat.anchorNames().constFirst();
3262 const uint curPage = pages.last();
3263 destCache.append(DestInfo({ anchor, curPage, QPointF(x1, y2) }));
3264 }
3265 }
3266
3267 QFontEngine *fe = ti.fontEngine;
3268
3269 QFontEngine::FaceId face_id = fe->faceId();
3270 bool noEmbed = false;
3271 if (!embedFonts
3272 || face_id.filename.isEmpty()
3273 || fe->fsType & 0x200 /* bitmap embedding only */
3274 || fe->fsType == 2 /* no embedding allowed */) {
3275 *currentPage << "Q\n";
3276 q->QPaintEngine::drawTextItem(p, ti);
3277 *currentPage << "q\n";
3278 if (face_id.filename.isEmpty())
3279 return;
3280 noEmbed = true;
3281 }
3282
3283 QFontSubset *font = fonts.value(face_id, nullptr);
3284 if (!font) {
3285 font = new QFontSubset(fe, requestObject());
3286 font->noEmbed = noEmbed;
3287 }
3288 fonts.insert(face_id, font);
3289
3290 if (!currentPage->fonts.contains(font->object_id))
3291 currentPage->fonts.append(font->object_id);
3292
3293 qreal size = ti.fontEngine->fontDef.pixelSize;
3294
3295 QVarLengthArray<glyph_t> glyphs;
3296 QVarLengthArray<QFixedPoint> positions;
3298 ti.fontEngine->getGlyphPositions(ti.glyphs, m, ti.flags,
3299 glyphs, positions);
3300 if (glyphs.size() == 0)
3301 return;
3302 int synthesized = ti.fontEngine->synthesized();
3303 qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
3304 Q_ASSERT(stretch > qreal(0));
3305
3306 *currentPage << "BT\n"
3307 << "/F" << font->object_id << size << "Tf "
3308 << stretch << (synthesized & QFontEngine::SynthesizedItalic
3309 ? "0 .3 -1 0 0 Tm\n"
3310 : "0 0 -1 0 0 Tm\n");
3311
3312
3313#if 0
3314 // #### implement actual text for complex languages
3315 const unsigned short *logClusters = ti.logClusters;
3316 int pos = 0;
3317 do {
3318 int end = pos + 1;
3319 while (end < ti.num_chars && logClusters[end] == logClusters[pos])
3320 ++end;
3321 *currentPage << "/Span << /ActualText <FEFF";
3322 for (int i = pos; i < end; ++i) {
3323 s << toHex((ushort)ti.chars[i].unicode(), buf);
3324 }
3325 *currentPage << "> >>\n"
3326 "BDC\n"
3327 "<";
3328 int ge = end == ti.num_chars ? ti.num_glyphs : logClusters[end];
3329 for (int gs = logClusters[pos]; gs < ge; ++gs)
3330 *currentPage << toHex((ushort)ti.glyphs[gs].glyph, buf);
3331 *currentPage << "> Tj\n"
3332 "EMC\n";
3333 pos = end;
3334 } while (pos < ti.num_chars);
3335#else
3336 qreal last_x = 0.;
3337 qreal last_y = 0.;
3338 for (int i = 0; i < glyphs.size(); ++i) {
3339 qreal x = positions[i].x.toReal();
3340 qreal y = positions[i].y.toReal();
3341 if (synthesized & QFontEngine::SynthesizedItalic)
3342 x += .3*y;
3343 x /= stretch;
3344 char buf[5];
3345 qsizetype g = font->addGlyph(glyphs[i]);
3346 *currentPage << x - last_x << last_y - y << "Td <"
3347 << QPdf::toHex((ushort)g, buf) << "> Tj\n";
3348 last_x = x;
3349 last_y = y;
3350 }
3351 if (synthesized & QFontEngine::SynthesizedBold) {
3352 *currentPage << stretch << (synthesized & QFontEngine::SynthesizedItalic
3353 ? "0 .3 -1 0 0 Tm\n"
3354 : "0 0 -1 0 0 Tm\n");
3355 *currentPage << "/Span << /ActualText <> >> BDC\n";
3356 last_x = 0.5*fe->lineThickness().toReal();
3357 last_y = 0.;
3358 for (int i = 0; i < glyphs.size(); ++i) {
3359 qreal x = positions[i].x.toReal();
3360 qreal y = positions[i].y.toReal();
3361 if (synthesized & QFontEngine::SynthesizedItalic)
3362 x += .3*y;
3363 x /= stretch;
3364 char buf[5];
3365 qsizetype g = font->addGlyph(glyphs[i]);
3366 *currentPage << x - last_x << last_y - y << "Td <"
3367 << QPdf::toHex((ushort)g, buf) << "> Tj\n";
3368 last_x = x;
3369 last_y = y;
3370 }
3371 *currentPage << "EMC\n";
3372 }
3373#endif
3374
3375 *currentPage << "ET\n";
3376}
3377
3379{
3380 qreal userUnit = calcUserUnit();
3381 qreal scale = 72. / userUnit / resolution;
3382 QTransform tmp(scale, 0.0, 0.0, -scale, 0.0, m_pageLayout.fullRectPoints().height() / userUnit);
3385 tmp.translate(r.left(), r.top());
3386 }
3387 return tmp;
3388}
3389
3391{
3394 writePage();
3395
3396 delete currentPage;
3397 currentPage = new QPdfPage;
3400 pages.append(requestObject());
3401
3402 *currentPage << "/GSa gs /CSp cs /CSp CS\n"
3404 << "q q\n";
3405}
3406
3408
3409#endif // QT_NO_PDF
\inmodule QtGui
Definition qbrush.h:30
void setStyle(Qt::BrushStyle)
Sets the brush style to style.
Definition qbrush.cpp:655
\inmodule QtCore \reentrant
Definition qbuffer.h:16
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
QByteArray & setNum(short, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qbytearray.h:688
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
void clear()
Clears the contents of the byte array and makes it null.
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
static constexpr QCmyk32 fromCmyk32(uint cmyk) noexcept
Definition qcmyk_p.h:62
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
Spec
The type of color specified, either RGB, extended RGB, HSV, CMYK or HSL.
Definition qcolor.h:35
@ Cmyk
Definition qcolor.h:35
@ Invalid
Definition qcolor.h:35
@ Hsv
Definition qcolor.h:35
@ ExtendedRgb
Definition qcolor.h:35
@ Rgb
Definition qcolor.h:35
@ Hsl
Definition qcolor.h:35
QRgb rgba() const noexcept
Returns the RGB value of the color, including its alpha.
Definition qcolor.cpp:1376
int alpha() const noexcept
Returns the alpha color component of this color.
Definition qcolor.cpp:1466
void setAlpha(int alpha)
Sets the alpha of this color to alpha.
Definition qcolor.cpp:1481
static QByteArray hash(QByteArrayView data, Algorithm method)
Returns the hash of data using method.
\inmodule QtCore\reentrant
Definition qdatastream.h:46
\inmodule QtCore\reentrant
Definition qdatetime.h:283
static QDateTime currentDateTime()
This is an overloaded member function, provided for convenience. It differs from the above function o...
\inmodule QtCore \reentrant
Definition qdatetime.h:29
int month() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
int day() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
int year() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
\inmodule QtGui
Definition qbrush.h:135
Spread spread() const
Returns the spread method use by this gradient.
Definition qbrush.h:347
CoordinateMode coordinateMode() const
Definition qbrush.cpp:1672
@ LogicalMode
Definition qbrush.h:154
@ ReflectSpread
Definition qbrush.h:148
@ RepeatSpread
Definition qbrush.h:149
@ PadSpread
Definition qbrush.h:147
Type type() const
Returns the type of gradient.
Definition qbrush.h:344
QGradientStops stops() const
Returns the stop points for this gradient.
Definition qbrush.cpp:1631
@ LinearGradient
Definition qbrush.h:139
@ NoGradient
Definition qbrush.h:142
@ ConicalGradient
Definition qbrush.h:141
@ RadialGradient
Definition qbrush.h:140
\inmodule QtCore
Definition qhash.h:1103
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1212
T value(const Key &key) const noexcept
Definition qhash.h:1054
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1216
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
\inmodule QtCore \reentrant
Definition qiodevice.h:34
virtual bool open(QIODeviceBase::OpenMode mode)
Opens the device and sets its OpenMode to mode.
virtual qint64 size() const
For open random-access devices, this function returns the size of the device.
virtual qint64 pos() const
For random-access devices, this function returns the position that data is written to or read from.
virtual bool isSequential() const
Returns true if this device is sequential; otherwise returns false.
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
virtual bool seek(qint64 pos)
For random-access devices, this function sets the current position to pos, returning true on success,...
virtual bool reset()
Seeks to the start of input for random-access devices.
virtual bool atEnd() const
Returns true if the current read and write position is at the end of the device (i....
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
The QImageWriter class provides a format independent interface for writing images to files or other d...
static QList< QByteArray > supportedImageFormats()
Returns the list of image formats supported by QImageWriter.
\inmodule QtGui
Definition qimage.h:37
int width() const
Returns the width of the image.
int height() const
Returns the height of the image.
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_CMYK8888
Definition qimage.h:78
@ Format_RGB32
Definition qimage.h:46
@ Format_MonoLSB
Definition qimage.h:44
@ Format_Mono
Definition qimage.h:43
@ Format_ARGB32
Definition qimage.h:47
qint64 cacheKey() const
Returns a number that identifies the contents of this QImage object.
Definition qimage.cpp:4507
\inmodule QtCore\compares equality \compareswith equality QLine \endcompareswith
Definition qline.h:192
\inmodule QtGui
Definition qbrush.h:394
QPointF start() const
Returns the start point of this linear gradient in logical coordinates.
Definition qbrush.cpp:1876
QPointF finalStop() const
Returns the final stop point of this linear gradient in logical coordinates.
Definition qbrush.cpp:1927
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
T & last()
Definition qlist.h:648
const T & constLast() const noexcept
Definition qlist.h:650
bool empty() const noexcept
Definition qlist.h:685
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
void prepend(rvalue_ref t)
Definition qlist.h:473
const T & constFirst() const noexcept
Definition qlist.h:647
void resize(qsizetype size)
Definition qlist.h:403
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qmargins.h:270
\inmodule QtGui
Definition qpagelayout.h:20
QRect fullRectPoints() const
Returns the full page rectangle in Postscript Points (1/72 of an inch).
Mode mode() const
Returns the page layout mode.
Unit
This enum type is used to specify the measurement unit for page layout and margins.
Definition qpagelayout.h:24
QRect paintRectPixels(int resolution) const
Returns the paintable rectangle in rounded device pixels for the given resolution.
Orientation
This enum type defines the page orientation.
Definition qpagelayout.h:33
QRect fullRectPixels(int resolution) const
Returns the full page rectangle in device pixels for the given resolution.
\inmodule QtGui
Definition qpagesize.h:22
@ PdmDevicePixelRatioScaled
static qreal devicePixelRatioFScale()
The QPaintEngineState class provides information about the active paint engine's current state....
QTransform transform() const
QPainterPath clipPath() const
Returns the clip path in the current paint engine state.
Qt::ClipOperation clipOperation() const
Returns the clip operation in the current paint engine state.
QPointF brushOrigin() const
Returns the brush origin in the current paint engine state.
qreal opacity() const
bool isClipEnabled() const
Returns whether clipping is enabled or not in the current paint engine state.
QBrush brush() const
Returns the brush in the current paint engine state.
QRegion clipRegion() const
Returns the clip region in the current paint engine state.
QPainter::RenderHints renderHints() const
Returns the render hints in the current paint engine state.
QPen pen() const
Returns the pen in the current paint engine state.
QPaintEngine::DirtyFlags state() const
Returns a combination of flags identifying the set of properties that need to be updated when updatin...
\inmodule QtGui
void setActive(bool newState)
Sets the active state of the paint engine to state.
virtual void drawTextItem(const QPointF &p, const QTextItem &textItem)
This function draws the text item textItem at position p.
PolygonDrawMode
\value OddEvenMode The polygon should be drawn using OddEven fill rule.
QPainter * painter() const
Returns the paint engine's painter.
@ ObjectBoundingModeGradients
QPaintEngineState * state
bool isActive() const
Returns true if the paint engine is actively drawing; otherwise returns false.
Type
\value X11 \value Windows \value MacPrinter \value CoreGraphics \macos's Quartz2D (CoreGraphics) \val...
\inmodule QtGui
\inmodule QtGui
void addRect(const QRectF &rect)
Adds the given rectangle to this path as a closed subpath.
void moveTo(const QPointF &p)
Moves the current point to the given point, implicitly starting a new subpath and closing the previou...
void setFillRule(Qt::FillRule fillRule)
Sets the fill rule of the painter path to the given fillRule.
void clear()
Clears the path elements stored.
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
@ NonCosmeticBrushPatterns
Definition qpainter.h:57
@ LosslessImageRendering
Definition qpainter.h:56
bool testRenderHint(RenderHint hint) const
Definition qpainter.h:407
uint requestObject()
Definition qpdf_p.h:219
QTransform pageMatrix() const
Definition qpdf.cpp:3378
int addBrushPattern(const QTransform &matrix, bool *specifyColor, int *gStateObject)
Definition qpdf.cpp:2922
int addConstantAlphaObject(int brushAlpha, int penAlpha=255)
Definition qpdf.cpp:2901
QString creator
Definition qpdf_p.h:264
int addImage(const QImage &image, bool *bitmap, bool lossless, qint64 serial_no)
Adds an image to the pdf and return the pdf-object id.
Definition qpdf.cpp:3024
QString title
Definition qpdf_p.h:263
QPageLayout m_pageLayout
Definition qpdf_p.h:269
QPdf::Stroker stroker
Definition qpdf_p.h:237
QHash< QFontEngine::FaceId, QFontSubset * > fonts
Definition qpdf_p.h:253
void writeHeader()
Definition qpdf.cpp:1546
QPdfEngine::PdfVersion pdfVersion
Definition qpdf_p.h:250
QPdfPage * currentPage
Definition qpdf_p.h:236
void drawTextItem(const QPointF &p, const QTextItemInt &ti)
Definition qpdf.cpp:3197
QPdfEngine::ColorModel colorModel
Definition qpdf_p.h:251
QPointF brushOrigin
Definition qpdf_p.h:239
void addFileAttachment(const QString &fileName, const QByteArray &data, const QString &mimeType)
Definition qpdf.cpp:1533
QPageLayout pageLayout() const
Definition qpdf.cpp:1380
void drawLines(const QLineF *lines, int lineCount) override
The default implementation splits the list of lines in lines into lineCount separate calls to drawPat...
Definition qpdf.cpp:791
void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override
Reimplement this virtual function to draw the polygon defined by the pointCount first points in point...
Definition qpdf.cpp:837
int metric(QPaintDevice::PaintDeviceMetric metricType) const
Definition qpdf.cpp:1387
void setupGraphicsState(QPaintEngine::DirtyFlags flags)
Definition qpdf.cpp:1160
void drawPixmap(const QRectF &rectangle, const QPixmap &pixmap, const QRectF &sr) override
Reimplement this function to draw the part of the pm specified by the sr rectangle in the given r.
Definition qpdf.cpp:901
QByteArray documentXmpMetadata() const
Definition qpdf.cpp:1349
void updateState(const QPaintEngineState &state) override
Reimplement this function to update the state of a paint engine.
Definition qpdf.cpp:1073
void drawPoints(const QPointF *points, int pointCount) override
Draws the first pointCount points in the buffer points.
Definition qpdf.cpp:773
void drawRects(const QRectF *rects, int rectCount) override
Draws the first rectCount rectangles in the buffer rects.
Definition qpdf.cpp:808
Type type() const override
Reimplement this function to return the paint engine \l{Type}.
Definition qpdf.cpp:1320
void setOutputFilename(const QString &filename)
Definition qpdf.cpp:766
void setPageOrientation(QPageLayout::Orientation orientation)
Definition qpdf.cpp:1367
bool begin(QPaintDevice *pdev) override
Reimplement this function to initialise your paint engine when painting is to start on the paint devi...
Definition qpdf.cpp:1450
bool newPage()
Definition qpdf.cpp:1306
void drawTextItem(const QPointF &p, const QTextItem &textItem) override
This function draws the text item textItem at position p.
Definition qpdf.cpp:1008
void setBrush()
Definition qpdf.cpp:1279
bool end() override
Reimplement this function to finish painting on the current paint device.
Definition qpdf.cpp:1508
QPdfEngine()
Definition qpdf.cpp:761
void setPageMargins(const QMarginsF &margins, QPageLayout::Unit units=QPageLayout::Point)
Definition qpdf.cpp:1373
void drawTiledPixmap(const QRectF &rectangle, const QPixmap &pixmap, const QPointF &point) override
Reimplement this function to draw the pixmap in the given rect, starting at the given p.
Definition qpdf.cpp:978
void setDocumentXmpMetadata(const QByteArray &xmpMetadata)
Definition qpdf.cpp:1343
void drawPath(const QPainterPath &path) override
The default implementation ignores the path and does nothing.
Definition qpdf.cpp:873
void setPdfVersion(PdfVersion version)
Definition qpdf.cpp:1337
void setPageLayout(const QPageLayout &pageLayout)
Definition qpdf.cpp:1355
void drawHyperlink(const QRectF &r, const QUrl &url)
Definition qpdf.cpp:1038
@ Version_A1b
Definition qpdf_p.h:136
@ Version_1_6
Definition qpdf_p.h:137
void setPen()
Definition qpdf.cpp:1229
void setPageSize(const QPageSize &pageSize)
Definition qpdf.cpp:1361
void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags=Qt::AutoColor) override
Reimplement this function to draw the part of the image specified by the sr rectangle in the given re...
Definition qpdf.cpp:944
void setResolution(int resolution)
Definition qpdf.cpp:1325
int resolution() const
Definition qpdf.cpp:1331
QSize pageSize
Definition qpdf_p.h:120
QList< uint > patterns
Definition qpdf_p.h:114
QList< uint > fonts
Definition qpdf_p.h:115
QList< uint > images
Definition qpdf_p.h:112
void streamImage(int w, int h, uint object)
Definition qpdf.cpp:748
QList< uint > graphicStates
Definition qpdf_p.h:113
QList< uint > annotations
Definition qpdf_p.h:116
QPdfPage()
Definition qpdf.cpp:743
ByteStream & operator<<(char chr)
Definition qpdf.cpp:201
ByteStream(bool fileBacking=false)
Definition qpdf.cpp:187
static int chunkSize()
Definition qpdf_p.h:61
QIODevice * stream()
Definition qpdf.cpp:262
static int maxMemorySize()
Definition qpdf_p.h:60
\inmodule QtGui
Definition qpen.h:28
qreal widthF() const
Returns the pen width with floating point precision.
Definition qpen.cpp:572
QList< qreal > dashPattern() const
Returns the dash pattern of this pen.
Definition qpen.cpp:400
bool isCosmetic() const
Returns true if the pen is cosmetic; otherwise returns false.
Definition qpen.cpp:757
QColor color() const
Returns the color of this pen's brush.
Definition qpen.cpp:692
Qt::PenCapStyle capStyle() const
Returns the pen's cap style.
Definition qpen.cpp:636
void setColor(const QColor &color)
Sets the color of this pen's brush to the given color.
Definition qpen.cpp:705
void setBrush(const QBrush &brush)
Sets the brush used to fill strokes generated with this pen to the given brush.
Definition qpen.cpp:726
Qt::PenJoinStyle joinStyle() const
Returns the pen's join style.
Definition qpen.cpp:663
qreal miterLimit() const
Returns the miter limit of the pen.
Definition qpen.cpp:524
QBrush brush() const
Returns the brush used to fill strokes generated with this pen.
Definition qpen.cpp:715
qreal dashOffset() const
Returns the dash offset for the pen.
Definition qpen.cpp:484
Qt::PenStyle style() const
Returns the pen style.
Definition qpen.cpp:366
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
QImage toImage() const
Converts the pixmap to a QImage.
Definition qpixmap.cpp:408
qint64 cacheKey() const
Returns a number that identifies this QPixmap.
Definition qpixmap.cpp:884
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
\inmodule QtGui
Definition qbrush.h:412
QPointF center() const
Returns the center of this radial gradient in logical coordinates.
Definition qbrush.cpp:2157
qreal focalRadius() const
Definition qbrush.cpp:2254
QPointF focalPoint() const
Returns the focal point of this radial gradient in logical coordinates.
Definition qbrush.cpp:2279
qreal centerRadius() const
Definition qbrush.cpp:2228
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr bool isEmpty() const noexcept
Returns true if the rectangle is empty, otherwise returns false.
Definition qrect.h:661
constexpr qreal bottom() const noexcept
Returns the y-coordinate of the rectangle's bottom edge.
Definition qrect.h:500
constexpr qreal y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:672
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:732
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:729
constexpr qreal x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:669
constexpr QPointF bottomLeft() const noexcept
Returns the position of the rectangle's bottom-left corner.
Definition qrect.h:514
constexpr qreal left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:497
constexpr QPointF topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:511
constexpr QPointF bottomRight() const noexcept
Returns the position of the rectangle's bottom-right corner.
Definition qrect.h:512
constexpr qreal top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:498
constexpr QPointF topRight() const noexcept
Returns the position of the rectangle's top-right corner.
Definition qrect.h:513
constexpr qreal right() const noexcept
Returns the x-coordinate of the rectangle's right edge.
Definition qrect.h:499
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:239
constexpr QSize size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:242
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
Definition qsize.h:124
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QByteArray toLatin1() const &
Definition qstring.h:630
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5455
QString sliced(qsizetype pos) const &
Definition qstring.h:394
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7263
QString toHtmlEscaped() const
void setCubicToHook(qStrokerCubicToHook cubicToHook)
Definition qstroker_p.h:114
void setMoveToHook(qStrokerMoveToHook moveToHook)
Definition qstroker_p.h:112
void setLineToHook(qStrokerLineToHook lineToHook)
Definition qstroker_p.h:113
void setStrokeWidth(qfixed width)
Definition qstroker_p.h:176
Internal QTextItem.
\inmodule QtGui
\inmodule QtCore \reentrant
Definition qdatetime.h:215
int hour() const
Returns the hour part (0 to 23) of the time.
int minute() const
Returns the minute part (0 to 59) of the time.
int second() const
Returns the second part (0 to 59) of the time.
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
QPoint map(const QPoint &p) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QTransform fromTranslate(qreal dx, qreal dy)
Creates a matrix which corresponds to a translation of dx along the x axis and dy along the y axis.
QTransform inverted(bool *invertible=nullptr) const
Returns an inverted copy of this matrix.
QTransform & translate(qreal dx, qreal dy)
Moves the coordinate system dx along the x axis and dy along the y axis, and returns a reference to t...
\inmodule QtCore
Definition qurl.h:94
QByteArray toEncoded(FormattingOptions options=FullyEncoded) const
Returns the encoded representation of the URL if it's valid; otherwise an empty QByteArray is returne...
Definition qurl.cpp:2967
static QUuid createUuid()
On any platform other than Windows, this function returns a new UUID with variant QUuid::DCE and vers...
Definition quuid.cpp:997
QString str
[2]
QPixmap p2
QPixmap p1
[0]
QDate date
[1]
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
rect
[4]
else opt state
[0]
Definition qpdf.cpp:177
const char * toHex(ushort u, char *buffer)
Definition qpdf.cpp:710
QByteArray generatePath(const QPainterPath &path, const QTransform &matrix, PathFlags flags)
Definition qpdf.cpp:303
PathFlags
Definition qpdf_p.h:74
@ StrokePath
Definition qpdf_p.h:77
@ FillAndStrokePath
Definition qpdf_p.h:78
@ FillPath
Definition qpdf_p.h:76
@ ClipPath
Definition qpdf_p.h:75
QByteArray ascii85Encode(const QByteArray &input)
Definition qpdf.cpp:654
QByteArray generateDashes(const QPen &pen)
Definition qpdf.cpp:379
QByteArray patternForBrush(const QBrush &b)
Definition qpdf.cpp:555
QByteArray generateMatrix(const QTransform &matrix)
Definition qpdf.cpp:365
Combined button and popup list for selecting options.
ClipOperation
@ ReplaceClip
@ IntersectClip
@ NoClip
@ gray
Definition qnamespace.h:33
@ white
Definition qnamespace.h:31
@ black
Definition qnamespace.h:30
@ NoPen
@ SvgMiterJoin
@ BevelJoin
@ MiterJoin
@ RoundJoin
BrushStyle
@ DiagCrossPattern
@ SolidPattern
@ RadialGradientPattern
@ Dense1Pattern
@ TexturePattern
@ LinearGradientPattern
@ NoBrush
@ ConicalGradientPattern
@ WindingFill
@ OddEvenFill
QTextStream & center(QTextStream &stream)
Calls QTextStream::setFieldAlignment(QTextStream::AlignCenter) on stream and returns stream.
@ SquareCap
@ RoundCap
@ FlatCap
Definition brush.cpp:5
Definition image.cpp:4
QPair< qreal, QColor > QGradientStop
Definition qbrush.h:131
QByteArray qCompress(const uchar *data, qsizetype nbytes, int compressionLevel)
Q_CORE_EXPORT int qvsnprintf(char *str, size_t n, const char *fmt, va_list ap)
#define rgb(r, g, b)
Definition qcolor.cpp:124
#define Q_FALLTHROUGH()
#define Q_LIKELY(x)
AudioChannelLayoutTag tag
static const QCssKnownValue positions[NumKnownPositionModes - 1]
static const QCssKnownValue properties[NumProperties - 1]
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
static QString header(const QString &name)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLStreamKHR stream
const char * mimeType
bool qIsFinite(qfloat16 f) noexcept
Definition qfloat16.h:285
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
#define qWarning
Definition qlogging.h:166
#define qFatal
Definition qlogging.h:168
return ret
int qFloor(T v)
Definition qmath.h:42
int qCeil(T v)
Definition qmath.h:36
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
GLenum mode
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLuint GLuint end
GLuint GLfloat GLfloat GLfloat x1
GLenum GLuint GLenum GLsizei length
GLuint object
[3]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum src
GLenum GLuint buffer
GLint GLsizei width
GLuint color
[2]
GLenum GLuint GLenum GLsizei const GLchar * buf
GLbitfield flags
GLsizei bufsize
GLsizei const GLenum * attachments
GLfloat units
GLuint start
GLenum GLuint GLintptr offset
GLboolean GLboolean g
GLenum attachment
GLint first
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLint GLsizei GLsizei GLenum format
GLenum GLsizeiptr const void * fontData
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLfixed GLfixed GLint GLint GLfixed points
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
GLuint GLfloat * val
GLfixed GLfixed GLfixed y2
GLint void * img
Definition qopenglext.h:233
GLenum array
GLuint shader
Definition qopenglext.h:665
GLsizei GLfixed GLfixed GLfixed GLfixed const GLubyte * bitmap
GLuint GLenum matrix
GLfixed GLfixed x2
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint in
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLenum GLenum GLenum GLenum mapping
GLfloat GLfloat p
[1]
GLuint GLenum option
GLenum GLsizei len
GLfloat GLfloat GLfloat alpha
Definition qopenglext.h:418
GLenum GLenum GLenum GLenum GLenum scale
GLubyte * pattern
GLenum GLenum GLenum input
bool qt_isExtendedRadialGradient(const QBrush &brush)
Definition qbrush.cpp:801
const char * qt_int_to_string(int val, char *buf)
Definition qpdf.cpp:152
static void initResources()
Definition qpdf.cpp:39
static bool is_monochrome(const QList< QRgb > &colorTable)
Definition qpdf.cpp:3013
static void removeTransparencyFromBrush(QBrush &brush)
Definition qpdf.cpp:61
static const char *const pattern_for_brush[]
Definition qpdf.cpp:402
static void lineToHook(qfixed x, qfixed y, void *data)
Definition qpdf.cpp:575
static const bool interpolateImages
Definition qpdf.cpp:37
static const bool do_compress
Definition qpdf.cpp:32
const char * qt_real_to_string(qreal val, char *buf)
Definition qpdf.cpp:100
QPainterPath qt_regionToPath(const QRegion &region)
Definition qregion.cpp:1007
constexpr QPaintEngine::PaintEngineFeatures qt_pdf_decide_features()
Definition qpdf.cpp:48
static void moveToHook(qfixed x, qfixed y, void *data)
Definition qpdf.cpp:564
static void cubicToHook(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey, void *data)
Definition qpdf.cpp:583
QT_BEGIN_NAMESPACE const char * qt_real_to_string(qreal val, char *buf)
Definition qpdf.cpp:100
static bool hasAlpha(const QImage &image)
static void grayscale(const QImage &image, QImage &dest, const QRect &rect=QRect())
static constexpr qint64 HeaderSize
QQuickAnchors * anchors(QQuickItem *item)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
constexpr int qRed(QRgb rgb)
Definition qrgb.h:18
constexpr int qGreen(QRgb rgb)
Definition qrgb.h:21
constexpr int qGray(int r, int g, int b)
Definition qrgb.h:36
constexpr int qBlue(QRgb rgb)
Definition qrgb.h:24
constexpr int qAlpha(QRgb rgb)
Definition qrgb.h:27
QT_BEGIN_NAMESPACE typedef qreal qfixed
Definition qstroker_p.h:63
#define Q_UNIMPLEMENTED()
#define Q_INIT_RESOURCE(name)
Definition qtresource.h:14
@ Q_PRIMITIVE_TYPE
Definition qtypeinfo.h:157
#define Q_DECLARE_TYPEINFO(TYPE, FLAGS)
Definition qtypeinfo.h:180
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
unsigned short ushort
Definition qtypes.h:33
double qreal
Definition qtypes.h:187
static const uint base
Definition qurlidna.cpp:20
QVideoFrameFormat::PixelFormat fmt
QT_BEGIN_NAMESPACE typedef uchar * output
static int deflate(Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
Definition qzip.cpp:127
QByteArray ba
[0]
QFile file
[0]
QTextStream out(stdout)
[7]
QUrl url("example.com")
[constructor-url-reference]
QObject::connect nullptr
QRect r1(100, 200, 11, 16)
[0]
QRect r2(QPoint(100, 200), QSize(11, 16))
QByteArray imageData
[15]
widget render & pixmap
QJSValueList args
\inmodule QtCore \reentrant
Definition qchar.h:18
qsizetype indexOf(const AT &t, qsizetype from=0) const noexcept
Definition qlist.h:962
bool contains(const AT &t) const noexcept
Definition qlist.h:45
bool cosmeticPen
Definition qpdf_p.h:92
ByteStream * stream
Definition qpdf_p.h:89
QTransform matrix
Definition qpdf_p.h:91
void setPen(const QPen &pen, QPainter::RenderHints hints)
Definition qpdf.cpp:613
void strokePath(const QPainterPath &path)
Definition qpdf.cpp:644