Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qpdfdocument.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 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 "qpdfdocument.h"
5#include "qpdfdocument_p.h"
6
7#include "third_party/pdfium/public/fpdf_doc.h"
8#include "third_party/pdfium/public/fpdf_text.h"
9
10#include <QDateTime>
11#include <QDebug>
12#include <QElapsedTimer>
13#include <QFile>
14#include <QHash>
15#include <QLoggingCategory>
16#include <QMetaEnum>
17#include <QMutex>
18#include <QPixmap>
19#include <QVector2D>
20
21#include <QtCore/private/qtools_p.h>
22
24
26static int libraryRefCount;
27static const double CharacterHitTolerance = 16.0;
28Q_LOGGING_CATEGORY(qLcDoc, "qt.pdf.document")
29
31 : std::unique_lock<QRecursiveMutex>(*pdfMutex())
32{
33}
34
36{
38public:
40 {
41 m_roleNames = QAbstractItemModel::roleNames();
42 QMetaEnum rolesMetaEnum = doc->metaObject()->enumerator(doc->metaObject()->indexOfEnumerator("PageModelRole"));
44 auto name = QByteArray(rolesMetaEnum.valueToKey(r));
46 m_roleNames.insert(r, name);
47 }
53 });
54 }
55
56 QVariant data(const QModelIndex &index, int role) const override
57 {
58 if (!index.isValid())
59 return QVariant();
60
61 switch (QPdfDocument::PageModelRole(role)) {
63 return document()->pageLabel(index.row());
65 return document()->pagePointSize(index.row());
67 break;
68 }
69
70 switch (role) {
72 return pageThumbnail(index.row());
73 case Qt::DisplayRole:
74 return document()->pageLabel(index.row());
75 }
76
77 return QVariant();
78 }
79
80 int rowCount(const QModelIndex & = QModelIndex()) const override { return document()->pageCount(); }
81
82 QHash<int, QByteArray> roleNames() const override { return m_roleNames; }
83
84private:
85 QPdfDocument *document() const { return static_cast<QPdfDocument *>(parent()); }
86 QPixmap pageThumbnail(int page) const
87 {
88 auto it = m_thumbnails.constFind(page);
89 if (it == m_thumbnails.constEnd()) {
90 auto doc = document();
91 auto size = doc->pagePointSize(page);
92 size.scale(128, 128, Qt::KeepAspectRatio);
93 // TODO use QPdfPageRenderer for threading?
94 auto image = document()->render(page, size.toSize());
96 m_thumbnails.insert(page, ret);
97 return ret;
98 }
99 return it.value();
100 }
101
102 QHash<int, QByteArray> m_roleNames;
103 mutable QHash<int, QPixmap> m_thumbnails;
104};
105
107 : avail(nullptr)
108 , doc(nullptr)
109 , loadComplete(false)
110 , status(QPdfDocument::Status::Null)
111 , lastError(QPdfDocument::Error::None)
112 , pageCount(0)
113{
116
117 const QPdfMutexLocker lock;
118
119 if (libraryRefCount == 0) {
121 timer.start();
122 FPDF_InitLibrary();
123 qCDebug(qLcDoc) << "FPDF_InitLibrary took" << timer.elapsed() << "ms";
124 }
126
127 // FPDF_FILEACCESS setup
128 m_Param = this;
129 m_GetBlock = fpdf_GetBlock;
130
131 // FX_FILEAVAIL setup
132 FX_FILEAVAIL::version = 1;
133 IsDataAvail = fpdf_IsDataAvail;
134
135 // FX_DOWNLOADHINTS setup
136 FX_DOWNLOADHINTS::version = 1;
137 AddSegment = fpdf_AddSegment;
138}
139
141{
142 q->close();
143
144 const QPdfMutexLocker lock;
145
146 if (!--libraryRefCount) {
147 qCDebug(qLcDoc) << "FPDF_DestroyLibrary";
148 FPDF_DestroyLibrary();
149 }
150}
151
153{
155
156 if (doc)
157 FPDF_CloseDocument(doc);
158 doc = nullptr;
159
160 if (avail)
161 FPDFAvail_Destroy(avail);
162 avail = nullptr;
163 lock.unlock();
164
165 if (pageCount != 0) {
166 pageCount = 0;
167 emit q->pageCountChanged(pageCount);
168 emit q->pageModelChanged();
169 }
170
171 loadComplete = false;
172
176
179}
180
182{
183 if (doc) {
185 return;
186 }
187
189 const unsigned long error = FPDF_GetLastError();
190 lock.unlock();
191
192 switch (error) {
193 case FPDF_ERR_SUCCESS: lastError = QPdfDocument::Error::None; break;
194 case FPDF_ERR_UNKNOWN: lastError = QPdfDocument::Error::Unknown; break;
195 case FPDF_ERR_FILE: lastError = QPdfDocument::Error::FileNotFound; break;
196 case FPDF_ERR_FORMAT: lastError = QPdfDocument::Error::InvalidFileFormat; break;
197 case FPDF_ERR_PASSWORD: lastError = QPdfDocument::Error::IncorrectPassword; break;
198 case FPDF_ERR_SECURITY: lastError = QPdfDocument::Error::UnsupportedSecurityScheme; break;
199 default:
200 Q_UNREACHABLE();
201 }
203 qCDebug(qLcDoc) << "FPDF error" << error << "->" << lastError;
204}
205
206void QPdfDocumentPrivate::load(QIODevice *newDevice, bool transferDeviceOwnership)
207{
208 if (transferDeviceOwnership)
209 ownDevice.reset(newDevice);
210 else
212
213 if (newDevice->isSequential()) {
214 sequentialSourceDevice = newDevice;
216 QNetworkReply *reply = qobject_cast<QNetworkReply*>(sequentialSourceDevice);
217
218 if (!reply) {
220 qWarning() << "QPdfDocument: Loading from sequential devices only supported with QNetworkAccessManager.";
221 return;
222 }
223
226 return;
227 }
228
231 this->setStatus(QPdfDocument::Status::Error);
232 }
233 });
234
237 else
239 } else {
240 device = newDevice;
242 if (!avail) {
244 return;
245 }
246
247 if (!doc)
249
250 if (!doc) {
253 return;
254 }
255
257 const int newPageCount = FPDF_GetPageCount(doc);
258 lock.unlock();
259 if (newPageCount != pageCount) {
260 pageCount = newPageCount;
261 emit q->pageCountChanged(pageCount);
262 emit q->pageModelChanged();
263 }
264
265 // If it's a local file, and the first couple of pages are available,
266 // probably the whole document is available.
267 if (checkPageComplete(0) && (pageCount < 2 || checkPageComplete(1))) {
269 } else {
272 }
273 }
274}
275
277{
278 if (avail)
279 return;
280
281 const QNetworkReply *networkReply = qobject_cast<QNetworkReply*>(sequentialSourceDevice);
282 if (!networkReply) {
284 return;
285 }
286
287 const QVariant contentLength = networkReply->header(QNetworkRequest::ContentLengthHeader);
288 if (!contentLength.isValid()) {
290 return;
291 }
292
294
296
299}
300
302{
303 // FPDF_FILEACCESS setup
304 m_FileLen = totalSize;
305
306 const QPdfMutexLocker lock;
307
308 avail = FPDFAvail_Create(this, this);
309}
310
312{
313 if (loadComplete)
314 return;
315
317 if (data.isEmpty())
318 return;
319
322
324}
325
327{
329 switch (FPDFAvail_IsDocAvail(avail, this)) {
330 case PDF_DATA_ERROR:
331 qCDebug(qLcDoc) << "error loading";
332 break;
333 case PDF_DATA_NOTAVAIL:
334 qCDebug(qLcDoc) << "data not yet available";
336 break;
337 case PDF_DATA_AVAIL:
339 break;
340 }
341
342 Q_ASSERT(!doc);
343
344 doc = FPDFAvail_GetDocument(avail, password);
345 lock.unlock();
346
350
352 FPDF_CloseDocument(doc);
353 doc = nullptr;
354
356 emit q->passwordRequired();
357 }
358}
359
361{
362 if (!avail || loadComplete)
363 return;
364
365 if (!doc)
367
368 if (!doc)
369 return;
370
371 loadComplete = true;
372
374
375 const int newPageCount = FPDF_GetPageCount(doc);
376 for (int i = 0; i < newPageCount; ++i) {
377 int result = PDF_DATA_NOTAVAIL;
378 while (result == PDF_DATA_NOTAVAIL) {
379 result = FPDFAvail_IsPageAvail(avail, i, this);
380 }
381
382 if (result == PDF_DATA_ERROR)
383 loadComplete = false;
384 }
385
386 lock.unlock();
387
388 if (loadComplete) {
389 if (newPageCount != pageCount) {
390 pageCount = newPageCount;
391 emit q->pageCountChanged(pageCount);
392 emit q->pageModelChanged();
393 }
394
396 }
397}
398
400{
401 if (page < 0 || page >= pageCount)
402 return false;
403
404 if (loadComplete)
405 return true;
406
408 int result = PDF_DATA_NOTAVAIL;
409 while (result == PDF_DATA_NOTAVAIL)
410 result = FPDFAvail_IsPageAvail(avail, page, this);
411 lock.unlock();
412
413 if (result == PDF_DATA_ERROR)
415
416 return (result != PDF_DATA_ERROR);
417}
418
420{
421 if (status == documentStatus)
422 return;
423
424 status = documentStatus;
425 emit q->statusChanged(status);
426}
427
428FPDF_BOOL QPdfDocumentPrivate::fpdf_IsDataAvail(_FX_FILEAVAIL *pThis, size_t offset, size_t size)
429{
430 QPdfDocumentPrivate *d = static_cast<QPdfDocumentPrivate*>(pThis);
431 return offset + size <= static_cast<quint64>(d->device->size());
432}
433
434int QPdfDocumentPrivate::fpdf_GetBlock(void *param, unsigned long position, unsigned char *pBuf, unsigned long size)
435{
436 QPdfDocumentPrivate *d = static_cast<QPdfDocumentPrivate*>(reinterpret_cast<FPDF_FILEACCESS*>(param));
437 d->device->seek(position);
438 return qMax(qint64(0), d->device->read(reinterpret_cast<char *>(pBuf), size));
439
440}
441
442void QPdfDocumentPrivate::fpdf_AddSegment(_FX_DOWNLOADHINTS *pThis, size_t offset, size_t size)
443{
444 Q_UNUSED(pThis);
446 Q_UNUSED(size);
447}
448
449QString QPdfDocumentPrivate::getText(FPDF_TEXTPAGE textPage, int startIndex, int count)
450{
452 // TODO is that enough space in case one unicode character is more than one in utf-16?
453 int len = FPDFText_GetText(textPage, startIndex, count, buf.data());
454 Q_ASSERT(len - 1 <= count); // len is number of characters written, including the terminator
455 return QString::fromUtf16(reinterpret_cast<const char16_t *>(buf.constData()), len - 1);
456}
457
458QPointF QPdfDocumentPrivate::getCharPosition(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex)
459{
460 double x, y;
461 int count = FPDFText_CountChars(textPage);
462 bool ok = FPDFText_GetCharOrigin(textPage, qMin(count - 1, charIndex), &x, &y);
463 if (!ok)
464 return QPointF();
465 return QPointF(x, pageHeight - y);
466}
467
468QRectF QPdfDocumentPrivate::getCharBox(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex)
469{
470 double l, t, r, b;
471 bool ok = FPDFText_GetCharBox(textPage, charIndex, &l, &r, &b, &t);
472 if (!ok)
473 return QRectF();
474 return QRectF(l, pageHeight - t, r - l, t - b);
475}
476
478{
479 const QPdfMutexLocker lock;
480
482 FPDF_PAGE pdfPage = FPDF_LoadPage(doc, page);
483 double pageHeight = FPDF_GetPageHeight(pdfPage);
484 FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
485 int hitIndex = FPDFText_GetCharIndexAtPos(textPage, position.x(), pageHeight - position.y(),
487 if (hitIndex >= 0) {
488 QPointF charPos = getCharPosition(textPage, pageHeight, hitIndex);
489 if (!charPos.isNull()) {
490 QRectF charBox = getCharBox(textPage, pageHeight, hitIndex);
491 // If the given position is past the end of the line, i.e. if the right edge of the found character's
492 // bounding box is closer to it than the left edge is, we say that we "hit" the next character index after
493 if (qAbs(charBox.right() - position.x()) < qAbs(charPos.x() - position.x())) {
494 charPos.setX(charBox.right());
495 ++hitIndex;
496 }
497 qCDebug(qLcDoc) << "on page" << page << "@" << position << "got char position" << charPos << "index" << hitIndex;
498 result = { charPos, charBox.height(), hitIndex };
499 }
500 }
501
502 FPDFText_ClosePage(textPage);
503 FPDF_ClosePage(pdfPage);
504
505 return result;
506}
507
520 : QObject(parent)
522{
523 d->q = this;
524}
525
530{
531}
532
537{
538 qCDebug(qLcDoc) << "loading" << fileName;
539
540 close();
541
543
544 std::unique_ptr<QFile> f(new QFile(fileName));
545 if (!f->open(QIODevice::ReadOnly)) {
548 } else {
549 d->load(f.release(), /*transfer ownership*/true);
550 }
551 return d->lastError;
552}
553
558QString QPdfDocument::fileName() const
559{
560 const QFile *f = qobject_cast<QFile *>(d->device.data());
561 if (f)
562 return f->fileName();
563 return QString();
564}
565
587{
588 return d->status;
589}
590
595{
596 close();
597
599
600 d->load(device, /*transfer ownership*/false);
601}
602
612{
613 const QByteArray newPassword = password.toUtf8();
614
615 if (d->password == newPassword)
616 return;
617
618 d->password = newPassword;
620}
621
623{
624 return QString::fromUtf8(d->password);
625}
626
651{
652 if (!d->doc)
653 return QString();
654
655 static QMetaEnum fieldsMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("MetaDataField"));
656 QByteArray fieldName;
657 switch (field) {
659 fieldName = "ModDate";
660 break;
661 default:
662 fieldName = QByteArray(fieldsMetaEnum.valueToKey(int(field)));
663 break;
664 }
665
667 const unsigned long len = FPDF_GetMetaText(d->doc, fieldName.constData(), nullptr, 0);
668
670 FPDF_GetMetaText(d->doc, fieldName.constData(), buf.data(), buf.size());
671 lock.unlock();
672
673 QString text = QString::fromUtf16(reinterpret_cast<const char16_t *>(buf.data()));
674
675 switch (field) {
676 case MetaDataField::Title: // fall through
682 return text;
683 case MetaDataField::CreationDate: // fall through
685 // convert a "D:YYYYMMDDHHmmSSOHH'mm'" into "YYYY-MM-DDTHH:mm:ss+HH:mm"
686 if (text.startsWith(QLatin1String("D:")))
687 text = text.mid(2);
688 text.insert(4, QLatin1Char('-'));
689 text.insert(7, QLatin1Char('-'));
690 text.insert(10, QLatin1Char('T'));
691 text.insert(13, QLatin1Char(':'));
692 text.insert(16, QLatin1Char(':'));
693 text.replace(QLatin1Char('\''), QLatin1Char(':'));
694 if (text.endsWith(QLatin1Char(':')))
695 text.chop(1);
696
697 return QDateTime::fromString(text, Qt::ISODate);
698 }
699
700 return QVariant();
701}
702
724{
725 return d->lastError;
726}
727
732{
733 if (!d->doc)
734 return;
735
737
738 d->clear();
739
740 if (!d->password.isEmpty()) {
741 d->password.clear();
743 }
744
746}
747
755{
756 return d->pageCount;
757}
758
763{
765 if (!d->doc || !d->checkPageComplete(page))
766 return result;
767
768 const QPdfMutexLocker lock;
769
770 FPDF_GetPageSizeByIndex(d->doc, page, &result.rwidth(), &result.rheight());
771 return result;
772}
773
793{
794 if (!d->pageModel)
795 d->pageModel = new QPdfPageModel(this);
796 return d->pageModel;
797}
798
816{
817 const unsigned long len = FPDF_GetPageLabel(d->doc, page, nullptr, 0);
818 if (len == 0)
819 return QString::number(page + 1);
822 FPDF_GetPageLabel(d->doc, page, buf.data(), len);
823 lock.unlock();
824 return QString::fromUtf16(buf.constData());
825}
826
833{
834 const auto trimmed = label.toString().trimmed();
835 for (int i = 0; i < d->pageCount; ++i) {
836 if (pageLabel(i) == trimmed)
837 return i;
838 }
839 return -1;
840}
841
853{
854 if (!d->doc || !d->checkPageComplete(page))
855 return QImage();
856
857 const QPdfMutexLocker lock;
858
860 if (Q_UNLIKELY(qLcDoc().isDebugEnabled()))
861 timer.start();
862 FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page);
863 if (!pdfPage)
864 return QImage();
865
868 FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(result.width(), result.height(), FPDFBitmap_BGRA, result.bits(), result.bytesPerLine());
869
870 const QPdfDocumentRenderOptions::RenderFlags renderFlags = renderOptions.renderFlags();
871 int flags = 0;
873 flags |= FPDF_ANNOT;
875 flags |= FPDF_LCD_TEXT;
877 flags |= FPDF_GRAYSCALE;
879 flags |= FPDF_RENDER_FORCEHALFTONE;
881 flags |= FPDF_RENDER_NO_SMOOTHTEXT;
883 flags |= FPDF_RENDER_NO_SMOOTHIMAGE;
885 flags |= FPDF_RENDER_NO_SMOOTHPATH;
886
887 if (renderOptions.scaledClipRect().isValid()) {
888 const QRect &clipRect = renderOptions.scaledClipRect();
889
890 // TODO take rotation into account, like cpdf_page.cpp lines 145-178
891 float x0 = clipRect.left();
892 float y0 = clipRect.top();
893 float x1 = clipRect.left();
894 float y1 = clipRect.bottom();
895 float x2 = clipRect.right();
896 float y2 = clipRect.top();
897 QSizeF origSize = pagePointSize(page);
898 QVector2D pageScale(1, 1);
899 if (!renderOptions.scaledSize().isNull()) {
900 pageScale = QVector2D(renderOptions.scaledSize().width() / float(origSize.width()),
901 renderOptions.scaledSize().height() / float(origSize.height()));
902 }
903 FS_MATRIX matrix {(x2 - x0) / result.width() * pageScale.x(),
904 (y2 - y0) / result.width() * pageScale.x(),
905 (x1 - x0) / result.height() * pageScale.y(),
906 (y1 - y0) / result.height() * pageScale.y(), -x0, -y0};
907
908 FS_RECTF clipRectF { 0, 0, float(imageSize.width()), float(imageSize.height()) };
909
910 FPDF_RenderPageBitmapWithMatrix(bitmap, pdfPage, &matrix, &clipRectF, flags);
911 qCDebug(qLcDoc) << "matrix" << matrix.a << matrix.b << matrix.c << matrix.d << matrix.e << matrix.f;
912 qCDebug(qLcDoc) << "page" << page << "region" << renderOptions.scaledClipRect()
913 << "size" << imageSize << "took" << timer.elapsed() << "ms";
914 } else {
915 const auto rotation = QPdfDocumentPrivate::toFPDFRotation(renderOptions.rotation());
916 FPDF_RenderPageBitmap(bitmap, pdfPage, 0, 0, result.width(), result.height(), rotation, flags);
917 qCDebug(qLcDoc) << "page" << page << "size" << imageSize << "took" << timer.elapsed() << "ms";
918 }
919
920 FPDFBitmap_Destroy(bitmap);
921
922 FPDF_ClosePage(pdfPage);
923 return result;
924}
925
931{
932 const QPdfMutexLocker lock;
933 FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page);
934 double pageHeight = FPDF_GetPageHeight(pdfPage);
935 FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
936 int startIndex = FPDFText_GetCharIndexAtPos(textPage, start.x(), pageHeight - start.y(),
938 int endIndex = FPDFText_GetCharIndexAtPos(textPage, end.x(), pageHeight - end.y(),
940
942
943 if (startIndex >= 0 && endIndex != startIndex) {
944 if (startIndex > endIndex)
945 qSwap(startIndex, endIndex);
946
947 // If the given end position is past the end of the line, i.e. if the right edge of the last character's
948 // bounding box is closer to it than the left edge is, then extend the char range by one
949 QRectF endCharBox = d->getCharBox(textPage, pageHeight, endIndex);
950 if (qAbs(endCharBox.right() - end.x()) < qAbs(endCharBox.x() - end.x()))
951 ++endIndex;
952
953 int count = endIndex - startIndex;
954 QString text = d->getText(textPage, startIndex, count);
955 QList<QPolygonF> bounds;
956 QRectF hull;
957 int rectCount = FPDFText_CountRects(textPage, startIndex, endIndex - startIndex);
958 for (int i = 0; i < rectCount; ++i) {
959 double l, r, b, t;
960 FPDFText_GetRect(textPage, i, &l, &t, &r, &b);
961 QRectF rect(l, pageHeight - t, r - l, t - b);
962 if (hull.isNull())
963 hull = rect;
964 else
965 hull = hull.united(rect);
966 bounds << QPolygonF(rect);
967 }
968 qCDebug(qLcDoc) << page << start << "->" << end << "found" << startIndex << "->" << endIndex << text;
969 result = QPdfSelection(text, bounds, hull, startIndex, endIndex);
970 } else {
971 qCDebug(qLcDoc) << page << start << "->" << end << "nothing found";
972 }
973
974 FPDFText_ClosePage(textPage);
975 FPDF_ClosePage(pdfPage);
976
977 return result;
978}
979
985{
986
987 if (page < 0 || startIndex < 0 || maxLength < 0)
988 return {};
989 const QPdfMutexLocker lock;
990 FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page);
991 double pageHeight = FPDF_GetPageHeight(pdfPage);
992 FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
993 int pageCount = FPDFText_CountChars(textPage);
994 if (startIndex >= pageCount)
995 return QPdfSelection();
996 QList<QPolygonF> bounds;
997 QRectF hull;
998 int rectCount = 0;
1000 if (maxLength > 0) {
1001 text = d->getText(textPage, startIndex, maxLength);
1002 rectCount = FPDFText_CountRects(textPage, startIndex, text.size());
1003 for (int i = 0; i < rectCount; ++i) {
1004 double l, r, b, t;
1005 FPDFText_GetRect(textPage, i, &l, &t, &r, &b);
1006 QRectF rect(l, pageHeight - t, r - l, t - b);
1007 if (hull.isNull())
1008 hull = rect;
1009 else
1010 hull = hull.united(rect);
1011 bounds << QPolygonF(rect);
1012 }
1013 }
1014 if (bounds.isEmpty())
1015 hull = QRectF(d->getCharPosition(textPage, pageHeight, startIndex), QSizeF());
1016 qCDebug(qLcDoc) << "on page" << page << "at index" << startIndex << "maxLength" << maxLength
1017 << "got" << text.size() << "chars," << rectCount << "rects within" << hull;
1018
1019 FPDFText_ClosePage(textPage);
1020 FPDF_ClosePage(pdfPage);
1021
1022 return QPdfSelection(text, bounds, hull, startIndex, startIndex + text.size());
1023}
1024
1029{
1030 const QPdfMutexLocker lock;
1031 FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page);
1032 double pageHeight = FPDF_GetPageHeight(pdfPage);
1033 FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
1034 int count = FPDFText_CountChars(textPage);
1035 if (count < 1)
1036 return QPdfSelection();
1037 QString text = d->getText(textPage, 0, count);
1038 QList<QPolygonF> bounds;
1039 QRectF hull;
1040 int rectCount = FPDFText_CountRects(textPage, 0, count);
1041 for (int i = 0; i < rectCount; ++i) {
1042 double l, r, b, t;
1043 FPDFText_GetRect(textPage, i, &l, &t, &r, &b);
1044 QRectF rect(l, pageHeight - t, r - l, t - b);
1045 if (hull.isNull())
1046 hull = rect;
1047 else
1048 hull = hull.united(rect);
1049 bounds << QPolygonF(rect);
1050 }
1051 qCDebug(qLcDoc) << "on page" << page << "got" << count << "chars," << rectCount << "rects within" << hull;
1052
1053 FPDFText_ClosePage(textPage);
1054 FPDF_ClosePage(pdfPage);
1055
1056 return QPdfSelection(text, bounds, hull, 0, count);
1057}
1058
1060
1061#include "qpdfdocument.moc"
1062#include "moc_qpdfdocument.cpp"
IOBluetoothDevice * device
void endResetModel()
Completes a model reset operation.
virtual QHash< int, QByteArray > roleNames() const
void beginResetModel()
Begins a model reset operation.
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:311
\inmodule QtCore
bool open(OpenMode openMode) override
\reimp
Definition qbuffer.cpp:296
void setData(const QByteArray &data)
Sets the contents of the internal buffer to be data.
Definition qbuffer.cpp:259
void close() override
\reimp
Definition qbuffer.cpp:316
qint64 size() const override
\reimp
Definition qbuffer.cpp:332
bool seek(qint64 off) override
\reimp
Definition qbuffer.cpp:341
QByteArrayView trimmed() const noexcept
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
void clear()
Clears the contents of the byte array and makes it null.
\inmodule QtCore
\inmodule QtCore
Definition qfile.h:93
\inmodule QtCore
Definition qhash.h:818
\inmodule QtCore \reentrant
Definition qiodevice.h:34
virtual qint64 size() const
For open random-access devices, this function returns the size of the device.
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 qint64 bytesAvailable() const
Returns the number of bytes that are available for reading.
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
\inmodule QtGui
Definition qimage.h:37
@ Format_ARGB32
Definition qimage.h:47
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
\inmodule QtCore
const char * valueToKey(int value) const
Returns the string that is used as the name of the given enumeration value, or \nullptr if value is n...
\inmodule QtCore
The QNetworkReply class contains the data and headers for a request sent with QNetworkAccessManager.
bool isFinished() const
NetworkError error() const
Returns the error that was found during the processing of this request.
QVariant header(QNetworkRequest::KnownHeaders header) const
Returns the value of the known header header, if that header was sent by the remote server.
void finished()
This signal is emitted when the reply has finished processing.
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3099
TextPosition hitTest(int page, QPointF position)
void _q_copyFromSequentialSourceDevice()
void _q_tryLoadingWithSizeFromContentHeader()
QPointer< QIODevice > device
void load(QIODevice *device, bool ownDevice)
static void fpdf_AddSegment(struct _FX_DOWNLOADHINTS *pThis, size_t offset, size_t size)
static FPDF_BOOL fpdf_IsDataAvail(struct _FX_FILEAVAIL *pThis, size_t offset, size_t size)
QPdfDocument::Status status
bool checkPageComplete(int page)
void initiateAsyncLoadWithTotalSizeKnown(quint64 totalSize)
QScopedPointer< QIODevice > ownDevice
static constexpr QFPDFRotation toFPDFRotation(QPdfDocumentRenderOptions::Rotation rotation)
QPointer< QIODevice > sequentialSourceDevice
QString getText(FPDF_TEXTPAGE textPage, int startIndex, int count)
QRectF getCharBox(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex)
static int fpdf_GetBlock(void *param, unsigned long position, unsigned char *pBuf, unsigned long size)
QPdfPageModel * pageModel
QPdfDocument::Error lastError
QPointF getCharPosition(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex)
void setStatus(QPdfDocument::Status status)
constexpr RenderFlags renderFlags() const noexcept
constexpr Rotation rotation() const noexcept
constexpr QSize scaledSize() const noexcept
constexpr QRect scaledClipRect() const noexcept
The QPdfDocument class loads a PDF document and renders pages from it.
Error error() const
Returns the type of error if \l status is Error, or NoError if there is no error.
QString password
This property holds the document password.
QImage render(int page, QSize imageSize, QPdfDocumentRenderOptions options=QPdfDocumentRenderOptions())
Renders the page into a QImage of size imageSize according to the provided renderOptions.
Status status
This property holds the current status of the document.
void close()
Closes the document.
MetaDataField
This enum describes the available fields of meta data.
QAbstractListModel * pageModel
This property holds an instance of QAbstractListModel to provide page-specific metadata,...
void passwordChanged()
Q_INVOKABLE QPdfSelection getAllText(int page)
Returns all the text and its bounds on the given page.
Status
This enum describes the current status of the document.
~QPdfDocument() override
Destroys the document.
Q_INVOKABLE QPdfSelection getSelectionAtIndex(int page, int startIndex, int maxLength)
Returns information about the text on the given page that can be found beginning at the given startIn...
PageModelRole
Roles in pageModel().
int pageCount
This property holds the number of pages in the loaded document or 0 if no document is loaded.
Error
This enum describes the error while attempting the last operation on the document.
Q_INVOKABLE QPdfSelection getSelection(int page, QPointF start, QPointF end)
Returns information about the text on the given page that can be found between the given start and en...
QVariant metaData(MetaDataField field) const
Returns the meta data of the document for the given field.
Q_INVOKABLE int pageIndexForLabel(QAnyStringView label)
Returns the index of the page that has the label, or -1 if not found.
Q_INVOKABLE QString pageLabel(int page)
Returns the page number to be used for display purposes.
void setPassword(const QString &password)
Error load(const QString &fileName)
Loads the document contents from fileName.
void statusChanged(QPdfDocument::Status status)
friend class QPdfPageModel
Q_INVOKABLE QSizeF pagePointSize(int page) const
Returns the size of page page in points (1/72 of an inch).
int rowCount(const QModelIndex &=QModelIndex()) const override
Returns the number of rows under the given parent.
QPdfPageModel(QPdfDocument *doc)
QHash< int, QByteArray > roleNames() const override
QVariant data(const QModelIndex &index, int role) const override
Returns the data stored under the given role for the item referred to by the index.
The QPdfSelection class defines a range of text that has been selected on one page in a PDF document,...
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
static QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags=Qt::AutoColor)
Converts the given image to a pixmap using the specified flags to control the conversion.
Definition qpixmap.cpp:1445
\inmodule QtCore\reentrant
Definition qpoint.h:214
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:333
constexpr void setX(qreal x) noexcept
Sets the x coordinate of this point to the given finite x coordinate.
Definition qpoint.h:343
bool isNull() const noexcept
Returns true if both the x and y coordinates are set to 0.0 (ignoring the sign); otherwise returns fa...
Definition qpoint.h:328
T * data() const
Definition qpointer.h:56
The QPolygonF class provides a list of points using floating point precision.
Definition qpolygon.h:96
\inmodule QtCore\reentrant
Definition qrect.h:483
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:718
constexpr qreal x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:655
constexpr bool isNull() const noexcept
Returns true if the rectangle is a null rectangle, otherwise returns false.
Definition qrect.h:644
QRectF united(const QRectF &other) const noexcept
Definition qrect.h:838
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 bool isValid() const noexcept
Returns true if the rectangle is valid, otherwise returns false.
Definition qrect.h:169
constexpr int bottom() const noexcept
Returns the y-coordinate of the rectangle's bottom edge.
Definition qrect.h:181
constexpr int top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:175
constexpr int left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:172
constexpr int right() const noexcept
Returns the x-coordinate of the rectangle's right edge.
Definition qrect.h:178
\inmodule QtCore
Definition qmutex.h:313
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
const_iterator constEnd() const noexcept
Definition qset.h:143
const_iterator constFind(const T &value) const
Definition qset.h:161
\inmodule QtCore
Definition qsize.h:207
constexpr qreal width() const noexcept
Returns the width.
Definition qsize.h:321
constexpr qreal height() const noexcept
Returns the height.
Definition qsize.h:324
\inmodule QtCore
Definition qsize.h:25
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 isNull() const noexcept
Returns true if both the width and height is 0; otherwise returns false.
Definition qsize.h:120
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
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
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6180
static QString fromUtf16(const char16_t *, qsizetype size=-1)
Definition qstring.cpp:5883
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 mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
QString & insert(qsizetype i, QChar c)
Definition qstring.cpp:3110
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
QByteArray toUtf8() const &
Definition qstring.h:563
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:208
\inmodule QtCore
Definition qvariant.h:64
bool isValid() const
Returns true if the storage type of this variant is not QMetaType::UnknownType; otherwise returns fal...
Definition qvariant.h:707
qulonglong toULongLong(bool *ok=nullptr) const
Returns the variant as an unsigned long long int if the variant has type() \l QMetaType::ULongLong,...
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:502
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:501
QString text
qSwap(pi, e)
QSet< QString >::iterator it
rect
[4]
Combined button and popup list for selecting options.
constexpr char toAsciiLower(char ch) noexcept
Definition qtools_p.h:87
@ KeepAspectRatio
@ transparent
Definition qnamespace.h:46
@ UserRole
@ DecorationRole
@ DisplayRole
@ ISODate
emscripten::val document()
Definition qwasmdom.h:20
Definition image.cpp:4
#define Q_UNLIKELY(x)
DBusConnection const char DBusError * error
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition qlogging.h:162
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLuint GLfloat GLfloat GLfloat x1
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLuint GLsizei const GLchar * label
[43]
GLuint GLfloat x0
GLenum GLuint GLenum GLsizei const GLchar * buf
GLbitfield flags
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint start
GLenum const GLint * param
GLenum GLuint GLintptr offset
GLuint name
GLuint GLfloat GLfloat y0
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei imageSize
GLint y
GLfixed GLfixed GLfixed y2
GLsizei maxLength
GLsizei GLfixed GLfixed GLfixed GLfixed const GLubyte * bitmap
GLenum GLsizei len
GLuint GLenum matrix
GLfixed GLfixed x2
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
static QT_BEGIN_NAMESPACE int libraryRefCount
static const double CharacterHitTolerance
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_OBJECT
#define emit
#define Q_PDF_EXPORT
Definition qtpdfglobal.h:16
#define Q_UNUSED(x)
unsigned long long quint64
Definition qtypes.h:56
long long qint64
Definition qtypes.h:55
obj metaObject() -> className()
QObject::connect nullptr
QByteArray page
[45]
QTimer * timer
[3]
QReadWriteLock lock
[0]
QNetworkReply * reply
\inmodule QtCore \reentrant
Definition qchar.h:17
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent