aboutsummaryrefslogtreecommitdiff
path: root/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp
diff options
context:
space:
mode:
authorjoe berner <stackyjoe@gmail.com>2018-12-18 10:59:15 -0600
committerjoe berner <stackyjoe@gmail.com>2018-12-18 10:59:15 -0600
commite42bc7e16e6d9f813b5c712261dffbef6b1bddb2 (patch)
tree0c9e39d7e3f425691f854288a733b8d56c5f6ed3 /src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp
parentMerge pull request #640 from lumina-desktop/q5sys-patch-1 (diff)
downloadlumina-e42bc7e16e6d9f813b5c712261dffbef6b1bddb2.tar.gz
lumina-e42bc7e16e6d9f813b5c712261dffbef6b1bddb2.tar.bz2
lumina-e42bc7e16e6d9f813b5c712261dffbef6b1bddb2.zip
Changes the UI interface so that only the current page is rendered on screen at a time. This has significant memory benefits for large PDF files. This also implements a least-recently used cache of tunable size, which improves responsiveness if you're hopping between two specific pages.
Diffstat (limited to 'src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp')
-rw-r--r--src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp289
1 files changed, 176 insertions, 113 deletions
diff --git a/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp b/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp
index 2b28d0c3..d90f36d3 100644
--- a/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp
+++ b/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp
@@ -1,82 +1,86 @@
#include "Renderer.h"
-#include <poppler/qt5/poppler-qt5.h>
+#include "drawablepage.h"
+#include "link.h"
+#include "lrucache.h"
#include <QThread>
+#include <atomic>
+#include <poppler/qt5/poppler-qt5.h>
-class Link {
- public:
- Link(TextData *_data, Poppler::Link *_link) : data(_data), link(_link) { }
- ~Link() { delete data; }
-
- TextData* getData() { return data; }
- Poppler::Link* getLink() { return link; }
-
- private:
- TextData *data;
- Poppler::Link *link;
-};
-
-static Poppler::Document *DOC;
+static std::unique_ptr<Poppler::Document> DOC;
QHash<int, QImage> loadingHash;
-QHash<int, QList<Link*>> linkHash;
-Renderer::Renderer(){
- DOC = 0;
- needpass = false;
- pnum = 0;
- degrees = 0;
+static std::vector<LuminaPDF::drawablePage> pages;
+static std::vector<QList<LuminaPDF::Link *>> links;
+static std::atomic<int> pagesStillLoading;
+static QHash<int, QList<LuminaPDF::Link *>> linkHash;
+static LuminaPDF::LRUCache<QImage> imageCache;
+
+Renderer::Renderer() : pnum(0), needpass(false), degrees(0) {
+ DOC.reset(nullptr);
+ pagesStillLoading = 1;
+ imageCache.setCacheSize(5);
}
-Renderer::~Renderer(){
- //qDeleteAll(loadingHash);
+Renderer::~Renderer() {
+ // qDeleteAll(loadingHash);
+ pages.clear();
+ for (auto &linkList : links) {
+ qDeleteAll(linkList);
+ }
loadingHash.clear();
}
-bool Renderer::loadMultiThread(){ return true; }
+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;
- if(linkHash.size() > 0) {
- foreach(QList<Link*> linkArray, linkHash) {
+bool Renderer::loadDocument(QString path, QString password) {
+ // qDebug() << "Load Document:" << path;
+ if (DOC != nullptr && path != docpath) {
+ // Clear out the old document first
+ DOC.reset(nullptr);
+ pages.clear();
+ if (linkHash.size() > 0) {
+ foreach (QList<LuminaPDF::Link *> linkArray, linkHash) {
qDeleteAll(linkArray);
}
linkHash.clear();
}
needpass = false;
- pnum=0;
+ pnum = 0;
docpath = path;
}
- //Load the Document (if needed);
- if(DOC==0){
- //qDebug() << "Loading Document";
- DOC = Poppler::Document::load(path);
+ // Load the Document (if needed);
+ if (DOC == nullptr) {
+ // qDebug() << "Loading Document";
+ DOC.reset(Poppler::Document::load(path));
docpath = path;
}
- if(DOC==0){
+ if (DOC == nullptr) {
qDebug() << "Could not open file:" << path;
return false;
- }else if(DOC->isLocked()){
- //qDebug() << "Document Locked";
+ }
+
+ 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
+ if (password.isEmpty() or
+ !DOC->unlock(QByteArray(), password.toLocal8Bit())) {
+ return false;
+ } // invalid password
}
- //qDebug() << "Opening File:" << path;
+ // qDebug() << "Opening File:" << path;
doctitle = DOC->title();
- if(doctitle.isEmpty()){ doctitle = path.section("/",-1); }
+ if (doctitle.isEmpty()) {
+ doctitle = path.section("/", -1);
+ }
pnum = DOC->numPages();
- //Setup the Document
+ // Setup the Document
Poppler::Page *PAGE = DOC->page(0);
- if(PAGE!=0){
+ if (PAGE != 0) {
/*switch(PAGE->orientation()){
case Poppler::Page::Landscape:
WIDGET->setOrientation(QPageLayout::Landscape); break;
@@ -84,62 +88,93 @@ bool Renderer::loadDocument(QString path, QString password){
WIDGET->setOrientation(QPageLayout::Portrait);
}*/
delete PAGE;
- return true; //could load the first page
+ pages.reserve(pnum + 1);
+
+ for (int i = 0; i < pnum + 1; ++i) {
+ LuminaPDF::drawablePage temp;
+ pages.emplace_back(std::move(temp));
+ }
+
+ for (int i = 0; i < pnum + 1; ++i) {
+ QList<LuminaPDF::Link *> temp;
+ links.push_back(temp);
+ }
+
+ pagesStillLoading = pnum;
+
+ return true; // could load the first page
}
- return false; //nothing to load
+ return false; // nothing to load
}
-void Renderer::renderPage(int pagenum, QSize DPI, int degrees){
- //qDebug() << "Render Page:" << pagenum << DPI << degrees;
+void Renderer::renderPage(int pagenum, QSize DPI, int degrees) {
+ // qDebug() << "Render Page:" << pagenum << DPI << degrees;
+
+ emit SetProgress(pages.size() - pagesStillLoading);
- if(DOC!=0){
- Poppler::Page *PAGE = DOC->page(pagenum);
+ if (DOC != nullptr) {
+ Poppler::Page *PAGE = DOC->page(pagenum - 1);
QImage img;
- if(PAGE!=0){
- Poppler::Page::Rotation rotation;
- switch(degrees) {
- case 90:
- rotation = Poppler::Page::Rotation::Rotate90;
- break;
- case 180:
- rotation = Poppler::Page::Rotation::Rotate180;
- break;
- case 270:
- rotation = Poppler::Page::Rotation::Rotate270;
- break;
- default:
- rotation = Poppler::Page::Rotation::Rotate0;
- }
- img = PAGE->renderToImage(DPI.width(), DPI.height(), -1, -1, -1, -1, rotation);
- loadingHash.insert(pagenum, img);
- QList<Link*> linkArray;
- foreach(Poppler::Link *link, PAGE->links()) {
+ if (PAGE != nullptr) {
+ Poppler::Page::Rotation rotation;
+ switch (degrees) {
+ case 90:
+ rotation = Poppler::Page::Rotation::Rotate90;
+ break;
+ case 180:
+ rotation = Poppler::Page::Rotation::Rotate180;
+ break;
+ case 270:
+ rotation = Poppler::Page::Rotation::Rotate270;
+ break;
+ default:
+ rotation = Poppler::Page::Rotation::Rotate0;
+ }
+
+ LuminaPDF::drawablePage temp(PAGE, DPI, rotation);
+ pages[pagenum] = std::move(temp);
+ // img = PAGE->renderToImage(DPI.width(), DPI.height(), -1, -1, -1, -1,
+ // rotation);
+ // loadingHash.insert(pagenum, img);
+ QList<LuminaPDF::Link *> linkArray;
+
+ foreach (Poppler::Link *link, PAGE->links()) {
QString location;
- if(link->linkType() == Poppler::Link::LinkType::Goto)
- location = dynamic_cast<Poppler::LinkGoto*>(link)->fileName();
- else if(link->linkType() == Poppler::Link::LinkType::Goto)
- location = dynamic_cast<Poppler::LinkBrowse*>(link)->url();
- Link *newLink = new Link(new TextData(link->linkArea(), pagenum, location), link);
+ if (link->linkType() == Poppler::Link::LinkType::Goto)
+ location = dynamic_cast<Poppler::LinkGoto *>(link)->fileName();
+ else if (link->linkType() == Poppler::Link::LinkType::Goto)
+ location = dynamic_cast<Poppler::LinkBrowse *>(link)->url();
+ LuminaPDF::Link *newLink = new LuminaPDF::Link(
+ new TextData(link->linkArea(), pagenum, location), link);
linkArray.append(newLink);
}
- linkHash.insert(pagenum, linkArray);
- delete PAGE;
+
+ links[pagenum] = linkArray;
+ // linkHash.insert(pagenum, linkArray);
}
- //qDebug() << "Done Render Page:" << pagenum << img.size();
- }else{
- loadingHash.insert(pagenum, QImage());
+ // qDebug() << "Done Render Page:" << pagenum << img.size();
+ } else {
+ pages[pagenum] = LuminaPDF::drawablePage();
+ // loadingHash.insert(pagenum, QImage());
}
- emit PageLoaded(pagenum);
+
+ if (pagesStillLoading == 1) {
+ emit PageLoaded();
+ }
+
+ --pagesStillLoading;
}
-QList<TextData*> Renderer::searchDocument(QString text, bool matchCase){
- QList<TextData*> results;
- 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);
+bool Renderer::isDoneLoading() { return pagesStillLoading == 0; }
+
+QList<TextData *> Renderer::searchDocument(QString text, bool matchCase) {
+ QList<TextData *> results;
+ 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);
}
}
@@ -147,59 +182,87 @@ QList<TextData*> Renderer::searchDocument(QString text, bool matchCase){
return results;
}
+QSize Renderer::imageSize(int pagenum) { return pages[pagenum].size(); }
+
QImage Renderer::imageHash(int pagenum) {
- return loadingHash[pagenum];
+ // while(pagesStillLoading > 0) { qDebug() << "pagesStillLoading!\n";}
+
+ std::optional<QImage> cachedImage = imageCache.get(pagenum);
+
+ if (cachedImage.has_value())
+ return *cachedImage;
+
+ imageCache.push(pagenum, pages[pagenum].render());
+ return *imageCache.get(pagenum);
}
int Renderer::hashSize() {
- return loadingHash.keys().length();
+ qDebug() << "pages contains " << pages.size() << " elements.\n";
+ return pages.size();
}
void Renderer::clearHash() {
loadingHash.clear();
+ pages.clear();
}
-//Highlighting found text, bookmarks, and page properties disabled for Poppler
+// Highlighting found text, bookmarks, and page properties disabled for Poppler
bool Renderer::supportsExtraFeatures() { return false; }
-void Renderer::traverseOutline(void *, int) { }
+void Renderer::traverseOutline(void *, int) {}
void Renderer::handleLink(QWidget *obj, QString linkDest) {
- Poppler::Link* trueLink;
- foreach(QList<Link*> linkArray, linkHash) {
- for(int i = 0; i < linkArray.size(); i++) {
- Poppler::Link* link = linkArray[i]->getLink();
- if(link->linkType() == Poppler::Link::LinkType::Browse) {
- if(linkDest == dynamic_cast<Poppler::LinkBrowse*>(link)->url())
+ Poppler::Link *trueLink;
+ foreach (QList<LuminaPDF::Link *> linkArray, linkHash) {
+ for (int i = 0; i < linkArray.size(); i++) {
+ Poppler::Link *link = linkArray[i]->getLink();
+ if (link->linkType() == Poppler::Link::LinkType::Browse) {
+ if (linkDest == dynamic_cast<Poppler::LinkBrowse *>(link)->url())
trueLink = link;
- }else if(link->linkType() == Poppler::Link::LinkType::Goto) {
- if(linkDest == dynamic_cast<Poppler::LinkGoto*>(link)->fileName())
+ } else if (link->linkType() == Poppler::Link::LinkType::Goto) {
+ if (linkDest == dynamic_cast<Poppler::LinkGoto *>(link)->fileName())
trueLink = link;
}
}
}
- if(trueLink) {
- if(trueLink->linkType() == Poppler::Link::LinkType::Goto)
- emit goToPosition(dynamic_cast<Poppler::LinkGoto*>(trueLink)->destination().pageNumber(), 0, 0);
- else if(trueLink->linkType() == Poppler::Link::LinkType::Browse) {
- if(QMessageBox::Yes == QMessageBox::question(obj, tr("Open External Link?"), QString(tr("Do you want to open %1 in the default browser")).arg(linkDest), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ){ QProcess::startDetached("firefox \""+linkDest+"\""); }
+ if (trueLink) {
+ if (trueLink->linkType() == Poppler::Link::LinkType::Goto)
+ emit goToPosition(dynamic_cast<Poppler::LinkGoto *>(trueLink)
+ ->destination()
+ .pageNumber(),
+ 0, 0);
+ else if (trueLink->linkType() == Poppler::Link::LinkType::Browse) {
+ if (QMessageBox::Yes ==
+ QMessageBox::question(
+ obj, tr("Open External Link?"),
+ QString(tr("Do you want to open %1 in the default browser"))
+ .arg(linkDest),
+ QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) {
+ QProcess::startDetached("firefox \"" + linkDest + "\"");
+ }
}
}
}
-TextData* Renderer::linkList(int pageNum, int entry) {
- if(linkHash[pageNum].size() > 0)
+TextData *Renderer::linkList(int pageNum, int entry) {
+ if (linkHash[pageNum].size() > 0)
return linkHash[pageNum][entry]->getData();
else
return 0;
}
-int Renderer::linkSize(int pageNum) { Q_UNUSED(pageNum) return linkHash[pageNum].size(); }
+int Renderer::linkSize(int pageNum) {
+ Q_UNUSED(pageNum) return linkHash[pageNum].size();
+}
int Renderer::annotSize(int pageNum) { Q_UNUSED(pageNum) return 0; }
-Annotation *Renderer::annotList(int pageNum, int entry) { Q_UNUSED(pageNum) Q_UNUSED(entry) return NULL; }
+Annotation *Renderer::annotList(int pageNum, int entry) {
+ Q_UNUSED(pageNum) Q_UNUSED(entry) return NULL;
+}
int Renderer::widgetSize(int pageNum) { Q_UNUSED(pageNum) return 0; }
-Widget *Renderer::widgetList(int pageNum, int entry) { Q_UNUSED(pageNum) Q_UNUSED(entry) return NULL; }
+Widget *Renderer::widgetList(int pageNum, int entry) {
+ Q_UNUSED(pageNum) Q_UNUSED(entry) return NULL;
+}
bgstack15