File indexing completed on 2024-05-12 04:33:58
0001 /* 0002 SPDX-FileCopyrightText: 2006-2009 Luigi Toscano <luigi.toscano@tiscali.it> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include <core/action.h> 0008 #include <core/document.h> 0009 #include <core/fileprinter.h> 0010 #include <core/page.h> 0011 #include <core/sourcereference.h> 0012 #include <core/textpage.h> 0013 0014 #include "TeXFont.h" 0015 #include "debug_dvi.h" 0016 #include "dviFile.h" 0017 #include "dviPageInfo.h" 0018 #include "dviRenderer.h" 0019 #include "dviexport.h" 0020 #include "generator_dvi.h" 0021 #include "pageSize.h" 0022 0023 #include <QApplication> 0024 #include <QDir> 0025 #include <QMutex> 0026 #include <QStack> 0027 #include <QString> 0028 #include <QTemporaryFile> 0029 #include <QUrl> 0030 #include <QVector> 0031 0032 #include <KAboutData> 0033 #include <KLocalizedString> 0034 #include <QDebug> 0035 0036 #ifdef DVI_OPEN_BUSYLOOP 0037 #include <QThread> 0038 #endif 0039 0040 OKULAR_EXPORT_PLUGIN(DviGenerator, "libokularGenerator_dvi.json") 0041 0042 DviGenerator::DviGenerator(QObject *parent, const QVariantList &args) 0043 : Okular::Generator(parent, args) 0044 , m_fontExtracted(false) 0045 , m_docSynopsis(nullptr) 0046 , m_dviRenderer(nullptr) 0047 { 0048 setFeature(Threaded); 0049 setFeature(TextExtraction); 0050 setFeature(FontInfo); 0051 setFeature(PrintPostscript); 0052 if (Okular::FilePrinter::ps2pdfAvailable()) { 0053 setFeature(PrintToFile); 0054 } 0055 } 0056 0057 bool DviGenerator::loadDocument(const QString &fileName, QVector<Okular::Page *> &pagesVector) 0058 { 0059 // qCDebug(OkularDviDebug) << "file:" << fileName; 0060 QUrl base(QUrl::fromLocalFile(fileName)); 0061 0062 (void)userMutex(); 0063 0064 m_dviRenderer = new dviRenderer(documentMetaData(TextHintingMetaData, QVariant()).toBool()); 0065 connect(m_dviRenderer, &dviRenderer::error, this, &DviGenerator::error); 0066 connect(m_dviRenderer, &dviRenderer::warning, this, &DviGenerator::warning); 0067 connect(m_dviRenderer, &dviRenderer::notice, this, &DviGenerator::notice); 0068 #ifdef DVI_OPEN_BUSYLOOP 0069 static const ushort s_waitTime = 800; // milliseconds 0070 static const int s_maxIterations = 10; 0071 int iter = 0; 0072 for (; !m_dviRenderer->isValidFile(fileName) && iter < s_maxIterations; ++iter) { 0073 qCDebug(OkularDviDebug).nospace() << "file not valid after iteration #" << iter << "/" << s_maxIterations << ", waiting for " << s_waitTime; 0074 QThread::msleep(s_waitTime); 0075 } 0076 if (iter >= s_maxIterations && !m_dviRenderer->isValidFile(fileName)) { 0077 qCDebug(OkularDviDebug) << "file still not valid after" << s_maxIterations; 0078 delete m_dviRenderer; 0079 m_dviRenderer = 0; 0080 return false; 0081 } 0082 #else 0083 if (!m_dviRenderer->isValidFile(fileName)) { 0084 delete m_dviRenderer; 0085 m_dviRenderer = nullptr; 0086 return false; 0087 } 0088 #endif 0089 if (!m_dviRenderer->setFile(fileName, base)) { 0090 delete m_dviRenderer; 0091 m_dviRenderer = nullptr; 0092 return false; 0093 } 0094 0095 qCDebug(OkularDviDebug) << "# of pages:" << m_dviRenderer->dviFile->total_pages; 0096 0097 m_resolution = dpi().height(); 0098 loadPages(pagesVector); 0099 0100 return true; 0101 } 0102 0103 bool DviGenerator::doCloseDocument() 0104 { 0105 delete m_docSynopsis; 0106 m_docSynopsis = nullptr; 0107 delete m_dviRenderer; 0108 m_dviRenderer = nullptr; 0109 0110 m_linkGenerated.clear(); 0111 m_fontExtracted = false; 0112 0113 return true; 0114 } 0115 0116 void DviGenerator::fillViewportFromAnchor(Okular::DocumentViewport &vp, const Anchor anch, const Okular::Page *page) const 0117 { 0118 fillViewportFromAnchor(vp, anch, page->width(), page->height()); 0119 } 0120 0121 void DviGenerator::fillViewportFromAnchor(Okular::DocumentViewport &vp, const Anchor anch, int pW, int pH) const 0122 { 0123 vp.pageNumber = static_cast<quint16>(anch.page) - 1; 0124 0125 SimplePageSize ps = m_dviRenderer->sizeOfPage(PageNumber(vp.pageNumber)); 0126 double resolution = 0; 0127 if (ps.isValid()) { 0128 resolution = (double)(pW) / ps.width().getLength_in_inch(); 0129 } else { 0130 resolution = m_resolution; 0131 } 0132 0133 double py = (double)anch.distance_from_top.getLength_in_inch() * resolution + 0.5; 0134 0135 vp.rePos.normalizedX = 0.5; 0136 vp.rePos.normalizedY = py / (double)pH; 0137 vp.rePos.enabled = true; 0138 vp.rePos.pos = Okular::DocumentViewport::Center; 0139 } 0140 0141 QList<Okular::ObjectRect *> DviGenerator::generateDviLinks(const dviPageInfo *pageInfo) 0142 { 0143 QList<Okular::ObjectRect *> dviLinks; 0144 0145 int pageWidth = pageInfo->width, pageHeight = pageInfo->height; 0146 0147 for (const Hyperlink &dviLink : std::as_const(pageInfo->hyperLinkList)) { 0148 QRect boxArea = dviLink.box; 0149 double nl = (double)boxArea.left() / pageWidth, nt = (double)boxArea.top() / pageHeight, nr = (double)boxArea.right() / pageWidth, nb = (double)boxArea.bottom() / pageHeight; 0150 0151 QString linkText = dviLink.linkText; 0152 if (linkText.startsWith(QLatin1String("#"))) { 0153 linkText = linkText.mid(1); 0154 } 0155 Anchor anch = m_dviRenderer->findAnchor(linkText); 0156 0157 Okular::Action *okuLink = nullptr; 0158 0159 /* distinguish between local (-> anchor) and remote links */ 0160 if (anch.isValid()) { 0161 /* internal link */ 0162 Okular::DocumentViewport vp; 0163 fillViewportFromAnchor(vp, anch, pageWidth, pageHeight); 0164 0165 okuLink = new Okular::GotoAction(QLatin1String(""), vp); 0166 } else { 0167 okuLink = new Okular::BrowseAction(QUrl::fromUserInput(dviLink.linkText)); 0168 } 0169 if (okuLink) { 0170 Okular::ObjectRect *orlink = new Okular::ObjectRect(nl, nt, nr, nb, false, Okular::ObjectRect::Action, okuLink); 0171 dviLinks.push_front(orlink); 0172 } 0173 } 0174 return dviLinks; 0175 } 0176 0177 QImage DviGenerator::image(Okular::PixmapRequest *request) 0178 { 0179 dviPageInfo *pageInfo = new dviPageInfo(); 0180 pageSize ps; 0181 QImage ret; 0182 0183 pageInfo->width = request->width(); 0184 pageInfo->height = request->height(); 0185 0186 pageInfo->pageNumber = request->pageNumber() + 1; 0187 0188 // pageInfo->resolution = m_resolution; 0189 0190 QMutexLocker lock(userMutex()); 0191 0192 if (m_dviRenderer) { 0193 SimplePageSize s = m_dviRenderer->sizeOfPage(pageInfo->pageNumber); 0194 0195 /* if ( s.width() != pageInfo->width) */ 0196 // if (!useDocumentSpecifiedSize) 0197 // s = userPreferredSize; 0198 0199 if (s.isValid()) { 0200 pageInfo->resolution = (double)(pageInfo->width) / s.width().getLength_in_inch(); 0201 } else { 0202 pageInfo->resolution = (double)(pageInfo->width) / ps.width().getLength_in_inch(); 0203 } 0204 0205 m_dviRenderer->drawPage(pageInfo); 0206 0207 if (!pageInfo->img.isNull()) { 0208 qCDebug(OkularDviDebug) << "Image OK"; 0209 0210 ret = pageInfo->img; 0211 0212 if (!m_linkGenerated[request->pageNumber()]) { 0213 request->page()->setObjectRects(generateDviLinks(pageInfo)); 0214 m_linkGenerated[request->pageNumber()] = true; 0215 } 0216 } 0217 } 0218 0219 lock.unlock(); 0220 0221 delete pageInfo; 0222 0223 return ret; 0224 } 0225 0226 Okular::TextPage *DviGenerator::textPage(Okular::TextRequest *request) 0227 { 0228 const Okular::Page *page = request->page(); 0229 0230 qCDebug(OkularDviDebug); 0231 dviPageInfo pageInfo; 0232 0233 pageInfo.width = page->width(); 0234 pageInfo.height = page->height(); 0235 0236 pageInfo.pageNumber = page->number() + 1; 0237 0238 pageInfo.resolution = m_resolution; 0239 0240 QMutexLocker lock(userMutex()); 0241 0242 // get page text from m_dviRenderer 0243 Okular::TextPage *ktp = nullptr; 0244 if (m_dviRenderer) { 0245 SimplePageSize s = m_dviRenderer->sizeOfPage(pageInfo.pageNumber); 0246 pageInfo.resolution = (double)(pageInfo.width) / s.width().getLength_in_inch(); 0247 0248 m_dviRenderer->getText(&pageInfo); 0249 lock.unlock(); 0250 0251 ktp = extractTextFromPage(pageInfo); 0252 } 0253 return ktp; 0254 } 0255 0256 Okular::TextPage *DviGenerator::extractTextFromPage(const dviPageInfo &pageInfo) 0257 { 0258 QList<Okular::TextEntity> textOfThePage; 0259 0260 int pageWidth = pageInfo.width, pageHeight = pageInfo.height; 0261 0262 for (const TextBox &curTB : std::as_const(pageInfo.textBoxList)) { 0263 textOfThePage.push_back(Okular::TextEntity(curTB.text, Okular::NormalizedRect(curTB.box, pageWidth, pageHeight))); 0264 } 0265 0266 Okular::TextPage *ktp = new Okular::TextPage(textOfThePage); 0267 0268 return ktp; 0269 } 0270 0271 Okular::DocumentInfo DviGenerator::generateDocumentInfo(const QSet<Okular::DocumentInfo::Key> &keys) const 0272 { 0273 Okular::DocumentInfo docInfo; 0274 0275 if (keys.contains(Okular::DocumentInfo::MimeType)) { 0276 docInfo.set(Okular::DocumentInfo::MimeType, QStringLiteral("application/x-dvi")); 0277 } 0278 0279 QMutexLocker lock(userMutex()); 0280 0281 if (m_dviRenderer && m_dviRenderer->dviFile) { 0282 dvifile *dvif = m_dviRenderer->dviFile; 0283 0284 // read properties from dvif 0285 // docInfo.set( "filename", dvif->filename, i18n("Filename") ); 0286 if (keys.contains(Okular::DocumentInfo::CustomKeys)) { 0287 docInfo.set(QStringLiteral("generatorDate"), dvif->generatorString, i18n("Generator/Date")); 0288 } 0289 if (keys.contains(Okular::DocumentInfo::Pages)) { 0290 docInfo.set(Okular::DocumentInfo::Pages, QString::number(dvif->total_pages)); 0291 } 0292 } 0293 return docInfo; 0294 } 0295 0296 const Okular::DocumentSynopsis *DviGenerator::generateDocumentSynopsis() 0297 { 0298 if (m_docSynopsis) { 0299 return m_docSynopsis; 0300 } 0301 0302 m_docSynopsis = new Okular::DocumentSynopsis(); 0303 0304 userMutex()->lock(); 0305 0306 QVector<PreBookmark> prebookmarks = m_dviRenderer->getPrebookmarks(); 0307 0308 userMutex()->unlock(); 0309 0310 if (prebookmarks.isEmpty()) { 0311 return m_docSynopsis; 0312 } 0313 0314 QStack<QDomElement> stack; 0315 0316 QVector<PreBookmark>::ConstIterator it = prebookmarks.constBegin(); 0317 QVector<PreBookmark>::ConstIterator itEnd = prebookmarks.constEnd(); 0318 for (; it != itEnd; ++it) { 0319 QDomElement domel = m_docSynopsis->createElement((*it).title); 0320 Anchor a = m_dviRenderer->findAnchor((*it).anchorName); 0321 if (a.isValid()) { 0322 Okular::DocumentViewport vp; 0323 0324 const Okular::Page *p = document()->page(static_cast<quint16>(a.page) - 1); 0325 0326 fillViewportFromAnchor(vp, a, (int)p->width(), (int)p->height()); 0327 domel.setAttribute(QStringLiteral("Viewport"), vp.toString()); 0328 } 0329 if (stack.isEmpty()) { 0330 m_docSynopsis->appendChild(domel); 0331 } else { 0332 stack.top().appendChild(domel); 0333 stack.pop(); 0334 } 0335 for (int i = 0; i < (*it).noOfChildren; ++i) { 0336 stack.push(domel); 0337 } 0338 } 0339 0340 return m_docSynopsis; 0341 } 0342 0343 Okular::FontInfo::List DviGenerator::fontsForPage(int page) 0344 { 0345 Q_UNUSED(page); 0346 0347 Okular::FontInfo::List list; 0348 0349 // the list of the fonts is extracted once 0350 if (m_fontExtracted) { 0351 return list; 0352 } 0353 0354 if (m_dviRenderer && m_dviRenderer->dviFile && m_dviRenderer->dviFile->font_pool) { 0355 const QList<TeXFontDefinition *> fonts = m_dviRenderer->dviFile->font_pool->fontList; 0356 for (const TeXFontDefinition *font : fonts) { 0357 Okular::FontInfo of; 0358 QString name; 0359 int zoom = (int)(font->enlargement * 100 + 0.5); 0360 #ifdef HAVE_FREETYPE 0361 if (font->getFullFontName().isEmpty()) { 0362 name = QStringLiteral("%1, %2%").arg(font->fontname).arg(zoom); 0363 } else { 0364 name = QStringLiteral("%1 (%2), %3%").arg(font->fontname, font->getFullFontName(), QString::number(zoom)); 0365 } 0366 #else 0367 name = QString("%1, %2%").arg(font->fontname).arg(zoom); 0368 #endif 0369 of.setName(name); 0370 0371 QString fontFileName; 0372 if (!(font->flags & TeXFontDefinition::FONT_VIRTUAL)) { 0373 if (font->font != nullptr) { 0374 fontFileName = font->font->errorMessage; 0375 } else { 0376 fontFileName = i18n("Font file not found"); 0377 } 0378 0379 if (fontFileName.isEmpty()) { 0380 fontFileName = font->filename; 0381 } 0382 } 0383 0384 of.setFile(fontFileName); 0385 0386 Okular::FontInfo::FontType ft; 0387 switch (font->getFontType()) { 0388 case TeXFontDefinition::TEX_PK: 0389 ft = Okular::FontInfo::TeXPK; 0390 break; 0391 case TeXFontDefinition::TEX_VIRTUAL: 0392 ft = Okular::FontInfo::TeXVirtual; 0393 break; 0394 case TeXFontDefinition::TEX_FONTMETRIC: 0395 ft = Okular::FontInfo::TeXFontMetric; 0396 break; 0397 case TeXFontDefinition::FREETYPE: 0398 ft = Okular::FontInfo::TeXFreeTypeHandled; 0399 break; 0400 } 0401 of.setType(ft); 0402 0403 // DVI has not the concept of "font embedding" 0404 of.setEmbedType(Okular::FontInfo::NotEmbedded); 0405 of.setCanBeExtracted(false); 0406 0407 list.append(of); 0408 } 0409 0410 m_fontExtracted = true; 0411 } 0412 0413 return list; 0414 } 0415 0416 void DviGenerator::loadPages(QVector<Okular::Page *> &pagesVector) 0417 { 0418 QSize pageRequiredSize; 0419 0420 int numofpages = m_dviRenderer->dviFile->total_pages; 0421 pagesVector.resize(numofpages); 0422 0423 m_linkGenerated.fill(false, numofpages); 0424 0425 // qCDebug(OkularDviDebug) << "resolution:" << m_resolution << ", dviFile->preferred?"; 0426 0427 /* get the suggested size */ 0428 if (m_dviRenderer->dviFile->suggestedPageSize) { 0429 pageRequiredSize = m_dviRenderer->dviFile->suggestedPageSize->sizeInPixel(m_resolution); 0430 } else { 0431 pageSize ps; 0432 pageRequiredSize = ps.sizeInPixel(m_resolution); 0433 } 0434 0435 for (int i = 0; i < numofpages; ++i) { 0436 // qCDebug(OkularDviDebug) << "getting status of page" << i << ":"; 0437 0438 if (pagesVector[i]) { 0439 delete pagesVector[i]; 0440 } 0441 0442 Okular::Page *page = new Okular::Page(i, pageRequiredSize.width(), pageRequiredSize.height(), Okular::Rotation0); 0443 0444 pagesVector[i] = page; 0445 } 0446 qCDebug(OkularDviDebug) << "pagesVector successfully inizialized!"; 0447 0448 // filling the pages with the source references rects 0449 const QVector<DVI_SourceFileAnchor> &sourceAnchors = m_dviRenderer->sourceAnchors(); 0450 QVector<QList<Okular::SourceRefObjectRect *>> refRects(numofpages); 0451 for (const DVI_SourceFileAnchor &sfa : sourceAnchors) { 0452 if (sfa.page < 1 || (int)sfa.page > numofpages) { 0453 continue; 0454 } 0455 0456 Okular::NormalizedPoint p(-1.0, (double)sfa.distance_from_top.getLength_in_pixel(dpi().height()) / (double)pageRequiredSize.height()); 0457 Okular::SourceReference *sourceRef = new Okular::SourceReference(sfa.fileName, sfa.line); 0458 refRects[sfa.page - 1].append(new Okular::SourceRefObjectRect(p, sourceRef)); 0459 } 0460 for (int i = 0; i < refRects.size(); ++i) { 0461 if (!refRects.at(i).isEmpty()) { 0462 pagesVector[i]->setSourceReferences(refRects.at(i)); 0463 } 0464 } 0465 } 0466 0467 Okular::Document::PrintError DviGenerator::print(QPrinter &printer) 0468 { 0469 // Create tempfile to write to 0470 QTemporaryFile tf(QDir::tempPath() + QLatin1String("/okular_XXXXXX.ps")); 0471 if (!tf.open()) { 0472 return Okular::Document::TemporaryFileOpenPrintError; 0473 } 0474 0475 const QList<int> pageList = Okular::FilePrinter::pageList(printer, static_cast<quint16>(m_dviRenderer->totalPages()), document()->currentPage() + 1, document()->bookmarkedPageList()); 0476 QString pages; 0477 QStringList printOptions; 0478 // List of pages to print. 0479 for (const int p : pageList) { 0480 pages += QStringLiteral(",%1").arg(p); 0481 } 0482 if (!pages.isEmpty()) { 0483 printOptions << QStringLiteral("-pp") << pages.mid(1); 0484 } 0485 0486 QEventLoop el; 0487 m_dviRenderer->setEventLoop(&el); 0488 m_dviRenderer->exportPS(tf.fileName(), printOptions, &printer, document()->orientation()); 0489 0490 tf.close(); 0491 0492 // Error messages are handled by the generator - ugly, but it works. 0493 return Okular::Document::NoPrintError; 0494 } 0495 0496 QVariant DviGenerator::metaData(const QString &key, const QVariant &option) const 0497 { 0498 if (key == QLatin1String("NamedViewport") && !option.toString().isEmpty()) { 0499 const Anchor anchor = m_dviRenderer->parseReference(option.toString()); 0500 if (anchor.isValid()) { 0501 const Okular::Page *page = document()->page(static_cast<quint16>(anchor.page) - 1); 0502 Q_ASSERT_X(page, "DviGenerator::metaData()", "NULL page as result of valid Anchor"); 0503 Okular::DocumentViewport viewport; 0504 fillViewportFromAnchor(viewport, anchor, page); 0505 if (viewport.isValid()) { 0506 return viewport.toString(); 0507 } 0508 } 0509 } 0510 return QVariant(); 0511 } 0512 0513 Q_LOGGING_CATEGORY(OkularDviDebug, "org.kde.okular.generators.dvi.core", QtWarningMsg) 0514 Q_LOGGING_CATEGORY(OkularDviShellDebug, "org.kde.okular.generators.dvi.shell", QtWarningMsg) 0515 0516 #include "generator_dvi.moc"