Commit dc4f42b8 authored by Volker Krause's avatar Volker Krause
Browse files

Provide access to external links in PDF documents

parent 3279bda9
......@@ -5,6 +5,7 @@
*/
#include <KItinerary/PdfDocument>
#include <KItinerary/PdfLink>
#include <QFile>
#include <QObject>
......@@ -73,6 +74,24 @@ private Q_SLOTS:
QCOMPARE(doc->modificationTime(), QDateTime());
}
void testPdfLink()
{
QFile f(QStringLiteral(SOURCE_DIR "/misc/link.pdf"));
QVERIFY(f.open(QFile::ReadOnly));
QVERIFY(PdfDocument::maybePdf(f.readAll()));
f.seek(0);
std::unique_ptr<PdfDocument> doc(PdfDocument::fromData(f.readAll()));
QVERIFY(doc);
QCOMPARE(doc->pageCount(), 1);
const auto page = doc->page(0);
QCOMPARE(page.linkCount(), 1);
const auto link = page.link(0);
QCOMPARE(link.url(), QLatin1String("https://kde.org"));
qDebug() << link.area();
QVERIFY(link.area().isValid());
}
void testInvalidPdfDocument()
{
QVERIFY(!PdfDocument::maybePdf(QByteArray()));
......
......@@ -17,6 +17,8 @@
"width": 79
}
],
"links": [
],
"text": " Akademy Airways\n Boarding Pass\nFrom: Vienna International, Terminal 2\nTo: Milano Malpensa, Terminal 1\nFlight: AK 1996\nGate: A36\nBoarding Time: 15:20\nDeparture Time: 15:45\nArrival Time: 17:20\nPassenger: Dragon, Dr. Konqi\n"
}
],
......@@ -76,6 +78,8 @@
"width": 79
}
],
"links": [
],
"text": " Akademy Airways\n Boarding Pass\nFrom: Vienna International, Terminal 2\nTo: Milano Malpensa, Terminal 1\nFlight: AK 1996\nGate: A36\nBoarding Time: 15:20\nDeparture Time: 15:45\nArrival Time: 17:20\nPassenger: Dragon, Dr. Konqi\n"
}
],
......
......@@ -72,6 +72,7 @@ target_sources(KPimItinerary PRIVATE
pdf/pdfdocument.cpp
pdf/pdfextractoroutputdevice.cpp
pdf/pdfimage.cpp
pdf/pdflink.cpp
pdf/pdfvectorpicture.cpp
pdf/popplerglobalparams.cpp
pdf/popplerutils.cpp
......@@ -294,6 +295,7 @@ ecm_generate_headers(KItinerary_Pdf_FORWARDING_HEADERS
HEADER_NAMES
PdfDocument
PdfImage
PdfLink
PREFIX KItinerary
REQUIRED_HEADERS KItinerary_Pdf_HEADERS
RELATIVE pdf
......
......@@ -34,6 +34,7 @@ void PdfPagePrivate::load()
PopplerGlobalParams gp;
PdfExtractorOutputDevice device;
m_doc->m_popplerDoc->displayPageSlice(&device, m_pageNum + 1, 72, 72, 0, false, true, false, -1, -1, -1, -1);
m_doc->m_popplerDoc->processLinks(&device, m_pageNum + 1);
device.finalize();
const auto pageRect = m_doc->m_popplerDoc->getPage(m_pageNum + 1)->getCropBox();
std::unique_ptr<GooString> s(device.getText(pageRect->x1, pageRect->y1, pageRect->x2, pageRect->y2));
......@@ -47,6 +48,12 @@ void PdfPagePrivate::load()
for (auto it = m_images.begin(); it != m_images.end(); ++it) {
(*it).d->m_page = this;
}
m_links = std::move(device.m_links);
for (auto &link : m_links) {
link.convertToPageRect(pageRect);
}
m_loaded = true;
}
......@@ -147,6 +154,27 @@ QVariantList PdfPage::imagesInRect(double left, double top, double right, double
return l;
}
int PdfPage::linkCount() const
{
d->load();
return d->m_links.size();
}
PdfLink PdfPage::link(int index) const
{
d->load();
return d->m_links[index];
}
QVariantList PdfPage::linksVariant() const
{
d->load();
QVariantList l;
l.reserve(d->m_links.size());
std::transform(d->m_links.begin(), d->m_links.end(), std::back_inserter(l), [](const PdfLink &link) { return QVariant::fromValue(link); });
return l;
}
PdfDocument::PdfDocument(QObject *parent)
: QObject(parent)
......
......@@ -20,6 +20,7 @@
namespace KItinerary {
class PdfLink;
class PdfPagePrivate;
/** A page in a PDF document.
......@@ -29,6 +30,7 @@ class KITINERARY_EXPORT PdfPage
Q_GADGET
Q_PROPERTY(QString text READ text)
Q_PROPERTY(QVariantList images READ imagesVariant)
Q_PROPERTY(QVariantList links READ linksVariant)
public:
PdfPage();
PdfPage(const PdfPage&);
......@@ -54,8 +56,15 @@ public:
*/
Q_INVOKABLE QVariantList imagesInRect(double left, double top, double right, double bottom) const;
/** The number of links found in this document. */
int linkCount() const;
/** The n-th link found in this document. */
PdfLink link(int index) const;
private:
QVariantList imagesVariant() const;
QVariantList linksVariant() const;
friend class PdfDocument;
QExplicitlySharedDataPointer<PdfPagePrivate> d;
......
......@@ -22,6 +22,7 @@ namespace KItinerary {
class PdfDocumentPrivate;
class PdfImage;
class PdfLink;
class PdfPage;
class PdfPagePrivate : public QSharedData {
......@@ -32,6 +33,7 @@ public:
bool m_loaded = false;
QString m_text;
std::vector<PdfImage> m_images;
std::vector<PdfLink> m_links;
PdfDocumentPrivate *m_doc;
};
......
......@@ -10,6 +10,10 @@
#include "pdfimage_p.h"
#include "popplerutils_p.h"
#include <Annot.h>
#include <Link.h>
#include <Page.h>
#include <QDebug>
using namespace KItinerary;
......@@ -233,3 +237,21 @@ void PdfExtractorOutputDevice::addVectorImage(const PdfVectorPicture &pic)
img.d->m_vectorPicture = pic;
m_images.push_back(img);
}
void PdfExtractorOutputDevice::processLink(AnnotLink *link)
{
TextOutputDev::processLink(link);
if (!link->isOk() || link->getAction()->getKind() != actionURI) {
return;
}
const auto uriLink = static_cast<LinkURI*>(link->getAction());
double xd1, yd1, xd2, yd2;
link->getRect(&xd1, &yd1, &xd2, &yd2);
double xu1, yu1, xu2, yu2;
cvtDevToUser(xd1, yd1, &xu1, &yu1);
cvtDevToUser(xd2, yd2, &xu2, &yu2);
PdfLink l(QString::fromStdString(uriLink->getURI()), QRectF(QPointF(std::min(xu1, xu2), std::min(yu1, yu2)), QPointF(std::max(xu1, xu2), std::max(yu1, yu2))));
m_links.push_back(std::move(l));
}
......@@ -6,6 +6,7 @@
#pragma once
#include "pdflink.h"
#include "pdfvectorpicture_p.h"
#include "popplertypes_p.h"
......@@ -35,6 +36,7 @@ public:
void stroke(GfxState *state) override;
void fill(GfxState *state) override;
void eoFill(GfxState *state) override;
void processLink(AnnotLink *link) override;
void addVectorImage(const PdfVectorPicture &pic);
......@@ -48,6 +50,9 @@ public:
PdfVectorPicture::PathStroke stroke;
};
std::vector<VectorOp> m_vectorOps;
// extracted links
std::vector<PdfLink> m_links;
};
}
......
/*
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "pdflink.h"
#include <QDebug>
#include <Page.h>
using namespace KItinerary;
namespace KItinerary {
class PdfLinkPrivate : public QSharedData
{
public:
QString url;
QRectF area;
};
}
PdfLink::PdfLink()
: d(new PdfLinkPrivate)
{
}
PdfLink::PdfLink(const QString& url, const QRectF& area)
: d(new PdfLinkPrivate)
{
d->url = url;
d->area = area;
}
PdfLink::PdfLink(const PdfLink&) = default;
PdfLink::~PdfLink() = default;
PdfLink& PdfLink::operator=(const PdfLink&) = default;
QString PdfLink::url() const
{
return d->url;
}
QRectF PdfLink::area() const
{
return d->area;
}
static double toRatio(double low, double high, double value)
{
return (value - low) / (high - low);
}
void PdfLink::convertToPageRect(const PDFRectangle *pageRect)
{
d->area.setLeft(toRatio(pageRect->x1, pageRect->x2, d->area.left()));
d->area.setRight(toRatio(pageRect->x1, pageRect->x2, d->area.right()));
d->area.setTop(toRatio(pageRect->y1, pageRect->y2, d->area.top()));
d->area.setBottom(toRatio(pageRect->y1, pageRect->y2, d->area.bottom()));
}
/*
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KITINERARY_PDFLINK_H
#define KITINERARY_PDFLINK_H
#include "kitinerary_export.h"
#include <QExplicitlySharedDataPointer>
#include <QMetaType>
#include <QRectF>
class PDFRectangle;
namespace KItinerary {
class PdfExtractorOutputDevice;
class PdfLinkPrivate;
class PdfPagePrivate;
/** An external link in a PDF file. */
class KITINERARY_EXPORT PdfLink
{
Q_GADGET
Q_PROPERTY(QString url READ url)
Q_PROPERTY(QRectF area READ area)
public:
PdfLink();
~PdfLink();
PdfLink(const PdfLink&);
PdfLink& operator=(const PdfLink&);
QString url() const;
QRectF area() const;
private:
friend class PdfExtractorOutputDevice;
friend class PdfPagePrivate;
explicit PdfLink(const QString &url, const QRectF &area);
void convertToPageRect(const PDFRectangle *pageRect);
QExplicitlySharedDataPointer<PdfLinkPrivate> d;
};
}
Q_DECLARE_METATYPE(KItinerary::PdfLink)
#endif // KITINERARY_PDFLINK_H
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment