summaryrefslogtreecommitdiffstats
path: root/src/pdf/qpdfdocument.cpp
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2020-02-21 11:38:27 +0100
committerShawn Rutledge <shawn.rutledge@qt.io>2020-02-21 11:40:49 +0100
commitd9349a299f66fb154ad24f410451872a7ca253fb (patch)
tree2e8258ef3679707a2a9245c85bc8490251b3e256 /src/pdf/qpdfdocument.cpp
parent50bc8b124705c33c5e27f035b1eab756e14247ba (diff)
parentc0aa9d794378846e4cc0b6fe94f2765bc31cefdd (diff)
Merge remote-tracking branch 'origin/wip/qtpdf' into 5.15v5.15.0-beta1
The feature set is mostly in place (except for some known shortcomings) and we need the merge to build it on iOS. Task-number: QTBUG-69519 Change-Id: Ib1ac82a9a7e0830d98d1c4327a1b15d4d7f4d4c1
Diffstat (limited to 'src/pdf/qpdfdocument.cpp')
-rw-r--r--src/pdf/qpdfdocument.cpp138
1 files changed, 133 insertions, 5 deletions
diff --git a/src/pdf/qpdfdocument.cpp b/src/pdf/qpdfdocument.cpp
index 690953691..1e8a0f527 100644
--- a/src/pdf/qpdfdocument.cpp
+++ b/src/pdf/qpdfdocument.cpp
@@ -38,6 +38,7 @@
#include "qpdfdocument_p.h"
#include "third_party/pdfium/public/fpdf_doc.h"
+#include "third_party/pdfium/public/fpdf_text.h"
#include <QDateTime>
#include <QDebug>
@@ -46,12 +47,14 @@
#include <QHash>
#include <QLoggingCategory>
#include <QMutex>
+#include <QVector2D>
QT_BEGIN_NAMESPACE
// The library is not thread-safe at all, it has a lot of global variables.
Q_GLOBAL_STATIC_WITH_ARGS(QMutex, pdfMutex, (QMutex::Recursive));
static int libraryRefCount;
+static const double CharacterHitTolerance = 6.0;
Q_LOGGING_CATEGORY(qLcDoc, "qt.pdf.document")
QPdfMutexLocker::QPdfMutexLocker()
@@ -186,7 +189,36 @@ void QPdfDocumentPrivate::load(QIODevice *newDevice, bool transferDeviceOwnershi
} else {
device = newDevice;
initiateAsyncLoadWithTotalSizeKnown(device->size());
- checkComplete();
+ if (!avail) {
+ setStatus(QPdfDocument::Error);
+ return;
+ }
+
+ if (!doc)
+ tryLoadDocument();
+
+ if (!doc) {
+ updateLastError();
+ setStatus(QPdfDocument::Error);
+ return;
+ }
+
+ QPdfMutexLocker lock;
+ const int newPageCount = FPDF_GetPageCount(doc);
+ lock.unlock();
+ if (newPageCount != pageCount) {
+ pageCount = newPageCount;
+ emit q->pageCountChanged(pageCount);
+ }
+
+ // If it's a local file, and the first couple of pages are available,
+ // probably the whole document is available.
+ if (checkPageComplete(0) && (pageCount < 2 || checkPageComplete(1))) {
+ setStatus(QPdfDocument::Ready);
+ } else {
+ updateLastError();
+ setStatus(QPdfDocument::Error);
+ }
}
}
@@ -311,6 +343,26 @@ void QPdfDocumentPrivate::checkComplete()
}
}
+bool QPdfDocumentPrivate::checkPageComplete(int page)
+{
+ if (page < 0 || page >= pageCount)
+ return false;
+
+ if (loadComplete)
+ return true;
+
+ QPdfMutexLocker lock;
+ int result = PDF_DATA_NOTAVAIL;
+ while (result == PDF_DATA_NOTAVAIL)
+ result = FPDFAvail_IsPageAvail(avail, page, this);
+ lock.unlock();
+
+ if (result == PDF_DATA_ERROR)
+ updateLastError();
+
+ return (result != PDF_DATA_ERROR);
+}
+
void QPdfDocumentPrivate::setStatus(QPdfDocument::Status documentStatus)
{
if (status == documentStatus)
@@ -368,6 +420,8 @@ QPdfDocument::~QPdfDocument()
QPdfDocument::DocumentError QPdfDocument::load(const QString &fileName)
{
+ qCDebug(qLcDoc) << "loading" << fileName;
+
close();
d->setStatus(QPdfDocument::Loading);
@@ -560,7 +614,7 @@ int QPdfDocument::pageCount() const
QSizeF QPdfDocument::pageSize(int page) const
{
QSizeF result;
- if (!d->doc)
+ if (!d->doc || !d->checkPageComplete(page))
return result;
const QPdfMutexLocker lock;
@@ -581,7 +635,7 @@ QSizeF QPdfDocument::pageSize(int page) const
*/
QImage QPdfDocument::render(int page, QSize imageSize, QPdfDocumentRenderOptions renderOptions)
{
- if (!d->doc)
+ if (!d->doc || !d->checkPageComplete(page))
return QImage();
const QPdfMutexLocker lock;
@@ -630,15 +684,89 @@ QImage QPdfDocument::render(int page, QSize imageSize, QPdfDocumentRenderOptions
if (renderFlags & QPdf::RenderPathAliased)
flags |= FPDF_RENDER_NO_SMOOTHPATH;
- FPDF_RenderPageBitmap(bitmap, pdfPage, 0, 0, result.width(), result.height(), rotation, flags);
+ if (renderOptions.scaledClipRect().isValid()) {
+ const QRect &clipRect = renderOptions.scaledClipRect();
+
+ // TODO take rotation into account, like cpdf_page.cpp lines 145-178
+ float x0 = clipRect.left();
+ float y0 = clipRect.top();
+ float x1 = clipRect.left();
+ float y1 = clipRect.bottom();
+ float x2 = clipRect.right();
+ float y2 = clipRect.top();
+ QSizeF origSize = pageSize(page);
+ QVector2D pageScale(1, 1);
+ if (!renderOptions.scaledSize().isNull()) {
+ pageScale = QVector2D(renderOptions.scaledSize().width() / float(origSize.width()),
+ renderOptions.scaledSize().height() / float(origSize.height()));
+ }
+ FS_MATRIX matrix {(x2 - x0) / result.width() * pageScale.x(),
+ (y2 - y0) / result.width() * pageScale.x(),
+ (x1 - x0) / result.height() * pageScale.y(),
+ (y1 - y0) / result.height() * pageScale.y(), -x0, -y0};
+
+ FS_RECTF clipRectF { 0, 0, float(imageSize.width()), float(imageSize.height()) };
+
+ FPDF_RenderPageBitmapWithMatrix(bitmap, pdfPage, &matrix, &clipRectF, flags);
+ qCDebug(qLcDoc) << "matrix" << matrix.a << matrix.b << matrix.c << matrix.d << matrix.e << matrix.f;
+ qCDebug(qLcDoc) << "page" << page << "region" << renderOptions.scaledClipRect()
+ << "size" << imageSize << "took" << timer.elapsed() << "ms";
+ } else {
+ FPDF_RenderPageBitmap(bitmap, pdfPage, 0, 0, result.width(), result.height(), rotation, flags);
+ qCDebug(qLcDoc) << "page" << page << "size" << imageSize << "took" << timer.elapsed() << "ms";
+ }
FPDFBitmap_Destroy(bitmap);
FPDF_ClosePage(pdfPage);
- qCDebug(qLcDoc) << "page" << page << imageSize << "took" << timer.elapsed() << "ms";
return result;
}
+/*!
+ Returns information about the text on the given \a page that can be found
+ between the given \a start and \a end points, if any.
+*/
+QPdfSelection QPdfDocument::getSelection(int page, QPointF start, QPointF end)
+{
+ const QPdfMutexLocker lock;
+ FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page);
+ double pageHeight = FPDF_GetPageHeight(pdfPage);
+ FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
+ int startIndex = FPDFText_GetCharIndexAtPos(textPage, start.x(), pageHeight - start.y(),
+ CharacterHitTolerance, CharacterHitTolerance);
+ int endIndex = FPDFText_GetCharIndexAtPos(textPage, end.x(), pageHeight - end.y(),
+ CharacterHitTolerance, CharacterHitTolerance);
+ if (startIndex >= 0 && endIndex != startIndex) {
+ QString text;
+ if (startIndex > endIndex)
+ qSwap(startIndex, endIndex);
+ int count = endIndex - startIndex + 1;
+ QVector<ushort> buf(count + 1);
+ // TODO is that enough space in case one unicode character is more than one in utf-16?
+ int len = FPDFText_GetText(textPage, startIndex, count, buf.data());
+ Q_ASSERT(len - 1 <= count); // len is number of characters written, including the terminator
+ text = QString::fromUtf16(buf.constData(), len - 1);
+ QVector<QPolygonF> bounds;
+ int rectCount = FPDFText_CountRects(textPage, startIndex, endIndex - startIndex);
+ for (int i = 0; i < rectCount; ++i) {
+ double l, r, b, t;
+ FPDFText_GetRect(textPage, i, &l, &t, &r, &b);
+ QPolygonF poly;
+ poly << QPointF(l, pageHeight - t);
+ poly << QPointF(r, pageHeight - t);
+ poly << QPointF(r, pageHeight - b);
+ poly << QPointF(l, pageHeight - b);
+ poly << QPointF(l, pageHeight - t);
+ bounds << poly;
+ }
+ qCDebug(qLcDoc) << page << start << "->" << end << "found" << startIndex << "->" << endIndex << text;
+ return QPdfSelection(text, bounds);
+ }
+
+ qCDebug(qLcDoc) << page << start << "->" << end << "nothing found";
+ return QPdfSelection();
+}
+
QT_END_NAMESPACE
#include "moc_qpdfdocument.cpp"