diff options
author | ZackaryWelch <welch.zackary@gmail.com> | 2018-03-17 17:31:57 -0400 |
---|---|---|
committer | ZackaryWelch <welch.zackary@gmail.com> | 2018-03-17 17:31:57 -0400 |
commit | 59eba81944d99f7cc1c699609f6ebe36af7db5e8 (patch) | |
tree | 3a69819e29021be83838fc6878535185d341f56f | |
parent | Cleanup the printing stuff for Qt 5.9+. (diff) | |
download | lumina-59eba81944d99f7cc1c699609f6ebe36af7db5e8.tar.gz lumina-59eba81944d99f7cc1c699609f6ebe36af7db5e8.tar.bz2 lumina-59eba81944d99f7cc1c699609f6ebe36af7db5e8.zip |
Overhaul of MuPDF rendering. Fixed bugs and improved the find highlight system. Moved the cache system to the backend. Reenabled the properties and find edit menu by default.
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp | 69 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/PrintWidget.h | 7 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp | 270 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp | 75 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/Renderer.h | 23 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/mainUI.cpp | 105 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/mainUI.h | 5 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/propDialog.cpp | 2 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/textData.h | 6 |
9 files changed, 332 insertions, 230 deletions
diff --git a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp index 5c7fa1dd..4ccae28c 100644 --- a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp +++ b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp @@ -1,7 +1,8 @@ #include "PrintWidget.h" -PrintWidget::PrintWidget(QWidget *parent) : QGraphicsView(parent), scene(0), curPage(1), - viewMode(SinglePageView), zoomMode(FitInView), zoomFactor(1), initialized(false), fitting(true) { +PrintWidget::PrintWidget(Renderer *backend, QWidget *parent) : + QGraphicsView(parent), scene(0), curPage(1), viewMode(SinglePageView), + zoomMode(FitInView), zoomFactor(1), initialized(false), fitting(true), BACKEND(backend) { this->setMouseTracking(true); QList<QWidget*> children = this->findChildren<QWidget*>("",Qt::FindChildrenRecursively); @@ -129,13 +130,24 @@ void PrintWidget::setCurrentPage(int pageNumber) { void PrintWidget::highlightText(TextData *text) { //Creates a rectangle around the text if the text has not already been highlighted if(!text->highlighted()) { - //qDebug() << "Highlighting text: " << text->text() << "At page: " << text->page(); - QRect rect = text->loc(); - double pageHeight = pages.at(0)->boundingRect().height(); - QRectF textRect = rect.adjusted(0, pageHeight*(text->page()-1), 0, 0); //move the rectangle onto the right page - QBrush highlightFill(QColor(255, 255, 177, 50)); - QPen highlightOutline(QColor(255, 255, 100, 98)); - scene->addRect(textRect, highlightOutline, highlightFill); + double pageHeight = pages.at(text->page()-1)->boundingRect().height(); + QRectF rect = text->loc(); + if(degrees != 0) { + QSize center = BACKEND->imageHash(text->page()-1).size()/2; + //Rotates the rectangle by the page's center + double cx = center.width(), cy = center.height(); + rect.adjust(-cx, -cy, -cx, -cy); + rect = rotMatrix.mapRect(rect); + if(degrees == 180) + rect.adjust(cx, cy, cx, cy); + else + rect.adjust(cy, cx, cy, cx); + } + //Moves the rectangle onto the right page + rect.moveTop(rect.y() + pageHeight*(text->page()-1)); + QBrush highlightFill(QColor(255, 255, 177, 100)); + QPen highlightOutline(QColor(255, 255, 100, 125)); + scene->addRect(rect, highlightOutline, highlightFill); text->highlighted(true); } } @@ -143,6 +155,7 @@ void PrintWidget::highlightText(TextData *text) { //Private functions void PrintWidget::generatePreview() { + qDebug() << "Generating Preview"; populateScene(); // i.e. setPreviewPrintedPictures() e.l. layoutPages(); curPage = qBound(1, curPage, pages.count()); @@ -158,8 +171,9 @@ void PrintWidget::layoutPages() { int numPagePlaces = numPages; int cols = 1; // singleMode and default + QSize pageSize = BACKEND->imageHash(0).size(); if (viewMode == AllPagesView) { - cols = ((pictures->value(0)).width() > (pictures->value(0)).height()) ? qFloor(qSqrt(numPages)) : qCeil(qSqrt(numPages)); + cols = pageSize.width() > pageSize.height() ? qFloor(qSqrt(numPages)) : qCeil(qSqrt(numPages)); cols += cols % 2; // Nicer with an even number of cols } else if (viewMode == FacingPagesView) { cols = 2; @@ -180,6 +194,7 @@ void PrintWidget::layoutPages() { } } scene->setSceneRect(scene->itemsBoundingRect()); + qDebug() << "Finished Page Layout"; } void PrintWidget::populateScene() @@ -189,14 +204,15 @@ void PrintWidget::populateScene() } qDeleteAll(pages); pages.clear(); - //qDebug() << "populateScene"; - if(pictures==0){ return; } //nothing to show yet - int numPages = pictures->count(); + qDebug() << "populateScene"; + int numPages = BACKEND->numPages(); + if(BACKEND->hashSize() < numPages){ return; } //nothing to show yet for (int i = 0; i < numPages; i++) { - QImage pagePicture = pictures->value(i); + QImage pagePicture = BACKEND->imageHash(i); + //qDebug() << "Loading Image:" << i; - QSize paperSize = pictures->value(i).size(); + QSize paperSize = BACKEND->imageHash(i).size(); //Changes the paper orientation if rotated by 90 or 270 degrees if(degrees == 90 or degrees == 270) @@ -302,27 +318,10 @@ void PrintWidget::fit(bool doFitting) { //zoomFactor = this->transform().m11() * (float(printer->logicalDpiY()) / this->logicalDpiY()); } -void PrintWidget::setPictures(QHash<int, QImage> *hash) { - pictures = hash; - this->setVisible(hash!=0); -} - -//Sets how much to rotate the image, by either 90, 180, or 270 degrees. Adds 90 degrees for cw and -90 for ccw. +//Makes sure degrees is between 0 and 360 then rotates the matrix and void PrintWidget::setDegrees(int degrees) { //Mods by 360, but adds and remods because of how C++ treats negative mods this->degrees = ( ( ( this->degrees + degrees ) % 360 ) + 360 ) % 360; - switch(this->degrees) { - case 270: - rotMatrix = QMatrix(0, -1, 1, 0, 0, 0); - break; - case 90: - rotMatrix = QMatrix(0, 1, -1, 0, 0, 0); - break; - case 180: - rotMatrix = QMatrix(-1, 0, 0, -1, 0, 0); - break; - default: - rotMatrix = QMatrix(1, 0, 0, 1, 0 ,0); - } - this->updatePreview(); + rotMatrix.rotate(degrees); + this->updatePreview(); } diff --git a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h index 38227e78..b66428e8 100644 --- a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h +++ b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h @@ -19,6 +19,7 @@ #include <QStyleOptionGraphicsItem> #include <QtMath> #include <QPageLayout> +#include "Renderer.h" #include "textData.h" @@ -47,7 +48,6 @@ public: painter->setRenderHint(QPainter::SmoothPixmapTransform); QRectF paperRect(0,0, paperSize.width(), paperSize.height()); - // Draw shadow painter->setClipRect(option->exposedRect); qreal shWidth = paperRect.width()/100; @@ -114,17 +114,16 @@ private: double zoomFactor; bool initialized, fitting; QList<QGraphicsItem*> pages; - QHash<int, QImage> *pictures; int degrees; + Renderer *BACKEND; public: - PrintWidget(QWidget *parent = 0); + PrintWidget(Renderer *backend, QWidget *parent = 0); ~PrintWidget(); double getZoomFactor() const { return this->zoomFactor; }; ZoomMode getZoomMode() const { return this->zoomMode; }; int currentPage() const { return publicPageNum; }; - void setPictures(QHash<int, QImage>*); signals: void resized(); diff --git a/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp b/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp index 799f6584..a9c79971 100644 --- a/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp +++ b/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp @@ -1,10 +1,40 @@ #include "Renderer.h" #include <QDateTime> #include <mupdf/fitz.h> -//#include <pthread.h> +#include <QMutex> +#include <QFuture> +#include <QtConcurrent> + +class Data { + public: + Data(int _pagenum, fz_context *_ctx, fz_display_list *_list, fz_rect _bbox, fz_pixmap *_pix, fz_matrix _ctm, double _sf) : + pagenumber(_pagenum), + ctx(_ctx), + list(_list), + bbox(_bbox), + pix(_pix), + ctm(_ctm), + sf(_sf) + { } + + ~Data() { } + + int pagenumber; + fz_context *ctx; + fz_display_list *list; + fz_rect bbox; + fz_pixmap *pix; + fz_matrix ctm; + QImage img; + QFuture<void> renderThread; + double sf; +}; fz_document *DOC; fz_context *CTX; +QHash<int, Data*> dataHash; +QMutex mutex[FZ_LOCK_MAX]; +fz_locks_context locks; inline QString getTextInfo(QString str) { char infoBuff[1000]; @@ -13,140 +43,176 @@ inline QString getTextInfo(QString str) { return ""; } -/*void lock_mutex(void *user, int lock) { - pthread_mutex_t *mutex = (pthread_mutex_t *) user; - pthread_mutex_lock(&mutex[lock]); +void lock_mutex(void *user, int lock) { + QMutex *mutex = (QMutex*) user; + mutex[lock].lock(); } void unlock_mutex(void *user, int lock) { - pthread_mutex_t *mutex = (pthread_mutex_t *) user; - pthread_mutex_unlock(&mutex[lock]); -}*/ + QMutex *mutex = (QMutex*) user; + mutex[lock].unlock(); +} Renderer::Renderer(){ - /*pthread_mutex_t mutex[FZ_LOCK_MAX]; - fz_locks_context locks; locks.user = mutex; locks.lock = lock_mutex; locks.unlock = unlock_mutex; - for (int i = 0; i < FZ_LOCK_MAX; i++) - pthread_mutex_init(&mutex[i], NULL);*/ - - mutex = new QMutex(); DOC = 0; - //CTX = fz_new_context(NULL, &locks, FZ_STORE_UNLIMITED); - CTX = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); - qDebug() << "Context created"; + qDebug() << "Creating Context"; + CTX = fz_new_context(NULL, &locks, FZ_STORE_UNLIMITED); needpass = false; } Renderer::~Renderer(){ - + qDebug() << "Calling destructor"; + qDeleteAll(dataHash); + dataHash.clear(); + fz_drop_document(CTX, DOC); + DOC = NULL; + fz_drop_context(CTX); + CTX = NULL; } -//bool Renderer::loadMultiThread(){ return true; } - -QJsonObject Renderer::properties(){ - QJsonObject jobj; - jobj.insert("title", getTextInfo("Title") ); - jobj.insert("subject", getTextInfo("Subject") ); - jobj.insert("author", getTextInfo("Author") ); - jobj.insert("creator", getTextInfo("Creator") ); - jobj.insert("producer", getTextInfo("Producer") ); - jobj.insert("keywords", getTextInfo("Keywords") ); - jobj.insert("dt_created", QDateTime::fromString( getTextInfo("CreationDate").left(16), "'D:'yyyyMMddHHmmss").toString() ); - jobj.insert("dt_modified", QDateTime::fromString( getTextInfo("ModDate").left(16), "'D:'yyyyMMddHHmmss").toString() ); - return jobj; +bool Renderer::loadMultiThread(){ return false; } + +void Renderer::cleanup() { + /*for(int i = 0; i < dataHash.size(); i++) { + fz_drop_pixmap(CTX, dataHash[i]->pix); + fz_drop_display_list(CTX, dataHash[i]->list); + } + fz_drop_document(CTX, DOC); + fz_drop_context(CTX);*/ } bool Renderer::loadDocument(QString path, QString password){ //first time through - if(DOC==0){ - fz_register_document_handlers(CTX); - qDebug() << "Document handlers registered"; - }else if(path!=docpath){ - //fz_close_document(DOC); - //Clear out the old document first - delete DOC; - DOC=0; - needpass = false; + if(path != docpath) { + if(DOC != 0) { + qDebug() << "New document"; + fz_drop_document(CTX, DOC); + DOC = NULL; + needpass = false; + docpath = path; + }else if(DOC==0){ + fz_register_document_handlers(CTX); + qDebug() << "Document handlers registered"; + } + + DOC = fz_open_document(CTX, path.toLocal8Bit().data()); docpath = path; - } - - if(DOC==0){ - DOC = fz_open_document(CTX, path.toLocal8Bit().data()); - docpath = path; - qDebug() << "File opened"; - if(DOC==0){ - qDebug() << "Could not open file:" << path; - return false; - } - needpass = (fz_needs_password(CTX, DOC) != 0); - } - - if(needpass && password.isEmpty()){ return false; } - else if(needpass){ - needpass = !fz_authenticate_password(CTX, DOC, password.toLocal8Bit()); - if(needpass){ return false; } //incorrect password - } - - qDebug() << "Password Check cleared"; - - //Could read/access the PDF - go ahead and load the info now - pnum = -1; - doctitle.clear(); - //qDebug() << "Opening File:" << path; - QString title = getTextInfo("Subject"); - if(!title.isNull()) - doctitle = title; - else - doctitle = path.section("/",-1); - pnum = fz_count_pages(CTX, DOC); - //Setup the Document - fz_page *PAGE = fz_load_page(CTX, DOC, 0); - qDebug() << "Page Loaded"; - //Possibly check Page orientation"; - - return (PAGE); + qDebug() << "File opened" << DOC; + if(DOC==0){ + qDebug() << "Could not open file:" << path; + return false; + } + needpass = (fz_needs_password(CTX, DOC) != 0); + + if(needpass && password.isEmpty()){ + return false; + }else if(needpass){ + needpass = !fz_authenticate_password(CTX, DOC, password.toLocal8Bit()); + if(needpass){ return false; } //incorrect password + } + + //qDebug() << "Password Check cleared"; + pnum = fz_count_pages(CTX, DOC); + qDebug() << "Page count: " << pnum; + + doctitle.clear(); + //qDebug() << "Opening File:" << path; + jobj.insert("title", getTextInfo("Title") ); + jobj.insert("subject", getTextInfo("Subject") ); + jobj.insert("author", getTextInfo("Author") ); + jobj.insert("creator", getTextInfo("Creator") ); + jobj.insert("producer", getTextInfo("Producer") ); + jobj.insert("keywords", getTextInfo("Keywords") ); + jobj.insert("dt_created", QDateTime::fromString( getTextInfo("CreationDate").left(16), "'D:'yyyyMMddHHmmss").toString() ); + jobj.insert("dt_modified", QDateTime::fromString( getTextInfo("ModDate").left(16), "'D:'yyyyMMddHHmmss").toString() ); + + if(!jobj["title"].isNull()) + doctitle = jobj["title"].toString(); + else + doctitle = path.section("/",-1); + + qDebug() << "Page Loaded"; + //Possibly check Page orientation + return true; + } + return false; } -//Consider rendering through a display list -QImage Renderer::renderPage(int pagenum, QSize DPI){ - QImage img; - fz_matrix matrix; - fz_pixmap *pixmap = nullptr; +void renderer(Data *data, Renderer *obj) +{ + int pagenum = data->pagenumber; + fz_context *ctx = data->ctx; + fz_display_list *list = data->list; + fz_rect bbox = data->bbox; + fz_pixmap *pixmap = data->pix; + fz_matrix ctm = data->ctm; + fz_device *dev; - fz_scale(&matrix, DPI.width()/96.0, DPI.height()/96.0); + ctx = fz_clone_context(ctx); + dev = fz_new_draw_device(ctx, &fz_identity, pixmap); + fz_run_display_list(ctx, list, dev, &ctm, &bbox, NULL); - //fz_context *ctx = fz_clone_context(CTX); - mutex->lock(); + data->img = QImage(pixmap->samples, pixmap->w, pixmap->h, pixmap->stride, QImage::Format_RGB888); + fz_close_device(ctx, dev); + fz_drop_device(ctx, dev); - fz_try(CTX) { - pixmap = fz_new_pixmap_from_page_number(CTX, DOC, pagenum, &matrix, fz_device_rgb(CTX), 0); - }fz_catch(CTX){ - qDebug() << "Error when rendering page using MuPDF"; - } + fz_drop_context(ctx); - mutex->unlock(); + dataHash.insert(pagenum, data); + emit obj->PageLoaded(pagenum); +} - img = QImage(pixmap->samples, pixmap->w, pixmap->h, pixmap->stride, QImage::Format_RGB888); - qDebug() << "Render Page:" << pagenum; - return img; +//Consider rendering through a display list +void Renderer::renderPage(int pagenum, QSize DPI){ + //qDebug() << "- Rendering Page:" << pagenum; + fz_matrix matrix; + fz_rect bbox; + fz_irect rbox; + fz_pixmap *pixmap; + fz_display_list *list; + + double pageDPI = 96.0; + double sf = DPI.width() / pageDPI; + fz_scale(&matrix, DPI.width()/pageDPI, DPI.height()/pageDPI); + + fz_page *PAGE = fz_load_page(CTX, DOC, pagenum); + fz_bound_page(CTX, PAGE, &bbox); + emit OrigSize(QSizeF(bbox.x1 - bbox.x0, bbox.y1 - bbox.y0)); + + fz_transform_rect(&bbox, &matrix); + list = fz_new_display_list(CTX, &bbox); + fz_device *dev = fz_new_list_device(CTX, list); + fz_run_page(CTX, PAGE, dev, &fz_identity, NULL); + + fz_close_device(CTX, dev); + fz_drop_device(CTX, dev); + fz_drop_page(CTX, PAGE); + + pixmap = fz_new_pixmap_with_bbox(CTX, fz_device_rgb(CTX), fz_round_rect(&rbox, &bbox), NULL, 0); + fz_clear_pixmap_with_value(CTX, pixmap, 0xff); + + //pixmap = fz_new_pixmap_from_page_number(CTX, DOC, pagenum, &matrix, fz_device_rgb(CTX), 0); + Data *data = new Data(pagenum, CTX, list, bbox, pixmap, matrix, sf); + data->renderThread = QtConcurrent::run(&renderer, data, this); } QList<TextData*> Renderer::searchDocument(QString text, bool matchCase){ fz_rect rectBuffer[1000]; QList<TextData*> results; for(int i = 0; i < pnum; i++) { - int count = fz_search_page_number(CTX, DOC, i, text.toLatin1().data(), rectBuffer, 1000); + int count = fz_search_display_list(CTX, dataHash[i]->list, text.toLatin1().data(), rectBuffer, 1000); //qDebug() << "Page " << i+1 << ": Count, " << count; for(int j = 0; j < count; j++) { - QRect rect(rectBuffer[j].x0, rectBuffer[j].y0, rectBuffer[j].x1-rectBuffer[j].x0, rectBuffer[j].y1 - rectBuffer[j].y0); + double sf = dataHash[i]->sf; + QRectF rect(rectBuffer[j].x0*sf, rectBuffer[j].y0*sf, (rectBuffer[j].x1-rectBuffer[j].x0)*sf, (rectBuffer[j].y1 - rectBuffer[j].y0)*sf); TextData *t = new TextData(rect, i+1, text); //MuPDF search does not match case, so retrieve the exact text at the location found and determine whether or not it matches the case of the search text if the user selected to match case if(matchCase){ - fz_stext_page *sPage = fz_new_stext_page_from_page_number(CTX, DOC, i, NULL); + fz_stext_page *sPage = fz_new_stext_page_from_display_list(CTX, dataHash[i]->list, NULL); QString currentStr = QString(fz_copy_selection(CTX, sPage, *fz_rect_min(&rectBuffer[j]), *fz_rect_max(&rectBuffer[j]), false)); if(currentStr.contains(text, Qt::CaseSensitive)){ results.append(t); } }else{ @@ -156,3 +222,15 @@ QList<TextData*> Renderer::searchDocument(QString text, bool matchCase){ } return results; } + +QImage Renderer::imageHash(int pagenum) { + return dataHash[pagenum]->img; +} + +int Renderer::hashSize() { + return dataHash.size(); +} + +void Renderer::clearHash() { + dataHash.clear(); +} diff --git a/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp b/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp index 693faa64..9ad29eba 100644 --- a/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp +++ b/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp @@ -1,7 +1,9 @@ #include "Renderer.h" #include <poppler/qt5/poppler-qt5.h> +#include <QThread> static Poppler::Document *DOC; +QHash<int, QImage> loadingHash; Renderer::Renderer(){ DOC = 0; @@ -10,14 +12,15 @@ Renderer::Renderer(){ } Renderer::~Renderer(){ - + //qDeleteAll(loadingHash); + loadingHash.clear(); } -//bool Renderer::loadMultiThread(){ return true; } +bool Renderer::loadMultiThread(){ return true; } -QJsonObject Renderer::properties(){ +/*QJsonObject Renderer::properties(){ return QJsonObject(); //TO-DO -} +}*/ bool Renderer::loadDocument(QString path, QString password){ //qDebug() << "Load Document:" << path; @@ -65,36 +68,50 @@ bool Renderer::loadDocument(QString path, QString password){ return false; //nothing to load } -QImage Renderer::renderPage(int pagenum, QSize DPI){ +void Renderer::cleanup() {} + +void Renderer::renderPage(int pagenum, QSize DPI){ //qDebug() << "Render Page:" << pagenum << DPI; - if(DOC==0){ return QImage(); } - Poppler::Page *PAGE = DOC->page(pagenum); - QImage img; - if(PAGE!=0){ - //qDebug() << "Render Page:" << pagenum; - img = PAGE->renderToImage(DPI.width(),DPI.height()); - delete PAGE; - } - //qDebug() << "Done Render Page:" << pagenum << img.size(); - return img; + if(DOC!=0){ + Poppler::Page *PAGE = DOC->page(pagenum); + QImage img; + if(PAGE!=0){ + //qDebug() << "Render Page:" << pagenum; + img = PAGE->renderToImage(DPI.width(),DPI.height()); + loadingHash.insert(pagenum, img); + //qDebug() << "Image after creation:" << img.isNull(); + delete PAGE; + } + //qDebug() << "Done Render Page:" << pagenum << img.size(); + }else{ + loadingHash.insert(pagenum, QImage()); + } + emit PageLoaded(pagenum); } QList<TextData*> Renderer::searchDocument(QString text, bool matchCase){ QList<TextData*> results; - /*for(int i = 0; i < pnum; i++) { - int count = fz_search_page_number(CTX, DOC, i, text.toLatin1().data(), rectBuffer, 1000); - //qDebug() << "Page " << i+1 << ": Count, " << count; - for(int j = 0; j < count; j++) { - TextData *t = new TextData(rectBuffer[j], i+1, text); - //MuPDF search does not match case, so retrieve the exact text at the location found and determine whether or not it matches the case of the search text if the user selected to match case - if(matchCase){ - fz_stext_page *sPage = fz_new_stext_page_from_page_number(CTX, DOC, i, NULL); - QString currentStr = QString(fz_copy_selection(CTX, sPage, *fz_rect_min(&rectBuffer[j]), *fz_rect_max(&rectBuffer[j]), false)); - if(currentStr.contains(text, Qt::CaseSensitive)){ results.append(t); } - }else{ - results.append(t); - } + for(int i = 0; i < pnum; i++) { + QList<Poppler::TextBox*> textList = DOC->page(i)->textList(); + for(int j = 0; j < textList.size(); j++) { + if(textList[j]->text().contains(text, + (matchCase) ? Qt::CaseSensitive : Qt::CaseInsensitive)) { + TextData *t = new TextData(textList[j]->boundingBox(), i+1, text); + results.append(t); + } } - }*/ + } return results; } + +QImage Renderer::imageHash(int pagenum) { + return loadingHash[pagenum]; +} + +int Renderer::hashSize() { + return loadingHash.keys().length(); +} + +void Renderer::clearHash() { + loadingHash.clear(); +} diff --git a/src-qt5/desktop-utils/lumina-pdf/Renderer.h b/src-qt5/desktop-utils/lumina-pdf/Renderer.h index c9e13c7f..60db25ef 100644 --- a/src-qt5/desktop-utils/lumina-pdf/Renderer.h +++ b/src-qt5/desktop-utils/lumina-pdf/Renderer.h @@ -11,31 +11,42 @@ #include <QImage> #include <QDebug> #include <QJsonObject> -#include <QMutex> #include "textData.h" -class Renderer{ +class Renderer : public QObject { +Q_OBJECT + private: int pnum; //number of pages - set on loading document bool needpass; QString docpath; //save the path for the currently-loaded document QString doctitle; - QMutex *mutex; + QJsonObject jobj; + public: Renderer(); ~Renderer(); - //bool loadMultiThread(); + bool loadMultiThread(); //Information functions (usually needs to be loaded first) int numPages(){ return pnum; } bool needPassword(){ return needpass; } QString title(){ return doctitle; } - QJsonObject properties(); + QJsonObject properties() { return jobj; } //Main access functions bool loadDocument(QString path, QString password); - QImage renderPage(int pagenum, QSize DPI); + void renderPage(int pagenum, QSize DPI); QList<TextData*> searchDocument(QString text, bool matchCase); + void cleanup(); + + QImage imageHash(int pagenum); + int hashSize(); + void clearHash(); + +signals: + void PageLoaded(int); + void OrigSize(QSizeF); }; #endif diff --git a/src-qt5/desktop-utils/lumina-pdf/mainUI.cpp b/src-qt5/desktop-utils/lumina-pdf/mainUI.cpp index 4d255f64..936243d2 100644 --- a/src-qt5/desktop-utils/lumina-pdf/mainUI.cpp +++ b/src-qt5/desktop-utils/lumina-pdf/mainUI.cpp @@ -32,10 +32,11 @@ MainUI::MainUI() : QMainWindow(), ui(new Ui::MainUI()){ lastdir = QDir::homePath(); BACKEND = new Renderer(); //Create the interface widgets - WIDGET = new PrintWidget(this->centralWidget()); + WIDGET = new PrintWidget(BACKEND, this->centralWidget()); WIDGET->setVisible(false); WIDGET->setContextMenuPolicy(Qt::CustomContextMenu); WIDGET->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + loadingQueue.clear(); clockTimer = new QTimer(this); clockTimer->setInterval(1000); //1-second updates to clock connect(clockTimer, SIGNAL(timeout()), this, SLOT(updateClock()) ); @@ -51,16 +52,12 @@ MainUI::MainUI() : QMainWindow(), ui(new Ui::MainUI()){ //Context Menu contextMenu = new QMenu(this); connect(contextMenu, SIGNAL(aboutToShow()), this, SLOT(updateContextMenu())); - //Now put the widgets into the UI - //ui->bookmarksFrame->setParent(WIDGET); - //ui->findGroup->setParent(WIDGET); - //qDebug() << "Setting central widget"; - this->centralWidget()->layout()->replaceWidget(ui->label_replaceme, WIDGET); //setCentralWidget(WIDGET); + this->centralWidget()->layout()->replaceWidget(ui->label_replaceme, WIDGET); ui->label_replaceme->setVisible(false); WIDGET->setContextMenuPolicy(Qt::CustomContextMenu); connect(WIDGET, SIGNAL(customContextMenuRequested(const QPoint&)),this, SLOT(showContextMenu(const QPoint&)) ); connect(WIDGET, SIGNAL(currentPageChanged()), this, SLOT(updatePageNumber()) ); - connect(this, SIGNAL(PageLoaded(int)), this, SLOT(slotPageLoaded(int)) ); + connect(BACKEND, SIGNAL(PageLoaded(int)), this, SLOT(slotPageLoaded(int)) ); PrintDLG = new QPrintDialog(this); connect(PrintDLG, SIGNAL(accepted(QPrinter*)), this, SLOT(paintToPrinter(QPrinter*)) ); @@ -106,8 +103,8 @@ MainUI::MainUI() : QMainWindow(), ui(new Ui::MainUI()){ //connect(ui->actionSelect_Mode, &QAction::triggered, this, [&] { this->setScroll(false); }); connect(ui->actionZoom_In, &QAction::triggered, WIDGET, [&] { WIDGET->zoomIn(1.2); }); connect(ui->actionZoom_Out, &QAction::triggered, WIDGET, [&] { WIDGET->zoomOut(1.2); }); - connect(ui->actionRotate_Counterclockwise, &QAction::triggered, this, [&] { WIDGET->setDegrees(-90); }); - connect(ui->actionRotate_Clockwise, &QAction::triggered, this, [&] { WIDGET->setDegrees(90); }); + connect(ui->actionRotate_Counterclockwise, &QAction::triggered, this, [&] { if(results.size() != 0) { foreach(TextData *x, results) { x->highlighted(false); } } WIDGET->setDegrees(-90); }); + connect(ui->actionRotate_Clockwise, &QAction::triggered, this, [&] { if(results.size() != 0) { foreach(TextData *x, results) { x->highlighted(false); } } WIDGET->setDegrees(90); }); connect(ui->actionZoom_In_2, &QAction::triggered, WIDGET, [&] { WIDGET->zoomIn(1.2); }); connect(ui->actionZoom_Out_2, &QAction::triggered, WIDGET, [&] { WIDGET->zoomOut(1.2); }); connect(ui->actionFirst_Page, SIGNAL(triggered()), this, SLOT(firstPage()) ); @@ -115,6 +112,7 @@ MainUI::MainUI() : QMainWindow(), ui(new Ui::MainUI()){ connect(ui->actionNext_Page, SIGNAL(triggered()), this, SLOT(nextPage()) ); connect(ui->actionLast_Page, SIGNAL(triggered()), this, SLOT(lastPage()) ); connect(ui->actionProperties, &QAction::triggered, WIDGET, [&] { PROPDIALOG->show(); }); + connect(BACKEND, &Renderer::OrigSize, this, [&](QSizeF _pageSize) { pageSize = _pageSize; }); connect(ui->actionFind, &QAction::triggered, this, [&] { if(ui->findGroup->isVisible()) { ui->findGroup->setVisible(false); @@ -213,18 +211,18 @@ MainUI::MainUI() : QMainWindow(), ui(new Ui::MainUI()){ //TESTING features/functionality ui->actionSettings->setEnabled(TESTING); ui->actionSettings->setVisible(TESTING); - ui->actionBookmarks->setEnabled(TESTING); - ui->actionBookmarks->setVisible(TESTING); + //ui->actionBookmarks->setEnabled(TESTING); + //ui->actionBookmarks->setVisible(TESTING); ui->actionScroll_Mode->setEnabled(TESTING); ui->actionScroll_Mode->setVisible(TESTING); ui->actionSelect_Mode->setEnabled(TESTING); ui->actionSelect_Mode->setVisible(TESTING); - ui->actionProperties->setEnabled(TESTING); - ui->actionProperties->setVisible(TESTING); - ui->menuSettings->setEnabled(TESTING); - ui->menuSettings->setVisible(TESTING); + //ui->actionProperties->setEnabled(TESTING); + //ui->actionProperties->setVisible(TESTING); + //ui->menuSettings->setEnabled(TESTING); + //ui->menuSettings->setVisible(TESTING); if(!TESTING){ - ui->menubar->removeAction(ui->menuSettings->menuAction() ); + //ui->menubar->removeAction(ui->menuSettings->menuAction() ); } } @@ -242,22 +240,21 @@ void MainUI::loadFile(QString path){ if(!ok){ break; } //cancelled } //Clear the current display - WIDGET->setPictures(0); //Turn off the loadingHash in the preview widget for now - loadingHash.clear(); - QTimer::singleShot(10, WIDGET, SLOT(updatePreview())); //start loading the file preview + + WIDGET->setVisible(false); + BACKEND->clearHash(); + QTimer::singleShot(10, WIDGET, SLOT(updatePreview())); //Load the new document info this->setWindowTitle( BACKEND->title()); if(BACKEND->needPassword()){ return; } //cancelled; - //qDebug() << " - Document Setup : start loading pages now"; - //startLoadingPages(); + qDebug() << " - Document Setup : start loading pages now"; QTimer::singleShot(50, this, SLOT(startLoadingPages()) ); } void MainUI::loadPage(int num, MainUI *obj, QSize dpi){ //qDebug() << " - Render Page:" << num; - QImage img = BACKEND->renderPage(num, dpi); - loadingHash.insert(num, img); - obj->emit PageLoaded(num); + BACKEND->renderPage(num, dpi); + //qDebug() << "Image at" << num << "accessed outside:" << BACKEND->imageHash(num).isNull(); } QScreen* MainUI::getScreen(bool current, bool &cancelled){ @@ -293,7 +290,7 @@ QScreen* MainUI::getScreen(bool current, bool &cancelled){ } void MainUI::startPresentation(bool atStart){ - if(loadingHash.isEmpty()){ return; } //just in case + if(BACKEND->hashSize() == 0){ return; } //just in case bool cancelled = false; QScreen *screen = getScreen(false, cancelled); //let the user select which screen to use (if multiples) if(cancelled){ return;} @@ -341,7 +338,7 @@ void MainUI::ShowPage(int page){ //qDebug() << "Show Page:" << page << "/" << numPages; CurrentPage = page; QImage PAGEIMAGE; - if(page<BACKEND->numPages()+1){ PAGEIMAGE = loadingHash[page-1]; } + if(page<BACKEND->numPages()+1){ PAGEIMAGE = BACKEND->imageHash(page-1); } //Now scale the image according to the user-designations and show it if(!PAGEIMAGE.isNull()){ @@ -369,8 +366,8 @@ void MainUI::endPresentation(){ void MainUI::startLoadingPages(){ //qDebug() <<"Start Loading Pages"; - if(!loadingHash.isEmpty()){ return; } //currently loaded[ing] - //loadingHash.clear(); + if(BACKEND->hashSize() != 0) { return; } //currently loaded[ing] + loadingQueue.clear(); //qDebug() << "Update Progress Bar"; progress->setRange(0, BACKEND->numPages()); progress->setValue(0); @@ -383,26 +380,31 @@ void MainUI::startLoadingPages(){ QSize DPI(300,300); //print-quality (some printers even go to 600 DPI nowdays) - /*qDebug() << "Screen Resolutions:"; + qDebug() << "Screen Resolutions:"; QList<QScreen*> screens = QApplication::screens(); for(int i=0; i<screens.length(); i++){ qDebug() << screens[i]->name() << screens[i]->logicalDotsPerInchX() << screens[i]->logicalDotsPerInchY(); - }*/ + } for(int i=0; i<BACKEND->numPages(); i++){ //qDebug() << " - Kickoff page load:" << i; - QtConcurrent::run(this, &MainUI::loadPage, i, this, DPI); + if(BACKEND->loadMultiThread()) { + QtConcurrent::run(this, &MainUI::loadPage, i, this, DPI); + }else{ + BACKEND->renderPage(i, DPI); + } } //qDebug() << "Finish page loading kickoff"; } void MainUI::slotPageLoaded(int page){ - Q_UNUSED(page); - //qDebug() << "Page Loaded:" << page; - int finished = loadingHash.keys().length(); - //qDebug() << " - finished:" << finished; + loadingQueue.push_back(page); + int finished = loadingQueue.size(); + //qDebug() << "Page Loaded:" << page << finished; if(finished == BACKEND->numPages()){ + BACKEND->cleanup(); + //qDebug() << " - finished:" << finished; progAct->setVisible(false); - WIDGET->setPictures(&loadingHash); + WIDGET->setVisible(true); WIDGET->setCurrentPage(1); PROPDIALOG = new PropDialog(BACKEND); PROPDIALOG->setSize(pageSize); @@ -410,7 +412,7 @@ void MainUI::slotPageLoaded(int page){ ui->actionStart_Here->setEnabled(true); ui->actionStart_Begin->setEnabled(true); pageAct->setVisible(true); - //qDebug() << " - Document Setup: All pages loaded"; + qDebug() << " - Document Setup: All pages loaded"; QTimer::singleShot(10, WIDGET, SLOT(updatePreview())); //start loading the file preview }else{ progress->setValue(finished); @@ -418,15 +420,14 @@ void MainUI::slotPageLoaded(int page){ } void MainUI::paintToPrinter(QPrinter *PRINTER){ - if(loadingHash.keys().length() != BACKEND->numPages()){ return; } + if(BACKEND->hashSize() != BACKEND->numPages()){ return; } //qDebug() << "paintToPrinter"; int pages = BACKEND->numPages(); int firstpage = 0; int copies = PRINTER->copyCount(); bool collate = PRINTER->collateCopies(); - bool reverse = (PRINTER->pageOrder()==QPrinter::LastPageFirst); + bool reverse = (PRINTER->pageOrder()==QPrinter::LastPageFirst); qDebug() << "PRINTER DPI:" << PRINTER->resolution() << PRINTER->supportedResolutions(); - //return; if(PRINTER->resolution() < 300){ //Try to get 300 DPI resolution at least PRINTER->setResolution(300); @@ -446,28 +447,25 @@ void MainUI::paintToPrinter(QPrinter *PRINTER){ //Make sure even/odd pages are not selected as desired //Qt 5.7.1 does not seem to have even/odd page selections - 8/11/2017 pageCount << i; //add this page to the list - //QT 5.9+ : Do not need to manually stack "copies". Already handled internally + //QT 5.9+ : Do not need to manually stack "copies". Already handled internally //for(int c=1; c<copies && !collate; c++){ pageCount << i; } //add any copies of this page as needed } //qDebug() << "Got Page Range:" << pageCount; - - //QT 5.9+ : Do not need to manually reverse the pages (already handled internally) - if(reverse){ + //QT 5.9+ : Do not need to manually reverse the pages (already handled internally) + if(reverse) { //Need to reverse the order of the list QList<int> tmp = pageCount; pageCount.clear(); for(int i=tmp.length()-1; i>=0; i--){ pageCount << tmp[i]; } //qDebug() << " - reversed:" << pageCount; } - - //QT 5.9+ : Do not need to manually stack "copies". Already handled internally + //QT 5.9+ : Do not need to manually stack "copies". Already handled internally; /*if(collate && copies>0){ QList<int> orig = pageCount; //original array of pages for(int c=1; c<copies; c++){ pageCount << orig; //add a new copy of the entire page range } }*/ - //qDebug() << "Final Page Range:" << pageCount; //Generate the sizing information for the printer QSize sz(PRINTER->pageRect().width(), PRINTER->pageRect().height()); @@ -487,10 +485,10 @@ void MainUI::paintToPrinter(QPrinter *PRINTER){ progress->setRange(0, pageCount.length()-1); for(int i=0; i<pageCount.length(); i++){ if(i!=0){ PRINTER->newPage(); } - //qDebug() << "Printing Page:" << pageCount[i]; + //qDebug() << "Printing Page:" << pageCount[i]; progress->setValue(i); QApplication::processEvents(); - QImage img = loadingHash[pageCount[i]].scaled(sz, Qt::KeepAspectRatio, Qt::SmoothTransformation); + QImage img = BACKEND->imageHash(pageCount[i]).scaled(sz, Qt::KeepAspectRatio, Qt::SmoothTransformation); //Now draw the image painter.drawImage(0,0,img); } @@ -567,7 +565,11 @@ void MainUI::keyPressEvent(QKeyEvent *event){ /*qDebug() << "Send Wheel Event"; QWheelEvent wEvent( WIDGET->mapFromGlobal(QCursor::pos()), QCursor::pos(),QPoint(0,0), QPoint(0,30), 0, Qt::Vertical, Qt::LeftButton, Qt::NoModifier); QApplication::sendEvent(WIDGET, &wEvent);*/ - }else{ + }else if(event->key() == Qt::Key_Enter) { + /*if(ui->findGroup->hasFocus()) { + find(ui->textEdit->text(), true); + }*/ + }else{ QMainWindow::keyPressEvent(event); } } @@ -598,7 +600,7 @@ void MainUI::find(QString text, bool forward) { delete td; results.clear(); } - QTimer::singleShot(10, WIDGET, SLOT(updatePreview())); + WIDGET->updatePreview(); ui->resultsLabel->setText(""); //Get the new search results results = BACKEND->searchDocument(text, matchCase); @@ -628,7 +630,6 @@ void MainUI::find(QString text, bool forward) { //qDebug() << "Jump to page: " << currentText.page; - //Current Bug: Does not highlight results[0] WIDGET->highlightText(currentText); }else{ ui->resultsLabel->setText("No results found"); diff --git a/src-qt5/desktop-utils/lumina-pdf/mainUI.h b/src-qt5/desktop-utils/lumina-pdf/mainUI.h index 74badbfd..af12c33c 100644 --- a/src-qt5/desktop-utils/lumina-pdf/mainUI.h +++ b/src-qt5/desktop-utils/lumina-pdf/mainUI.h @@ -47,6 +47,7 @@ private: QString lastdir; bool matchCase; QList<TextData*> results; + QList<int> loadingQueue; int currentHighlight; //Other Interface elements @@ -60,7 +61,6 @@ private: //PDF Page Loading cache variables Renderer *BACKEND; - QHash<int, QImage> loadingHash; void loadPage(int num, MainUI *obj, QSize dpi); @@ -100,9 +100,6 @@ private slots: void updateContextMenu(); //void setScroll(bool); -signals: - void PageLoaded(int); - protected: void keyPressEvent(QKeyEvent*); void wheelEvent(QWheelEvent*); diff --git a/src-qt5/desktop-utils/lumina-pdf/propDialog.cpp b/src-qt5/desktop-utils/lumina-pdf/propDialog.cpp index c40f537e..6226234d 100644 --- a/src-qt5/desktop-utils/lumina-pdf/propDialog.cpp +++ b/src-qt5/desktop-utils/lumina-pdf/propDialog.cpp @@ -41,7 +41,7 @@ PropDialog::PropDialog(Renderer *Backend) : QDialog(), ui(new Ui::PropDialog()){ ui->keywordE->setText( info.value("keywords").toString() ); ui->createdEntry->setText( info.value("dt_created").toString() ); ui->modifiedEntry->setText( info.value("dt_modified").toString() ); - ui->numberL->setText( QString::number(Backend->numPages()) ); + ui->numberL->setText( ui->numberL->text() + QString::number(Backend->numPages()) ); } diff --git a/src-qt5/desktop-utils/lumina-pdf/textData.h b/src-qt5/desktop-utils/lumina-pdf/textData.h index 546920b3..9ec4c5db 100644 --- a/src-qt5/desktop-utils/lumina-pdf/textData.h +++ b/src-qt5/desktop-utils/lumina-pdf/textData.h @@ -5,19 +5,19 @@ class TextData { private: - QRect _loc; + QRectF _loc; bool _highlighted=false; int _page=0; QString _text=""; public: - TextData(QRect _loc, int _page, QString _text) { + TextData(QRectF _loc, int _page, QString _text) { this->_loc = _loc; this->_page = _page; this->_text = _text; } - QRect loc() { return this->_loc; } + QRectF loc() { return this->_loc; } bool highlighted() { return this->_highlighted; } int page() { return this->_page; } QString text() { return this->_text; } |