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