File indexing completed on 2024-05-12 04:33:56
0001 // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; c-brace-offset: 0; -*- 0002 // 0003 // Class: dviRenderer 0004 // 0005 // Class for rendering TeX DVI files. 0006 // Part of KDVI- A previewer for TeX DVI files. 0007 // 0008 // SPDX-FileCopyrightText: 2001-2005 Stefan Kebekus 0009 // SPDX-License-Identifier: GPL-2.0-or-later 0010 0011 #include <config.h> 0012 0013 #include "debug_dvi.h" 0014 #include "dviFile.h" 0015 #include "dviRenderer.h" 0016 #include "dvisourcesplitter.h" 0017 #include "hyperlink.h" 0018 #include "psgs.h" 0019 //#include "renderedDviPagePixmap.h" 0020 0021 #include <KConfig> 0022 #include <KLocalizedString> 0023 #include <QMimeDatabase> 0024 #include <QMimeType> 0025 #include <QTime> 0026 #include <QVBoxLayout> 0027 #include <math.h> 0028 0029 #include <QApplication> 0030 #include <QCheckBox> 0031 #include <QEventLoop> 0032 #include <QFileInfo> 0033 #include <QHBoxLayout> 0034 #include <QLabel> 0035 #include <QPainter> 0036 #include <QProgressBar> 0037 #include <QRegularExpression> 0038 0039 //------ now comes the dviRenderer class implementation ---------- 0040 0041 dviRenderer::dviRenderer(bool useFontHinting) 0042 : dviFile(nullptr) 0043 , font_pool(useFontHinting) 0044 , resolutionInDPI(0) 0045 , embedPS_progress(nullptr) 0046 , embedPS_numOfProgressedFiles(0) 0047 , shrinkfactor(3) 0048 , source_href(nullptr) 0049 , HTML_href(nullptr) 0050 , editorCommand(QLatin1String("")) 0051 , PostScriptOutPutString(nullptr) 0052 , PS_interface(new ghostscript_interface) 0053 , _postscript(true) 0054 , line_boundary_encountered(false) 0055 , word_boundary_encountered(false) 0056 , current_page(0) 0057 , penWidth_in_mInch(0) 0058 , number_of_elements_in_path(0) 0059 , currentlyDrawnPage(nullptr) 0060 , m_eventLoop(nullptr) 0061 , foreGroundPainter(nullptr) 0062 , fontpoolLocateFontsDone(false) 0063 { 0064 #ifdef DEBUG_DVIRENDERER 0065 // qCDebug(OkularDviDebug) << "dviRenderer( parent=" << par << " )"; 0066 #endif 0067 0068 connect(&font_pool, &fontPool::error, this, &dviRenderer::error); 0069 connect(&font_pool, &fontPool::warning, this, &dviRenderer::warning); 0070 connect(PS_interface, &ghostscript_interface::error, this, &dviRenderer::error); 0071 } 0072 0073 dviRenderer::~dviRenderer() 0074 { 0075 #ifdef DEBUG_DVIRENDERER 0076 qCDebug(OkularDviDebug) << "~dviRenderer"; 0077 #endif 0078 0079 QMutexLocker locker(&mutex); 0080 0081 delete PS_interface; 0082 delete dviFile; 0083 } 0084 0085 //------ this function calls the dvi interpreter ---------- 0086 0087 void dviRenderer::drawPage(RenderedDocumentPagePixmap *page) 0088 { 0089 #ifdef DEBUG_DVIRENDERER 0090 // qCDebug(OkularDviDebug) << "dviRenderer::drawPage(documentPage *) called, page number " << page->pageNumber; 0091 #endif 0092 0093 // Paranoid safety checks 0094 if (page == nullptr) { 0095 qCCritical(OkularDviDebug) << "dviRenderer::drawPage(documentPage *) called with argument == 0"; 0096 return; 0097 } 0098 // Paranoid safety checks 0099 if (!page->pageNumber.isValid()) { 0100 qCCritical(OkularDviDebug) << "dviRenderer::drawPage(documentPage *) called for a documentPage with page number 0"; 0101 return; 0102 } 0103 0104 QMutexLocker locker(&mutex); 0105 0106 if (dviFile == nullptr) { 0107 qCCritical(OkularDviDebug) << "dviRenderer::drawPage(documentPage *) called, but no dviFile class allocated."; 0108 page->clear(); 0109 return; 0110 } 0111 if (static_cast<quint16>(page->pageNumber) > dviFile->total_pages) { 0112 qCCritical(OkularDviDebug) << "dviRenderer::drawPage(documentPage *) called for a documentPage with page number " << static_cast<quint16>(page->pageNumber) << " but the current dviFile has only " << dviFile->total_pages 0113 << " pages."; 0114 return; 0115 } 0116 if (dviFile->dvi_Data() == nullptr) { 0117 qCCritical(OkularDviDebug) << "dviRenderer::drawPage(documentPage *) called, but no dviFile is loaded yet."; 0118 page->clear(); 0119 return; 0120 } 0121 0122 /* locateFonts() is here just once (if it has not been executed 0123 not been executed yet), so that it is possible to easily intercept 0124 the cancel signal (because for example the user tries to open 0125 another document); it would not have been possible (or more 0126 complicated) in case it was still in the document loading section. 0127 */ 0128 if (!fontpoolLocateFontsDone) { 0129 font_pool.locateFonts(); 0130 fontpoolLocateFontsDone = true; 0131 } 0132 0133 double resolution = page->resolution; 0134 0135 if (resolution != resolutionInDPI) { 0136 setResolution(resolution); 0137 } 0138 0139 currentlyDrawnPage = page; 0140 shrinkfactor = 1200 / resolutionInDPI; 0141 current_page = static_cast<quint16>(page->pageNumber) - 1; 0142 0143 // Reset colors 0144 colorStack.clear(); 0145 globalColor = Qt::black; 0146 0147 int pageWidth = page->width; 0148 int pageHeight = page->height; 0149 0150 QImage img(pageWidth, pageHeight, QImage::Format_RGB32); 0151 foreGroundPainter = new QPainter(&img); 0152 if (foreGroundPainter != nullptr) { 0153 errorMsg.clear(); 0154 draw_page(); 0155 delete foreGroundPainter; 0156 foreGroundPainter = nullptr; 0157 } else { 0158 qCDebug(OkularDviDebug) << "painter creation failed."; 0159 } 0160 page->img = img; 0161 // page->setImage(img); 0162 0163 // Postprocess hyperlinks 0164 // Without that, based on the way TeX draws certain characters like german "Umlaute", 0165 // some hyperlinks would be broken into two overlapping parts, in the middle of a word. 0166 QVector<Hyperlink>::iterator i = page->hyperLinkList.begin(); 0167 QVector<Hyperlink>::iterator j; 0168 while (i != page->hyperLinkList.end()) { 0169 // Iterator j always points to the element after i. 0170 j = i; 0171 j++; 0172 0173 if (j == page->hyperLinkList.end()) { 0174 break; 0175 } 0176 0177 Hyperlink &hi = *i; 0178 Hyperlink &hj = *j; 0179 0180 bool merged = false; 0181 0182 // Merge all hyperlinks that point to the same target, and have the same baseline. 0183 while (hi.linkText == hj.linkText && hi.baseline == hj.baseline) { 0184 merged = true; 0185 hi.box = hi.box.united(hj.box); 0186 0187 j++; 0188 if (j == page->hyperLinkList.end()) { 0189 break; 0190 } 0191 0192 hj = *j; 0193 } 0194 0195 if (merged) { 0196 i = page->hyperLinkList.erase(++i, j); 0197 } else { 0198 i++; 0199 } 0200 } 0201 0202 if (errorMsg.isEmpty() != true) { 0203 Q_EMIT error(i18n("File corruption. %1", errorMsg), -1); 0204 errorMsg.clear(); 0205 currentlyDrawnPage = nullptr; 0206 return; 0207 } 0208 currentlyDrawnPage = nullptr; 0209 } 0210 0211 void dviRenderer::getText(RenderedDocumentPagePixmap *page) 0212 { 0213 bool postscriptBackup = _postscript; 0214 // Disable postscript-specials temporarily to speed up text extraction. 0215 _postscript = false; 0216 0217 drawPage(page); 0218 0219 _postscript = postscriptBackup; 0220 } 0221 0222 /* 0223 void dviRenderer::showThatSourceInformationIsPresent() 0224 { 0225 // In principle, we should use a KMessagebox here, but we want to 0226 // add a button "Explain in more detail..." which opens the 0227 // Helpcenter. Thus, we practically re-implement the KMessagebox 0228 // here. Most of the code is stolen from there. 0229 0230 // Check if the 'Don't show again' feature was used 0231 KConfig *config = KSharedConfig::openConfig(); 0232 KConfigGroup saver(config, "Notification Messages"); 0233 bool showMsg = config->readEntry( "KDVI-info_on_source_specials", true); 0234 0235 if (showMsg) { 0236 KDialogBase dialog(i18n("KDVI: Information"), KDialogBase::Yes, KDialogBase::Yes, KDialogBase::Yes, 0237 parentWidget, "information", true, true, KStandardGuiItem::ok()); 0238 0239 QWidget *topcontents = new QWidget (&dialog); 0240 QVBoxLayout *topcontentsVBoxLayout = new QVBoxLayout(topcontents); 0241 topcontentsVBoxLayout->setContentsMargins(0, 0, 0, 0); 0242 topcontentsVBoxLayout->setSpacing(KDialog::spacingHint()*2); 0243 topcontentsVBoxLayout->setContentsMargins(KDialog::marginHint()*2, KDialog::marginHint()*2, KDialog::marginHint()*2, KDialog::marginHint()*2); 0244 0245 QWidget *contents = new QWidget(topcontents); 0246 topcontentsVBoxLayout->addWidget(contents); 0247 QHBoxLayout * lay = new QHBoxLayout(contents); 0248 lay->setSpacing(KDialog::spacingHint()*2); 0249 0250 lay->addStretch(1); 0251 QLabel *label1 = new QLabel( contents); 0252 label1->setPixmap(QMessageBox::standardIcon(QMessageBox::Information)); 0253 lay->addWidget(label1); 0254 QLabel *label2 = new QLabel( i18n("<qt>This DVI file contains source file information. You may click into the text with the " 0255 "middle mouse button, and an editor will open the TeX-source file immediately.</qt>"), 0256 contents); 0257 label2->setMinimumSize(label2->sizeHint()); 0258 lay->addWidget(label2); 0259 lay->addStretch(1); 0260 QSize extraSize = QSize(50,30); 0261 QCheckBox *checkbox = new QCheckBox(i18n("Do not show this message again"), topcontents); 0262 topcontentsVBoxLayout->addWidget(checkbox); 0263 extraSize = QSize(50,0); 0264 dialog.setHelpLinkText(i18n("Explain in more detail...")); 0265 dialog.setHelp("inverse-search", "kdvi"); 0266 dialog.enableLinkedHelp(true); 0267 dialog.setMainWidget(topcontents); 0268 dialog.enableButtonSeparator(false); 0269 dialog.incInitialSize( extraSize ); 0270 dialog.exec(); 0271 0272 showMsg = !checkbox->isChecked(); 0273 if (!showMsg) { 0274 KConfigGroup saver(config, "Notification Messages"); 0275 config->writeEntry( "KDVI-info_on_source_specials", showMsg); 0276 } 0277 config->sync(); 0278 } 0279 } 0280 */ 0281 0282 void dviRenderer::embedPostScript() 0283 { 0284 #ifdef DEBUG_DVIRENDERER 0285 qCDebug(OkularDviDebug) << "dviRenderer::embedPostScript()"; 0286 #endif 0287 0288 if (!dviFile) { 0289 return; 0290 } 0291 0292 /* embedPS_progress = new QProgressDialog(parentWidget); 0293 embedPS_progress->setWindowTitle(i18n("Embedding PostScript Files")); 0294 embedPS_progress->setLabelText(QString()); 0295 */ 0296 if (!embedPS_progress) { 0297 return; 0298 } 0299 embedPS_progress->setCancelButton(nullptr); 0300 embedPS_progress->setCancelButton(nullptr); 0301 embedPS_progress->setMinimumDuration(400); 0302 embedPS_progress->setMaximum(dviFile->numberOfExternalPSFiles); 0303 embedPS_progress->setValue(0); 0304 embedPS_numOfProgressedFiles = 0; 0305 0306 quint16 currPageSav = current_page; 0307 errorMsg.clear(); 0308 for (current_page = 0; current_page < dviFile->total_pages; current_page++) { 0309 if (current_page < dviFile->total_pages) { 0310 command_pointer = dviFile->dvi_Data() + dviFile->page_offset[int(current_page)]; 0311 end_pointer = dviFile->dvi_Data() + dviFile->page_offset[int(current_page + 1)]; 0312 } else { 0313 command_pointer = end_pointer = nullptr; 0314 } 0315 0316 memset((char *)&currinf.data, 0, sizeof(currinf.data)); 0317 currinf.fonttable = &(dviFile->tn_table); 0318 currinf._virtual = nullptr; 0319 prescan(&dviRenderer::prescan_embedPS); 0320 } 0321 0322 delete embedPS_progress; 0323 embedPS_progress = nullptr; 0324 0325 if (!errorMsg.isEmpty()) { 0326 Q_EMIT warning(i18n("Not all PostScript files could be embedded into your document. %1", errorMsg), -1); 0327 errorMsg.clear(); 0328 } else { 0329 Q_EMIT notice(i18n("All external PostScript files were embedded into your document."), -1); 0330 } 0331 0332 // Prescan phase starts here 0333 #ifdef PERFORMANCE_MEASUREMENT 0334 // qCDebug(OkularDviDebug) << "Time elapsed till prescan phase starts " << performanceTimer.elapsed() << "ms"; 0335 // QTime preScanTimer; 0336 // preScanTimer.start(); 0337 #endif 0338 dviFile->numberOfExternalPSFiles = 0; 0339 prebookmarks.clear(); 0340 for (current_page = 0; current_page < dviFile->total_pages; current_page++) { 0341 PostScriptOutPutString = new QString(); 0342 0343 if (current_page < dviFile->total_pages) { 0344 command_pointer = dviFile->dvi_Data() + dviFile->page_offset[int(current_page)]; 0345 end_pointer = dviFile->dvi_Data() + dviFile->page_offset[int(current_page + 1)]; 0346 } else { 0347 command_pointer = end_pointer = nullptr; 0348 } 0349 0350 memset((char *)&currinf.data, 0, sizeof(currinf.data)); 0351 currinf.fonttable = &(dviFile->tn_table); 0352 currinf._virtual = nullptr; 0353 0354 prescan(&dviRenderer::prescan_parseSpecials); 0355 0356 if (!PostScriptOutPutString->isEmpty()) { 0357 PS_interface->setPostScript(current_page, *PostScriptOutPutString); 0358 } 0359 delete PostScriptOutPutString; 0360 } 0361 PostScriptOutPutString = nullptr; 0362 0363 #ifdef PERFORMANCE_MEASUREMENT 0364 // qCDebug(OkularDviDebug) << "Time required for prescan phase: " << preScanTimer.restart() << "ms"; 0365 #endif 0366 current_page = currPageSav; 0367 _isModified = true; 0368 } 0369 0370 bool dviRenderer::isValidFile(const QString &filename) const 0371 { 0372 QFile f(filename); 0373 if (!f.open(QIODevice::ReadOnly)) { 0374 return false; 0375 } 0376 0377 unsigned char test[4]; 0378 if (f.read((char *)test, 2) < 2 || test[0] != 247 || test[1] != 2) { 0379 return false; 0380 } 0381 0382 int n = f.size(); 0383 if (n < 134) { // Too short for a dvi file 0384 return false; 0385 } 0386 f.seek(n - 4); 0387 0388 unsigned char trailer[4] = {0xdf, 0xdf, 0xdf, 0xdf}; 0389 0390 if (f.read((char *)test, 4) < 4 || strncmp((char *)test, (char *)trailer, 4) != 0) { 0391 return false; 0392 } 0393 // We suppose now that the dvi file is complete and OK 0394 return true; 0395 } 0396 0397 bool dviRenderer::setFile(const QString &fname, const QUrl &base) 0398 { 0399 #ifdef DEBUG_DVIRENDERER 0400 qCDebug(OkularDviDebug) << "dviRenderer::setFile( fname='" << fname << "' )"; //, ref='" << ref << "', sourceMarker=" << sourceMarker << " )"; 0401 #endif 0402 0403 // QMutexLocker lock(&mutex); 0404 0405 QFileInfo fi(fname); 0406 QString filename = fi.absoluteFilePath(); 0407 0408 // If fname is the empty string, then this means: "close". Delete 0409 // the dvifile and the pixmap. 0410 if (fname.isEmpty()) { 0411 // Delete DVI file 0412 delete dviFile; 0413 dviFile = nullptr; 0414 return true; 0415 } 0416 0417 // Make sure the file actually exists. 0418 if (!fi.exists() || fi.isDir()) { 0419 Q_EMIT error(i18n("The specified file '%1' does not exist.", filename), -1); 0420 return false; 0421 } 0422 0423 QApplication::setOverrideCursor(Qt::WaitCursor); 0424 dvifile *dviFile_new = new dvifile(filename, &font_pool); 0425 0426 if ((dviFile == nullptr) || (dviFile->filename != filename)) { 0427 dviFile_new->sourceSpecialMarker = true; 0428 } else { 0429 dviFile_new->sourceSpecialMarker = false; 0430 } 0431 0432 if ((dviFile_new->dvi_Data() == nullptr) || (dviFile_new->errorMsg.isEmpty() != true)) { 0433 QApplication::restoreOverrideCursor(); 0434 if (dviFile_new->errorMsg.isEmpty() != true) { 0435 Q_EMIT error(i18n("File corruption. %1", dviFile_new->errorMsg), -1); 0436 } 0437 delete dviFile_new; 0438 return false; 0439 } 0440 0441 delete dviFile; 0442 dviFile = dviFile_new; 0443 numPages = dviFile->total_pages; 0444 _isModified = false; 0445 baseURL = base; 0446 0447 font_pool.setExtraSearchPath(fi.absolutePath()); 0448 font_pool.setCMperDVIunit(dviFile->getCmPerDVIunit()); 0449 0450 // Extract PostScript from the DVI file, and store the PostScript 0451 // specials in PostScriptDirectory, and the headers in the 0452 // PostScriptHeaderString. 0453 PS_interface->clear(); 0454 0455 // If the DVI file comes from a remote URL (e.g. downloaded from a 0456 // web server), we limit the PostScript files that can be accessed 0457 // by this file to the download directory, in order to limit the 0458 // possibilities of a denial of service attack. 0459 QString includePath; 0460 if (!baseURL.isLocalFile()) { 0461 includePath = filename; 0462 includePath.truncate(includePath.lastIndexOf(QLatin1Char('/'))); 0463 } 0464 PS_interface->setIncludePath(includePath); 0465 0466 // We will also generate a list of hyperlink-anchors and source-file 0467 // anchors in the document. So declare the existing lists empty. 0468 // anchorList.clear(); 0469 sourceHyperLinkAnchors.clear(); 0470 // bookmarks.clear(); 0471 prebookmarks.clear(); 0472 0473 if (dviFile->page_offset.isEmpty() == true) { 0474 return false; 0475 } 0476 0477 // We should pre-scan the document now (to extract embedded, 0478 // PostScript, Hyperlinks, ets). 0479 0480 // PRESCAN STARTS HERE 0481 #ifdef PERFORMANCE_MEASUREMENT 0482 // qCDebug(OkularDviDebug) << "Time elapsed till prescan phase starts " << performanceTimer.elapsed() << "ms"; 0483 // QTime preScanTimer; 0484 // preScanTimer.start(); 0485 #endif 0486 dviFile->numberOfExternalPSFiles = 0; 0487 quint16 currPageSav = current_page; 0488 prebookmarks.clear(); 0489 0490 for (current_page = 0; current_page < dviFile->total_pages; current_page++) { 0491 PostScriptOutPutString = new QString(); 0492 0493 if (current_page < dviFile->total_pages) { 0494 command_pointer = dviFile->dvi_Data() + dviFile->page_offset[int(current_page)]; 0495 end_pointer = dviFile->dvi_Data() + dviFile->page_offset[int(current_page + 1)]; 0496 } else { 0497 command_pointer = end_pointer = nullptr; 0498 } 0499 0500 memset((char *)&currinf.data, 0, sizeof(currinf.data)); 0501 currinf.fonttable = &(dviFile->tn_table); 0502 currinf._virtual = nullptr; 0503 prescan(&dviRenderer::prescan_parseSpecials); 0504 0505 if (!PostScriptOutPutString->isEmpty()) { 0506 PS_interface->setPostScript(current_page, *PostScriptOutPutString); 0507 } 0508 delete PostScriptOutPutString; 0509 } 0510 PostScriptOutPutString = nullptr; 0511 0512 #ifdef PERFORMANCE_MEASUREMENT 0513 // qCDebug(OkularDviDebug) << "Time required for prescan phase: " << preScanTimer.restart() << "ms"; 0514 #endif 0515 current_page = currPageSav; 0516 // PRESCAN ENDS HERE 0517 0518 pageSizes.resize(0); 0519 if (dviFile->suggestedPageSize != nullptr) { 0520 // Fill the vector pageSizes with total_pages identical entries 0521 pageSizes.fill(*(dviFile->suggestedPageSize), dviFile->total_pages); 0522 } 0523 QApplication::restoreOverrideCursor(); 0524 return true; 0525 } 0526 0527 Anchor dviRenderer::parseReference(const QString &reference) 0528 { 0529 QMutexLocker locker(&mutex); 0530 0531 #ifdef DEBUG_DVIRENDERER 0532 qCCritical(OkularDviDebug) << "dviRenderer::parseReference( " << reference << " ) called"; 0533 #endif 0534 0535 if (dviFile == nullptr) { 0536 return Anchor(); 0537 } 0538 0539 // case 1: The reference is a number, which we'll interpret as a 0540 // page number. 0541 bool ok; 0542 int page = reference.toInt(&ok); 0543 if (ok == true) { 0544 if (page < 0) { 0545 page = 0; 0546 } 0547 if (page > dviFile->total_pages) { 0548 page = dviFile->total_pages; 0549 } 0550 0551 return Anchor(page, Length()); 0552 } 0553 0554 // case 2: The reference is of form "src:1111Filename", where "1111" 0555 // points to line number 1111 in the file "Filename". KDVI then 0556 // looks for source specials of the form "src:xxxxFilename", and 0557 // tries to find the special with the biggest xxxx 0558 if (reference.indexOf(QStringLiteral("src:"), 0, Qt::CaseInsensitive) == 0) { 0559 // Extract the file name and the numeral part from the reference string 0560 DVI_SourceFileSplitter splitter(reference, dviFile->filename); 0561 quint32 refLineNumber = splitter.line(); 0562 QString refFileName = splitter.filePath(); 0563 0564 if (sourceHyperLinkAnchors.isEmpty()) { 0565 Q_EMIT warning(i18n("You have asked Okular to locate the place in the DVI file which corresponds to " 0566 "line %1 in the TeX-file %2. It seems, however, that the DVI file " 0567 "does not contain the necessary source file information. ", 0568 refLineNumber, 0569 refFileName), 0570 -1); 0571 return Anchor(); 0572 } 0573 0574 // Go through the list of source file anchors, and find the anchor 0575 // whose line number is the biggest among those that are smaller 0576 // than the refLineNumber. That way, the position in the DVI file 0577 // which is highlighted is always a little further up than the 0578 // position in the editor, e.g. if the DVI file contains 0579 // positional information at the beginning of every paragraph, 0580 // KDVI jumps to the beginning of the paragraph that the cursor is 0581 // in, and never to the next paragraph. If source file anchors for 0582 // the refFileName can be found, but none of their line numbers is 0583 // smaller than the refLineNumber, the reason is most likely, that 0584 // the cursor in the editor stands somewhere in the preamble of 0585 // the LaTeX file. In that case, we jump to the beginning of the 0586 // document. 0587 bool anchorForRefFileFound = false; // Flag that is set if source file anchors for the refFileName could be found at all 0588 0589 QVector<DVI_SourceFileAnchor>::iterator bestMatch = sourceHyperLinkAnchors.end(); 0590 QVector<DVI_SourceFileAnchor>::iterator it; 0591 for (it = sourceHyperLinkAnchors.begin(); it != sourceHyperLinkAnchors.end(); ++it) { 0592 if (refFileName.trimmed() == it->fileName.trimmed() || refFileName.trimmed() == it->fileName.trimmed() + QStringLiteral(".tex")) { 0593 anchorForRefFileFound = true; 0594 0595 if ((it->line <= refLineNumber) && ((bestMatch == sourceHyperLinkAnchors.end()) || (it->line > bestMatch->line))) { 0596 bestMatch = it; 0597 } 0598 } 0599 } 0600 0601 if (bestMatch != sourceHyperLinkAnchors.end()) { 0602 return Anchor(bestMatch->page, bestMatch->distance_from_top); 0603 } else if (anchorForRefFileFound == false) { 0604 Q_EMIT warning(i18n("Okular was not able to locate the place in the DVI file which corresponds to " 0605 "line %1 in the TeX-file %2.", 0606 refLineNumber, 0607 refFileName), 0608 -1); 0609 } else { 0610 return Anchor(); 0611 } 0612 return Anchor(); 0613 } 0614 return Anchor(); 0615 } 0616 0617 void dviRenderer::setResolution(double resolution_in_DPI) 0618 { 0619 // Ignore minute changes. The difference to the current value would 0620 // hardly be visible anyway. That saves a lot of re-painting, 0621 // e.g. when the user resizes the window, and a flickery mouse 0622 // changes the window size by 1 pixel all the time. 0623 if (fabs(resolutionInDPI - resolution_in_DPI) < 1) { 0624 return; 0625 } 0626 0627 resolutionInDPI = resolution_in_DPI; 0628 0629 // Pass the information on to the font pool. 0630 font_pool.setDisplayResolution(resolutionInDPI); 0631 shrinkfactor = 1200 / resolutionInDPI; 0632 return; 0633 } 0634 0635 void dviRenderer::handleSRCLink(const QString &linkText, const QPoint point, DocumentWidget *widget) 0636 { 0637 Q_UNUSED(linkText); 0638 Q_UNUSED(point); 0639 Q_UNUSED(widget); 0640 } 0641 0642 QString dviRenderer::PDFencodingToQString(const QString &_pdfstring) 0643 { 0644 // This method locates special PDF characters in a string and 0645 // replaces them by UTF8. See Section 3.2.3 of the PDF reference 0646 // guide for information. 0647 QString pdfstring = _pdfstring; 0648 pdfstring = pdfstring.replace(QLatin1String("\\n"), QLatin1String("\n")); 0649 pdfstring = pdfstring.replace(QLatin1String("\\r"), QLatin1String("\n")); 0650 pdfstring = pdfstring.replace(QLatin1String("\\t"), QLatin1String("\t")); 0651 pdfstring = pdfstring.replace(QLatin1String("\\f"), QLatin1String("\f")); 0652 pdfstring = pdfstring.replace(QLatin1String("\\("), QLatin1String("(")); 0653 pdfstring = pdfstring.replace(QLatin1String("\\)"), QLatin1String(")")); 0654 pdfstring = pdfstring.replace(QLatin1String("\\\\"), QLatin1String("\\")); 0655 0656 // Now replace octal character codes with the characters they encode 0657 static const QRegularExpression xyzRegex(QStringLiteral("(\\\\)(\\d\\d\\d)")); // matches "\xyz" where x,y,z are numbers 0658 QRegularExpressionMatch match; 0659 while ((match = xyzRegex.match(pdfstring)).hasMatch()) { 0660 pdfstring = pdfstring.replace(match.capturedStart(0), 4, QChar(match.captured(2).toInt(nullptr, 8))); 0661 } 0662 static const QRegularExpression xyRegex(QStringLiteral("(\\\\)(\\d\\d)")); // matches "\xy" where x,y are numbers 0663 while ((match = xyRegex.match(pdfstring)).hasMatch()) { 0664 pdfstring = pdfstring.replace(match.capturedStart(0), 3, QChar(match.captured(2).toInt(nullptr, 8))); 0665 } 0666 static const QRegularExpression xRegex(QStringLiteral("(\\\\)(\\d)")); // matches "\x" where x is a number 0667 while ((match = xRegex.match(pdfstring)).hasMatch()) { 0668 pdfstring = pdfstring.replace(match.capturedStart(0), 2, QChar(match.captured(2).toInt(nullptr, 8))); 0669 } 0670 0671 return pdfstring; 0672 } 0673 0674 void dviRenderer::exportPDF() 0675 { 0676 /* 0677 QExplicitlySharedDataPointer<DVIExport> exporter(new DVIExportToPDF(*this, parentWidget)); 0678 if (exporter->started()) 0679 all_exports_[exporter.data()] = exporter; 0680 */ 0681 } 0682 0683 void dviRenderer::exportPS(const QString &fname, const QStringList &options, QPrinter *printer, QPageLayout::Orientation orientation) 0684 { 0685 QExplicitlySharedDataPointer<DVIExport> exporter(new DVIExportToPS(*this, fname, options, printer, font_pool.getUseFontHints(), orientation)); 0686 if (exporter->started()) { 0687 all_exports_[exporter.data()] = exporter; 0688 } 0689 } 0690 0691 /* 0692 void dviRenderer::editor_finished(const DVISourceEditor*) 0693 { 0694 editor_.attach(0); 0695 } 0696 */ 0697 0698 void dviRenderer::export_finished(const DVIExport *key) 0699 { 0700 typedef QMap<const DVIExport *, QExplicitlySharedDataPointer<DVIExport>> ExportMap; 0701 ExportMap::iterator it = all_exports_.find(key); 0702 if (it != all_exports_.end()) { 0703 all_exports_.remove(key); 0704 } 0705 } 0706 0707 void dviRenderer::setEventLoop(QEventLoop *el) 0708 { 0709 if (el == nullptr) { 0710 delete m_eventLoop; 0711 m_eventLoop = nullptr; 0712 } else { 0713 m_eventLoop = el; 0714 } 0715 }