File indexing completed on 2024-05-12 16:06:37

0001 // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; c-brace-offset: 0; -*-
0002 // dviRenderer_prescan.cpp
0003 //
0004 // Part of KDVI - A DVI previewer for the KDE desktop environment
0005 //
0006 // SPDX-FileCopyrightText: 2003-2004 Stefan Kebekus
0007 // SPDX-License-Identifier: GPL-2.0-or-later
0008 
0009 #include <config.h>
0010 
0011 #include "TeXFont.h"
0012 #include "debug_dvi.h"
0013 #include "dvi.h"
0014 #include "dviFile.h"
0015 #include "dviRenderer.h"
0016 #include "prebookmark.h"
0017 #include "psgs.h"
0018 
0019 #include <KLocalizedString>
0020 #include <KProcess>
0021 #include <QMimeDatabase>
0022 #include <QMimeType>
0023 
0024 #include <QApplication>
0025 #include <QByteArray>
0026 #include <QDir>
0027 #include <QFileInfo>
0028 #include <QImage>
0029 #include <QPaintDevice>
0030 #include <QProgressBar>
0031 #include <QStandardPaths>
0032 #include <QTextStream>
0033 
0034 extern QPainter foreGroundPaint;
0035 extern void parse_special_argument(const QString &strg, const char *argument_name, int *variable);
0036 
0037 //#define DEBUG_PRESCAN
0038 
0039 void dviRenderer::prescan_embedPS(char *cp, quint8 *beginningOfSpecialCommand)
0040 {
0041 #ifdef DEBUG_PRESCAN
0042     qCDebug(OkularDviDebug) << "dviRenderer::prescan_embedPS( cp = " << cp << " ) ";
0043 #endif
0044 
0045     // Encapsulated Postscript File
0046     if (qstrnicmp(cp, "PSfile=", 7) != 0) {
0047         return;
0048     }
0049 
0050     QString command = QString::fromLocal8Bit(cp + 7);
0051 
0052     QString include_command = command.simplified();
0053 
0054     // The line is supposed to start with "..ile=", and then comes the
0055     // filename. Figure out what the filename is and stow it away. Of
0056     // course, this does not work if the filename contains spaces
0057     // (already the simplified() above is wrong). If you have
0058     // files like this, go away.
0059     QString EPSfilename = include_command;
0060     EPSfilename.truncate(EPSfilename.indexOf(QLatin1Char(' ')));
0061 
0062     // Strip enclosing quotation marks which are included by some LaTeX
0063     // macro packages (but not by others). This probably means that
0064     // graphic files are no longer found if the filename really does
0065     // contain quotes, but we don't really care that much.
0066     if ((EPSfilename.at(0) == QLatin1Char('"')) && (EPSfilename.at(EPSfilename.length() - 1) == QLatin1Char('"'))) {
0067         EPSfilename = EPSfilename.mid(1, EPSfilename.length() - 2);
0068     }
0069 
0070     // Now locate the Gfx file on the hard disk...
0071     EPSfilename = ghostscript_interface::locateEPSfile(EPSfilename, baseURL);
0072 
0073     // If the file is neither a PostScript not a PDF file, we exit here.
0074     // The graphic file is later read when the page is rendered.
0075     QMimeDatabase db;
0076     QMimeType const mime_type = db.mimeTypeForFile(EPSfilename, QMimeDatabase::MatchContent);
0077     QString const &mime_type_name = mime_type.isValid() ? mime_type.name() : QString();
0078 
0079     bool const is_ps_file = (mime_type_name == QLatin1String("application/postscript") || mime_type_name == QLatin1String("image/x-eps"));
0080     bool const is_pdf_file = (!is_ps_file && mime_type_name == QLatin1String("application/pdf"));
0081     if (!(is_ps_file || is_pdf_file)) {
0082         return;
0083     }
0084 
0085     QString originalFName = EPSfilename;
0086 
0087     embedPS_progress->setLabelText(i18n("Embedding %1", EPSfilename));
0088     qApp->processEvents();
0089 
0090     // If the EPSfilename really points to a PDF file, convert that file now.
0091     if (is_pdf_file) {
0092         EPSfilename = dviFile->convertPDFtoPS(EPSfilename);
0093     }
0094 
0095     if (!QFile::exists(EPSfilename)) {
0096         // Find the number of the page
0097         quint32 currentOffset = beginningOfSpecialCommand - dviFile->dvi_Data();
0098         int page = 0;
0099         for (; page < dviFile->total_pages; page++) {
0100             if ((dviFile->page_offset[page] <= currentOffset) && (currentOffset <= dviFile->page_offset[page + 1])) {
0101                 break;
0102             }
0103         }
0104         if (is_pdf_file) {
0105             errorMsg += i18n("Page %1: The PDF file <strong>%2</strong> could not be converted to PostScript.<br>", page + 1, originalFName);
0106         } else {
0107             errorMsg += i18n("Page %1: The PostScript file <strong>%2</strong> could not be found.<br>", page + 1, originalFName);
0108         }
0109         embedPS_progress->setValue(embedPS_progress->value() + 1);
0110         qApp->processEvents();
0111         return;
0112     }
0113 
0114     // Now parse the arguments.
0115     int llx = 0;
0116     int lly = 0;
0117     int urx = 0;
0118     int ury = 0;
0119     int rwi = 0;
0120     int rhi = 0;
0121     int angle = 0;
0122 
0123     // just to avoid ambiguities; the filename could contain keywords
0124     include_command = include_command.mid(include_command.indexOf(QLatin1Char(' ')));
0125 
0126     parse_special_argument(include_command, "llx=", &llx);
0127     parse_special_argument(include_command, "lly=", &lly);
0128     parse_special_argument(include_command, "urx=", &urx);
0129     parse_special_argument(include_command, "ury=", &ury);
0130     parse_special_argument(include_command, "rwi=", &rwi);
0131     parse_special_argument(include_command, "rhi=", &rhi);
0132     parse_special_argument(include_command, "angle=", &angle);
0133 
0134     int clip = include_command.indexOf(QStringLiteral(" clip")); // -1 if clip keyword is not present, >= 0 otherwise
0135 
0136     // Generate the PostScript commands to be included
0137     QString PS = QStringLiteral("ps: @beginspecial %1 @llx %2 @lly %3 @urx %4 @ury").arg(llx).arg(lly).arg(urx).arg(ury);
0138     if (rwi != 0) {
0139         PS.append(QStringLiteral(" %1 @rwi").arg(rwi));
0140     }
0141     if (rhi != 0) {
0142         PS.append(QStringLiteral(" %1 @rhi").arg(rhi));
0143     }
0144     if (angle != 0) {
0145         PS.append(QStringLiteral(" %1 @angle").arg(angle));
0146     }
0147     if (clip != -1) {
0148         PS.append(QStringLiteral(" @clip"));
0149     }
0150     PS.append(QStringLiteral(" @setspecial\n"));
0151 
0152     QFile file(EPSfilename);
0153     if (file.open(QIODevice::ReadOnly)) {
0154         QTextStream stream(&file);
0155         while (!stream.atEnd()) {
0156             PS += stream.readLine().section(QLatin1Char('%'), 0, 0);
0157             PS += QLatin1Char('\n');
0158         }
0159         file.close();
0160     }
0161     PS.append(QStringLiteral("@endspecial"));
0162     PS = PS.simplified();
0163 
0164     _isModified = true;
0165     quint32 lengthOfOldSpecial = command_pointer - beginningOfSpecialCommand;
0166     quint32 lengthOfNewSpecial = PS.length() + 5;
0167 
0168     QVector<quint8> newDVI(dviFile->size_of_file + lengthOfNewSpecial - lengthOfOldSpecial);
0169 
0170     quint8 *commandPtrSav = command_pointer;
0171     quint8 *endPtrSav = end_pointer;
0172     end_pointer = newDVI.data() + dviFile->size_of_file + lengthOfNewSpecial - lengthOfOldSpecial;
0173     memcpy(newDVI.data(), dviFile->dvi_Data(), beginningOfSpecialCommand - dviFile->dvi_Data());
0174     command_pointer = newDVI.data() + (beginningOfSpecialCommand - dviFile->dvi_Data());
0175     command_pointer[0] = XXX4;
0176     command_pointer++;
0177     writeUINT32(PS.length());
0178     memcpy(newDVI.data() + (beginningOfSpecialCommand - dviFile->dvi_Data()) + 5, PS.toLatin1().constData(), PS.length());
0179     memcpy(
0180         newDVI.data() + (beginningOfSpecialCommand - dviFile->dvi_Data()) + lengthOfNewSpecial, beginningOfSpecialCommand + lengthOfOldSpecial, dviFile->size_of_file - (beginningOfSpecialCommand - dviFile->dvi_Data()) - lengthOfOldSpecial);
0181 
0182     // Adjust page pointers in the DVI file
0183     dviFile->size_of_file = dviFile->size_of_file + lengthOfNewSpecial - lengthOfOldSpecial;
0184     end_pointer = newDVI.data() + dviFile->size_of_file;
0185     quint32 currentOffset = beginningOfSpecialCommand - dviFile->dvi_Data();
0186     for (int i = 0; i < dviFile->total_pages; i++) {
0187         if (dviFile->page_offset[i] > currentOffset) {
0188             dviFile->page_offset[i] = dviFile->page_offset[i] + lengthOfNewSpecial - lengthOfOldSpecial;
0189             command_pointer = dviFile->page_offset[i] + newDVI.data() + 4 * 10 + 1;
0190             quint32 a = readUINT32();
0191             if (a > currentOffset) {
0192                 a = a + lengthOfNewSpecial - lengthOfOldSpecial;
0193                 command_pointer = dviFile->page_offset[i] + newDVI.data() + 4 * 10 + 1;
0194                 writeUINT32(a);
0195             }
0196         }
0197     }
0198 
0199     dviFile->beginning_of_postamble = dviFile->beginning_of_postamble + lengthOfNewSpecial - lengthOfOldSpecial;
0200     dviFile->page_offset[int(dviFile->total_pages)] = dviFile->beginning_of_postamble;
0201 
0202     command_pointer = newDVI.data() + dviFile->beginning_of_postamble + 1;
0203     quint32 a = readUINT32();
0204     if (a > currentOffset) {
0205         a = a + lengthOfNewSpecial - lengthOfOldSpecial;
0206         command_pointer = newDVI.data() + dviFile->beginning_of_postamble + 1;
0207         writeUINT32(a);
0208     }
0209 
0210     command_pointer = newDVI.data() + dviFile->size_of_file - 1;
0211     while ((*command_pointer == TRAILER) && (command_pointer > newDVI.data())) {
0212         command_pointer--;
0213     }
0214     command_pointer -= 4;
0215     writeUINT32(dviFile->beginning_of_postamble);
0216     command_pointer -= 4;
0217 
0218     command_pointer = commandPtrSav;
0219     end_pointer = endPtrSav;
0220 
0221     // Modify all pointers to point to the newly allocated memory
0222     command_pointer = newDVI.data() + (command_pointer - dviFile->dvi_Data()) + lengthOfNewSpecial - lengthOfOldSpecial;
0223     end_pointer = newDVI.data() + (end_pointer - dviFile->dvi_Data()) + lengthOfNewSpecial - lengthOfOldSpecial;
0224 
0225     dviFile->setNewData(newDVI);
0226 
0227     embedPS_progress->setValue(embedPS_progress->value() + 1);
0228     qApp->processEvents();
0229     return;
0230 }
0231 
0232 void dviRenderer::prescan_removePageSizeInfo(char *cp, quint8 *beginningOfSpecialCommand)
0233 {
0234 #ifdef DEBUG_PRESCAN
0235     qCDebug(OkularDviDebug) << "dviRenderer::prescan_embedPS( cp = " << cp << " ) ";
0236 #endif
0237 
0238     // Encapsulated Postscript File
0239     if (qstrnicmp(cp, "papersize=", 10) != 0) {
0240         return;
0241     }
0242 
0243     for (quint8 *ptr = beginningOfSpecialCommand; ptr < command_pointer; ptr++) {
0244         *ptr = NOP;
0245     }
0246 }
0247 
0248 void dviRenderer::prescan_ParsePapersizeSpecial(const QString &_cp)
0249 {
0250 #ifdef DEBUG_PRESCAN
0251     qCDebug(OkularDviDebug) << "Papersize-Special : papersize" << _cp;
0252 #endif
0253 
0254     QString cp = _cp.simplified();
0255 
0256     if (cp[0] == QLatin1Char('=')) {
0257         cp = cp.mid(1);
0258         dviFile->suggestedPageSize = new pageSize;
0259         dviFile->suggestedPageSize->setPageSize(cp);
0260     } else {
0261         printErrorMsgForSpecials(i18n("The papersize data '%1' could not be parsed.", cp));
0262     }
0263 
0264     return;
0265 }
0266 
0267 void dviRenderer::prescan_ParseBackgroundSpecial(const QString &cp)
0268 {
0269     QColor col = parseColorSpecification(cp.trimmed());
0270     if (col.isValid()) {
0271         for (quint16 page = current_page; page < dviFile->total_pages; page++) {
0272             PS_interface->setBackgroundColor(page, col);
0273         }
0274     }
0275     return;
0276 }
0277 
0278 void dviRenderer::prescan_ParseHTMLAnchorSpecial(const QString &_cp)
0279 {
0280     QString cp = _cp;
0281     cp.truncate(cp.indexOf(QLatin1Char('"')));
0282     Length l;
0283     l.setLength_in_inch(currinf.data.dvi_v / (resolutionInDPI * shrinkfactor));
0284     anchorList[cp] = Anchor(current_page + 1, l);
0285 }
0286 
0287 void dviRenderer::prescan_ParsePSHeaderSpecial(const QString &cp)
0288 {
0289 #ifdef DEBUG_PRESCAN
0290     qCDebug(OkularDviDebug) << "PostScript-special, header " << cp;
0291 #endif
0292 
0293     QString _file = cp;
0294 
0295     // If the file is not found in the current directory, use kpsewhich
0296     // to find it.
0297     if (!QFile::exists(_file)) {
0298         // Otherwise, use kpsewhich to find the eps file.
0299         // Make sure kpsewhich is in PATH and not just in the CWD
0300         static const QString fullPath = QStandardPaths::findExecutable(QStringLiteral("kpsewhich"));
0301         if (!fullPath.isEmpty()) {
0302             KProcess proc;
0303             proc << fullPath << cp;
0304             proc.setOutputChannelMode(KProcess::SeparateChannels);
0305             proc.execute();
0306             _file = QString::fromLocal8Bit(proc.readLine().trimmed());
0307         }
0308     }
0309 
0310     if (QFile::exists(_file)) {
0311         PS_interface->PostScriptHeaderString->append(QStringLiteral(" (%1) run\n").arg(_file));
0312     }
0313 }
0314 
0315 void dviRenderer::prescan_ParsePSBangSpecial(const QString &cp)
0316 {
0317 #ifdef DEBUG_PRESCAN
0318     qCDebug(OkularDviDebug) << "PostScript-special, literal header " << cp;
0319 #endif
0320 
0321     PS_interface->PostScriptHeaderString->append(QStringLiteral(" @defspecial \n"));
0322     PS_interface->PostScriptHeaderString->append(cp);
0323     PS_interface->PostScriptHeaderString->append(QStringLiteral(" @fedspecial \n"));
0324 }
0325 
0326 void dviRenderer::prescan_ParsePSQuoteSpecial(const QString &cp)
0327 {
0328 #ifdef DEBUG_PRESCAN
0329     qCCritical(OkularDviDebug) << "PostScript-special, literal PostScript " << cp;
0330 #endif
0331 
0332     double PS_H = (currinf.data.dvi_h * 300.0) / (65536 * 1200) - 300;
0333     double PS_V = (currinf.data.dvi_v * 300.0) / 1200 - 300;
0334     PostScriptOutPutString->append(QStringLiteral(" %1 %2 moveto\n").arg(PS_H).arg(PS_V));
0335     PostScriptOutPutString->append(QStringLiteral(" @beginspecial @setspecial \n"));
0336     PostScriptOutPutString->append(cp);
0337     PostScriptOutPutString->append(QStringLiteral(" @endspecial \n"));
0338 }
0339 
0340 void dviRenderer::prescan_ParsePSSpecial(const QString &cp)
0341 {
0342 #ifdef DEBUG_PRESCAN
0343     qCDebug(OkularDviDebug) << "PostScript-special, direct PostScript " << cp;
0344 #endif
0345 
0346     // Unfortunately, in some TeX distribution the hyperref package uses
0347     // the dvips driver by default, rather than the hypertex driver. As
0348     // a result, the DVI files produced are full of PostScript that
0349     // specifies links and anchors, and KDVI would call the ghostscript
0350     // interpreter for every page which makes it really slow. This is a
0351     // major nuisance, so that we try to filter and interpret the
0352     // hypertex generated PostScript here.
0353     if (cp.startsWith(QLatin1String("ps:SDict begin"))) {
0354         // We suspect this may be hyperref generated nonsense. Let's check
0355         // for some known code that hyperref generates.
0356         if (cp == QLatin1String("ps:SDict begin H.S end")) {
0357             return; // start of hyperref rectangle
0358         }
0359         if (cp == QLatin1String("ps:SDict begin H.R end")) {
0360             return; // end of hyperref rectangle
0361         }
0362         if (cp.endsWith(QLatin1String("H.A end"))) {
0363             return; // end of hyperref anchor
0364         }
0365         if (cp.endsWith(QLatin1String("H.L end"))) {
0366             return; // end of hyperref link
0367         }
0368         if (cp.startsWith(QLatin1String("ps:SDict begin /product where{pop product(Distiller)"))) {
0369             return; // hyperref tries to work around Distiller bug
0370         }
0371         if (cp.startsWith(QLatin1String("ps:SDict begin [")) && cp.endsWith(QLatin1String(" pdfmark end"))) { // hyperref definition of link/anchor/bookmark/etc
0372             if (cp.contains(QStringLiteral("/DEST"))) {                                                       // The PostScript code defines an anchor
0373                 QString anchorName = cp.section(QLatin1Char('('), 1, 1).section(QLatin1Char(')'), 0, 0);
0374                 Length l;
0375                 l.setLength_in_inch(currinf.data.dvi_v / (resolutionInDPI * shrinkfactor));
0376                 anchorList[anchorName] = Anchor(current_page + 1, l);
0377             }
0378             // The PostScript code defines a bookmark
0379             if (cp.contains(QStringLiteral("/Dest")) && cp.contains(QStringLiteral("/Title"))) {
0380                 const QString childrenNumberAndMoreStuff = cp.section(QLatin1Char('-'), 1, 1); // Contains from the - symbol to the end of cp, effectively containing the number of children and some stuff after it
0381                 int indexOfFirstNonDigit = 0;
0382                 for (const QChar &c : childrenNumberAndMoreStuff) {
0383                     if (c.isDigit()) {
0384                         ++indexOfFirstNonDigit;
0385                     } else {
0386                         break;
0387                     }
0388                 }
0389                 prebookmarks.append(PreBookmark(PDFencodingToQString(cp.section(QLatin1Char('('), 2, 2).section(QLatin1Char(')'), 0, 0)),
0390                                                 cp.section(QLatin1Char('('), 1, 1).section(QLatin1Char(')'), 0, 0),
0391                                                 QStringView {childrenNumberAndMoreStuff}.left(indexOfFirstNonDigit).toUInt()));
0392             }
0393             return;
0394         }
0395     }
0396 
0397     double PS_H = (currinf.data.dvi_h * 300.0) / (65536 * 1200) - 300;
0398     double PS_V = (currinf.data.dvi_v * 300.0) / 1200 - 300;
0399 
0400     if (cp.indexOf(QStringLiteral("ps::[begin]"), 0, Qt::CaseInsensitive) == 0) {
0401         PostScriptOutPutString->append(QStringLiteral(" %1 %2 moveto\n").arg(PS_H).arg(PS_V));
0402         PostScriptOutPutString->append(QStringLiteral(" %1\n").arg(cp.mid(11)));
0403     } else {
0404         if (cp.indexOf(QStringLiteral("ps::[end]"), 0, Qt::CaseInsensitive) == 0) {
0405             PostScriptOutPutString->append(QStringLiteral(" %1\n").arg(cp.mid(9)));
0406         } else {
0407             if (cp.indexOf(QStringLiteral("ps::"), 0, Qt::CaseInsensitive) == 0) {
0408                 PostScriptOutPutString->append(QStringLiteral(" %1\n").arg(cp.mid(4)));
0409             } else {
0410                 PostScriptOutPutString->append(QStringLiteral(" %1 %2 moveto\n").arg(PS_H).arg(PS_V));
0411                 PostScriptOutPutString->append(QStringLiteral(" %1\n").arg(cp.mid(3)));
0412             }
0413         }
0414     }
0415 }
0416 
0417 void dviRenderer::prescan_ParsePSFileSpecial(const QString &cp)
0418 {
0419 #ifdef DEBUG_PRESCAN
0420     qCDebug(OkularDviDebug) << "epsf-special: psfile=" << cp;
0421 #endif
0422 
0423     QString include_command = cp.simplified();
0424 
0425     // The line is supposed to start with "..ile=", and then comes the
0426     // filename. Figure out what the filename is and stow it away. Of
0427     // course, this does not work if the filename contains spaces
0428     // (already the simplified() above is wrong). If you have
0429     // files like this, go away.
0430     QString EPSfilename = include_command;
0431     EPSfilename.truncate(EPSfilename.indexOf(QLatin1Char(' ')));
0432 
0433     // Strip enclosing quotation marks which are included by some LaTeX
0434     // macro packages (but not by others). This probably means that
0435     // graphic files are no longer found if the filename really does
0436     // contain quotes, but we don't really care that much.
0437     if ((EPSfilename.at(0) == QLatin1Char('\"')) && (EPSfilename.at(EPSfilename.length() - 1) == QLatin1Char('\"'))) {
0438         EPSfilename = EPSfilename.mid(1, EPSfilename.length() - 2);
0439     }
0440 
0441     // If the file name ends in 'png', 'gif', 'jpg' or 'jpeg', we assume
0442     // that this is NOT a PostScript file, and we exit here.
0443     QString ending = EPSfilename.section(QLatin1Char('.'), -1).toLower();
0444     if ((ending == QLatin1String("png")) || (ending == QLatin1String("gif")) || (ending == QLatin1String("jpg")) || (ending == QLatin1String("jpeg"))) {
0445         dviFile->numberOfExternalNONPSFiles++;
0446         return;
0447     }
0448 
0449     // Now assume that the graphics file *is* a PostScript file
0450     dviFile->numberOfExternalPSFiles++;
0451 
0452     // Now locate the Gfx file on the hard disk...
0453     EPSfilename = ghostscript_interface::locateEPSfile(EPSfilename, baseURL);
0454 
0455     // If the EPSfilename really points to a PDF file, convert that file now.
0456     if (ending == QLatin1String("pdf")) {
0457         QString convErrorMsg;
0458         EPSfilename = dviFile->convertPDFtoPS(EPSfilename, &convErrorMsg);
0459         if (convErrorMsg.isEmpty() != true) {
0460             Q_EMIT error(convErrorMsg, -1);
0461             return;
0462         }
0463     }
0464 
0465     // Now parse the arguments.
0466     int llx = 0;
0467     int lly = 0;
0468     int urx = 0;
0469     int ury = 0;
0470     int rwi = 0;
0471     int rhi = 0;
0472     int angle = 0;
0473 
0474     // just to avoid ambiguities; the filename could contain keywords
0475     include_command = include_command.mid(include_command.indexOf(QLatin1Char(' ')));
0476 
0477     parse_special_argument(include_command, "llx=", &llx);
0478     parse_special_argument(include_command, "lly=", &lly);
0479     parse_special_argument(include_command, "urx=", &urx);
0480     parse_special_argument(include_command, "ury=", &ury);
0481     parse_special_argument(include_command, "rwi=", &rwi);
0482     parse_special_argument(include_command, "rhi=", &rhi);
0483     parse_special_argument(include_command, "angle=", &angle);
0484 
0485     int clip = include_command.indexOf(QStringLiteral(" clip")); // -1 if clip keyword is not present, >= 0 otherwise
0486 
0487     if (QFile::exists(EPSfilename)) {
0488         double PS_H = (currinf.data.dvi_h * 300.0) / (65536 * 1200) - 300;
0489         double PS_V = (currinf.data.dvi_v * 300.0) / 1200 - 300;
0490         PostScriptOutPutString->append(QStringLiteral(" %1 %2 moveto\n").arg(PS_H).arg(PS_V));
0491         PostScriptOutPutString->append(QStringLiteral("@beginspecial "));
0492         PostScriptOutPutString->append(QStringLiteral(" %1 @llx").arg(llx));
0493         PostScriptOutPutString->append(QStringLiteral(" %1 @lly").arg(lly));
0494         PostScriptOutPutString->append(QStringLiteral(" %1 @urx").arg(urx));
0495         PostScriptOutPutString->append(QStringLiteral(" %1 @ury").arg(ury));
0496         if (rwi != 0) {
0497             PostScriptOutPutString->append(QStringLiteral(" %1 @rwi").arg(rwi));
0498         }
0499         if (rhi != 0) {
0500             PostScriptOutPutString->append(QStringLiteral(" %1 @rhi").arg(rhi));
0501         }
0502         if (angle != 0) {
0503             PostScriptOutPutString->append(QStringLiteral(" %1 @angle").arg(angle));
0504         }
0505         if (clip != -1) {
0506             PostScriptOutPutString->append(QStringLiteral(" @clip"));
0507         }
0508         PostScriptOutPutString->append(QStringLiteral(" @setspecial \n"));
0509         PostScriptOutPutString->append(QStringLiteral(" (%1) run\n").arg(EPSfilename));
0510         PostScriptOutPutString->append(QStringLiteral("@endspecial \n"));
0511     }
0512 
0513     return;
0514 }
0515 
0516 void dviRenderer::prescan_ParseSourceSpecial(QStringView cp)
0517 {
0518     // if no rendering takes place, i.e. when the DVI file is first
0519     // loaded, generate a DVI_SourceFileAnchor. These anchors are used
0520     // in forward search, i.e. to relate references line
0521     // "src:123file.tex" to positions in the DVI file
0522 
0523     // extract the file name and the numeral part from the string
0524     qint32 j;
0525     for (j = 0; j < cp.length(); j++) {
0526         if (!cp.at(j).isNumber()) {
0527             break;
0528         }
0529     }
0530     quint32 sourceLineNumber = cp.left(j).toUInt();
0531     QFileInfo fi1(dviFile->filename);
0532     QString sourceFileName = QFileInfo(fi1.dir(), cp.mid(j).trimmed().toString()).absoluteFilePath();
0533     Length l;
0534     l.setLength_in_inch(currinf.data.dvi_v / (resolutionInDPI * shrinkfactor));
0535     DVI_SourceFileAnchor sfa(sourceFileName, sourceLineNumber, current_page + 1, l);
0536     sourceHyperLinkAnchors.push_back(sfa);
0537 }
0538 
0539 void dviRenderer::prescan_parseSpecials(char *cp, quint8 *)
0540 {
0541     QString special_command = QString::fromUtf8(cp);
0542 
0543     // Now to those specials which are only interpreted during the
0544     // prescan phase, and NOT during rendering.
0545 
0546     // PaperSize special
0547     if (qstrnicmp(cp, "papersize", 9) == 0) {
0548         prescan_ParsePapersizeSpecial(special_command.mid(9));
0549         return;
0550     }
0551 
0552     // color special for background color
0553     if (qstrnicmp(cp, "background", 10) == 0) {
0554         prescan_ParseBackgroundSpecial(special_command.mid(10));
0555         return;
0556     }
0557 
0558     // HTML anchor special
0559     if (qstrnicmp(cp, "html:<A name=", 13) == 0) {
0560         prescan_ParseHTMLAnchorSpecial(special_command.mid(14));
0561         return;
0562     }
0563 
0564     // Postscript Header File
0565     if (qstrnicmp(cp, "header=", 7) == 0) {
0566         prescan_ParsePSHeaderSpecial(special_command.mid(7));
0567         return;
0568     }
0569 
0570     // Literal Postscript Header
0571     if (cp[0] == '!') {
0572         prescan_ParsePSBangSpecial(special_command.mid(1));
0573         return;
0574     }
0575 
0576     // Literal Postscript inclusion
0577     if (cp[0] == '"') {
0578         prescan_ParsePSQuoteSpecial(special_command.mid(1));
0579         return;
0580     }
0581 
0582     // PS-Postscript inclusion
0583     if (qstrnicmp(cp, "ps:", 3) == 0) {
0584         prescan_ParsePSSpecial(special_command);
0585         return;
0586     }
0587 
0588     // Encapsulated Postscript File
0589     if (qstrnicmp(cp, "PSfile=", 7) == 0) {
0590         prescan_ParsePSFileSpecial(special_command.mid(7));
0591         return;
0592     }
0593 
0594     // source special
0595     if (qstrnicmp(cp, "src:", 4) == 0) {
0596         prescan_ParseSourceSpecial(special_command.mid(4));
0597         return;
0598     }
0599 
0600     // Finally there are those special commands which must be considered
0601     // both during rendering and during the pre-scan phase
0602 
0603     // HTML anchor end
0604     if (qstrnicmp(cp, "html:</A>", 9) == 0) {
0605         html_anchor_end();
0606         return;
0607     }
0608 
0609     return;
0610 }
0611 
0612 void dviRenderer::prescan_setChar(unsigned int ch)
0613 {
0614     TeXFontDefinition *fontp = currinf.fontp;
0615     if (fontp == nullptr) {
0616         return;
0617     }
0618 
0619     if (currinf.set_char_p == &dviRenderer::set_char) {
0620         glyph *g = ((TeXFont *)(currinf.fontp->font))->getGlyph(ch, true, globalColor);
0621         if (g == nullptr) {
0622             return;
0623         }
0624         currinf.data.dvi_h += (int)(currinf.fontp->scaled_size_in_DVI_units * dviFile->getCmPerDVIunit() * (1200.0 / 2.54) / 16.0 * g->dvi_advance_in_units_of_design_size_by_2e20 + 0.5);
0625         return;
0626     }
0627 
0628     if (currinf.set_char_p == &dviRenderer::set_vf_char) {
0629         macro *m = &currinf.fontp->macrotable[ch];
0630         if (m->pos == nullptr) {
0631             return;
0632         }
0633         currinf.data.dvi_h += (int)(currinf.fontp->scaled_size_in_DVI_units * dviFile->getCmPerDVIunit() * (1200.0 / 2.54) / 16.0 * m->dvi_advance_in_units_of_design_size_by_2e20 + 0.5);
0634         return;
0635     }
0636 }
0637 
0638 void dviRenderer::prescan(parseSpecials specialParser)
0639 {
0640 #ifdef DEBUG_PRESCAN
0641     qCDebug(OkularDviDebug) << "dviRenderer::prescan( ... )";
0642 #endif
0643 
0644     if (resolutionInDPI == 0.0) {
0645         setResolution(100);
0646     }
0647 
0648     qint32 RRtmp = 0, WWtmp = 0, XXtmp = 0, YYtmp = 0, ZZtmp = 0;
0649     quint8 ch;
0650     double fontPixelPerDVIunit = dviFile->getCmPerDVIunit() * 1200.0 / 2.54;
0651 
0652     stack.clear();
0653 
0654     currinf.fontp = nullptr;
0655     currinf.set_char_p = &dviRenderer::set_no_char;
0656 
0657     for (;;) {
0658         ch = readUINT8();
0659 
0660         if (ch <= (unsigned char)(SETCHAR0 + 127)) {
0661             prescan_setChar(ch);
0662             continue;
0663         }
0664 
0665         if (FNTNUM0 <= ch && ch <= (unsigned char)(FNTNUM0 + 63)) {
0666             currinf.fontp = currinf.fonttable->value(ch - FNTNUM0);
0667             if (currinf.fontp == nullptr) {
0668                 errorMsg = i18n("The DVI code referred to font #%1, which was not previously defined.", ch - FNTNUM0);
0669                 return;
0670             }
0671             currinf.set_char_p = currinf.fontp->set_char_p;
0672             continue;
0673         }
0674 
0675         qint32 a, b;
0676 
0677         switch (ch) {
0678         case SET1:
0679             prescan_setChar(readUINT8());
0680             break;
0681 
0682         case SETRULE:
0683             /* Be careful, dvicopy outputs rules with height =
0684                0x80000000. We don't want any SIGFPE here. */
0685             a = readUINT32();
0686             b = readUINT32();
0687             b = ((long)(b * 65536.0 * fontPixelPerDVIunit));
0688             currinf.data.dvi_h += b;
0689             break;
0690 
0691         case PUTRULE:
0692             a = readUINT32();
0693             b = readUINT32();
0694             break;
0695 
0696         case PUT1:
0697         case NOP:
0698             break;
0699 
0700         case BOP:
0701             command_pointer += 11 * 4;
0702             currinf.data.dvi_h = 1200 << 16; // Reminder: DVI-coordinates start at (1",1") from top of page
0703             currinf.data.dvi_v = 1200;
0704             currinf.data.pxl_v = int(currinf.data.dvi_v / shrinkfactor);
0705             currinf.data.w = currinf.data.x = currinf.data.y = currinf.data.z = 0;
0706             break;
0707 
0708         case PUSH:
0709             stack.push(currinf.data);
0710             break;
0711 
0712         case POP:
0713             if (stack.isEmpty()) {
0714                 return;
0715             } else {
0716                 currinf.data = stack.pop();
0717             }
0718             break;
0719 
0720         case RIGHT1:
0721         case RIGHT2:
0722         case RIGHT3:
0723         case RIGHT4:
0724             RRtmp = readINT(ch - RIGHT1 + 1);
0725             currinf.data.dvi_h += ((long)(RRtmp * 65536.0 * fontPixelPerDVIunit));
0726             break;
0727 
0728         case W1:
0729         case W2:
0730         case W3:
0731         case W4:
0732             WWtmp = readINT(ch - W0);
0733             currinf.data.w = ((long)(WWtmp * 65536.0 * fontPixelPerDVIunit));
0734             // fallthrough
0735         case W0:
0736             currinf.data.dvi_h += currinf.data.w;
0737             break;
0738 
0739         case X1:
0740         case X2:
0741         case X3:
0742         case X4:
0743             XXtmp = readINT(ch - X0);
0744             currinf.data.x = ((long)(XXtmp * 65536.0 * fontPixelPerDVIunit));
0745             // fallthrough
0746         case X0:
0747             currinf.data.dvi_h += currinf.data.x;
0748             break;
0749 
0750         case DOWN1:
0751         case DOWN2:
0752         case DOWN3:
0753         case DOWN4: {
0754             qint32 DDtmp = readINT(ch - DOWN1 + 1);
0755             currinf.data.dvi_v += ((long)(DDtmp * 65536.0 * fontPixelPerDVIunit)) / 65536;
0756             currinf.data.pxl_v = int(currinf.data.dvi_v / shrinkfactor);
0757         } break;
0758 
0759         case Y1:
0760         case Y2:
0761         case Y3:
0762         case Y4:
0763             YYtmp = readINT(ch - Y0);
0764             currinf.data.y = ((long)(YYtmp * 65536.0 * fontPixelPerDVIunit));
0765             // fallthrough
0766         case Y0:
0767             currinf.data.dvi_v += currinf.data.y / 65536;
0768             currinf.data.pxl_v = int(currinf.data.dvi_v / shrinkfactor);
0769             break;
0770 
0771         case Z1:
0772         case Z2:
0773         case Z3:
0774         case Z4:
0775             ZZtmp = readINT(ch - Z0);
0776             currinf.data.z = ((long)(ZZtmp * 65536.0 * fontPixelPerDVIunit));
0777             // fallthrough
0778         case Z0:
0779             currinf.data.dvi_v += currinf.data.z / 65536;
0780             currinf.data.pxl_v = int(currinf.data.dvi_v / shrinkfactor);
0781             break;
0782 
0783         case FNT1:
0784         case FNT2:
0785         case FNT3:
0786         case FNT4:
0787             currinf.fontp = currinf.fonttable->value(readUINT(ch - FNT1 + 1));
0788             if (currinf.fontp == nullptr) {
0789                 return;
0790             }
0791             currinf.set_char_p = currinf.fontp->set_char_p;
0792             break;
0793 
0794         case XXX1:
0795         case XXX2:
0796         case XXX3:
0797         case XXX4: {
0798             quint8 *beginningOfSpecialCommand = command_pointer - 1;
0799             a = readUINT(ch - XXX1 + 1);
0800             if (a > 0) {
0801                 char *cmd = new char[a + 1];
0802                 strncpy(cmd, (char *)command_pointer, a);
0803                 command_pointer += a;
0804                 cmd[a] = '\0';
0805                 (this->*specialParser)(cmd, beginningOfSpecialCommand);
0806                 delete[] cmd;
0807             }
0808         } break;
0809 
0810         case FNTDEF1:
0811         case FNTDEF2:
0812         case FNTDEF3:
0813         case FNTDEF4:
0814             command_pointer += 12 + ch - FNTDEF1 + 1;
0815             command_pointer += readUINT8() + readUINT8();
0816             break;
0817 
0818         default:
0819             return;
0820         } /* end switch */
0821     }     /* end for */
0822 }