From 253bacc6ee0d825564bef6aa507296e79b2611ff Mon Sep 17 00:00:00 2001 From: ZackaryWelch Date: Fri, 27 Apr 2018 17:26:02 -0400 Subject: Added more annotation and widget support. Currently incomplete. Updated to MuPDF 1.13. --- src-qt5/desktop-utils/lumina-pdf/Annotation.h | 46 +++ src-qt5/desktop-utils/lumina-pdf/BookmarkMenu.cpp | 2 +- src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp | 58 +++- src-qt5/desktop-utils/lumina-pdf/PrintWidget.h | 83 +++-- .../desktop-utils/lumina-pdf/Renderer-mupdf.cpp | 350 +++++++++++++++++---- .../desktop-utils/lumina-pdf/Renderer-poppler.cpp | 39 +-- src-qt5/desktop-utils/lumina-pdf/Renderer.h | 46 +-- src-qt5/desktop-utils/lumina-pdf/Widget.h | 29 ++ src-qt5/desktop-utils/lumina-pdf/lumina-pdf.pro | 4 +- src-qt5/desktop-utils/lumina-pdf/mainUI.cpp | 13 + 10 files changed, 520 insertions(+), 150 deletions(-) create mode 100644 src-qt5/desktop-utils/lumina-pdf/Annotation.h create mode 100644 src-qt5/desktop-utils/lumina-pdf/Widget.h (limited to 'src-qt5') diff --git a/src-qt5/desktop-utils/lumina-pdf/Annotation.h b/src-qt5/desktop-utils/lumina-pdf/Annotation.h new file mode 100644 index 00000000..60df6a2d --- /dev/null +++ b/src-qt5/desktop-utils/lumina-pdf/Annotation.h @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +class Annotation{ + public: + Annotation(int _annotType, double _opacity, QRectF _loc = QRectF()) : annotType(_annotType), opacity(_opacity), loc(_loc) { } + + virtual ~Annotation() { } + + virtual int getType() { return annotType; } + virtual QRectF getLoc() { return loc; } + + virtual QString getAuthor() { return author; } + virtual QString getText() { return text; } + virtual QList getQuadList() { return quadList; } + virtual QVector> getInkList() { return inkList; } + virtual QColor getColor() { return color; } + virtual QColor getInternalColor() { return iColor; } + virtual double getOpacity() { return opacity; } + virtual bool print() { return canPrint; } + + virtual void setAuthor(QString _author) { author = _author; } + virtual void setContents(QString _text) { text = _text; } + virtual void setColor(QColor _color) { color = _color; }; + virtual void setInternalColor(QColor _iColor) { iColor = _iColor; }; + virtual void setQuadList(QList _quadList) { quadList = _quadList; }; + virtual void setInkList(QVector> _inkList) { inkList = _inkList; }; + virtual void setPrint(bool _print) { canPrint = _print; } + + virtual QImage renderImage() = 0; + + private: + int annotType; + double opacity; + QRectF loc; + + QString author; + QString text; + QColor color; + QColor iColor; + QList quadList; + QVector> inkList; + bool canPrint; +}; diff --git a/src-qt5/desktop-utils/lumina-pdf/BookmarkMenu.cpp b/src-qt5/desktop-utils/lumina-pdf/BookmarkMenu.cpp index a90007d8..902fe0ff 100644 --- a/src-qt5/desktop-utils/lumina-pdf/BookmarkMenu.cpp +++ b/src-qt5/desktop-utils/lumina-pdf/BookmarkMenu.cpp @@ -16,7 +16,7 @@ BookmarkMenu::BookmarkMenu(Renderer *Backend, QWidget *parent) : QWidget(parent) connect(ui->closeButton, &QPushButton::clicked, parent, [=]() { static_cast(parent)->setSizes(QList() << 0 << this->width()); }); connect(ui->closeButton, &QPushButton::clicked, parent, [=]() { }); connect(ui->bookmarks, &QTreeWidget::itemClicked, this, [=](QTreeWidgetItem *item) { - Backend->handleLink(item->data(1, Qt::UserRole).toString()); }); + Backend->handleLink(parent, item->data(1, Qt::UserRole).toString()); }); } void BookmarkMenu::loadBookmarks() { diff --git a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp index e9a0ed34..f64106d3 100644 --- a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp +++ b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.cpp @@ -1,4 +1,6 @@ #include "PrintWidget.h" +#include +#include PrintWidget::PrintWidget(Renderer *backend, QWidget *parent) : QGraphicsView(parent), scene(0), curPage(1), viewMode(SinglePageView), @@ -226,23 +228,23 @@ void PrintWidget::populateScene() } qDeleteAll(pages); pages.clear(); - //links.clear(); - //annots.clear(); + links.clear(); + annots.clear(); int numPages = BACKEND->numPages(); if(BACKEND->hashSize() < numPages){ return; } //nothing to show yet for (int i = 0; i < numPages; i++) { QImage pagePicture = BACKEND->imageHash(i); QSize paperSize = pagePicture.size(); - //QList linkLocations; - //QList annotLocations; + QList linkLocations; + QList annotLocations; if(pagePicture.isNull()) { qDebug() << "NULL IMAGE ON PAGE " << i; continue; } - PageItem* item = new PageItem(i+1, pagePicture, paperSize); + PageItem* item = new PageItem(i+1, pagePicture, paperSize, BACKEND); scene->addItem(item); pages.append(item); @@ -250,17 +252,51 @@ void PrintWidget::populateScene() 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); + linkLocations.append(lItem); } //qDebug() << "Creating annotations for:" << i; for(int k = 0; k < BACKEND->annotSize(i); k++) { - AnnotItem *aItem = new AnnotItem(item, BACKEND->annotList(i, k), BACKEND->annotLoc(i, k)); - AnnotZone *aZone = new AnnotZone(item, BACKEND->annotLoc(i, k), aItem); + Annotation *annot = BACKEND->annotList(i, k); + if(annot->getType() == 14) { + InkItem *iItem = new InkItem(item, annot); + annotLocations.append(iItem); + } + PopupItem *aItem = new PopupItem(item, annot); + AnnotZone *aZone = new AnnotZone(item, annot, aItem); aItem->setVisible(false); - //annotLocations.append(aItem); + annotLocations.append(aItem); + annotLocations.append(aZone); } - //links.insert(i, linkLocations); - //annots.insert(i, annotLocations); + + for(int k = 0; k < BACKEND->widgetSize(i); k++) { + Widget *widget = BACKEND->widgetList(i, k); + int type = widget->getWidgetType(); + QRectF loc = widget->getLocation(); + QString text = widget->getCurrentText(); + if(type == 0) { + QPushButton *button = new QPushButton(widget->getCurrentText()); + button->setGeometry(loc.toRect()); + button->setText(text); + QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(item); + proxy->setWidget(button); + }else if(type == 1) { + + }else if(type == 2) { + + }else if(type == 3) { + + }else if(type == 4) { + + }else if(type == 5) { + + }else if(type == 6) { + + }else { + qDebug() << "INVALID WIDGET"; + } + } + links.insert(i, linkLocations); + annots.insert(i, annotLocations); } } } diff --git a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h index d7e3d92e..c353cb84 100644 --- a/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h +++ b/src-qt5/desktop-utils/lumina-pdf/PrintWidget.h @@ -20,12 +20,41 @@ #include #include #include +#include #include "Renderer.h" #include "TextData.h" -class AnnotItem: public QGraphicsItem { +class InkItem: public QGraphicsItem { public: - AnnotItem(QGraphicsItem *parent, QList textData, QRectF loc) : QGraphicsItem(parent), author(textData[0]), text(textData[1]) { + InkItem(QGraphicsItem *parent, Annotation *_annot) : QGraphicsItem(parent), pointData(_annot->getInkList()), inkColor(_annot->getColor()), annot(_annot) { + setCacheMode(DeviceCoordinateCache); + bbox = annot->getLoc(); + } + + QRectF boundingRect() const Q_DECL_OVERRIDE { return bbox; } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) Q_DECL_OVERRIDE + { + Q_UNUSED(widget); + painter->setClipRect(option->exposedRect); + QPen inkPen = QPen(inkColor); + painter->setPen(inkPen); + foreach(QVector pointList, pointData) { + painter->drawLines(pointList); + } + } + +private: + QRectF bbox; + QVector> pointData; + QColor inkColor; + Annotation *annot; +}; + +class PopupItem: public QGraphicsItem { +public: + PopupItem(QGraphicsItem *parent, Annotation *_annot) : QGraphicsItem(parent), author(_annot->getAuthor()), text(_annot->getText()) { + QRectF loc = _annot->getLoc(); setCacheMode(DeviceCoordinateCache); QString allText = "Author: " + author + "\n\n" + text; QTextDocument document; @@ -59,14 +88,22 @@ private: class AnnotZone: public QGraphicsItem { public: - AnnotZone(QGraphicsItem *parent, QRectF _bbox, AnnotItem *_annot) : QGraphicsItem(parent), bbox(_bbox), annot(_annot) { } + AnnotZone(QGraphicsItem *parent, Annotation *_annot, PopupItem *_annotItem) : QGraphicsItem(parent), bbox(_annot->getLoc()), annot(_annotItem) { + _hasText = !_annot->getText().isEmpty(); + _hasAuthor = !_annot->getAuthor().isEmpty(); + } + QRectF boundingRect() const Q_DECL_OVERRIDE { return bbox; } - AnnotItem* annotation() const { return annot; } + PopupItem* annotation() const { return annot; } + bool hasText() const { return _hasText; } + bool hasAuthor() const { return _hasAuthor; } void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) Q_DECL_OVERRIDE { Q_UNUSED(widget); Q_UNUSED(painter); Q_UNUSED(option); } private: QRectF bbox; - AnnotItem *annot; + PopupItem *annot; + bool _hasText; + bool _hasAuthor; }; class LinkItem: public QGraphicsItem { @@ -98,8 +135,8 @@ private: class PageItem : public QGraphicsItem { public: - PageItem(int _pageNum, QImage _pagePicture, QSize _paperSize) - : pageNum(_pageNum), pagePicture(_pagePicture), paperSize(_paperSize) + PageItem(int _pageNum, QImage _pagePicture, QSize _paperSize, Renderer *_backend) + : pageNum(_pageNum), pagePicture(_pagePicture), paperSize(_paperSize), BACKEND(_backend) { brect = QRectF(QPointF(-25, -25), QSizeF(paperSize)+QSizeF(50, 50)); @@ -146,6 +183,10 @@ public: painter->setClipRect(paperRect & option->exposedRect); painter->fillRect(paperRect, Qt::white); painter->drawImage(QPoint(0,0), pagePicture); + for(int k = 0; k < BACKEND->annotSize(pageNum-1); k++) { + Annotation *annot = BACKEND->annotList(pageNum-1, k); + painter->drawImage(annot->getLoc(), annot->renderImage()); + } } private: @@ -153,6 +194,7 @@ private: QImage pagePicture; QSize paperSize; QRectF brect; + Renderer *BACKEND; }; class PrintWidget : public QGraphicsView @@ -187,6 +229,7 @@ private: bool initialized, fitting; QList pages; QHash> links; + QHash> annots; int degrees; Renderer *BACKEND; @@ -239,17 +282,18 @@ protected: continue; if(graphicsItem == dynamic_cast(graphicsItem)) graphicsItem->setOpacity(0.1); - if(graphicsItem == dynamic_cast(graphicsItem)) + if(graphicsItem == dynamic_cast(graphicsItem)) graphicsItem->setVisible(false); } } void mouseMoveEvent(QMouseEvent *e) Q_DECL_OVERRIDE { QGraphicsView::mouseMoveEvent(e); + static bool cursorSet = false; if(QGraphicsItem *item = scene->itemAt(mapToScene(e->pos()), transform())) { QList linkList; - if(item == dynamic_cast(item)) + if(item == dynamic_cast(item)) item = item->parentItem(); if(PageItem *page = dynamic_cast(item)) @@ -259,11 +303,19 @@ protected: if(LinkItem *link = dynamic_cast(item)){ item->setOpacity(1); - }else if(AnnotZone *annotZone = dynamic_cast(item)){ - annotZone->annotation()->setVisible(true); + if(!cursorSet) { + QApplication::setOverrideCursor(QCursor(Qt::PointingHandCursor)); + cursorSet = true; + } + }else if(cursorSet){ + QApplication::restoreOverrideCursor(); + cursorSet = false; + } + if(AnnotZone *annotZone = dynamic_cast(item)){ + if(annotZone->hasText() or annotZone->hasAuthor()) + annotZone->annotation()->setVisible(true); item = annotZone->annotation(); } - clearItems(linkList, item); } } @@ -273,12 +325,7 @@ protected: QPointF scenePoint = mapToScene(e->pos()); QGraphicsItem *item = scene->itemAt(scenePoint, transform()); if(LinkItem *link = dynamic_cast(item)) { - PageItem *page = dynamic_cast(link->parentItem()); - if(!BACKEND->isExternalLink(page->pageNumber()-1, link->getData()->text())) { - BACKEND->handleLink(link->getData()->text()); - }else{ - //External Link - } + BACKEND->handleLink(this, link->getData()->text()); link->setOpacity(0.1); } } diff --git a/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp b/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp index 31a07e01..a52fd5b4 100644 --- a/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp +++ b/src-qt5/desktop-utils/lumina-pdf/Renderer-mupdf.cpp @@ -1,34 +1,17 @@ #include "Renderer.h" #include "TextData.h" -#include #include #include +#include #include #include #include -class Annot{ - public: - Annot(fz_context *_ctx, pdf_annot *_fzAnnot, char *_text, char *_author, QRectF _loc = QRectF()) : fzAnnot(_fzAnnot), ctx(_ctx), loc(_loc) { - author = (_author) ? QString::fromLocal8Bit(_author) : QString(); - text = (_text) ? QString::fromLocal8Bit(_text) : QString(); - } - - ~Annot() { - fz_drop_annot(ctx, (fz_annot*)fzAnnot); - } - - QString getAuthor() { return author; } - QString getText() { return text; } - QRectF getLoc() { return loc; } +class Annot; - private: - pdf_annot *fzAnnot; - fz_context *ctx; - QString author; - QString text; - QRectF loc; -}; +inline QRectF convertRect(fz_rect bbox, double sf=1.0) { + return QRectF(sf*bbox.x0, sf*bbox.y0, sf*(bbox.x1-bbox.x0), sf*(bbox.y1 - bbox.y0)); +} class Link { public: @@ -52,15 +35,14 @@ class Link { class Data { public: - Data(int _pagenum, fz_context *_ctx, fz_display_list *_list, fz_rect _bbox, fz_matrix _ctm, double _sf, fz_link *_link, QList &_annot) : pagenumber(_pagenum), ctx(_ctx), list(_list), bbox(_bbox), ctm(_ctm), sf(_sf), annotList(_annot) { + Data(int _pagenum, fz_context *_ctx, fz_display_list *_list, fz_rect _bbox, fz_matrix _ctm, double _sf, fz_link *_link, QList &_annot, QList &_widgets) : pagenumber(_pagenum), ctx(_ctx), list(_list), bbox(_bbox), ctm(_ctm), sf(_sf), annotList(_annot), widgetList(_widgets) { 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)); + QRectF rect = convertRect(_link->rect, sf); Link *link = new Link(_ctx, _link, _link->uri, _pagenum, rect); linkList.append(link); _link = _link->next; } - } ~Data() { @@ -70,14 +52,19 @@ class Data { linkList.clear(); qDeleteAll(annotList); annotList.clear(); + qDeleteAll(widgetList); + widgetList.clear(); } int getPage() { return pagenumber; } QList getLinkList() { return linkList; } - QList getAnnotList() { return annotList; } + Annot* getAnnotList(int i) { return annotList[i]; } + Widget* getWidgetList(int i) { return widgetList[i]; } + int getAnnotSize() { return annotList.size(); } + int getWidgetSize() { return widgetList.size(); } 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)); } + QRectF getScaledRect() { return convertRect(bbox, sf); } fz_rect getBoundingBox() { return bbox; } fz_matrix getMatrix() { return ctm; } QImage getImage() { return img; } @@ -95,6 +82,7 @@ class Data { QList linkList; double sf; QList annotList; + QList widgetList; fz_pixmap *pix; QImage img; @@ -107,6 +95,32 @@ QHash dataHash; QMutex mutex[FZ_LOCK_MAX]; fz_locks_context locks; +class Annot : public Annotation{ + public: + Annot(fz_context *_ctx, pdf_annot *_fzAnnot, int _type, int _i, float _opacity, QRectF _loc = QRectF()) : Annotation(_type, _opacity, _loc), ctx(_ctx), fzAnnot(_fzAnnot), pageNum(_i) { } + virtual ~Annot() { fz_drop_annot(ctx, (fz_annot*)fzAnnot); } + + virtual QImage renderImage() { + fz_rect bbox; + fz_irect rbox; + pdf_bound_annot(ctx, fzAnnot, &bbox); + + 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); + fz_device *dev = fz_new_draw_device(ctx, &fz_identity, pixmap); + fz_run_display_list(ctx, dataHash[pageNum]->getDisplayList(), dev, &fz_identity, &bbox, NULL); + + QImage image(pixmap->samples, pixmap->w, pixmap->h, pixmap->stride, QImage::Format_RGB888); + return image; + } + + private: + fz_context *ctx; + pdf_annot *fzAnnot; + + int pageNum; +}; + inline QString getTextInfo(QString str) { char infoBuff[1000]; int size = DOC->lookup_metadata(CTX, DOC, ("info:"+str).toLocal8Bit().data(), infoBuff, 1000); @@ -137,6 +151,7 @@ Renderer::Renderer(){ } Renderer::~Renderer(){ + //pdf_clean_page_contents qDebug() << "Dropping Context"; clearHash(); fz_drop_document(CTX, DOC); @@ -147,7 +162,7 @@ Renderer::~Renderer(){ bool Renderer::loadMultiThread(){ return false; } -void Renderer::handleLink(QString link) { +void Renderer::handleLink(QWidget *obj, QString link) { float xp = 0.0, yp = 0.0; int pagenum = 0; @@ -155,9 +170,13 @@ void Renderer::handleLink(QString link) { char *uri = linkData.data(); if(!link.isEmpty()) { - pagenum = fz_resolve_link(CTX, DOC, uri, &xp, &yp); - if(pagenum != -1) - emit goToPosition(pagenum+1, xp, yp); + if(fz_is_external_link(CTX, uri)) { + if(QMessageBox::Yes == QMessageBox::question(obj, tr("Open External Link?"), QString(tr("Do you want to open %1 in the default browser")).arg(link), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ){ QProcess::startDetached("firefox \""+link+"\""); } + }else{ + pagenum = fz_resolve_link(CTX, DOC, uri, &xp, &yp); + if(pagenum != -1) + emit goToPosition(pagenum+1, xp, yp); + } } } @@ -193,6 +212,8 @@ bool Renderer::loadDocument(QString path, QString password){ qDebug() << "Document handlers registered"; } + //fz_page_presentation + docpath = path; DOC = fz_open_document(CTX, path.toLocal8Bit().data()); qDebug() << "File opened" << DOC; @@ -280,49 +301,250 @@ void Renderer::renderPage(int pagenum, QSize DPI, int degrees){ fz_rect bbox; fz_display_list *list; - //double pageDPI = 96.0; - //double sf = DPI.width() / pageDPI; - double sf = 1; - //fz_scale(&matrix, sf, sf); - fz_rotate(&matrix, degrees); + double pageDPI = 96.0; + double sf = DPI.width() / pageDPI; + fz_scale(&matrix, sf, sf); + fz_pre_rotate(&matrix, degrees); - fz_page *PAGE = fz_load_page(CTX, DOC, pagenum); - fz_bound_page(CTX, PAGE, &bbox); + pdf_page *PAGE = pdf_load_page(CTX, (pdf_document*)DOC, pagenum); + pdf_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); + pdf_run_page_contents(CTX, PAGE, dev, &fz_identity, NULL); - fz_link *link = fz_load_links(CTX, PAGE); - pdf_annot *_annot = pdf_first_annot(CTX, (pdf_page*)PAGE); + fz_link *link = pdf_load_links(CTX, PAGE); + pdf_annot *_annot = pdf_first_annot(CTX, PAGE); QList annotList; + QList widgetList; //qDebug() << "Starting annotations for:" << pagenum; while(_annot) { - pdf_run_annot(CTX, _annot, dev, &fz_identity, NULL); + //if(pdf_annot_is_dirty(CTX, _annot)) + //qDebug() << "DIRTY ANNOT"; + + int type = pdf_annot_type(CTX, _annot); + /**TYPES + 0 = TEXT + 1 = LINK + 2 = FREE_TEXT + 3 = LINE + 4 = SQUARE + ... + 24 = 3D + -1 = UNKNOWN + **/ + //qDebug() << "Page Number:" << pagenum+1 << type << pdf_string_from_annot_type(CTX, (enum pdf_annot_type)type); fz_rect anotBox; pdf_bound_annot(CTX, _annot, &anotBox); - QRectF rect(sf*anotBox.x0, sf*anotBox.y0, sf*(anotBox.x1-anotBox.x0), sf*(anotBox.y1 - anotBox.y0)); + QRectF rect = convertRect(anotBox, sf); + float opacity = pdf_annot_opacity(CTX, _annot); + /**TYPES + 0 = LEFT + 1 = CENTER + 2 = RIGHT + **/ + int quadding = pdf_annot_quadding(CTX, _annot); + + Annot *annot = new Annot(CTX, _annot, type, pagenum, opacity, rect); + + int flags = pdf_annot_flags(CTX, _annot); + /**FLAGS + 1 << 0 = INVISIBLE + 1 << 1 = HIDDEN + 1 << 2 = PRINT + 1 << 3 = NO_ZOOM + 1 << 4 = NO_ROTATE + 1 << 5 = NO_VIEW + 1 << 6 = READ_ONLY + 1 << 7 = LOCKED + 1 << 8 = TOGGLE_NO_VIEW + 1 << 9 = LOCKED_CONTENTS + **/ + + annot->setPrint((flags & PDF_ANNOT_IS_PRINT) == PDF_ANNOT_IS_PRINT); + char *contents = NULL, *author = NULL; - fz_try(CTX) - contents = pdf_copy_annot_contents(CTX, _annot); - fz_catch(CTX) { } - fz_try(CTX) - author = pdf_copy_annot_author(CTX, _annot); - fz_catch(CTX) { } + if(type != 19 and type != 20) { + fz_try(CTX){ + contents = pdf_copy_annot_contents(CTX, _annot); + if(contents) + annot->setContents(QString::fromLocal8Bit(contents)); + }fz_catch(CTX) { } + fz_try(CTX){ + author = pdf_copy_annot_author(CTX, _annot); + if(author) + annot->setAuthor(QString::fromLocal8Bit(author)); + }fz_catch(CTX) { } + } + //pdf_annot_modification_date(CTX, _annot); + + if(pdf_annot_has_ink_list(CTX, _annot)) { + int inkCount = pdf_annot_ink_list_count(CTX, _annot); + QVector> inkList; + for(int i = 0; i < inkCount; i++) { + int strokeCount = pdf_annot_ink_list_stroke_count(CTX, _annot, i); + QVector inkPoints; + for(int k = 0; k < strokeCount; k++) { + fz_point strokeVertex = pdf_annot_ink_list_stroke_vertex(CTX, _annot, i, k); + QPointF vertexPoint(strokeVertex.x, strokeVertex.y); + inkPoints.append(sf*vertexPoint); + } + inkList.append(inkPoints); + annot->setInkList(inkList); + } + fz_rect annotRect; + pdf_annot_rect(CTX, _annot, &annotRect); + //qDebug() << "ANNOT RECT:" << convertRect(annotRect, sf); + //qDebug() << "INK LIST:" << inkList; + } + + float color[4] = {0, 0, 0, 1}; + int n; + fz_try(CTX) { + pdf_annot_color(CTX, _annot, &n, color); + QColor inkColor = QColor(color[0]*255, color[1]*255, color[2]*255, color[3]*255); + annot->setColor(inkColor); + //qDebug() << "COLOR:" << inkColor; + }fz_catch(CTX) { + //qDebug() << "NO COLOR"; + annot->setColor(QColor()); + } + + if(pdf_annot_has_interior_color(CTX, _annot)) { + fz_try(CTX) { + color[0] = 0; color[1] = 0; color[2] = 0; color[3] = 1; + pdf_annot_interior_color(CTX, _annot, &n, color); + QColor internalColor = QColor(color[0]*255, color[1]*255, color[2]*255, color[3]*255); + //qDebug() << "INTERNAL COLOR:" << internalColor; + annot->setInternalColor(internalColor); + }fz_catch(CTX) { + //qDebug() << "NO INTERNAL COLOR"; + annot->setInternalColor(QColor()); + } + } + + //qDebug() << "BORDER:" << pdf_annot_border(CTX, _annot); + + if(pdf_annot_has_quad_points(CTX, _annot)) { + //qDebug() << "HAS QUAD POINTS" << "Page Number:" << pagenum << type; + int pointCount = pdf_annot_quad_point_count(CTX, _annot); + QList quadList; + for(int i = 0; i < pointCount; i++) { + float qp[8]; + pdf_annot_quad_point(CTX, _annot, i, qp); + QPolygonF quad = QPolygonF(QVector() << QPointF(qp[0], qp[1]) << QPointF(qp[2], qp[3]) << QPointF(qp[4], qp[5]) << QPointF(qp[6], qp[7])); + quadList.append(quad); + } + annot->setQuadList(quadList); + } - Annot *annot = new Annot(CTX, _annot, contents, author, rect); + if(pdf_annot_has_line_ending_styles(CTX, _annot)) { + //qDebug() << "HAS LINE ENDING STYLES" << "Page Number:" << pagenum << type; + pdf_line_ending start, end; + /**LINE ENDING + 0 = NONE + 1 = SQUARE + 2 = CIRCLE + ... + 9 = SLASH + **/ + pdf_annot_line_ending_styles(CTX, _annot, &start, &end); + //qDebug() << "START:" << start << "END:" << end; + } + + if(pdf_annot_has_vertices(CTX, _annot)) { + //qDebug() << "HAS VERTICIES" << "Page Number:" << pagenum << type; + int vertexCount = pdf_annot_vertex_count(CTX, _annot); + QList vertexList; + for(int i = 0; i < vertexCount; i++) { + fz_point v = pdf_annot_vertex(CTX, _annot, i); + vertexList.append(sf*QPointF(v.x, v.y)); + } + //qDebug() << vertexList; + } + + if(pdf_annot_has_line(CTX, _annot)) { + fz_point a, b; + pdf_annot_line(CTX, _annot, &a, &b); + QPointF pa(a.x, a.y); + QPointF pb(b.x, b.y); + //qDebug() << pa << pb; + } + + if(pdf_annot_has_icon_name(CTX, _annot)) { + QString iconName = QString::fromLocal8Bit(pdf_annot_icon_name(CTX, _annot)); + //qDebug() << iconName; + } + + if(!pdf_annot_has_ink_list(CTX, _annot)) { + pdf_run_annot(CTX, _annot, dev, &fz_identity, NULL); + } + annotList.append(annot); - _annot = _annot->next; + _annot = pdf_next_annot(CTX, _annot); + } + + pdf_widget *widget = pdf_first_widget(CTX, (pdf_document*)DOC, PAGE); + while(widget) { + int type = pdf_widget_type(CTX, widget); + /** + -1 = NOT_WIDGET + 0 = PUSHBUTTON + 1 = CHECKBOX + 2 = RADIOBUTTON + 3 = TEXT + 4 = LISTBOX + 5 = COMBOBOX + 6 = SIGNATURE + **/ + + fz_rect wrect; + pdf_bound_widget(CTX, widget, &wrect); + QRectF WRECT = convertRect(wrect, sf); + char *currText = pdf_text_widget_text(CTX, (pdf_document*)DOC, widget); + int maxLen = pdf_text_widget_max_len(CTX, (pdf_document*)DOC, widget); + int contentType = pdf_text_widget_content_type(CTX, (pdf_document*)DOC, widget); + /** + 0 = UNRESTRAINED + 1 = NUMBER + 2 = SPECIAL + 3 = DATE + 4 = TIME + **/ + + Widget *WIDGET = new Widget(type, WRECT, QString::fromLocal8Bit(currText), maxLen, contentType); + if(type == 4 or type == 5) { + QStringList optionList, exportList; + bool multi = pdf_choice_widget_is_multiselect(CTX, (pdf_document*)DOC, widget); + + if(int listS = pdf_choice_widget_options(CTX, (pdf_document*)DOC, widget, 0, NULL)) { + char *opts[listS]; + pdf_choice_widget_options(CTX, (pdf_document*)DOC, widget, 0, opts); + for(int i = 0; i < listS; i++) { optionList.append(QString::fromLocal8Bit(opts[i])); } + WIDGET->setOptions(optionList); + } + + if(int exportS = pdf_choice_widget_options(CTX, (pdf_document*)DOC, widget, 1, NULL)) { + char *opts[exportS]; + pdf_choice_widget_options(CTX, (pdf_document*)DOC, widget, 1, opts); + for(int i = 0; i < exportS; i++) { exportList.append(QString::fromLocal8Bit(opts[i])); } + WIDGET->setExports(exportList); + } + } + + widgetList.push_back(WIDGET); + + widget = pdf_next_widget(CTX, widget); } fz_close_device(CTX, dev); fz_drop_device(CTX, dev); - fz_drop_page(CTX, PAGE); + //fz_drop_page(CTX, PAGE); - data = new Data(pagenum, CTX, list, bbox, matrix, sf, link, annotList); + data = new Data(pagenum, CTX, list, bbox, matrix, sf, link, annotList, widgetList); data->setRenderThread(QtConcurrent::run(&renderer, data, this)); } @@ -368,26 +590,20 @@ int Renderer::linkSize(int pagenum) { return dataHash[pagenum]->getLinkList().size(); } -QList Renderer::annotList(int pagenum, int entry) { - return QList() << dataHash[pagenum]->getAnnotList()[entry]->getAuthor() << dataHash[pagenum]->getAnnotList()[entry]->getText(); +Annotation *Renderer::annotList(int pagenum, int entry) { + return static_cast(dataHash[pagenum]->getAnnotList(entry)); } -QRectF Renderer::annotLoc(int pagenum, int entry) { - return dataHash[pagenum]->getAnnotList()[entry]->getLoc(); +int Renderer::annotSize(int pagenum) { + return dataHash[pagenum]->getAnnotSize(); } -int Renderer::annotSize(int pagenum) { - return dataHash[pagenum]->getAnnotList().size(); +Widget *Renderer::widgetList(int pagenum, int entry) { + return dataHash[pagenum]->getWidgetList(entry); } -bool Renderer::isExternalLink(int pagenum, QString text) { - QList 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; +int Renderer::widgetSize(int pagenum) { + return dataHash[pagenum]->getWidgetSize(); } 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 52412871..b8828557 100644 --- a/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp +++ b/src-qt5/desktop-utils/lumina-pdf/Renderer-poppler.cpp @@ -163,7 +163,7 @@ bool Renderer::supportsExtraFeatures() { return false; } void Renderer::traverseOutline(void *, int) { } -void Renderer::handleLink(QString linkDest) { +void Renderer::handleLink(QWidget *obj, QString linkDest) { Poppler::Link* trueLink; foreach(QList linkArray, linkHash) { for(int i = 0; i < linkArray.size(); i++) { @@ -180,6 +180,9 @@ void Renderer::handleLink(QString linkDest) { if(trueLink) { if(trueLink->linkType() == Poppler::Link::LinkType::Goto) emit goToPosition(dynamic_cast(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+"\""); } + } } } @@ -190,38 +193,12 @@ TextData* Renderer::linkList(int pageNum, int entry) { return 0; } -QList Renderer::annotList(int pageNum, int entry) { - Q_UNUSED(pageNum) - Q_UNUSED(entry) - return QList() << QString() << QString(); -} - int Renderer::linkSize(int pageNum) { Q_UNUSED(pageNum) return linkHash[pageNum].size(); } int Renderer::annotSize(int pageNum) { Q_UNUSED(pageNum) return 0; } -QRectF Renderer::annotLoc(int pageNum, int entry) { Q_UNUSED(pageNum) Q_UNUSED(entry) return QRectF(); } +Annotation *Renderer::annotList(int pageNum, int entry) { Q_UNUSED(pageNum) Q_UNUSED(entry) return NULL; } -bool Renderer::isExternalLink(int pageNum, QString text) { - Q_UNUSED(pageNum) - Poppler::Link* trueLink; - foreach(QList 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(link)->url()) - trueLink = link; - }else if(link->linkType() == Poppler::Link::LinkType::Goto) { - if(text == dynamic_cast(link)->fileName()) - trueLink = link; - } - } - } - if(trueLink) { - if(trueLink->linkType() == Poppler::Link::LinkType::Goto) - return dynamic_cast(trueLink)->isExternal(); - if(trueLink->linkType() == Poppler::Link::LinkType::Browse) - return true; - } - return false; -} +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; } diff --git a/src-qt5/desktop-utils/lumina-pdf/Renderer.h b/src-qt5/desktop-utils/lumina-pdf/Renderer.h index 4dd3e3b1..59d7024b 100644 --- a/src-qt5/desktop-utils/lumina-pdf/Renderer.h +++ b/src-qt5/desktop-utils/lumina-pdf/Renderer.h @@ -10,8 +10,12 @@ #include #include #include +#include +#include #include "TextData.h" #include "Bookmark.h" +#include "Annotation.h" +#include "Widget.h" class Renderer : public QObject { Q_OBJECT @@ -31,29 +35,29 @@ public: bool loadMultiThread(); //Information functions (usually needs to be loaded first) - int numPages(){ return pnum; } - bool needPassword(){ return needpass; } - QString title(){ return doctitle; } - QJsonObject properties() { return jobj; } - int hashSize(); - QImage imageHash(int pagenum); - int rotatedDegrees() { return degrees; } - QList getBookmarks() { return bookmarks; } + virtual int numPages(){ return pnum; } + virtual bool needPassword(){ return needpass; } + virtual QString title(){ return doctitle; } + virtual QJsonObject properties() { return jobj; } + virtual int hashSize(); + virtual QImage imageHash(int pagenum); + virtual int rotatedDegrees() { return degrees; } + virtual QList getBookmarks() { return bookmarks; } //Main access functions - bool loadDocument(QString path, QString password); - void renderPage(int pagenum, QSize DPI, int degrees=0); - QList searchDocument(QString text, bool matchCase); - void traverseOutline(void *, int); - void handleLink(QString); - TextData *linkList(int, int); - int linkSize(int); - QList annotList(int, int); - int annotSize(int); - QRectF annotLoc(int, int); - bool isExternalLink(int, QString); + virtual bool loadDocument(QString path, QString password); + virtual void renderPage(int pagenum, QSize DPI, int degrees=0); + virtual QList searchDocument(QString text, bool matchCase); + virtual void traverseOutline(void *, int); + virtual void handleLink(QWidget*, QString); + virtual TextData *linkList(int, int); + virtual int linkSize(int); + virtual Annotation *annotList(int, int); + virtual int annotSize(int); + virtual Widget *widgetList(int, int); + virtual int widgetSize(int); - void clearHash(); + virtual void clearHash(); //Makes sure degrees is between 0 and 360 then rotates the matrix and void setDegrees(int degrees) { //Mods by 360, but adds and remods because of how C++ treats negative mods @@ -61,7 +65,7 @@ public: emit reloadPages(this->degrees); } - bool supportsExtraFeatures(); + virtual bool supportsExtraFeatures(); signals: void PageLoaded(int); diff --git a/src-qt5/desktop-utils/lumina-pdf/Widget.h b/src-qt5/desktop-utils/lumina-pdf/Widget.h new file mode 100644 index 00000000..0bfe5cdd --- /dev/null +++ b/src-qt5/desktop-utils/lumina-pdf/Widget.h @@ -0,0 +1,29 @@ +#include + +class Widget{ + public: + Widget(int _widgetType, QRectF _loc, QString _currText, int _maxLen, int _contentType) : widgetType(_widgetType), loc(_loc), currText(_currText), maxLen(_maxLen), contentType(_contentType) { } + + virtual ~Widget() { } + + virtual int getWidgetType() { return widgetType; } + virtual QRectF getLocation() { return loc; } + virtual QString getCurrentText() { return currText; } + virtual int getMaxLength() { return maxLen; } + virtual int getContentType() { return contentType; } + virtual QStringList getOptionList() { return optionList; } + virtual QStringList getExportList() { return exportList; } + + virtual void setOptions(QStringList _optionList) { optionList = _optionList; } + virtual void setExports(QStringList _exportList) { exportList = _exportList; } + + private: + int widgetType; + QRectF loc; + QString currText; + int maxLen; + int contentType; + + QStringList optionList; + QStringList exportList; +}; diff --git a/src-qt5/desktop-utils/lumina-pdf/lumina-pdf.pro b/src-qt5/desktop-utils/lumina-pdf/lumina-pdf.pro index c63386c0..80360bb0 100644 --- a/src-qt5/desktop-utils/lumina-pdf/lumina-pdf.pro +++ b/src-qt5/desktop-utils/lumina-pdf/lumina-pdf.pro @@ -30,7 +30,9 @@ HEADERS += mainUI.h \ Renderer.h \ TextData.h \ Bookmark.h \ - BookmarkMenu.h + BookmarkMenu.h \ + Annotation.h \ + Widget.h FORMS += mainUI.ui \ PropDialog.ui \ diff --git a/src-qt5/desktop-utils/lumina-pdf/mainUI.cpp b/src-qt5/desktop-utils/lumina-pdf/mainUI.cpp index aa80489d..3d308b2f 100644 --- a/src-qt5/desktop-utils/lumina-pdf/mainUI.cpp +++ b/src-qt5/desktop-utils/lumina-pdf/mainUI.cpp @@ -484,6 +484,19 @@ void MainUI::paintToPrinter(QPrinter *PRINTER){ QImage img = BACKEND->imageHash(pageCount[i]).scaled(sz, Qt::KeepAspectRatio, Qt::SmoothTransformation); //Now draw the image painter.drawImage(0,0,img); + //Also paint the annotations at their locations + for(int k = 0; k < BACKEND->annotSize(i); k++) { + Annotation *annot = BACKEND->annotList(i, k); + if(annot->print()) { + if(annot->getType() == 14) { + painter.setPen(QPen(annot->getColor())); + foreach(QVector pointList, annot->getInkList()) + painter.drawLines(pointList); + }else{ + painter.drawImage(annot->getLoc(), annot->renderImage()); + } + } + } } progAct->setVisible(false); } -- cgit