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  * SPDX-FileCopyrightText: 1994 Paul Vojta. All rights reserved.
0004  *
0005  * SPDX-License-Identifier: BSD-2-Clause
0006  *
0007  * NOTE:
0008  *        xdvi is based on prior work as noted in the modification history, below.
0009  */
0010 
0011 /*
0012  * DVI previewer for X.
0013  *
0014  * Eric Cooper, CMU, September 1985.
0015  *
0016  * Code derived from dvi-imagen.c.
0017  *
0018  * Modification history:
0019  * 1/1986        Modified for X.10        --Bob Scheifler, MIT LCS.
0020  * 7/1988        Modified for X.11        --Mark Eichin, MIT
0021  * 12/1988        Added 'R' option, toolkit, magnifying glass
0022  *                                        --Paul Vojta, UC Berkeley.
0023  * 2/1989        Added tpic support       --Jeffrey Lee, U of Toronto
0024  * 4/1989        Modified for System V    --Donald Richardson, Clarkson Univ.
0025  * 3/1990        Added VMS support        --Scott Allendorf, U of Iowa
0026  * 7/1990        Added reflection mode    --Michael Pak, Hebrew U of Jerusalem
0027  * 1/1992        Added greyscale code     --Till Brychcy, Techn. Univ. Muenchen
0028  *                                          and Lee Hetherington, MIT
0029  * 4/1994        Added DPS support, bounding box
0030  *                                        --Ricardo Telichevesky
0031  *                                          and Luis Miguel Silveira, MIT RLE.
0032  */
0033 
0034 //#define DEBUG_RENDER 0
0035 
0036 #include <config.h>
0037 
0038 #include "debug_dvi.h"
0039 #include "dvi.h"
0040 #include "dviFile.h"
0041 #include "dviRenderer.h"
0042 #include "hyperlink.h"
0043 #include "psgs.h"
0044 //#include "renderedDviPagePixmap.h"
0045 #include "TeXFont.h"
0046 #include "textBox.h"
0047 #include "xdvi.h"
0048 
0049 #include <KLocalizedString>
0050 
0051 #include <QLoggingCategory>
0052 #include <QPainter>
0053 
0054 /** Routine to print characters.  */
0055 
0056 void dviRenderer::set_char(unsigned int cmd, unsigned int ch)
0057 {
0058 #ifdef DEBUG_RENDER
0059     qCDebug(OkularDviDebug) << "set_char #" << ch;
0060 #endif
0061 
0062     glyph *g;
0063     if (colorStack.isEmpty()) {
0064         g = ((TeXFont *)(currinf.fontp->font))->getGlyph(ch, true, globalColor);
0065     } else {
0066         g = ((TeXFont *)(currinf.fontp->font))->getGlyph(ch, true, colorStack.top());
0067     }
0068     if (g == nullptr) {
0069         return;
0070     }
0071 
0072     long dvi_h_sav = currinf.data.dvi_h;
0073 
0074     QImage pix = g->shrunkenCharacter;
0075     int x = ((int)((currinf.data.dvi_h) / (shrinkfactor * 65536))) - g->x2;
0076     int y = currinf.data.pxl_v - g->y2;
0077 
0078     // Draw the character.
0079     foreGroundPainter->drawImage(x, y, pix);
0080 
0081     // Are we drawing text for a hyperlink? And are hyperlinks
0082     // enabled?
0083     if (HTML_href != nullptr) {
0084         // Now set up a rectangle which is checked against every mouse
0085         // event.
0086         if (line_boundary_encountered == true) {
0087             // Set up hyperlink
0088             Hyperlink dhl;
0089             dhl.baseline = currinf.data.pxl_v;
0090             dhl.box.setRect(x, y, pix.width(), pix.height());
0091             dhl.linkText = *HTML_href;
0092             currentlyDrawnPage->hyperLinkList.push_back(dhl);
0093         } else {
0094             QRect dshunion = currentlyDrawnPage->hyperLinkList[currentlyDrawnPage->hyperLinkList.size() - 1].box.united(QRect(x, y, pix.width(), pix.height()));
0095             currentlyDrawnPage->hyperLinkList[currentlyDrawnPage->hyperLinkList.size() - 1].box = dshunion;
0096         }
0097     }
0098 
0099     // Are we drawing text for a source hyperlink? And are source
0100     // hyperlinks enabled?
0101     // If we are printing source hyperlinks are irrelevant, otherwise we
0102     // actually got a pointer to a RenderedDviPagePixmap.
0103     RenderedDviPagePixmap *currentDVIPage = dynamic_cast<RenderedDviPagePixmap *>(currentlyDrawnPage);
0104     if (source_href != nullptr && currentDVIPage) {
0105         // Now set up a rectangle which is checked against every mouse
0106         // event.
0107         if (line_boundary_encountered == true) {
0108             // Set up source hyperlinks
0109             Hyperlink dhl;
0110             dhl.baseline = currinf.data.pxl_v;
0111             dhl.box.setRect(x, y, pix.width(), pix.height());
0112             if (source_href != nullptr) {
0113                 dhl.linkText = *source_href;
0114             } else {
0115                 dhl.linkText = QLatin1String("");
0116             }
0117             currentDVIPage->sourceHyperLinkList.push_back(dhl);
0118         } else {
0119             QRect dshunion = currentDVIPage->sourceHyperLinkList[currentDVIPage->sourceHyperLinkList.size() - 1].box.united(QRect(x, y, pix.width(), pix.height()));
0120             currentDVIPage->sourceHyperLinkList[currentDVIPage->sourceHyperLinkList.size() - 1].box = dshunion;
0121         }
0122     }
0123 
0124     // Code for DVI -> text functions (e.g. marking of text, full text
0125     // search, etc.). Set up the currentlyDrawnPage->textBoxList.
0126     TextBox link;
0127     link.box.setRect(x, y, pix.width(), pix.height());
0128     link.text = QLatin1String("");
0129     currentlyDrawnPage->textBoxList.push_back(link);
0130 
0131     switch (ch) {
0132     case 0x0b:
0133         currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QLatin1String("ff");
0134         break;
0135     case 0x0c:
0136         currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QLatin1String("fi");
0137         break;
0138     case 0x0d:
0139         currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QLatin1String("fl");
0140         break;
0141     case 0x0e:
0142         currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QLatin1String("ffi");
0143         break;
0144     case 0x0f:
0145         currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QLatin1String("ffl");
0146         break;
0147 
0148     case 0x7b:
0149         currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QLatin1Char('-');
0150         break;
0151     case 0x7c:
0152         currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QLatin1String("---");
0153         break;
0154     case 0x7d:
0155         currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QLatin1String("\"");
0156         break;
0157     case 0x7e:
0158         currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QLatin1Char('~');
0159         break;
0160     case 0x7f:
0161         currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QLatin1String("@@"); // @@@ check!
0162         break;
0163 
0164     default:
0165         if ((ch >= 0x21) && (ch <= 0x7a)) {
0166             currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QChar(ch);
0167         } else {
0168             currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QLatin1Char('?');
0169         }
0170         break;
0171     }
0172 
0173     if (cmd == PUT1) {
0174         currinf.data.dvi_h = dvi_h_sav;
0175     } else {
0176         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);
0177     }
0178 
0179     word_boundary_encountered = false;
0180     line_boundary_encountered = false;
0181 }
0182 
0183 void dviRenderer::set_empty_char(unsigned int, unsigned int)
0184 {
0185     return;
0186 }
0187 
0188 void dviRenderer::set_vf_char(unsigned int cmd, unsigned int ch)
0189 {
0190 #ifdef DEBUG_RENDER
0191     qCDebug(OkularDviDebug) << "dviRenderer::set_vf_char( cmd=" << cmd << ", ch=" << ch << " )";
0192 #endif
0193 
0194     static unsigned char c;
0195     macro *m = &currinf.fontp->macrotable[ch];
0196     if (m->pos == nullptr) {
0197         qCCritical(OkularDviDebug) << "Character " << ch << " not defined in font " << currinf.fontp->fontname;
0198         m->pos = m->end = &c;
0199         return;
0200     }
0201 
0202     long dvi_h_sav = currinf.data.dvi_h;
0203 
0204     struct drawinf oldinfo = currinf;
0205     currinf.data.w = 0;
0206     currinf.data.x = 0;
0207     currinf.data.y = 0;
0208     currinf.data.z = 0;
0209 
0210     currinf.fonttable = &(currinf.fontp->vf_table);
0211     currinf._virtual = currinf.fontp;
0212     quint8 *command_ptr_sav = command_pointer;
0213     quint8 *end_ptr_sav = end_pointer;
0214     command_pointer = m->pos;
0215     end_pointer = m->end;
0216     draw_part(currinf.fontp->scaled_size_in_DVI_units * (dviFile->getCmPerDVIunit() * 1200.0 / 2.54) / 16.0, true);
0217     command_pointer = command_ptr_sav;
0218     end_pointer = end_ptr_sav;
0219     currinf = oldinfo;
0220 
0221     if (cmd == PUT1) {
0222         currinf.data.dvi_h = dvi_h_sav;
0223     } else {
0224         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);
0225     }
0226 }
0227 
0228 void dviRenderer::set_no_char(unsigned int cmd, unsigned int ch)
0229 {
0230 #ifdef DEBUG_RENDER
0231     qCDebug(OkularDviDebug) << "dviRenderer::set_no_char( cmd=" << cmd << ", ch =" << ch << " )";
0232 #endif
0233 
0234     if (currinf._virtual) {
0235         currinf.fontp = currinf._virtual->first_font;
0236         if (currinf.fontp != nullptr) {
0237             currinf.set_char_p = currinf.fontp->set_char_p;
0238             (this->*currinf.set_char_p)(cmd, ch);
0239             return;
0240         }
0241     }
0242 
0243     errorMsg = i18n("The DVI code set a character of an unknown font.");
0244     return;
0245 }
0246 
0247 void dviRenderer::draw_part(double current_dimconv, bool is_vfmacro)
0248 {
0249 #ifdef DEBUG_RENDER
0250     qCDebug(OkularDviDebug) << "draw_part";
0251 #endif
0252 
0253     qint32 RRtmp = 0, WWtmp = 0, XXtmp = 0, YYtmp = 0, ZZtmp = 0;
0254     quint8 ch;
0255 
0256     currinf.fontp = nullptr;
0257     currinf.set_char_p = &dviRenderer::set_no_char;
0258 
0259     int last_space_index = 0;
0260     bool space_encountered = false;
0261     bool after_space = false;
0262     for (;;) {
0263         space_encountered = false;
0264         ch = readUINT8();
0265         if (ch <= (unsigned char)(SETCHAR0 + 127)) {
0266             (this->*currinf.set_char_p)(ch, ch);
0267         } else if (FNTNUM0 <= ch && ch <= (unsigned char)(FNTNUM0 + 63)) {
0268             currinf.fontp = currinf.fonttable->value(ch - FNTNUM0);
0269             if (currinf.fontp == nullptr) {
0270                 errorMsg = i18n("The DVI code referred to font #%1, which was not previously defined.", ch - FNTNUM0);
0271                 return;
0272             }
0273             currinf.set_char_p = currinf.fontp->set_char_p;
0274         } else {
0275             qint32 a, b;
0276 
0277             switch (ch) {
0278             case SET1:
0279             case PUT1:
0280                 (this->*currinf.set_char_p)(ch, readUINT8());
0281                 break;
0282 
0283             case SETRULE:
0284                 if (is_vfmacro == false) {
0285                     word_boundary_encountered = true;
0286                     line_boundary_encountered = true;
0287                 }
0288                 /* Be careful, dvicopy outputs rules with height =
0289                    0x80000000. We don't want any SIGFPE here. */
0290                 a = readUINT32();
0291                 b = readUINT32();
0292                 b = ((long)(b * current_dimconv));
0293                 if (a > 0 && b > 0) {
0294                     int h = ((int)ROUNDUP(((long)(a * current_dimconv)), shrinkfactor * 65536));
0295                     int w = ((int)ROUNDUP(b, shrinkfactor * 65536));
0296 
0297                     if (colorStack.isEmpty()) {
0298                         foreGroundPainter->fillRect(((int)((currinf.data.dvi_h) / (shrinkfactor * 65536))), currinf.data.pxl_v - h + 1, w ? w : 1, h ? h : 1, globalColor);
0299                     } else {
0300                         foreGroundPainter->fillRect(((int)((currinf.data.dvi_h) / (shrinkfactor * 65536))), currinf.data.pxl_v - h + 1, w ? w : 1, h ? h : 1, colorStack.top());
0301                     }
0302                 }
0303                 currinf.data.dvi_h += b;
0304                 break;
0305 
0306             case PUTRULE:
0307                 if (is_vfmacro == false) {
0308                     word_boundary_encountered = true;
0309                     line_boundary_encountered = true;
0310                 }
0311                 a = readUINT32();
0312                 b = readUINT32();
0313                 a = ((long)(a * current_dimconv));
0314                 b = ((long)(b * current_dimconv));
0315                 if (a > 0 && b > 0) {
0316                     int h = ((int)ROUNDUP(a, shrinkfactor * 65536));
0317                     int w = ((int)ROUNDUP(b, shrinkfactor * 65536));
0318                     if (colorStack.isEmpty()) {
0319                         foreGroundPainter->fillRect(((int)((currinf.data.dvi_h) / (shrinkfactor * 65536))), currinf.data.pxl_v - h + 1, w ? w : 1, h ? h : 1, globalColor);
0320                     } else {
0321                         foreGroundPainter->fillRect(((int)((currinf.data.dvi_h) / (shrinkfactor * 65536))), currinf.data.pxl_v - h + 1, w ? w : 1, h ? h : 1, colorStack.top());
0322                     }
0323                 }
0324                 break;
0325 
0326             case NOP:
0327                 break;
0328 
0329             case BOP:
0330                 if (is_vfmacro == false) {
0331                     word_boundary_encountered = true;
0332                     line_boundary_encountered = true;
0333                 }
0334                 command_pointer += 11 * 4;
0335                 currinf.data.dvi_h = 1200 << 16; // Reminder: DVI-coordinates start at (1",1") from top of page
0336                 currinf.data.dvi_v = 1200;
0337                 currinf.data.pxl_v = int(currinf.data.dvi_v / shrinkfactor);
0338                 currinf.data.w = currinf.data.x = currinf.data.y = currinf.data.z = 0;
0339                 break;
0340 
0341             case EOP:
0342                 // Check if we are just at the end of a virtual font macro.
0343                 if (is_vfmacro == false) {
0344                     // This is really the end of a page, and not just the end
0345                     // of a macro. Mark the end of the current word.
0346                     word_boundary_encountered = true;
0347                     line_boundary_encountered = true;
0348                     // Sanity check for the dvi-file: The DVI-standard asserts
0349                     // that at the end of a page, the stack should always be
0350                     // empty.
0351                     if (!stack.isEmpty()) {
0352                         qCDebug(OkularDviDebug) << "DRAW: The stack was not empty when the EOP command was encountered.";
0353                         errorMsg = i18n("The stack was not empty when the EOP command was encountered.");
0354                         return;
0355                     }
0356                 }
0357                 return;
0358 
0359             case PUSH:
0360                 stack.push(currinf.data);
0361                 break;
0362 
0363             case POP:
0364                 if (stack.isEmpty()) {
0365                     errorMsg = i18n("The stack was empty when a POP command was encountered.");
0366                     return;
0367                 } else {
0368                     currinf.data = stack.pop();
0369                 }
0370                 word_boundary_encountered = true;
0371                 line_boundary_encountered = true;
0372                 break;
0373 
0374             case RIGHT1:
0375             case RIGHT2:
0376             case RIGHT3:
0377             case RIGHT4:
0378                 RRtmp = readINT(ch - RIGHT1 + 1);
0379 
0380                 // A horizontal motion in the range 4 * font space [f] < h <
0381                 // font space [f] will be treated as a kern that is not
0382                 // indicated in the printouts that DVItype produces between
0383                 // brackets. We allow a larger space in the negative
0384                 // direction than in the positive one, because TEX makes
0385                 // comparatively large backspaces when it positions
0386                 // accents. (comments stolen from the source of dvitype)
0387                 if ((is_vfmacro == false) && (currinf.fontp != nullptr) && ((RRtmp >= currinf.fontp->scaled_size_in_DVI_units / 6) || (RRtmp <= -4 * (currinf.fontp->scaled_size_in_DVI_units / 6))) &&
0388                     (currentlyDrawnPage->textBoxList.size() > 0)) {
0389                     // currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += ' ';
0390                     space_encountered = true;
0391                 }
0392                 currinf.data.dvi_h += ((long)(RRtmp * current_dimconv));
0393                 break;
0394 
0395             case W1:
0396             case W2:
0397             case W3:
0398             case W4:
0399                 WWtmp = readINT(ch - W0);
0400                 currinf.data.w = ((long)(WWtmp * current_dimconv));
0401                 // fallthrough
0402             case W0:
0403                 if ((is_vfmacro == false) && (currinf.fontp != nullptr) && ((WWtmp >= currinf.fontp->scaled_size_in_DVI_units / 6) || (WWtmp <= -4 * (currinf.fontp->scaled_size_in_DVI_units / 6))) &&
0404                     (currentlyDrawnPage->textBoxList.size() > 0)) {
0405                     // currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += ' ';
0406                     space_encountered = true;
0407                 }
0408                 currinf.data.dvi_h += currinf.data.w;
0409                 break;
0410 
0411             case X1:
0412             case X2:
0413             case X3:
0414             case X4:
0415                 XXtmp = readINT(ch - X0);
0416                 currinf.data.x = ((long)(XXtmp * current_dimconv));
0417                 // fallthrough
0418             case X0:
0419                 if ((is_vfmacro == false) && (currinf.fontp != nullptr) && ((XXtmp >= currinf.fontp->scaled_size_in_DVI_units / 6) || (XXtmp <= -4 * (currinf.fontp->scaled_size_in_DVI_units / 6))) &&
0420                     (currentlyDrawnPage->textBoxList.size() > 0)) {
0421                     // currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size()-1].text += ' ';
0422                     space_encountered = true;
0423                 }
0424                 currinf.data.dvi_h += currinf.data.x;
0425                 break;
0426 
0427             case DOWN1:
0428             case DOWN2:
0429             case DOWN3:
0430             case DOWN4: {
0431                 qint32 DDtmp = readINT(ch - DOWN1 + 1);
0432                 if ((is_vfmacro == false) && (currinf.fontp != nullptr) && (abs(DDtmp) >= 5 * (currinf.fontp->scaled_size_in_DVI_units / 6)) && (currentlyDrawnPage->textBoxList.size() > 0)) {
0433                     word_boundary_encountered = true;
0434                     line_boundary_encountered = true;
0435                     space_encountered = true;
0436                     if (abs(DDtmp) >= 10 * (currinf.fontp->scaled_size_in_DVI_units / 6)) {
0437                         currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QLatin1Char('\n');
0438                     }
0439                 }
0440                 currinf.data.dvi_v += ((long)(DDtmp * current_dimconv)) / 65536;
0441                 currinf.data.pxl_v = int(currinf.data.dvi_v / shrinkfactor);
0442             } break;
0443 
0444             case Y1:
0445             case Y2:
0446             case Y3:
0447             case Y4:
0448                 YYtmp = readINT(ch - Y0);
0449                 currinf.data.y = ((long)(YYtmp * current_dimconv));
0450                 // fallthrough
0451             case Y0:
0452                 if ((is_vfmacro == false) && (currinf.fontp != nullptr) && (abs(YYtmp) >= 5 * (currinf.fontp->scaled_size_in_DVI_units / 6)) && (currentlyDrawnPage->textBoxList.size() > 0)) {
0453                     word_boundary_encountered = true;
0454                     line_boundary_encountered = true;
0455                     space_encountered = true;
0456                     if (abs(YYtmp) >= 10 * (currinf.fontp->scaled_size_in_DVI_units / 6)) {
0457                         currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QLatin1Char('\n');
0458                     }
0459                 }
0460                 currinf.data.dvi_v += currinf.data.y / 65536;
0461                 currinf.data.pxl_v = int(currinf.data.dvi_v / shrinkfactor);
0462                 break;
0463 
0464             case Z1:
0465             case Z2:
0466             case Z3:
0467             case Z4:
0468                 ZZtmp = readINT(ch - Z0);
0469                 currinf.data.z = ((long)(ZZtmp * current_dimconv));
0470                 // fallthrough
0471             case Z0:
0472                 if ((is_vfmacro == false) && (currinf.fontp != nullptr) && (abs(ZZtmp) >= 5 * (currinf.fontp->scaled_size_in_DVI_units / 6)) && (currentlyDrawnPage->textBoxList.size() > 0)) {
0473                     word_boundary_encountered = true;
0474                     line_boundary_encountered = true;
0475                     space_encountered = true;
0476                     if (abs(ZZtmp) >= 10 * (currinf.fontp->scaled_size_in_DVI_units / 6)) {
0477                         currentlyDrawnPage->textBoxList[currentlyDrawnPage->textBoxList.size() - 1].text += QLatin1Char('\n');
0478                     }
0479                 }
0480                 currinf.data.dvi_v += currinf.data.z / 65536;
0481                 currinf.data.pxl_v = int(currinf.data.dvi_v / shrinkfactor);
0482                 break;
0483 
0484             case FNT1:
0485             case FNT2:
0486             case FNT3:
0487                 currinf.fontp = currinf.fonttable->value(readUINT(ch - FNT1 + 1));
0488                 if (currinf.fontp == nullptr) {
0489                     errorMsg = i18n("The DVI code referred to a font which was not previously defined.");
0490                     return;
0491                 }
0492                 currinf.set_char_p = currinf.fontp->set_char_p;
0493                 break;
0494 
0495             case FNT4:
0496                 currinf.fontp = currinf.fonttable->value(readINT(ch - FNT1 + 1));
0497                 if (currinf.fontp == nullptr) {
0498                     errorMsg = i18n("The DVI code referred to a font which was not previously defined.");
0499                     return;
0500                 }
0501                 currinf.set_char_p = currinf.fontp->set_char_p;
0502                 break;
0503 
0504             case XXX1:
0505             case XXX2:
0506             case XXX3:
0507             case XXX4:
0508                 if (is_vfmacro == false) {
0509                     word_boundary_encountered = true;
0510                     line_boundary_encountered = true;
0511                     space_encountered = true;
0512                 }
0513                 a = readUINT(ch - XXX1 + 1);
0514                 if (a > 0) {
0515                     char *cmd = new char[a + 1];
0516                     strncpy(cmd, (char *)command_pointer, a);
0517                     command_pointer += a;
0518                     cmd[a] = '\0';
0519                     applicationDoSpecial(cmd);
0520                     delete[] cmd;
0521                 }
0522                 break;
0523 
0524             case FNTDEF1:
0525             case FNTDEF2:
0526             case FNTDEF3:
0527             case FNTDEF4:
0528                 command_pointer += 12 + ch - FNTDEF1 + 1;
0529                 {
0530                     quint8 tempa = readUINT8();
0531                     quint8 tempb = readUINT8();
0532                     command_pointer += tempa + tempb;
0533                 }
0534                 break;
0535 
0536             case PRE:
0537             case POST:
0538             case POSTPOST:
0539                 errorMsg = i18n("An illegal command was encountered.");
0540                 return;
0541                 break;
0542 
0543             default:
0544                 errorMsg = i18n("The unknown op-code %1 was encountered.", ch);
0545                 return;
0546             } /* end switch*/
0547         }     /* end else (ch not a SETCHAR or FNTNUM) */
0548 
0549 #ifdef DEBUG_RENDER
0550         if (currentlyDrawnPage->textBoxList.size() > 0)
0551             qCDebug(OkularDviDebug) << "Element:" << currentlyDrawnPage->textBoxList.last().box << currentlyDrawnPage->textBoxList.last().text << " ? s:" << space_encountered << " / nl:" << line_boundary_encountered
0552                                     << " / w:" << word_boundary_encountered << ", " << last_space_index << "/" << currentlyDrawnPage->textBoxList.size();
0553 #endif
0554 
0555         /* heuristic to properly detect newlines; a space is needed */
0556         if (after_space && line_boundary_encountered && word_boundary_encountered) {
0557             if (currentlyDrawnPage->textBoxList.last().text.endsWith(QLatin1Char('\n'))) {
0558                 currentlyDrawnPage->textBoxList.last().text.chop(1);
0559             }
0560             currentlyDrawnPage->textBoxList.last().text += QLatin1String(" \n");
0561             after_space = false;
0562         }
0563 
0564         /* a "space" has been found and there is some (new) character
0565            in the array */
0566         if (space_encountered && (currentlyDrawnPage->textBoxList.size() > last_space_index)) {
0567             for (int lidx = last_space_index + 1; lidx < currentlyDrawnPage->textBoxList.size(); ++lidx) {
0568                 // merge two adjacent boxes which are part of the same word
0569                 currentlyDrawnPage->textBoxList[lidx - 1].box.setRight(currentlyDrawnPage->textBoxList[lidx].box.x());
0570             }
0571 #ifdef DEBUG_RENDER
0572             QString lastword(currentlyDrawnPage->textBoxList[last_space_index].text);
0573             for (int lidx = last_space_index + 1; lidx < currentlyDrawnPage->textBoxList.size(); ++lidx)
0574                 lastword += currentlyDrawnPage->textBoxList[lidx].text;
0575             qCDebug(OkularDviDebug) << "space encountered: '" << lastword << "'";
0576 #endif
0577             last_space_index = currentlyDrawnPage->textBoxList.size();
0578             after_space = true;
0579         }
0580     } /* end for */
0581 }
0582 
0583 void dviRenderer::draw_page()
0584 {
0585     // Reset a couple of variables
0586     HTML_href = nullptr;
0587     source_href = nullptr;
0588     penWidth_in_mInch = 0.0;
0589 
0590     // Calling resize() here rather than clear() means that the memory
0591     // taken up by the vector is not freed. This is faster than
0592     // constantly allocating/freeing memory.
0593     currentlyDrawnPage->textBoxList.resize(0);
0594 
0595     RenderedDviPagePixmap *currentDVIPage = dynamic_cast<RenderedDviPagePixmap *>(currentlyDrawnPage);
0596     if (currentDVIPage) {
0597         currentDVIPage->sourceHyperLinkList.resize(0);
0598     }
0599 
0600 #ifdef PERFORMANCE_MEASUREMENT
0601     // If this is the first time a page is drawn, take the time that is
0602     // elapsed till the kdvi_multipage was constructed, and print
0603     // it. Set the flag so that is message will not be printed again.
0604     if (performanceFlag == 0) {
0605         qCDebug(OkularDviDebug) << "Time elapsed till the first page is drawn: " << performanceTimer.restart() << "ms";
0606         performanceFlag = 1;
0607     }
0608 #endif
0609 
0610 #ifdef DEBUG_RENDER
0611     qCDebug(OkularDviDebug) << "draw_page";
0612 #endif
0613 
0614     foreGroundPainter->fillRect(foreGroundPainter->viewport(), PS_interface->getBackgroundColor(current_page));
0615 
0616     // Render the PostScript background, if there is one.
0617     if (_postscript) {
0618         PS_interface->restoreBackgroundColor(current_page);
0619 
0620         PS_interface->graphics(current_page, resolutionInDPI, dviFile->getMagnification(), foreGroundPainter);
0621     }
0622 
0623     // Now really write the text
0624     if (dviFile->page_offset.isEmpty() == true) {
0625         return;
0626     }
0627     if (current_page < dviFile->total_pages) {
0628         command_pointer = dviFile->dvi_Data() + dviFile->page_offset[int(current_page)];
0629         end_pointer = dviFile->dvi_Data() + dviFile->page_offset[int(current_page + 1)];
0630     } else {
0631         command_pointer = end_pointer = nullptr;
0632     }
0633 
0634     memset((char *)&currinf.data, 0, sizeof(currinf.data));
0635     currinf.fonttable = &(dviFile->tn_table);
0636     currinf._virtual = nullptr;
0637 
0638     double fontPixelPerDVIunit = dviFile->getCmPerDVIunit() * 1200.0 / 2.54;
0639 
0640     draw_part(65536.0 * fontPixelPerDVIunit, false);
0641     if (HTML_href != nullptr) {
0642         delete HTML_href;
0643         HTML_href = nullptr;
0644     }
0645     if (source_href != nullptr) {
0646         delete source_href;
0647         source_href = nullptr;
0648     }
0649 }