mirror of
https://invent.kde.org/graphics/okular
synced 2024-10-02 22:23:43 +00:00
252236d413
We don't need the special properties of QLinkedList in any of those and QLinkedList is deprecated, so move away from it
407 lines
14 KiB
C++
407 lines
14 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 2006 Pino Toscano <toscano.pino@tiscali.it>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "generator_djvu.h"
|
|
|
|
#include <core/action.h>
|
|
#include <core/annotations.h>
|
|
#include <core/area.h>
|
|
#include <core/document.h>
|
|
#include <core/fileprinter.h>
|
|
#include <core/page.h>
|
|
#include <core/textpage.h>
|
|
#include <core/utils.h>
|
|
|
|
#include <QDomDocument>
|
|
#include <QMutex>
|
|
#include <QPixmap>
|
|
#include <QPrinter>
|
|
#include <QString>
|
|
#include <QUuid>
|
|
|
|
#include <KAboutData>
|
|
#include <KLocalizedString>
|
|
#include <QDebug>
|
|
#include <QDir>
|
|
#include <QTemporaryFile>
|
|
|
|
static void recurseCreateTOC(QDomDocument &maindoc, const QDomNode &parent, QDomNode &parentDestination, KDjVu *djvu)
|
|
{
|
|
QDomNode n = parent.firstChild();
|
|
while (!n.isNull()) {
|
|
QDomElement el = n.toElement();
|
|
|
|
QDomElement newel = maindoc.createElement(el.attribute(QStringLiteral("title")));
|
|
parentDestination.appendChild(newel);
|
|
|
|
QString dest;
|
|
if (!(dest = el.attribute(QStringLiteral("PageNumber"))).isEmpty()) {
|
|
Okular::DocumentViewport vp;
|
|
vp.pageNumber = dest.toInt() - 1;
|
|
newel.setAttribute(QStringLiteral("Viewport"), vp.toString());
|
|
} else if (!(dest = el.attribute(QStringLiteral("PageName"))).isEmpty()) {
|
|
Okular::DocumentViewport vp;
|
|
vp.pageNumber = djvu->pageNumber(dest);
|
|
newel.setAttribute(QStringLiteral("Viewport"), vp.toString());
|
|
} else if (!(dest = el.attribute(QStringLiteral("URL"))).isEmpty()) {
|
|
newel.setAttribute(QStringLiteral("URL"), dest);
|
|
}
|
|
|
|
if (el.hasChildNodes()) {
|
|
recurseCreateTOC(maindoc, n, newel, djvu);
|
|
}
|
|
n = n.nextSibling();
|
|
}
|
|
}
|
|
|
|
OKULAR_EXPORT_PLUGIN(DjVuGenerator, "libokularGenerator_djvu.json")
|
|
|
|
DjVuGenerator::DjVuGenerator(QObject *parent, const QVariantList &args)
|
|
: Okular::Generator(parent, args)
|
|
, m_docSyn(nullptr)
|
|
{
|
|
setFeature(TextExtraction);
|
|
setFeature(Threaded);
|
|
setFeature(PrintPostscript);
|
|
if (Okular::FilePrinter::ps2pdfAvailable()) {
|
|
setFeature(PrintToFile);
|
|
}
|
|
|
|
m_djvu = new KDjVu();
|
|
m_djvu->setCacheEnabled(false);
|
|
}
|
|
|
|
DjVuGenerator::~DjVuGenerator()
|
|
{
|
|
delete m_djvu;
|
|
}
|
|
|
|
bool DjVuGenerator::loadDocument(const QString &fileName, QVector<Okular::Page *> &pagesVector)
|
|
{
|
|
QMutexLocker locker(userMutex());
|
|
if (!m_djvu->openFile(fileName)) {
|
|
return false;
|
|
}
|
|
|
|
locker.unlock();
|
|
|
|
loadPages(pagesVector, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DjVuGenerator::doCloseDocument()
|
|
{
|
|
userMutex()->lock();
|
|
m_djvu->closeFile();
|
|
userMutex()->unlock();
|
|
|
|
delete m_docSyn;
|
|
m_docSyn = nullptr;
|
|
|
|
return true;
|
|
}
|
|
|
|
QImage DjVuGenerator::image(Okular::PixmapRequest *request)
|
|
{
|
|
userMutex()->lock();
|
|
QImage img = m_djvu->image(request->pageNumber(), request->width(), request->height(), request->page()->rotation());
|
|
userMutex()->unlock();
|
|
return img;
|
|
}
|
|
|
|
Okular::DocumentInfo DjVuGenerator::generateDocumentInfo(const QSet<Okular::DocumentInfo::Key> &keys) const
|
|
{
|
|
Okular::DocumentInfo docInfo;
|
|
|
|
if (keys.contains(Okular::DocumentInfo::MimeType)) {
|
|
docInfo.set(Okular::DocumentInfo::MimeType, QStringLiteral("image/vnd.djvu"));
|
|
}
|
|
|
|
if (m_djvu) {
|
|
// compile internal structure reading properties from KDjVu
|
|
if (keys.contains(Okular::DocumentInfo::Author)) {
|
|
docInfo.set(Okular::DocumentInfo::Title, m_djvu->metaData(QStringLiteral("title")).toString());
|
|
}
|
|
if (keys.contains(Okular::DocumentInfo::Author)) {
|
|
docInfo.set(Okular::DocumentInfo::Author, m_djvu->metaData(QStringLiteral("author")).toString());
|
|
}
|
|
if (keys.contains(Okular::DocumentInfo::CreationDate)) {
|
|
docInfo.set(Okular::DocumentInfo::CreationDate, m_djvu->metaData(QStringLiteral("year")).toString());
|
|
}
|
|
if (keys.contains(Okular::DocumentInfo::CustomKeys)) {
|
|
docInfo.set(QStringLiteral("editor"), m_djvu->metaData(QStringLiteral("editor")).toString(), i18n("Editor"));
|
|
docInfo.set(QStringLiteral("publisher"), m_djvu->metaData(QStringLiteral("publisher")).toString(), i18n("Publisher"));
|
|
docInfo.set(QStringLiteral("volume"), m_djvu->metaData(QStringLiteral("volume")).toString(), i18n("Volume"));
|
|
docInfo.set(QStringLiteral("documentType"), m_djvu->metaData(QStringLiteral("documentType")).toString(), i18n("Type of document"));
|
|
QVariant numcomponents = m_djvu->metaData(QStringLiteral("componentFile"));
|
|
docInfo.set(QStringLiteral("componentFile"), numcomponents.type() != QVariant::Int ? i18nc("Unknown number of component files", "Unknown") : numcomponents.toString(), i18n("Component Files"));
|
|
}
|
|
}
|
|
|
|
return docInfo;
|
|
}
|
|
|
|
const Okular::DocumentSynopsis *DjVuGenerator::generateDocumentSynopsis()
|
|
{
|
|
QMutexLocker locker(userMutex());
|
|
if (m_docSyn) {
|
|
return m_docSyn;
|
|
}
|
|
|
|
const QDomDocument *doc = m_djvu->documentBookmarks();
|
|
if (doc) {
|
|
m_docSyn = new Okular::DocumentSynopsis();
|
|
recurseCreateTOC(*m_docSyn, *doc, *m_docSyn, m_djvu);
|
|
}
|
|
locker.unlock();
|
|
|
|
return m_docSyn;
|
|
}
|
|
|
|
Okular::Document::PrintError DjVuGenerator::print(QPrinter &printer)
|
|
{
|
|
// Create tempfile to write to
|
|
QTemporaryFile tf(QDir::tempPath() + QLatin1String("/okular_XXXXXX.ps"));
|
|
if (!tf.open()) {
|
|
return Okular::Document::TemporaryFileOpenPrintError;
|
|
}
|
|
const QString fileName = tf.fileName();
|
|
|
|
QMutexLocker locker(userMutex());
|
|
QList<int> pageList = Okular::FilePrinter::pageList(printer, m_djvu->pages().count(), document()->currentPage() + 1, document()->bookmarkedPageList());
|
|
|
|
if (m_djvu->exportAsPostScript(&tf, pageList)) {
|
|
tf.setAutoRemove(false);
|
|
tf.close();
|
|
return Okular::FilePrinter::printFile(printer, fileName, document()->orientation(), Okular::FilePrinter::SystemDeletesFiles, Okular::FilePrinter::ApplicationSelectsPages, document()->bookmarkedPageRange());
|
|
}
|
|
|
|
return Okular::Document::UnknownPrintError;
|
|
}
|
|
|
|
QVariant DjVuGenerator::metaData(const QString &key, const QVariant &option) const
|
|
{
|
|
Q_UNUSED(option)
|
|
if (key == QLatin1String("DocumentTitle")) {
|
|
return m_djvu->metaData(QStringLiteral("title"));
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
Okular::TextPage *DjVuGenerator::textPage(Okular::TextRequest *request)
|
|
{
|
|
userMutex()->lock();
|
|
const Okular::Page *page = request->page();
|
|
QList<KDjVu::TextEntity> te;
|
|
if (te.isEmpty()) {
|
|
te = m_djvu->textEntities(page->number(), QStringLiteral("word"));
|
|
}
|
|
if (te.isEmpty()) {
|
|
te = m_djvu->textEntities(page->number(), QStringLiteral("line"));
|
|
}
|
|
userMutex()->unlock();
|
|
QList<KDjVu::TextEntity>::ConstIterator it = te.constBegin();
|
|
QList<KDjVu::TextEntity>::ConstIterator itEnd = te.constEnd();
|
|
QList<Okular::TextEntity *> words;
|
|
const KDjVu::Page *djvupage = m_djvu->pages().at(page->number());
|
|
for (; it != itEnd; ++it) {
|
|
const KDjVu::TextEntity &cur = *it;
|
|
words.append(new Okular::TextEntity(cur.text(), new Okular::NormalizedRect(cur.rect(), djvupage->width(), djvupage->height())));
|
|
}
|
|
Okular::TextPage *textpage = new Okular::TextPage(words);
|
|
return textpage;
|
|
}
|
|
|
|
void DjVuGenerator::loadPages(QVector<Okular::Page *> &pagesVector, int rotation)
|
|
{
|
|
const QVector<KDjVu::Page *> &djvu_pages = m_djvu->pages();
|
|
int numofpages = djvu_pages.count();
|
|
pagesVector.resize(numofpages);
|
|
|
|
for (int i = 0; i < numofpages; ++i) {
|
|
const KDjVu::Page *p = djvu_pages.at(i);
|
|
if (pagesVector[i]) {
|
|
delete pagesVector[i];
|
|
}
|
|
int w = p->width();
|
|
int h = p->height();
|
|
if (rotation % 2 == 1) {
|
|
qSwap(w, h);
|
|
}
|
|
Okular::Page *page = new Okular::Page(i, w, h, (Okular::Rotation)(p->orientation() + rotation));
|
|
pagesVector[i] = page;
|
|
|
|
QList<KDjVu::Annotation *> annots;
|
|
QList<KDjVu::Link *> links;
|
|
userMutex()->lock();
|
|
m_djvu->linksAndAnnotationsForPage(i, &links, &annots);
|
|
userMutex()->unlock();
|
|
if (!links.isEmpty()) {
|
|
QList<Okular::ObjectRect *> rects;
|
|
QList<KDjVu::Link *>::ConstIterator it = links.constBegin();
|
|
QList<KDjVu::Link *>::ConstIterator itEnd = links.constEnd();
|
|
for (; it != itEnd; ++it) {
|
|
KDjVu::Link *curlink = (*it);
|
|
Okular::ObjectRect *newrect = convertKDjVuLink(i, curlink);
|
|
if (newrect) {
|
|
rects.append(newrect);
|
|
}
|
|
// delete the links as soon as we process them
|
|
delete curlink;
|
|
}
|
|
if (rects.count() > 0) {
|
|
page->setObjectRects(rects);
|
|
}
|
|
}
|
|
if (!annots.isEmpty()) {
|
|
QList<KDjVu::Annotation *>::ConstIterator it = annots.constBegin();
|
|
QList<KDjVu::Annotation *>::ConstIterator itEnd = annots.constEnd();
|
|
for (; it != itEnd; ++it) {
|
|
KDjVu::Annotation *ann = (*it);
|
|
Okular::Annotation *newann = convertKDjVuAnnotation(w, h, ann);
|
|
if (newann) {
|
|
page->addAnnotation(newann);
|
|
}
|
|
// delete the annotations as soon as we process them
|
|
delete ann;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Okular::ObjectRect *DjVuGenerator::convertKDjVuLink(int page, KDjVu::Link *link) const
|
|
{
|
|
Okular::Action *newlink = nullptr;
|
|
Okular::ObjectRect *newrect = nullptr;
|
|
switch (link->type()) {
|
|
case KDjVu::Link::PageLink: {
|
|
KDjVu::PageLink *l = static_cast<KDjVu::PageLink *>(link);
|
|
bool ok = true;
|
|
QString target = l->page();
|
|
if ((target.length() > 0) && target.at(0) == QLatin1Char('#')) {
|
|
target.remove(0, 1);
|
|
}
|
|
int tmppage = target.toInt(&ok);
|
|
if (ok || target.isEmpty()) {
|
|
Okular::DocumentViewport vp;
|
|
if (!target.isEmpty()) {
|
|
vp.pageNumber = (target.at(0) == QLatin1Char('+') || target.at(0) == QLatin1Char('-')) ? page + tmppage : tmppage - 1;
|
|
}
|
|
newlink = new Okular::GotoAction(QString(), vp);
|
|
}
|
|
break;
|
|
}
|
|
case KDjVu::Link::UrlLink: {
|
|
KDjVu::UrlLink *l = static_cast<KDjVu::UrlLink *>(link);
|
|
QString url = l->url();
|
|
newlink = new Okular::BrowseAction(QUrl(url));
|
|
break;
|
|
}
|
|
}
|
|
if (newlink) {
|
|
const KDjVu::Page *p = m_djvu->pages().at(page);
|
|
int width = p->width();
|
|
int height = p->height();
|
|
bool scape_orientation = false; // hack by tokoe, should always create default page
|
|
if (scape_orientation) {
|
|
qSwap(width, height);
|
|
}
|
|
switch (link->areaType()) {
|
|
case KDjVu::Link::RectArea:
|
|
case KDjVu::Link::EllipseArea: {
|
|
QRect r(QPoint(link->point().x(), p->height() - link->point().y() - link->size().height()), link->size());
|
|
bool ellipse = (link->areaType() == KDjVu::Link::EllipseArea);
|
|
newrect = new Okular::ObjectRect(Okular::NormalizedRect(Okular::Utils::rotateRect(r, width, height, 0), width, height), ellipse, Okular::ObjectRect::Action, newlink);
|
|
break;
|
|
}
|
|
case KDjVu::Link::PolygonArea: {
|
|
QPolygon poly = link->polygon();
|
|
QPolygonF newpoly;
|
|
for (int i = 0; i < poly.count(); ++i) {
|
|
int x = poly.at(i).x();
|
|
int y = poly.at(i).y();
|
|
if (scape_orientation) {
|
|
qSwap(x, y);
|
|
} else {
|
|
y = height - y;
|
|
}
|
|
newpoly << QPointF((double)(x) / width, (double)(y) / height);
|
|
}
|
|
if (!newpoly.isEmpty()) {
|
|
newpoly << newpoly.first();
|
|
newrect = new Okular::ObjectRect(newpoly, Okular::ObjectRect::Action, newlink);
|
|
}
|
|
break;
|
|
}
|
|
default:;
|
|
}
|
|
if (!newrect) {
|
|
delete newlink;
|
|
}
|
|
}
|
|
return newrect;
|
|
}
|
|
|
|
Okular::Annotation *DjVuGenerator::convertKDjVuAnnotation(int w, int h, KDjVu::Annotation *ann) const
|
|
{
|
|
Okular::Annotation *newann = nullptr;
|
|
switch (ann->type()) {
|
|
case KDjVu::Annotation::TextAnnotation: {
|
|
KDjVu::TextAnnotation *txtann = static_cast<KDjVu::TextAnnotation *>(ann);
|
|
Okular::TextAnnotation *newtxtann = new Okular::TextAnnotation();
|
|
// boundary
|
|
QRect rect(QPoint(txtann->point().x(), h - txtann->point().y() - txtann->size().height()), txtann->size());
|
|
newtxtann->setBoundingRectangle(Okular::NormalizedRect(Okular::Utils::rotateRect(rect, w, h, 0), w, h));
|
|
// type
|
|
newtxtann->setTextType(txtann->inlineText() ? Okular::TextAnnotation::InPlace : Okular::TextAnnotation::Linked);
|
|
newtxtann->style().setOpacity(txtann->color().alphaF());
|
|
// FIXME remove once the annotation text handling is fixed
|
|
newtxtann->setContents(ann->comment());
|
|
newann = newtxtann;
|
|
break;
|
|
}
|
|
case KDjVu::Annotation::LineAnnotation: {
|
|
KDjVu::LineAnnotation *lineann = static_cast<KDjVu::LineAnnotation *>(ann);
|
|
Okular::LineAnnotation *newlineann = new Okular::LineAnnotation();
|
|
// boundary
|
|
QPoint a(lineann->point().x(), h - lineann->point().y());
|
|
QPoint b(lineann->point2().x(), h - lineann->point2().y());
|
|
QRect rect = QRect(a, b).normalized();
|
|
newlineann->setBoundingRectangle(Okular::NormalizedRect(Okular::Utils::rotateRect(rect, w, h, 0), w, h));
|
|
// line points
|
|
QList<Okular::NormalizedPoint> points;
|
|
points.append(Okular::NormalizedPoint(a.x(), a.y(), w, h));
|
|
points.append(Okular::NormalizedPoint(b.x(), b.y(), w, h));
|
|
newlineann->setLinePoints(points);
|
|
// arrow?
|
|
if (lineann->isArrow()) {
|
|
newlineann->setLineEndStyle(Okular::LineAnnotation::OpenArrow);
|
|
}
|
|
// width
|
|
newlineann->style().setWidth(lineann->width());
|
|
newann = newlineann;
|
|
break;
|
|
}
|
|
}
|
|
if (newann) {
|
|
// setting the common parameters
|
|
newann->style().setColor(ann->color());
|
|
newann->setContents(ann->comment());
|
|
// creating an id as name for the annotation
|
|
QString uid = QUuid::createUuid().toString();
|
|
uid.remove(0, 1);
|
|
uid.chop(1);
|
|
uid.remove(QLatin1Char('-'));
|
|
newann->setUniqueName(uid);
|
|
// is external
|
|
newann->setFlags(newann->flags() | Okular::Annotation::External);
|
|
}
|
|
return newann;
|
|
}
|
|
|
|
#include "generator_djvu.moc"
|