okular/generators/dvi/generator_dvi.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

523 lines
16 KiB
C++
Raw Normal View History

2021-05-24 07:25:56 +00:00
/*
SPDX-FileCopyrightText: 2006-2009 Luigi Toscano <luigi.toscano@tiscali.it>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <core/action.h>
#include <core/document.h>
#include <core/fileprinter.h>
#include <core/page.h>
#include <core/sourcereference.h>
#include <core/textpage.h>
#include "TeXFont.h"
2014-09-11 19:12:27 +00:00
#include "debug_dvi.h"
#include "dviFile.h"
#include "dviPageInfo.h"
#include "dviRenderer.h"
#include "dviexport.h"
#include "generator_dvi.h"
#include "pageSize.h"
2018-08-31 09:23:45 +00:00
#include <QApplication>
#include <QDir>
#include <QMutex>
#include <QStack>
#include <QString>
#include <QTemporaryFile>
#include <QUrl>
#include <QVector>
#include <KAboutData>
2014-10-08 11:37:04 +00:00
#include <KLocalizedString>
2018-08-31 09:23:45 +00:00
#include <QDebug>
#ifdef DVI_OPEN_BUSYLOOP
#include <QThread>
#endif
OKULAR_EXPORT_PLUGIN(DviGenerator, "libokularGenerator_dvi.json")
DviGenerator::DviGenerator(QObject *parent, const QVariantList &args)
: Okular::Generator(parent, args)
, m_fontExtracted(false)
, m_docSynopsis(nullptr)
, m_dviRenderer(nullptr)
{
setFeature(Threaded);
setFeature(TextExtraction);
setFeature(FontInfo);
setFeature(PrintPostscript);
if (Okular::FilePrinter::ps2pdfAvailable()) {
setFeature(PrintToFile);
}
}
bool DviGenerator::loadDocument(const QString &fileName, QVector<Okular::Page *> &pagesVector)
{
2014-09-11 19:12:27 +00:00
// qCDebug(OkularDviDebug) << "file:" << fileName;
2015-01-29 19:55:57 +00:00
QUrl base(QUrl::fromLocalFile(fileName));
(void)userMutex();
m_dviRenderer = new dviRenderer(documentMetaData(TextHintingMetaData, QVariant()).toBool());
connect(m_dviRenderer, &dviRenderer::error, this, &DviGenerator::error);
connect(m_dviRenderer, &dviRenderer::warning, this, &DviGenerator::warning);
connect(m_dviRenderer, &dviRenderer::notice, this, &DviGenerator::notice);
#ifdef DVI_OPEN_BUSYLOOP
static const ushort s_waitTime = 800; // milliseconds
static const int s_maxIterations = 10;
int iter = 0;
for (; !m_dviRenderer->isValidFile(fileName) && iter < s_maxIterations; ++iter) {
2014-09-11 19:12:27 +00:00
qCDebug(OkularDviDebug).nospace() << "file not valid after iteration #" << iter << "/" << s_maxIterations << ", waiting for " << s_waitTime;
QThread::msleep(s_waitTime);
}
if (iter >= s_maxIterations && !m_dviRenderer->isValidFile(fileName)) {
2014-09-11 19:12:27 +00:00
qCDebug(OkularDviDebug) << "file still not valid after" << s_maxIterations;
delete m_dviRenderer;
m_dviRenderer = 0;
return false;
}
#else
if (!m_dviRenderer->isValidFile(fileName)) {
delete m_dviRenderer;
m_dviRenderer = nullptr;
return false;
}
#endif
if (!m_dviRenderer->setFile(fileName, base)) {
delete m_dviRenderer;
m_dviRenderer = nullptr;
return false;
}
2014-09-11 19:12:27 +00:00
qCDebug(OkularDviDebug) << "# of pages:" << m_dviRenderer->dviFile->total_pages;
m_resolution = dpi().height();
loadPages(pagesVector);
return true;
}
bool DviGenerator::doCloseDocument()
{
delete m_docSynopsis;
m_docSynopsis = nullptr;
delete m_dviRenderer;
m_dviRenderer = nullptr;
m_linkGenerated.clear();
m_fontExtracted = false;
return true;
}
void DviGenerator::fillViewportFromAnchor(Okular::DocumentViewport &vp, const Anchor anch, const Okular::Page *page) const
{
fillViewportFromAnchor(vp, anch, page->width(), page->height());
}
void DviGenerator::fillViewportFromAnchor(Okular::DocumentViewport &vp, const Anchor anch, int pW, int pH) const
{
vp.pageNumber = static_cast<quint16>(anch.page) - 1;
SimplePageSize ps = m_dviRenderer->sizeOfPage(PageNumber(vp.pageNumber));
double resolution = 0;
if (ps.isValid()) {
resolution = (double)(pW) / ps.width().getLength_in_inch();
} else {
resolution = m_resolution;
}
double py = (double)anch.distance_from_top.getLength_in_inch() * resolution + 0.5;
vp.rePos.normalizedX = 0.5;
vp.rePos.normalizedY = py / (double)pH;
vp.rePos.enabled = true;
vp.rePos.pos = Okular::DocumentViewport::Center;
}
QList<Okular::ObjectRect *> DviGenerator::generateDviLinks(const dviPageInfo *pageInfo)
{
QList<Okular::ObjectRect *> dviLinks;
int pageWidth = pageInfo->width, pageHeight = pageInfo->height;
for (const Hyperlink &dviLink : qAsConst(pageInfo->hyperLinkList)) {
QRect boxArea = dviLink.box;
double nl = (double)boxArea.left() / pageWidth, nt = (double)boxArea.top() / pageHeight, nr = (double)boxArea.right() / pageWidth, nb = (double)boxArea.bottom() / pageHeight;
QString linkText = dviLink.linkText;
2015-10-29 12:37:11 +00:00
if (linkText.startsWith(QLatin1String("#"))) {
linkText = linkText.mid(1);
}
Anchor anch = m_dviRenderer->findAnchor(linkText);
Okular::Action *okuLink = nullptr;
/* distinguish between local (-> anchor) and remote links */
if (anch.isValid()) {
/* internal link */
Okular::DocumentViewport vp;
fillViewportFromAnchor(vp, anch, pageWidth, pageHeight);
2015-10-29 12:37:11 +00:00
okuLink = new Okular::GotoAction(QLatin1String(""), vp);
} else {
2014-08-24 20:52:23 +00:00
okuLink = new Okular::BrowseAction(QUrl::fromUserInput(dviLink.linkText));
}
if (okuLink) {
Okular::ObjectRect *orlink = new Okular::ObjectRect(nl, nt, nr, nb, false, Okular::ObjectRect::Action, okuLink);
dviLinks.push_front(orlink);
}
}
return dviLinks;
}
QImage DviGenerator::image(Okular::PixmapRequest *request)
{
dviPageInfo *pageInfo = new dviPageInfo();
pageSize ps;
QImage ret;
pageInfo->width = request->width();
pageInfo->height = request->height();
pageInfo->pageNumber = request->pageNumber() + 1;
// pageInfo->resolution = m_resolution;
QMutexLocker lock(userMutex());
if (m_dviRenderer) {
SimplePageSize s = m_dviRenderer->sizeOfPage(pageInfo->pageNumber);
/* if ( s.width() != pageInfo->width) */
// if (!useDocumentSpecifiedSize)
// s = userPreferredSize;
if (s.isValid()) {
2020-02-21 15:11:42 +00:00
pageInfo->resolution = (double)(pageInfo->width) / s.width().getLength_in_inch();
} else {
pageInfo->resolution = (double)(pageInfo->width) / ps.width().getLength_in_inch();
}
m_dviRenderer->drawPage(pageInfo);
if (!pageInfo->img.isNull()) {
2014-09-11 19:12:27 +00:00
qCDebug(OkularDviDebug) << "Image OK";
ret = pageInfo->img;
if (!m_linkGenerated[request->pageNumber()]) {
request->page()->setObjectRects(generateDviLinks(pageInfo));
m_linkGenerated[request->pageNumber()] = true;
}
}
}
lock.unlock();
delete pageInfo;
return ret;
}
Okular::TextPage *DviGenerator::textPage(Okular::TextRequest *request)
{
const Okular::Page *page = request->page();
2014-09-11 19:12:27 +00:00
qCDebug(OkularDviDebug);
dviPageInfo *pageInfo = new dviPageInfo();
pageSize ps;
pageInfo->width = page->width();
pageInfo->height = page->height();
pageInfo->pageNumber = page->number() + 1;
pageInfo->resolution = m_resolution;
QMutexLocker lock(userMutex());
// get page text from m_dviRenderer
Okular::TextPage *ktp = nullptr;
if (m_dviRenderer) {
SimplePageSize s = m_dviRenderer->sizeOfPage(pageInfo->pageNumber);
pageInfo->resolution = (double)(pageInfo->width) / ps.width().getLength_in_inch();
m_dviRenderer->getText(pageInfo);
lock.unlock();
ktp = extractTextFromPage(pageInfo);
}
delete pageInfo;
return ktp;
}
Okular::TextPage *DviGenerator::extractTextFromPage(dviPageInfo *pageInfo)
{
QList<Okular::TextEntity *> textOfThePage;
QVector<TextBox>::ConstIterator it = pageInfo->textBoxList.constBegin();
QVector<TextBox>::ConstIterator itEnd = pageInfo->textBoxList.constEnd();
int pageWidth = pageInfo->width, pageHeight = pageInfo->height;
for (; it != itEnd; ++it) {
TextBox curTB = *it;
textOfThePage.push_back(new Okular::TextEntity(curTB.text, new Okular::NormalizedRect(curTB.box, pageWidth, pageHeight)));
}
Okular::TextPage *ktp = new Okular::TextPage(textOfThePage);
return ktp;
}
Okular::DocumentInfo DviGenerator::generateDocumentInfo(const QSet<Okular::DocumentInfo::Key> &keys) const
{
Okular::DocumentInfo docInfo;
if (keys.contains(Okular::DocumentInfo::MimeType)) {
2015-10-29 12:37:11 +00:00
docInfo.set(Okular::DocumentInfo::MimeType, QStringLiteral("application/x-dvi"));
}
QMutexLocker lock(userMutex());
if (m_dviRenderer && m_dviRenderer->dviFile) {
dvifile *dvif = m_dviRenderer->dviFile;
// read properties from dvif
// docInfo.set( "filename", dvif->filename, i18n("Filename") );
if (keys.contains(Okular::DocumentInfo::CustomKeys)) {
2015-10-29 12:37:11 +00:00
docInfo.set(QStringLiteral("generatorDate"), dvif->generatorString, i18n("Generator/Date"));
}
if (keys.contains(Okular::DocumentInfo::Pages)) {
docInfo.set(Okular::DocumentInfo::Pages, QString::number(dvif->total_pages));
}
}
return docInfo;
}
const Okular::DocumentSynopsis *DviGenerator::generateDocumentSynopsis()
{
if (m_docSynopsis) {
return m_docSynopsis;
}
m_docSynopsis = new Okular::DocumentSynopsis();
userMutex()->lock();
QVector<PreBookmark> prebookmarks = m_dviRenderer->getPrebookmarks();
userMutex()->unlock();
if (prebookmarks.isEmpty()) {
return m_docSynopsis;
}
QStack<QDomElement> stack;
QVector<PreBookmark>::ConstIterator it = prebookmarks.constBegin();
QVector<PreBookmark>::ConstIterator itEnd = prebookmarks.constEnd();
for (; it != itEnd; ++it) {
QDomElement domel = m_docSynopsis->createElement((*it).title);
Anchor a = m_dviRenderer->findAnchor((*it).anchorName);
if (a.isValid()) {
Okular::DocumentViewport vp;
const Okular::Page *p = document()->page(static_cast<quint16>(a.page) - 1);
fillViewportFromAnchor(vp, a, (int)p->width(), (int)p->height());
2015-10-29 12:37:11 +00:00
domel.setAttribute(QStringLiteral("Viewport"), vp.toString());
}
if (stack.isEmpty()) {
m_docSynopsis->appendChild(domel);
} else {
stack.top().appendChild(domel);
stack.pop();
}
for (int i = 0; i < (*it).noOfChildren; ++i) {
stack.push(domel);
}
}
return m_docSynopsis;
}
Okular::FontInfo::List DviGenerator::fontsForPage(int page)
{
Q_UNUSED(page);
Okular::FontInfo::List list;
// the list of the fonts is extracted once
if (m_fontExtracted) {
return list;
}
if (m_dviRenderer && m_dviRenderer->dviFile && m_dviRenderer->dviFile->font_pool) {
const QList<TeXFontDefinition *> fonts = m_dviRenderer->dviFile->font_pool->fontList;
for (const TeXFontDefinition *font : fonts) {
Okular::FontInfo of;
QString name;
int zoom = (int)(font->enlargement * 100 + 0.5);
#ifdef HAVE_FREETYPE
if (font->getFullFontName().isEmpty()) {
2015-10-29 12:37:11 +00:00
name = QStringLiteral("%1, %2%").arg(font->fontname).arg(zoom);
} else {
2016-10-29 14:32:24 +00:00
name = QStringLiteral("%1 (%2), %3%").arg(font->fontname, font->getFullFontName(), QString::number(zoom));
}
#else
name = QString("%1, %2%").arg(font->fontname).arg(zoom);
#endif
of.setName(name);
QString fontFileName;
if (!(font->flags & TeXFontDefinition::FONT_VIRTUAL)) {
if (font->font != nullptr) {
fontFileName = font->font->errorMessage;
} else {
fontFileName = i18n("Font file not found");
}
if (fontFileName.isEmpty()) {
fontFileName = font->filename;
}
}
of.setFile(fontFileName);
Okular::FontInfo::FontType ft;
switch (font->getFontType()) {
case TeXFontDefinition::TEX_PK:
ft = Okular::FontInfo::TeXPK;
break;
case TeXFontDefinition::TEX_VIRTUAL:
ft = Okular::FontInfo::TeXVirtual;
break;
case TeXFontDefinition::TEX_FONTMETRIC:
ft = Okular::FontInfo::TeXFontMetric;
break;
case TeXFontDefinition::FREETYPE:
ft = Okular::FontInfo::TeXFreeTypeHandled;
break;
}
of.setType(ft);
// DVI has not the concept of "font embedding"
of.setEmbedType(Okular::FontInfo::NotEmbedded);
of.setCanBeExtracted(false);
list.append(of);
}
m_fontExtracted = true;
}
return list;
}
void DviGenerator::loadPages(QVector<Okular::Page *> &pagesVector)
{
QSize pageRequiredSize;
int numofpages = m_dviRenderer->dviFile->total_pages;
pagesVector.resize(numofpages);
m_linkGenerated.fill(false, numofpages);
2014-09-11 19:12:27 +00:00
// qCDebug(OkularDviDebug) << "resolution:" << m_resolution << ", dviFile->preferred?";
/* get the suggested size */
if (m_dviRenderer->dviFile->suggestedPageSize) {
pageRequiredSize = m_dviRenderer->dviFile->suggestedPageSize->sizeInPixel(m_resolution);
} else {
pageSize ps;
pageRequiredSize = ps.sizeInPixel(m_resolution);
}
for (int i = 0; i < numofpages; ++i) {
2014-09-11 19:12:27 +00:00
// qCDebug(OkularDviDebug) << "getting status of page" << i << ":";
if (pagesVector[i]) {
delete pagesVector[i];
}
Okular::Page *page = new Okular::Page(i, pageRequiredSize.width(), pageRequiredSize.height(), Okular::Rotation0);
pagesVector[i] = page;
}
2014-09-11 19:12:27 +00:00
qCDebug(OkularDviDebug) << "pagesVector successfully inizialized!";
// filling the pages with the source references rects
const QVector<DVI_SourceFileAnchor> &sourceAnchors = m_dviRenderer->sourceAnchors();
QVector<QList<Okular::SourceRefObjectRect *>> refRects(numofpages);
for (const DVI_SourceFileAnchor &sfa : sourceAnchors) {
if (sfa.page < 1 || (int)sfa.page > numofpages) {
continue;
}
Okular::NormalizedPoint p(-1.0, (double)sfa.distance_from_top.getLength_in_pixel(dpi().height()) / (double)pageRequiredSize.height());
Okular::SourceReference *sourceRef = new Okular::SourceReference(sfa.fileName, sfa.line);
refRects[sfa.page - 1].append(new Okular::SourceRefObjectRect(p, sourceRef));
}
for (int i = 0; i < refRects.size(); ++i) {
if (!refRects.at(i).isEmpty()) {
pagesVector[i]->setSourceReferences(refRects.at(i));
}
}
}
Okular::Document::PrintError DviGenerator::print(QPrinter &printer)
{
// Create tempfile to write to
2014-09-17 22:30:39 +00:00
QTemporaryFile tf(QDir::tempPath() + QLatin1String("/okular_XXXXXX.ps"));
if (!tf.open()) {
return Okular::Document::TemporaryFileOpenPrintError;
}
const QList<int> pageList = Okular::FilePrinter::pageList(printer, static_cast<quint16>(m_dviRenderer->totalPages()), document()->currentPage() + 1, document()->bookmarkedPageList());
QString pages;
QStringList printOptions;
// List of pages to print.
for (const int p : pageList) {
2015-10-29 12:37:11 +00:00
pages += QStringLiteral(",%1").arg(p);
}
if (!pages.isEmpty()) {
2015-10-29 12:37:11 +00:00
printOptions << QStringLiteral("-pp") << pages.mid(1);
}
QEventLoop el;
m_dviRenderer->setEventLoop(&el);
m_dviRenderer->exportPS(tf.fileName(), printOptions, &printer, document()->orientation());
tf.close();
// Error messages are handled by the generator - ugly, but it works.
return Okular::Document::NoPrintError;
}
QVariant DviGenerator::metaData(const QString &key, const QVariant &option) const
{
2015-10-29 12:37:11 +00:00
if (key == QLatin1String("NamedViewport") && !option.toString().isEmpty()) {
const Anchor anchor = m_dviRenderer->parseReference(option.toString());
if (anchor.isValid()) {
const Okular::Page *page = document()->page(static_cast<quint16>(anchor.page) - 1);
Q_ASSERT_X(page, "DviGenerator::metaData()", "NULL page as result of valid Anchor");
Okular::DocumentViewport viewport;
fillViewportFromAnchor(viewport, anchor, page);
if (viewport.isValid()) {
return viewport.toString();
}
}
}
return QVariant();
}
Q_LOGGING_CATEGORY(OkularDviDebug, "org.kde.okular.generators.dvi.core", QtWarningMsg)
Q_LOGGING_CATEGORY(OkularDviShellDebug, "org.kde.okular.generators.dvi.shell", QtWarningMsg)
2014-09-11 19:12:27 +00:00
#include "generator_dvi.moc"