aboutsummaryrefslogtreecommitdiff
path: root/src-qt5/desktop-utils/lumina-pdf
diff options
context:
space:
mode:
authorZackaryWelch <welch.zackary@gmail.com>2018-03-30 18:47:20 -0400
committerZackaryWelch <welch.zackary@gmail.com>2018-03-30 18:47:20 -0400
commit62727f05297af2894d62630d387fb39e672b4451 (patch)
tree3c68671e3173fdce7e7994d702c9d3c348f218b6 /src-qt5/desktop-utils/lumina-pdf
parentReset the default backend renderer to poppler. (diff)
downloadlumina-62727f05297af2894d62630d387fb39e672b4451.tar.gz
lumina-62727f05297af2894d62630d387fb39e672b4451.tar.bz2
lumina-62727f05297af2894d62630d387fb39e672b4451.zip
Added support for internal links. Code added for Poppler backend but not enabled. Code enabled for MuPDF. Does not support extrnal links yet.
Diffstat (limited to 'src-qt5/desktop-utils/lumina-pdf')
-rw-r--r--src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp21
-rw-r--r--src-qt5/desktop-utils/lumina-pdf/PrintWidget.h66
-rw-r--r--src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp151
-rw-r--r--src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp82
-rw-r--r--src-qt5/desktop-utils/lumina-pdf/Renderer.h3
5 files changed, 270 insertions, 53 deletions
diff --git a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp
index 013708ac..73e47091 100644
--- a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp
+++ b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp
@@ -131,7 +131,7 @@ 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()) {
+ if(!text->highlighted() && !text->loc().isNull()) {
int degrees = BACKEND->rotatedDegrees();
//Shows the text's location on a non-rotated page
QRectF rect = text->loc();
@@ -224,6 +224,13 @@ void PrintWidget::populateScene()
for (int i = 0; i < pages.size(); i++){
scene->removeItem(pages.at(i));
}
+ for(int i = 0; i < links.size(); i++) {
+ if(links[i].size() > 0) {
+ qDeleteAll(links[i]);
+ links[i].clear();
+ }
+ }
+ links.clear();
qDeleteAll(pages);
pages.clear();
int numPages = BACKEND->numPages();
@@ -231,16 +238,26 @@ void PrintWidget::populateScene()
for (int i = 0; i < numPages; i++) {
QImage pagePicture = BACKEND->imageHash(i);
-
QSize paperSize = pagePicture.size();
+ QList<QGraphicsItem*> linkLocations;
if(pagePicture.isNull()) {
qDebug() << "NULL IMAGE ON PAGE " << i;
continue;
}
+
PageItem* item = new PageItem(i+1, pagePicture, paperSize);
scene->addItem(item);
pages.append(item);
+
+ if(BACKEND->supportsExtraFeatures()) {
+ for(int k = 0; k < BACKEND->linkSize(i); k++) {
+ LinkItem *lItem = new LinkItem(item, BACKEND->linkList(i, k));
+ lItem->setOpacity(0.1);
+ linkLocations.append(lItem);
+ }
+ links.insert(i, linkLocations);
+ }
}
}
diff --git a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h
index 92d82e11..3860b011 100644
--- a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h
+++ b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h
@@ -22,6 +22,33 @@
#include "Renderer.h"
#include "TextData.h"
+class LinkItem: public QGraphicsItem {
+public:
+ LinkItem(QGraphicsItem *parent, TextData *_data) : QGraphicsItem(parent), bbox(_data->loc()), data(_data) {
+ setCacheMode(DeviceCoordinateCache);
+ setAcceptHoverEvents(true);
+ }
+
+QRectF boundingRect() const Q_DECL_OVERRIDE
+ { return bbox; }
+
+inline TextData* getData() const
+ { return data; }
+
+void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) Q_DECL_OVERRIDE
+{
+ Q_UNUSED(widget);
+ painter->setClipRect(option->exposedRect);
+ painter->setBrush(QBrush(QColor(255, 255, 177, 100)));
+ painter->setPen(QPen(QColor(255, 255, 100, 125)));
+ painter->drawRect(bbox);
+}
+
+private:
+ QRectF bbox;
+ TextData *data;
+};
+
class PageItem : public QGraphicsItem {
public:
PageItem(int _pageNum, QImage _pagePicture, QSize _paperSize)
@@ -30,6 +57,7 @@ public:
brect = QRectF(QPointF(-25, -25),
QSizeF(paperSize)+QSizeF(50, 50));
setCacheMode(DeviceCoordinateCache);
+ setAcceptHoverEvents(true);
}
QRectF boundingRect() const Q_DECL_OVERRIDE
@@ -80,7 +108,6 @@ private:
QRectF brect;
};
-
class PrintWidget : public QGraphicsView
{
Q_OBJECT
@@ -112,6 +139,7 @@ private:
double zoomFactor;
bool initialized, fitting;
QList<QGraphicsItem*> pages;
+ QHash<int, QList<QGraphicsItem*>> links;
int degrees;
Renderer *BACKEND;
@@ -158,6 +186,42 @@ protected:
emit resized();
}
+ void mouseMoveEvent(QMouseEvent *e) Q_DECL_OVERRIDE {
+ QGraphicsView::mouseMoveEvent(e);
+ QGraphicsItem *item = scene->itemAt(mapToScene(e->pos()), transform());
+
+ if(item) {
+ LinkItem *link = dynamic_cast<LinkItem*>(item);
+ if(link)
+ link->setOpacity(1);
+ QList<QGraphicsItem*> linkList;
+ if(PageItem *page = dynamic_cast<PageItem*>(item))
+ linkList = page->childItems();
+ else
+ linkList = link->parentItem()->childItems();
+ foreach(QGraphicsItem *linkItem, linkList) {
+ if(dynamic_cast<LinkItem*>(linkItem) == link)
+ continue;
+ dynamic_cast<LinkItem*>(linkItem)->setOpacity(0.1);
+ }
+ }
+ }
+
+ void mouseReleaseEvent(QMouseEvent *e) Q_DECL_OVERRIDE {
+ QGraphicsView::mouseReleaseEvent(e);
+ QPointF scenePoint = mapToScene(e->pos());
+ QGraphicsItem *item = scene->itemAt(scenePoint, transform());
+ if(LinkItem *link = dynamic_cast<LinkItem*>(item)) {
+ PageItem *page = dynamic_cast<PageItem*>(link->parentItem());
+ if(!BACKEND->isExternalLink(page->pageNumber()-1, link->getData()->text())) {
+ BACKEND->handleLink(link->getData()->text());
+ }else{
+ //Handle external link
+ }
+ link->setOpacity(0.1);
+ }
+ }
+
void showEvent(QShowEvent* e) Q_DECL_OVERRIDE {
QGraphicsView::showEvent(e);
emit resized();
diff --git a/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp b/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp
index c36c6b08..9b2a43f3 100644
--- a/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp
+++ b/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp
@@ -1,4 +1,5 @@
-#include "Renderer.h"
+#include "Renderer.h"
+#include "TextData.h"
#include <QDateTime>
#include <mupdf/fitz.h>
#include <mupdf/pdf.h>
@@ -6,28 +7,70 @@
#include <QFuture>
#include <QtConcurrent>
+class Link {
+ public:
+ Link(fz_context *_ctx, fz_link *_fzLink, char *_uri, int _page, QRectF _loc = QRectF()) : fzLink(_fzLink), ctx(_ctx) {
+ QString uri = QString::fromLocal8Bit(_uri);
+ data = new TextData(_loc, _page, uri);
+ }
+
+ ~Link() {
+ fz_drop_link(ctx, fzLink);
+ delete data;
+ }
+
+ TextData* getData() { return data; }
+
+ private:
+ TextData *data;
+ fz_link *fzLink;
+ fz_context *ctx;
+};
+
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 _pagenum, fz_context *_ctx, fz_display_list *_list, fz_rect _bbox, fz_matrix _ctm, double _sf, fz_link *_link) : pagenumber(_pagenum), ctx(_ctx), list(_list), bbox(_bbox), ctm(_ctm), sf(_sf) {
- ~Data() { }
+ while(_link) {
+ QRectF rect(sf*_link->rect.x0, sf*_link->rect.y0, sf*(_link->rect.x1 - _link->rect.x0), sf*(_link->rect.y1 - _link->rect.y0));
+ Link *link = new Link(_ctx, _link, _link->uri, _pagenum, rect);
+ linkList.push_back(link);
+ _link = _link->next;
+ }
+ }
+
+ ~Data() {
+ fz_drop_pixmap(ctx, pix);
+ fz_drop_display_list(ctx, list);
+ qDeleteAll(linkList);
+ linkList.clear();
+ }
+ int getPage() { return pagenumber; }
+ QList<Link*> getLinkList() { return linkList; }
+ fz_context* getContext() { return ctx; }
+ fz_display_list* getDisplayList() { return list; }
+ QRectF getScaledRect() { return QRectF(sf*bbox.x0, sf*bbox.y0, sf*(bbox.x1-bbox.x0), sf*(bbox.y1 - bbox.y0)); }
+ fz_rect getBoundingBox() { return bbox; }
+ fz_matrix getMatrix() { return ctm; }
+ QImage getImage() { return img; }
+
+ void setImage(QImage _img) { img = _img; }
+ void setRenderThread(QFuture<void> thread) { renderThread = thread; }
+ void setPixmap(fz_pixmap *_pix) { pix = _pix; }
+
+ private:
int pagenumber;
fz_context *ctx;
fz_display_list *list;
fz_rect bbox;
- fz_pixmap *pix;
fz_matrix ctm;
+ QList<Link*> linkList;
+ double sf;
+
+ fz_pixmap *pix;
QImage img;
QFuture<void> renderThread;
- double sf;
};
fz_document *DOC;
@@ -85,7 +128,8 @@ void Renderer::handleLink(QString link) {
if(!link.isEmpty()) {
pagenum = fz_resolve_link(CTX, DOC, uri, &xp, &yp);
- emit goToPosition(pagenum+1, xp, yp);
+ if(pagenum != -1)
+ emit goToPosition(pagenum+1, xp, yp);
}
}
@@ -174,29 +218,29 @@ bool Renderer::loadDocument(QString path, QString password){
void renderer(Data *data, Renderer *obj)
{
- int pagenum = data->pagenumber;
+ int pagenum = data->getPage();
//qDebug() << "Rendering:" << pagenum;
- 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_context *ctx = data->getContext();
+ fz_rect bbox = data->getBoundingBox();
+ fz_matrix ctm = data->getMatrix();
fz_device *dev;
+ fz_irect rbox;
ctx = fz_clone_context(ctx);
+ fz_pixmap *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);
+
dev = fz_new_draw_device(ctx, &fz_identity, pixmap);
- fz_run_display_list(ctx, list, dev, &ctm, &bbox, NULL);
+ fz_run_display_list(ctx, data->getDisplayList(), dev, &ctm, &bbox, NULL);
+
+ data->setImage(QImage(pixmap->samples, pixmap->w, pixmap->h, pixmap->stride, QImage::Format_RGB888));
+ data->setPixmap(pixmap);
+ dataHash.insert(pagenum, data);
- 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_drop_context(ctx);
- if(dataHash.find(pagenum) == dataHash.end())
- dataHash.insert(pagenum, data);
- else
- dataHash[pagenum] = data;
emit obj->PageLoaded(pagenum);
}
@@ -205,8 +249,6 @@ void Renderer::renderPage(int pagenum, QSize DPI, int degrees){
Data *data;
fz_matrix matrix;
fz_rect bbox;
- fz_irect rbox;
- fz_pixmap *pixmap;
fz_display_list *list;
double pageDPI = 96.0;
@@ -222,42 +264,39 @@ void Renderer::renderPage(int pagenum, QSize DPI, int degrees){
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_annot *annot = fz_first_annot(CTX, PAGE);
+ fz_annot *annot = fz_first_annot(CTX, PAGE);
while(annot) {
- fz_run_annot(CTX, annot, dev, &fz_identity, NULL);
- fz_rect bbox;
- fz_bound_annot(CTX, annot, &bbox);
+ fz_rect anotBox;
+ fz_bound_annot(CTX, annot, &anotBox);
+ fz_run_annot(CTX, annot, dev, &matrix, NULL);
- QRectF rect(bbox.x0, bbox.y0, (bbox.x1-bbox.x0), (bbox.y1 - bbox.y0));
+ QRectF rect(anotBox.x0, anotBox.y0, (anotBox.x1-anotBox.x0), (anotBox.y1 - anotBox.y0));
qDebug() << "Annotation:" << rect << "at" << pagenum;
annot = fz_next_annot(CTX, annot);
- }*/
+ }
+ fz_link *link = fz_load_links(CTX, PAGE);
+
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);
-
- data = new Data(pagenum, CTX, list, bbox, pixmap, matrix, sf);
- data->renderThread = QtConcurrent::run(&renderer, data, this);
+ data = new Data(pagenum, CTX, list, bbox, matrix, sf, link);
+ data->setRenderThread(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_display_list(CTX, dataHash[i]->list, text.toLocal8Bit().data(), rectBuffer, 1000);
+ int count = fz_search_display_list(CTX, dataHash[i]->getDisplayList(), text.toLocal8Bit().data(), rectBuffer, 1000);
//qDebug() << "Page " << i+1 << ": Count, " << count;
for(int j = 0; j < count; j++) {
- 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);
+ TextData *t = new TextData(dataHash[i]->getScaledRect(), 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_display_list(CTX, dataHash[i]->list, NULL);
+ fz_stext_page *sPage = fz_new_stext_page_from_display_list(CTX, dataHash[i]->getDisplayList(), 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{
@@ -269,7 +308,7 @@ QList<TextData*> Renderer::searchDocument(QString text, bool matchCase){
}
QImage Renderer::imageHash(int pagenum) {
- return dataHash[pagenum]->img;
+ return dataHash[pagenum]->getImage();
}
int Renderer::hashSize() {
@@ -277,12 +316,26 @@ int Renderer::hashSize() {
}
void Renderer::clearHash() {
- for(int i = 0; i < dataHash.size(); i++) {
- fz_drop_pixmap(CTX, dataHash[i]->pix);
- fz_drop_display_list(CTX, dataHash[i]->list);
- }
qDeleteAll(dataHash);
dataHash.clear();
}
+TextData* Renderer::linkList(int pagenum, int entry) {
+ return dataHash[pagenum]->getLinkList()[entry]->getData();
+}
+
+int Renderer::linkSize(int pagenum) {
+ return dataHash[pagenum]->getLinkList().size();
+}
+
+bool Renderer::isExternalLink(int pagenum, QString text) {
+ QList<Link*> linkList = dataHash[pagenum]->getLinkList();
+ foreach(Link *link, linkList) {
+ if(link->getData()->text() == text)
+ return fz_is_external_link(CTX, link->getData()->text().toLocal8Bit().data());
+ }
+ return false;
+}
+
bool Renderer::supportsExtraFeatures() { return true; }
+
diff --git a/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp b/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp
index 7f2baa5e..98a351bd 100644
--- a/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp
+++ b/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp
@@ -2,8 +2,22 @@
#include <poppler/qt5/poppler-qt5.h>
#include <QThread>
+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;
QHash<int, QImage> loadingHash;
+QHash<int, QList<Link*>> linkHash;
Renderer::Renderer(){
DOC = 0;
@@ -29,6 +43,12 @@ bool Renderer::loadDocument(QString path, QString password){
//Clear out the old document first
delete DOC;
DOC=0;
+ if(linkHash.size() > 0) {
+ foreach(QList<Link*> linkArray, linkHash) {
+ qDeleteAll(linkArray);
+ }
+ linkHash.clear();
+ }
needpass = false;
pnum=0;
docpath = path;
@@ -91,6 +111,17 @@ void Renderer::renderPage(int pagenum, QSize DPI, int degrees){
}
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()) {
+ 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);
+ linkArray.append(newLink);
+ }
+ linkHash.insert(pagenum, linkArray);
delete PAGE;
}
//qDebug() << "Done Render Page:" << pagenum << img.size();
@@ -132,4 +163,53 @@ bool Renderer::supportsExtraFeatures() { return false; }
void Renderer::traverseOutline(void *, int) { }
-void Renderer::handleLink(QString link) { }
+void Renderer::handleLink(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())
+ trueLink = link;
+ }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);
+ }
+}
+
+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) { return linkHash[pageNum].size(); }
+
+bool Renderer::isExternalLink(int pageNum, QString text) {
+ 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(text == dynamic_cast<Poppler::LinkBrowse*>(link)->url())
+ trueLink = link;
+ }else if(link->linkType() == Poppler::Link::LinkType::Goto) {
+ if(text == dynamic_cast<Poppler::LinkGoto*>(link)->fileName())
+ trueLink = link;
+ }
+ }
+ }
+ if(trueLink) {
+ if(trueLink->linkType() == Poppler::Link::LinkType::Goto)
+ return dynamic_cast<Poppler::LinkGoto*>(trueLink)->isExternal();
+ if(trueLink->linkType() == Poppler::Link::LinkType::Browse)
+ return true;
+ }
+}
diff --git a/src-qt5/desktop-utils/lumina-pdf/Renderer.h b/src-qt5/desktop-utils/lumina-pdf/Renderer.h
index 22548278..cc48b089 100644
--- a/src-qt5/desktop-utils/lumina-pdf/Renderer.h
+++ b/src-qt5/desktop-utils/lumina-pdf/Renderer.h
@@ -46,6 +46,9 @@ public:
QList<TextData*> searchDocument(QString text, bool matchCase);
void traverseOutline(void *, int);
void handleLink(QString);
+ TextData *linkList(int, int);
+ int linkSize(int);
+ bool isExternalLink(int, QString);
void clearHash();
//Makes sure degrees is between 0 and 360 then rotates the matrix and
bgstack15