diff options
author | Ken Moore <ken@ixsystems.com> | 2018-02-27 10:24:37 -0500 |
---|---|---|
committer | Ken Moore <ken@ixsystems.com> | 2018-02-27 10:24:37 -0500 |
commit | 6f7bfe5269912021229e570675ac0fbd9ad4f082 (patch) | |
tree | 83e4f30b9ec64c71ec89c723cca5a59fa4dbc2a4 /src-qt5/desktop-utils | |
parent | Added highlighting for search results (diff) | |
download | lumina-6f7bfe5269912021229e570675ac0fbd9ad4f082.tar.gz lumina-6f7bfe5269912021229e570675ac0fbd9ad4f082.tar.bz2 lumina-6f7bfe5269912021229e570675ac0fbd9ad4f082.zip |
Get lumina-pdf all up and running again.
1. Abstract out the backend renderer into a new class
2. The Renderer can be selected at build time by setting the "USE_MUPDF" build flag to use mupdf instead of poppler (default).
Diffstat (limited to 'src-qt5/desktop-utils')
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp | 34 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/PrintWidget.h | 6 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp | 136 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp | 98 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/Renderer.h | 39 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/lumina-pdf.pro | 16 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/mainUI.cpp | 185 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/mainUI.h | 20 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/propDialog.cpp | 44 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/propDialog.h | 9 | ||||
-rw-r--r-- | src-qt5/desktop-utils/lumina-pdf/propDialog.ui | 136 |
11 files changed, 469 insertions, 254 deletions
diff --git a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp index 790ae2ad..45353295 100644 --- a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp +++ b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp @@ -21,7 +21,7 @@ PrintWidget::PrintWidget(QWidget *parent) : QGraphicsView(parent), scene(0), cur scene->setBackgroundBrush(Qt::gray); this->setScene(scene); this->degrees = 0; - this->rotMatrix = QMatrix(1, 0, 0, 1, 0 ,0); + this->rotMatrix = QMatrix(1, 0, 0, 1, 0 ,0); /*QVBoxLayout *layout = new QVBoxLayout; setLayout(layout); @@ -184,21 +184,23 @@ void PrintWidget::layoutPages() { void PrintWidget::populateScene() { - for (int i = 0; i < pages.size(); i++) - scene->removeItem(pages.at(i)); - qDeleteAll(pages); - pages.clear(); - - int numPages = pictures->count(); + for (int i = 0; i < pages.size(); i++){ + scene->removeItem(pages.at(i)); + } + qDeleteAll(pages); + pages.clear(); + //qDebug() << "populateScene"; + if(pictures==0){ return; } //nothing to show yet + int numPages = pictures->count(); //Replace from loadingHash resolution - QSize paperSize = pictures->value(0).size(); + QSize paperSize = pictures->value(0).size(); //qDebug() << "Image paperSize" << paperSize; //Changes the paper orientation if rotated by 90 or 270 degrees - if(degrees == 90 or degrees == 270) + if(degrees == 90 or degrees == 270) paperSize.transpose(); - for (int i = 0; i < numPages; i++) { + for (int i = 0; i < numPages; i++) { QImage pagePicture = pictures->value(i); if(degrees != 0) { pagePicture = pagePicture.transformed(rotMatrix, Qt::SmoothTransformation); @@ -211,7 +213,7 @@ void PrintWidget::populateScene() PageItem* item = new PageItem(i+1, pagePicture, paperSize); scene->addItem(item); pages.append(item); - } + } } //Private Slots @@ -302,11 +304,7 @@ void PrintWidget::fit(bool doFitting) { void PrintWidget::setPictures(QHash<int, QImage> *hash) { pictures = hash; -} - -void PrintWidget::receiveDocument(fz_document *DOC) { - this->doc = DOC; - this->setVisible(true); + 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. @@ -315,7 +313,7 @@ void PrintWidget::setDegrees(int degrees) { this->degrees = ( ( ( this->degrees + degrees ) % 360 ) + 360 ) % 360; switch(this->degrees) { case 270: - rotMatrix = QMatrix(0, -1, 1, 0, 0, 0); + rotMatrix = QMatrix(0, -1, 1, 0, 0, 0); break; case 90: rotMatrix = QMatrix(0, 1, -1, 0, 0, 0); @@ -324,7 +322,7 @@ void PrintWidget::setDegrees(int degrees) { rotMatrix = QMatrix(-1, 0, 0, -1, 0, 0); break; default: - rotMatrix = QMatrix(1, 0, 0, 1, 0 ,0); + rotMatrix = QMatrix(1, 0, 0, 1, 0 ,0); } this->updatePreview(); } diff --git a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h index fa2869b6..41902330 100644 --- a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h +++ b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h @@ -21,7 +21,6 @@ #include <QPageLayout> #include "textData.h" -#include <mupdf/fitz.h> class PageItem : public QGraphicsItem { public: @@ -72,7 +71,7 @@ public: painter->fillRect(cshadow, QBrush(cgrad)); painter->setClipRect(paperRect & option->exposedRect); painter->fillRect(paperRect, Qt::white); - painter->drawImage(QPoint(0,0), pagePicture); + painter->drawImage(QPoint(0,0), pagePicture); } private: @@ -139,8 +138,7 @@ public slots: void setCurrentPage(int); void setVisible(bool) Q_DECL_OVERRIDE; void highlightText(TextData*); - void receiveDocument(fz_document*); - void setDegrees(int); + void setDegrees(int); void updatePreview(); void fitView(); diff --git a/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp b/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp new file mode 100644 index 00000000..05854e1b --- /dev/null +++ b/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp @@ -0,0 +1,136 @@ +#include "Renderer.h" +#include <mupdf/fitz.h> +#include <mupdf/pdf.h> + +#include <QDateTime> + +fz_document *DOC; +pdf_document *PDOC; +fz_context *CTX; + +inline QString getTextInfo(pdf_obj *info, QString str) { + pdf_obj *obj = pdf_dict_gets(CTX, info, str.toLatin1().data()); + if(obj){ return pdf_to_utf8(CTX, obj); } + return ""; +} + +Renderer::Renderer(){ + DOC = 0; + CTX = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); + PDOC = 0; + needpass = false; +} + +Renderer::~Renderer(){ + +} + +bool Renderer::loadMultiThread(){ return false; } + +QJsonObject Renderer::properties(){ + QJsonObject jobj; + pdf_obj *info = pdf_dict_gets(CTX, pdf_trailer(CTX, PDOC), "Info"); + jobj.insert("title", getTextInfo(info, "Title") ); + jobj.insert("subject", getTextInfo(info, "Subject") ); + jobj.insert("author", getTextInfo(info, "Author") ); + jobj.insert("creator", getTextInfo(info, "Creator") ); + jobj.insert("producer", getTextInfo(info, "Producer") ); + jobj.insert("keywords", getTextInfo(info, "Keywords") ); + jobj.insert("dt_created", QDateTime::fromString( getTextInfo(info, "CreationDate").left(16), "'D:'yyyyMMddHHmmss").toString() ); + jobj.insert("dt_modified", QDateTime::fromString( getTextInfo(info, "ModDate").left(16), "'D:'yyyyMMddHHmmss").toString() ); + return jobj; +} + +bool Renderer::loadDocument(QString path, QString password){ + if(DOC==0){ fz_register_document_handlers(CTX); } //first time through + else if(path!=docpath){ + //fz_close_document(DOC); + //Clear out the old document first + delete DOC; + DOC=0; + if(PDOC!=0){ delete PDOC; PDOC=0; } + needpass = false; + } + if(DOC==0){ + DOC = fz_open_document(CTX, path.toLocal8Bit().data()); + if(DOC==0){ + qDebug() << "Could not open file:" << path; + return false; + } + needpass = (fz_needs_password(CTX, DOC) != 0); + } + if(PDOC==0){ + PDOC = pdf_open_document(CTX, path.toLocal8Bit().data()); + } + + if(needpass && password.isEmpty()){ return false; } + else if(needpass){ + needpass = !fz_authenticate_password(CTX, DOC, password.toLocal8Bit()); + if(needpass){ return false; } //incorrect password + } + + //Could read/access the PDF - go ahead and load the info now + pnum = -1; + doctitle.clear(); + //qDebug() << "Opening File:" << path; + pdf_obj *info = pdf_dict_gets(CTX, pdf_trailer(CTX, (pdf_document*)DOC), "Info"); + if(info) { + if(pdf_obj *obj = pdf_dict_gets(CTX, info, (char *)"Title")){ doctitle = pdf_to_utf8(CTX, obj); } + } + if(doctitle.isEmpty()){ doctitle = path.section("/",-1); } + pnum = fz_count_pages(CTX, DOC); + //Setup the Document + fz_page *PAGE = fz_load_page(CTX, DOC, 0); + if(PAGE!=0){ + /*switch(PAGE->orientation()){ + case Poppler::Page::Landscape: + WIDGET->setOrientation(QPageLayout::Landscape); break; + default: + WIDGET->setOrientation(QPageLayout::Portrait); + }*/ + delete PAGE; + return true; //could see the first page + } + return false; +} + +QImage Renderer::renderPage(int pagenum, QSize DPI){ + //MuPDF uses a scaling factor, not DPI + fz_page *PAGE = fz_load_page(CTX, DOC, pagenum); + QImage img; + if(PAGE!=0){ + fz_matrix matrix; + //double scaleFactorW = dpi.width()/72.0; + //double scaleFactorH = dpi.height()/72.0; + //fz_scale(&matrix, scaleFactorW, scaleFactorH); + fz_scale(&matrix, 4.0, 4.0); //need to adjust this later according to DPI + fz_rotate(&matrix, 0.0); + fz_pixmap *pixmap = fz_new_pixmap_from_page_number(CTX, DOC, pagenum, &matrix, fz_device_rgb(CTX), 0); + //unsigned char *samples = fz_pixmap_samples(CTX, pixmap); + img = QImage(pixmap->samples, pixmap->w, pixmap->h, QImage::Format_RGB888); //make the raw image a bit larger than the end result + } + if(PAGE!=0){ delete PAGE; } + qDebug() << "Render Page:" << pagenum << img.size(); + return img; +} + +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); + //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); + } + } + } + return results; +} diff --git a/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp b/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp new file mode 100644 index 00000000..b2d7dd32 --- /dev/null +++ b/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp @@ -0,0 +1,98 @@ +#include "Renderer.h" +#include <poppler/qt5/poppler-qt5.h> + +static Poppler::Document *DOC; + +Renderer::Renderer(){ + DOC = 0; + needpass = false; + pnum = 0; +} + +Renderer::~Renderer(){ + +} + +bool Renderer::loadMultiThread(){ return true; } + +QJsonObject Renderer::properties(){ + return QJsonObject(); //TO-DO +} + +bool Renderer::loadDocument(QString path, QString password){ + //qDebug() << "Load Document:" << path; + if(DOC!=0 && path!=docpath){ + //Clear out the old document first + delete DOC; + DOC=0; + needpass = false; + pnum=0; + } + //Load the Document (if needed); + if(DOC==0){ + //qDebug() << "Loading Document"; + DOC = Poppler::Document::load(path); + } + + if(DOC==0){ + qDebug() << "Could not open file:" << path; + return false; + }else if(DOC->isLocked()){ + //qDebug() << "Document Locked"; + needpass = true; + if(password.isEmpty()){ return false; } //stop here - need to get password from user before continuing + needpass = !DOC->unlock(QByteArray(), password.toLocal8Bit()); + if(needpass){ return false; } //invalid password + } + //qDebug() << "Opening File:" << path; + doctitle = DOC->title(); + if(doctitle.isEmpty()){ doctitle = path.section("/",-1); } + pnum = DOC->numPages(); + //Setup the Document + Poppler::Page *PAGE = DOC->page(0); + if(PAGE!=0){ + /*switch(PAGE->orientation()){ + case Poppler::Page::Landscape: + WIDGET->setOrientation(QPageLayout::Landscape); break; + default: + WIDGET->setOrientation(QPageLayout::Portrait); + }*/ + delete PAGE; + return true; //could load the first page + } + return false; //nothing to load +} + +QImage 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; +} + +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); + } + } + }*/ + return results; +} diff --git a/src-qt5/desktop-utils/lumina-pdf/Renderer.h b/src-qt5/desktop-utils/lumina-pdf/Renderer.h new file mode 100644 index 00000000..09e9c425 --- /dev/null +++ b/src-qt5/desktop-utils/lumina-pdf/Renderer.h @@ -0,0 +1,39 @@ +// ================================ +// Simple abstraction class between backend renderers +// ================================ +// Written by Ken Moore: Feb 26, 2018 +// Available under the 3-Clause BSD License +// ================================ +#ifndef _LUMINA_PDF_BACKEND_RENDERER_H +#define _LUMINA_PDF_BACKEND_RENDERER_H + +#include <QString> +#include <QImage> +#include <QDebug> +#include <QJsonObject> +#include "textData.h" + +class Renderer{ +private: + int pnum; //number of pages - set on loading document + bool needpass; + QString docpath; //save the path for the currently-loaded document + QString doctitle; +public: + Renderer(); + ~Renderer(); + bool loadMultiThread(); + + //Information functions (usually needs to be loaded first) + int numPages(){ return pnum; } + bool needPassword(){ return needpass; } + QString title(){ return doctitle; } + QJsonObject properties(); + + //Main access functions + bool loadDocument(QString path, QString password); + QImage renderPage(int pagenum, QSize DPI); + QList<TextData*> searchDocument(QString text, bool matchCase); +}; + +#endif diff --git a/src-qt5/desktop-utils/lumina-pdf/lumina-pdf.pro b/src-qt5/desktop-utils/lumina-pdf/lumina-pdf.pro index bf0d3665..8ed0e9f3 100644 --- a/src-qt5/desktop-utils/lumina-pdf/lumina-pdf.pro +++ b/src-qt5/desktop-utils/lumina-pdf/lumina-pdf.pro @@ -25,13 +25,23 @@ SOURCES += main.cpp \ HEADERS += mainUI.h \ PrintWidget.h \ PresentationLabel.h \ - PropDialog.h \ - textData.h + PropDialog.h \ + Renderer.h \ + textData.h FORMS += mainUI.ui \ propDialog.ui -LIBS += -lpoppler-qt5 -lmupdf -lmujs +isEmpty(USE_MUPDF){ + message("Using Poppler-Qt5 Backend") + LIBS += -lpoppler-qt5 + SOURCES += Renderer-poppler.cpp +}else{ + message("Using MuPDF Backend") + LIBS += -lmupdf -lmupdfthird + SOURCES += Renderer-mupdf.cpp + +} TRANSLATIONS = i18n/l-pdf_af.ts \ i18n/l-pdf_ar.ts \ diff --git a/src-qt5/desktop-utils/lumina-pdf/mainUI.cpp b/src-qt5/desktop-utils/lumina-pdf/mainUI.cpp index 92e5cb65..aecf01ac 100644 --- a/src-qt5/desktop-utils/lumina-pdf/mainUI.cpp +++ b/src-qt5/desktop-utils/lumina-pdf/mainUI.cpp @@ -30,6 +30,7 @@ MainUI::MainUI() : QMainWindow(), ui(new Ui::MainUI()){ presentationLabel = 0; CurrentPage = 1; lastdir = QDir::homePath(); + BACKEND = new Renderer(); //Create the interface widgets WIDGET = new PrintWidget(this->centralWidget()); WIDGET->setVisible(false); @@ -59,10 +60,7 @@ MainUI::MainUI() : QMainWindow(), ui(new Ui::MainUI()){ WIDGET->setContextMenuPolicy(Qt::CustomContextMenu); connect(WIDGET, SIGNAL(customContextMenuRequested(const QPoint&)),this, SLOT(showContextMenu(const QPoint&)) ); connect(WIDGET, SIGNAL(currentPageChanged()), this, SLOT(updatePageNumber()) ); - DOC = 0; - CTX = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); connect(this, SIGNAL(PageLoaded(int)), this, SLOT(slotPageLoaded(int)) ); - connect(this, SIGNAL(sendDocument(fz_document*)), WIDGET, SLOT(receiveDocument(fz_document*))); PrintDLG = new QPrintDialog(this); connect(PrintDLG, SIGNAL(accepted(QPrinter*)), this, SLOT(paintToPrinter(QPrinter*)) ); @@ -223,97 +221,33 @@ MainUI::MainUI() : QMainWindow(), ui(new Ui::MainUI()){ } MainUI::~MainUI(){ - //fz_free(CTX, CTX); + delete BACKEND; } void MainUI::loadFile(QString path){ if(!QFile::exists(path) || path.isEmpty() ){ return; } - fz_register_document_handlers(CTX); - fz_document *TDOC = fz_open_document(CTX, path.toLocal8Bit().data()); - PDOC = pdf_open_document(CTX, path.toLocal8Bit().data()); - if(TDOC==0){ - qDebug() << "Could not open file:" << path; - return; - }else if(fz_needs_password(CTX, TDOC) != 0){ - //Prompt for password to unlock the document - QString pass = ""; - bool ok = true; //flag this to go into the loop the first time (if password prompt is cancelled - this becomes false) - - while( (ok ? true : !fz_authenticate_password(CTX, TDOC, pass.toLocal8Bit())) ){ //make sure the unlock function is only called when ok is true - pass = QInputDialog::getText(this, tr("Unlock PDF"), tr("Password:"), QLineEdit::Password, "", &ok); - } - if(fz_needs_password(CTX, TDOC) != 0){ return; } - } - //qpdf.processFile(path.toLatin1().data(), pass.toLatin1().data()); - - if(DOC!=0){ - //fz_close_document(DOC); - //Clear out the old document first - delete DOC; - DOC=0; - } - //loadingHash.clear(); //clear out this hash - - numPages = -1; - DOC = TDOC; //Save this for later - qDebug() << "Opening File:" << path; - pdf_obj *info = pdf_dict_gets(CTX, pdf_trailer(CTX, (pdf_document*)DOC), "Info"); - if(info) { - if(pdf_obj *obj = pdf_dict_gets(CTX, info, (char *)"Title")) - this->setWindowTitle(pdf_to_utf8(CTX, obj)); + QString password; + bool ok = true; + while( ok && !BACKEND->loadDocument(path, password) && BACKEND->needPassword() ){ + password = QInputDialog::getText(this, tr("Unlock PDF"), tr("Password:"), QLineEdit::Password, "", &ok); + if(!ok){ break; } //cancelled } - if(this->windowTitle().isEmpty()){ this->setWindowTitle(path.section("/",-1)); } - - //Setup the Document - //QVector<QPDFObjectHandle> pages = (QVector<QPDFObjectHandle>)pdf.getAllPages(); - //QPDFObjectHandle page = pages.at(0); - fz_page *PAGE = fz_load_page(CTX, TDOC, 0); - if(PAGE!=0){ - lastdir = path.section("/",0,-2); //save this for later - /*switch(PAGE->orientation()){ - case Poppler::Page::Landscape: - WIDGET->setOrientation(QPageLayout::Landscape); break; - default: - WIDGET->setOrientation(QPageLayout::Portrait); - }*/ - delete PAGE; - qDebug() << " - Document Setup : start loading pages now"; - startLoadingPages(); - //QTimer::singleShot(10, WIDGET, SLOT(updatePreview())); //start loading the file preview - } - + //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 + //Load the new document info + this->setWindowTitle( BACKEND->title()); + if(BACKEND->needPassword()){ return; } //cancelled; + //qDebug() << " - Document Setup : start loading pages now"; + //startLoadingPages(); + QTimer::singleShot(50, this, SLOT(startLoadingPages()) ); } -void MainUI::loadPage(int num, fz_document *doc, MainUI *obj, QSize dpi){ +void MainUI::loadPage(int num, MainUI *obj, QSize dpi){ //qDebug() << " - Render Page:" << num; - fz_page *PAGE = fz_load_page(CTX, doc, num); - if(PAGE!=0){ - fz_matrix matrix; - - //double scaleFactorW = dpi.width()/72.0; - //double scaleFactorH = dpi.height()/72.0; - //fz_scale(&matrix, scaleFactorW, scaleFactorH); - fz_scale(&matrix, 4.0, 4.0); - fz_rotate(&matrix, 0.0); - fz_pixmap *pixmap = fz_new_pixmap_from_page_number(CTX, doc, num, &matrix, fz_device_rgb(CTX), 0); - //unsigned char *samples = fz_pixmap_samples(CTX, pixmap); - QImage raw(pixmap->samples, pixmap->w, pixmap->h, QImage::Format_RGB888); //make the raw image a bit larger than the end result - //loadingHash.insert(num, raw.scaled(page.width(), page.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation) ); - loadingHash.insert(num, raw); - //raw = QImage(); //clear it - //fz_drop_pixmap(CTX, pixmap); - /* - QList<Annotation*> anno = PAGE->annotations(Annotations::AText ); - QStringList annoS; - for(int i=0; i<anno.length(); i++){ - annoS << static_cast<TextAnnotation*>(anno[i])->??? - } - annotateHash.insert(num, PAGE-> - */ - }else{ - loadingHash.insert(num, QImage()); - } - if(PAGE!=0){ delete PAGE; } + QImage img = BACKEND->renderPage(num, dpi); + loadingHash.insert(num, img); obj->emit PageLoaded(num); } @@ -350,7 +284,7 @@ QScreen* MainUI::getScreen(bool current, bool &cancelled){ } void MainUI::startPresentation(bool atStart){ - if(DOC==0){ return; } //just in case + if(loadingHash.isEmpty()){ 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;} @@ -386,8 +320,8 @@ void MainUI::startPresentation(bool atStart){ void MainUI::ShowPage(int page){ //Check for valid document/page - //qDebug() << "Load Page:" << page << "/" << numPages << "Index:" << page; - if(page <= 0 || page > numPages || (page==numPages && CurrentPage==page) ){ + //qDebug() << "Load Page:" << page << "/" << BACKEND->numPages() << "Index:" << page; + if(page <= 0 || page > BACKEND->numPages() || (page==BACKEND->numPages() && CurrentPage==page) ){ endPresentation(); return; //invalid - no document loaded or invalid page specified } @@ -398,7 +332,7 @@ void MainUI::ShowPage(int page){ //qDebug() << "Show Page:" << page << "/" << numPages; CurrentPage = page; QImage PAGEIMAGE; - if(page<numPages+1){ PAGEIMAGE = loadingHash[page-1]; } + if(page<BACKEND->numPages()+1){ PAGEIMAGE = loadingHash[page-1]; } //Now scale the image according to the user-designations and show it if(!PAGEIMAGE.isNull()){ @@ -425,22 +359,23 @@ void MainUI::endPresentation(){ } void MainUI::startLoadingPages(){ - if(numPages>0){ return; } //currently loaded[ing] - loadingHash.clear(); - numPages = fz_count_pages(CTX, DOC); - progress->setRange(0,numPages); + //qDebug() <<"Start Loading Pages"; + if(!loadingHash.isEmpty()){ return; } //currently loaded[ing] + //loadingHash.clear(); + //qDebug() << "Update Progress Bar"; + progress->setRange(0, BACKEND->numPages()); progress->setValue(0); progAct->setVisible(true); pageAct->setVisible(false); //PERFORMANCE NOTES: - // Using MuPdf to scale the image (adjust dpi value) helps a bit but you take a larger CPU loading hit (and still quite a lot of pixelization) + // Using Poppler to scale the image (adjust dpi value) helps a bit but you take a larger CPU loading hit (and still quite a lot of pixelization) // Using Qt to scale the image (adjust page value) smooths out the image quite a bit without a lot of performance loss (but cannot scale up without pixelization) // The best approach seams to be to increase the DPI a bit, but match that with the same scaling on the page size (smoothing) //double scalefactor = 2.5; - fz_rect page_rect; - fz_bound_page(CTX, fz_load_page(CTX, DOC, 0), &page_rect); - pageSize = QSizeF((page_rect.x1 - page_rect.x0)/72.0, (page_rect.y1 - page_rect.y0)/72.0); + //fz_rect page_rect; + //fz_bound_page(CTX, fz_load_page(CTX, DOC, 0), &page_rect); + //pageSize = QSizeF((page_rect.x1 - page_rect.x0)/72.0, (page_rect.y1 - page_rect.y0)/72.0); QSize DPI(300,300); //print-quality (some printers even go to 600 DPI nowdays) /*qDebug() << "Screen Resolutions:"; @@ -448,11 +383,16 @@ void MainUI::startLoadingPages(){ for(int i=0; i<screens.length(); i++){ qDebug() << screens[i]->name() << screens[i]->logicalDotsPerInchX() << screens[i]->logicalDotsPerInchY(); }*/ - for(int i=0; i<numPages; i++){ + bool async = BACKEND->loadMultiThread(); + for(int i=0; i<BACKEND->numPages(); i++){ //qDebug() << " - Kickoff page load:" << i; - loadPage(i, DOC, this, DPI); - //QtConcurrent::run(this, &MainUI::loadPage, i, DOC, this, DPI, pageSize); + if(async){ + QtConcurrent::run(this, &MainUI::loadPage, i, this, DPI); + }else{ + loadPage(i, this, DPI); + } } + //qDebug() << "Finish page loading kickoff"; } void MainUI::slotPageLoaded(int page){ @@ -460,26 +400,27 @@ void MainUI::slotPageLoaded(int page){ //qDebug() << "Page Loaded:" << page; int finished = loadingHash.keys().length(); //qDebug() << " - finished:" << finished; - if(finished == numPages){ + if(finished == BACKEND->numPages()){ progAct->setVisible(false); WIDGET->setPictures(&loadingHash); - emit sendDocument(DOC); - PROPDIALOG = new PropDialog(CTX, PDOC); + WIDGET->setCurrentPage(1); + PROPDIALOG = new PropDialog(BACKEND); PROPDIALOG->setSize(pageSize); ui->actionStop_Presentation->setEnabled(false); 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); } } void MainUI::paintToPrinter(QPrinter *PRINTER){ - if(loadingHash.keys().length() != numPages){ return; } - - int pages = fz_count_pages(CTX, DOC); + if(loadingHash.keys().length() != BACKEND->numPages()){ return; } + //qDebug() << "paintToPrinter"; + int pages = BACKEND->numPages(); int firstpage = 0; int copies = PRINTER->copyCount(); bool collate = PRINTER->collateCopies(); @@ -560,10 +501,11 @@ void MainUI::updateClock(){ } void MainUI::updatePageNumber(){ + //qDebug() << "UpdatePageNumber"; QString text; if(presentationLabel==0 || !presentationLabel->isVisible()){ text = tr("Page %1 of %2"); } else{ text = "%1/%2"; } - label_page->setText( text.arg( QString::number(WIDGET->currentPage()), QString::number(numPages) )); + label_page->setText( text.arg( QString::number(WIDGET->currentPage()), QString::number(BACKEND->numPages()) )); } /*void MainUI::setScroll(bool tog) { @@ -575,9 +517,10 @@ void MainUI::updatePageNumber(){ }*/ void MainUI::updateContextMenu(){ + //qDebug() << "UpdateContextMenu"; contextMenu->clear(); contextMenu->addSection( QString(tr("Page %1 of %2")).arg(QString::number(WIDGET->currentPage()), - QString::number(numPages) ) ); + QString::number(BACKEND->numPages()) ) ); contextMenu->addAction(ui->actionPrevious_Page); contextMenu->addAction(ui->actionNext_Page); contextMenu->addSeparator(); @@ -641,6 +584,7 @@ void MainUI::find(QString text, bool forward) { //Clear results and highlights if the user gives a new search string if(newSearch) { + //clear out the old results if(!results.empty()) { foreach (TextData* td, results) delete td; @@ -648,25 +592,8 @@ void MainUI::find(QString text, bool forward) { } QTimer::singleShot(10, WIDGET, SLOT(updatePreview())); ui->resultsLabel->setText(""); - fz_rect rectBuffer[1000]; - for(int i = 0; i < numPages; 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_sheet *sheet = fz_new_stext_sheet(CTX); - fz_stext_page *sPage = fz_new_stext_page_from_page_number(CTX, DOC, i, sheet, NULL); - QString currentStr = QString(fz_copy_selection(CTX, sPage, rectBuffer[j])); - if(currentStr.contains(text, Qt::CaseSensitive)) - results.append(t); - }else{ - results.append(t); - } - } - } + //Get the new search results + results = BACKEND->searchDocument(text, matchCase); //qDebug() << "Total Results: " << results.size(); currentHighlight = (forward) ? -1 : results.size(); } diff --git a/src-qt5/desktop-utils/lumina-pdf/mainUI.h b/src-qt5/desktop-utils/lumina-pdf/mainUI.h index bc121f7c..68245f16 100644 --- a/src-qt5/desktop-utils/lumina-pdf/mainUI.h +++ b/src-qt5/desktop-utils/lumina-pdf/mainUI.h @@ -19,8 +19,7 @@ #include <QApplication> #include <QMenu> -#include <mupdf/fitz.h> -#include <mupdf/pdf.h> +#include "Renderer.h" #include "PresentationLabel.h" #include "propDialog.h" #include "PrintWidget.h" @@ -39,17 +38,14 @@ public: void loadFile(QString path); private: - fz_document *DOC; - pdf_document *PDOC; - fz_context *CTX; - QSizeF pageSize; + QSizeF pageSize; PrintWidget *WIDGET; Ui::MainUI *ui; PropDialog *PROPDIALOG; QPrintDialog *PrintDLG; QString lastdir; bool matchCase; - QList<TextData*> results; + QList<TextData*> results; int currentHighlight; //Other Interface elements @@ -62,10 +58,10 @@ private: QAction *clockAct, *pageAct; //PDF Page Loading cache variables + Renderer *BACKEND; QHash<int, QImage> loadingHash; - int numPages; - void loadPage(int num, fz_document *doc, MainUI *obj, QSize dpi); + void loadPage(int num, MainUI *obj, QSize dpi); //Functions/variables for the presentation mode PresentationLabel *presentationLabel; @@ -83,7 +79,7 @@ private slots: void nextPage(){ ShowPage( WIDGET->currentPage()+1 ); } //currentPage() starts at 1 rather than 0 void prevPage(){ ShowPage( WIDGET->currentPage()-1 ); } //currentPage() starts at 1 rather than 0 void firstPage(){ ShowPage(1); } - void lastPage(){ ShowPage(numPages); } + void lastPage(){ ShowPage(BACKEND->numPages()); } void startPresentationHere(){ startPresentation(false); } void startPresentationBeginning(){ startPresentation(true); } void closePresentation(){ endPresentation(); } @@ -101,11 +97,11 @@ private slots: void updatePageNumber(); void showContextMenu(const QPoint&){ contextMenu->popup(QCursor::pos()); } void updateContextMenu(); - //void setScroll(bool); + //void setScroll(bool); signals: void PageLoaded(int); - void sendDocument(fz_document*); + void sendDocument(fz_document*); protected: void keyPressEvent(QKeyEvent*); diff --git a/src-qt5/desktop-utils/lumina-pdf/propDialog.cpp b/src-qt5/desktop-utils/lumina-pdf/propDialog.cpp index d1ef0c49..c40f537e 100644 --- a/src-qt5/desktop-utils/lumina-pdf/propDialog.cpp +++ b/src-qt5/desktop-utils/lumina-pdf/propDialog.cpp @@ -10,18 +10,10 @@ #include <LuminaXDG.h> -void PropDialog::setInfo(fz_context *CTX, pdf_obj *info, QTextEdit *widget, QString str) { - pdf_obj *obj = pdf_dict_gets(CTX, info, str.toLatin1().data()); - if(obj) - widget->setText(pdf_to_utf8(CTX, obj)); -} - -PropDialog::PropDialog(fz_context *CTX, pdf_document *DOC) : QDialog(), ui(new Ui::PropDialog()){ +PropDialog::PropDialog(Renderer *Backend) : QDialog(), ui(new Ui::PropDialog()){ + ui->setupUi(this); this->setWindowTitle(tr("PDF Information")); this->setWindowIcon( LXDG::findIcon("dialog-information","unknown")); - pdf_obj *info = pdf_dict_gets(CTX, pdf_trailer(CTX, DOC), "Info"); - - ui->setupUi(this); connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(close())); @@ -39,29 +31,21 @@ PropDialog::PropDialog(fz_context *CTX, pdf_document *DOC) : QDialog(), ui(new U ui->saveButton->setText(tr("Save")); ui->closeButton->setText(tr("Close")); + QJsonObject info = Backend->properties(); //Fill the text boxes with information from the document - if(info) { - setInfo(CTX, info, ui->titleE, "Title"); - setInfo(CTX, info, ui->subjectE, "Subject"); - setInfo(CTX, info, ui->authorE, "Author"); - setInfo(CTX, info, ui->creatorE, "Creator"); - setInfo(CTX, info, ui->producerE, "Producer"); - setInfo(CTX, info, ui->keywordE, "Keywords"); - pdf_obj *obj = pdf_dict_gets(CTX, info, (char *)"CreationDate"); - char *str = pdf_to_utf8(CTX, obj); - if(obj) - ui->createdEntry->setText(QDateTime::fromString(QString(str).left(16), "'D:'yyyyMMddHHmmss").toString()); - //ModDate not displaying when should, possibly broken - obj = pdf_dict_gets(CTX, info, (char *)"ModDate"); - str = pdf_to_utf8(CTX, obj); - if(obj) - ui->modifiedEntry->setText(QDateTime::fromString(QString(str).left(16), "'D:'yyyyMMddHHmmss").toString()); - ui->numberL->setText(ui->numberL->text()+QString::number(pdf_count_pages(CTX, DOC))); - free(str); - } + ui->titleE->setText( info.value("title").toString() ); + ui->subjectE->setText( info.value("subject").toString() ); + ui->authorE->setText( info.value("author").toString() ); + ui->creatorE->setText( info.value("creator").toString() ); + ui->producerE->setText( info.value("producer").toString() ); + 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()) ); + } //Load size from mainUI after pages have loaded -void PropDialog::setSize(QSizeF pageSize) { +void PropDialog::setSize(QSizeF pageSize) { ui->sizeL->setText(ui->sizeL->text()+QString::number(pageSize.width())+", "+QString::number(pageSize.height())); } diff --git a/src-qt5/desktop-utils/lumina-pdf/propDialog.h b/src-qt5/desktop-utils/lumina-pdf/propDialog.h index 05d3231a..a09f2563 100644 --- a/src-qt5/desktop-utils/lumina-pdf/propDialog.h +++ b/src-qt5/desktop-utils/lumina-pdf/propDialog.h @@ -9,8 +9,7 @@ #include <QDialog> #include <QTextEdit> -#include <mupdf/fitz.h> -#include <mupdf/pdf.h> +#include "Renderer.h" namespace Ui{ class PropDialog; @@ -19,12 +18,12 @@ namespace Ui{ class PropDialog : public QDialog { Q_OBJECT public: - PropDialog(fz_context*, pdf_document*); - + PropDialog(Renderer *Backend); + void setSize(QSizeF); private: - void setInfo(fz_context*, pdf_obj*, QTextEdit*, QString); + //void setInfo(fz_context*, pdf_obj*, QTextEdit*, QString); Ui::PropDialog *ui; }; diff --git a/src-qt5/desktop-utils/lumina-pdf/propDialog.ui b/src-qt5/desktop-utils/lumina-pdf/propDialog.ui index 0e8ba90b..d79d2087 100644 --- a/src-qt5/desktop-utils/lumina-pdf/propDialog.ui +++ b/src-qt5/desktop-utils/lumina-pdf/propDialog.ui @@ -6,21 +6,14 @@ <rect> <x>0</x> <y>0</y> - <width>400</width> - <height>300</height> + <width>627</width> + <height>655</height> </rect> </property> <property name="windowTitle"> <string>PDF Information</string> </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="8" column="0" colspan="3"> - <widget class="QLabel" name="sizeL"> - <property name="text"> - <string>Page Size:</string> - </property> - </widget> - </item> + <layout class="QFormLayout" name="formLayout"> <item row="0" column="0"> <widget class="QLabel" name="titleL"> <property name="text"> @@ -28,7 +21,7 @@ </property> </widget> </item> - <item row="0" column="1" colspan="2"> + <item row="0" column="1"> <widget class="QTextEdit" name="titleE"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> @@ -45,7 +38,7 @@ <property name="maximumSize"> <size> <width>512</width> - <height>16</height> + <height>128</height> </size> </property> <property name="acceptDrops"> @@ -71,14 +64,14 @@ </property> </widget> </item> - <item row="1" column="0"> + <item row="2" column="0"> <widget class="QLabel" name="subjectL"> <property name="text"> <string>Subject:</string> </property> </widget> </item> - <item row="1" column="1" colspan="2"> + <item row="2" column="1"> <widget class="QTextEdit" name="subjectE"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> @@ -95,7 +88,7 @@ <property name="maximumSize"> <size> <width>512</width> - <height>16</height> + <height>128</height> </size> </property> <property name="acceptDrops"> @@ -121,14 +114,14 @@ </property> </widget> </item> - <item row="2" column="0"> + <item row="4" column="0"> <widget class="QLabel" name="authorL"> <property name="text"> <string>Author:</string> </property> </widget> </item> - <item row="2" column="1" colspan="2"> + <item row="4" column="1"> <widget class="QTextEdit" name="authorE"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> @@ -145,7 +138,7 @@ <property name="maximumSize"> <size> <width>512</width> - <height>16</height> + <height>128</height> </size> </property> <property name="acceptDrops"> @@ -171,14 +164,14 @@ </property> </widget> </item> - <item row="3" column="0"> + <item row="6" column="0"> <widget class="QLabel" name="creatorL"> <property name="text"> <string>Creator:</string> </property> </widget> </item> - <item row="3" column="1" colspan="2"> + <item row="6" column="1"> <widget class="QTextEdit" name="creatorE"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> @@ -195,7 +188,7 @@ <property name="maximumSize"> <size> <width>512</width> - <height>16</height> + <height>128</height> </size> </property> <property name="acceptDrops"> @@ -224,14 +217,14 @@ </property> </widget> </item> - <item row="4" column="0"> + <item row="8" column="0"> <widget class="QLabel" name="producerL"> <property name="text"> <string>Producer:</string> </property> </widget> </item> - <item row="4" column="1" colspan="2"> + <item row="8" column="1"> <widget class="QTextEdit" name="producerE"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> @@ -248,7 +241,7 @@ <property name="maximumSize"> <size> <width>512</width> - <height>16</height> + <height>128</height> </size> </property> <property name="acceptDrops"> @@ -271,14 +264,14 @@ </property> </widget> </item> - <item row="5" column="0"> + <item row="10" column="0"> <widget class="QLabel" name="keywordsL"> <property name="text"> <string>Keywords:</string> </property> </widget> </item> - <item row="5" column="1" colspan="2"> + <item row="10" column="1"> <widget class="QTextEdit" name="keywordE"> <property name="enabled"> <bool>true</bool> @@ -298,7 +291,7 @@ <property name="maximumSize"> <size> <width>512</width> - <height>16</height> + <height>128</height> </size> </property> <property name="acceptDrops"> @@ -330,14 +323,14 @@ </property> </widget> </item> - <item row="6" column="0"> + <item row="12" column="0"> <widget class="QLabel" name="createdL"> <property name="text"> <string>Created: </string> </property> </widget> </item> - <item row="6" column="1" colspan="2"> + <item row="12" column="1"> <widget class="QTextEdit" name="createdEntry"> <property name="enabled"> <bool>true</bool> @@ -357,7 +350,7 @@ <property name="maximumSize"> <size> <width>512</width> - <height>16</height> + <height>128</height> </size> </property> <property name="acceptDrops"> @@ -389,14 +382,14 @@ </property> </widget> </item> - <item row="7" column="0"> + <item row="14" column="0"> <widget class="QLabel" name="modifiedL"> <property name="text"> <string>Modified: </string> </property> </widget> </item> - <item row="7" column="1" colspan="2"> + <item row="14" column="1"> <widget class="QTextEdit" name="modifiedEntry"> <property name="enabled"> <bool>true</bool> @@ -416,7 +409,7 @@ <property name="maximumSize"> <size> <width>512</width> - <height>16</height> + <height>128</height> </size> </property> <property name="acceptDrops"> @@ -448,36 +441,73 @@ </property> </widget> </item> - <item row="10" column="1"> - <widget class="QPushButton" name="saveButton"> - <property name="text"> - <string>Save</string> - </property> - </widget> - </item> - <item row="10" column="2"> - <widget class="QPushButton" name="closeButton"> + <item row="16" column="0"> + <widget class="QLabel" name="sizeL"> <property name="text"> - <string>Close</string> - </property> - <property name="autoDefault"> - <bool>true</bool> - </property> - <property name="default"> - <bool>true</bool> - </property> - <property name="flat"> - <bool>false</bool> + <string>Page Size:</string> </property> </widget> </item> - <item row="9" column="0" colspan="3"> + <item row="17" column="0" colspan="2"> <widget class="QLabel" name="numberL"> <property name="text"> <string>Number of Pages:</string> </property> </widget> </item> + <item row="19" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="saveButton"> + <property name="text"> + <string>Save</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="closeButton"> + <property name="text"> + <string>Close</string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + <property name="flat"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </item> + <item row="18" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> </layout> </widget> <resources/> |