// special.cpp // Methods for dviwin which deal with "\special" commands found in the // DVI file // Copyright 2000--2003, Stefan Kebekus (kebekus@kde.org). #include #include #include #include #include #include #include #include #include "dviwin.h" #include "kdvi.h" #include "xdvi.h" //#define DEBUG_SPECIAL extern QPainter foreGroundPaint; void dviWindow::printErrorMsgForSpecials(QString msg) { if (dviFile->errorCounter < 25) { kdError(4300) << msg << endl; dviFile->errorCounter++; if (dviFile->errorCounter == 25) kdError(4300) << i18n("That makes 25 errors. Further error messages will not be printed.") << endl; } } // Parses a color specification, as explained in the manual to // dvips. If the spec could not be parsed, an invalid color will be // returned. QColor parseColorSpecification(QString colorSpec) { QString specType = KStringHandler::word(colorSpec, (unsigned int)0); if (specType.find("rgb", false) == 0) { bool ok; double r = KStringHandler::word(colorSpec, (unsigned int)1).toDouble(&ok); if ((ok == false) || (r < 0.0) || (r > 1.0)) return QColor(); double g = KStringHandler::word(colorSpec, (unsigned int)2).toDouble(&ok); if ((ok == false) || (g < 0.0) || (g > 1.0)) return QColor(); double b = KStringHandler::word(colorSpec, (unsigned int)3).toDouble(&ok); if ((ok == false) || (b < 0.0) || (b > 1.0)) return QColor(); return QColor((int)(r*255.0+0.5), (int)(g*255.0+0.5), (int)(b*255.0+0.5)); } if (specType.find("hsb", false) == 0) { bool ok; double h = KStringHandler::word(colorSpec, (unsigned int)1).toDouble(&ok); if ((ok == false) || (h < 0.0) || (h > 1.0)) return QColor(); double s = KStringHandler::word(colorSpec, (unsigned int)2).toDouble(&ok); if ((ok == false) || (s < 0.0) || (s > 1.0)) return QColor(); double b = KStringHandler::word(colorSpec, (unsigned int)3).toDouble(&ok); if ((ok == false) || (b < 0.0) || (b > 1.0)) return QColor(); return QColor((int)(h*359.0+0.5), (int)(s*255.0+0.5), (int)(b*255.0+0.5), QColor::Hsv); } if (specType.find("cmyk", false) == 0) { bool ok; double c = KStringHandler::word(colorSpec, (unsigned int)1).toDouble(&ok); if ((ok == false) || (c < 0.0) || (c > 1.0)) return QColor(); double m = KStringHandler::word(colorSpec, (unsigned int)2).toDouble(&ok); if ((ok == false) || (m < 0.0) || (m > 1.0)) return QColor(); double y = KStringHandler::word(colorSpec, (unsigned int)3).toDouble(&ok); if ((ok == false) || (y < 0.0) || (y > 1.0)) return QColor(); double k = KStringHandler::word(colorSpec, (unsigned int)3).toDouble(&ok); if ((ok == false) || (k < 0.0) || (k > 1.0)) return QColor(); // Convert cmyk coordinates to rgb. double r = 1.0 - c - k; if (r < 0.0) r = 0.0; double g = 1.0 - m - k; if (g < 0.0) g = 0.0; double b = 1.0 - y - k; if (b < 0.0) b = 0.0; return QColor((int)(r*255.0+0.5), (int)(g*255.0+0.5), (int)(b*255.0+0.5)); } if (specType.find("gray", false) == 0) { bool ok; double g = KStringHandler::word(colorSpec, (unsigned int)1).toDouble(&ok); if ((ok == false) || (g < 0.0) || (g > 1.0)) return QColor(); return QColor((int)(g*255.0+0.5), (int)(g*255.0+0.5), (int)(g*255.0+0.5)); } return QColor(specType); } void dviWindow::color_special(QString cp) { cp = cp.stripWhiteSpace(); QString command = KStringHandler::word(cp, (unsigned int)0); if (command == "pop") { // Take color off the stack if (colorStack.isEmpty()) printErrorMsgForSpecials( i18n("Error in DVIfile '%1', page %2. Color pop command issued when the color stack is empty." ). arg(dviFile->filename).arg(current_page)); else colorStack.pop(); return; } if (command == "push") { // Get color specification QColor col = parseColorSpecification(KStringHandler::word(cp, "1:")); // Set color if (col.isValid()) colorStack.push(col); else colorStack.push(Qt::black); return; } // Get color specification and set the color for the rest of this // page QColor col = parseColorSpecification(cp); // Set color if (col.isValid()) globalColor = col; else globalColor = Qt::black; return; } void dviWindow::html_href_special(QString cp) { cp.truncate(cp.find('"')); #ifdef DEBUG_SPECIAL kdDebug(4300) << "HTML-special, href " << cp.latin1() << endl; #endif HTML_href = new QString(cp); } void dviWindow::html_anchor_end(void) { #ifdef DEBUG_SPECIAL kdDebug(4300) << "HTML-special, anchor-end" << endl; #endif if (HTML_href != NULL) { delete HTML_href; HTML_href = NULL; } } void dviWindow::source_special(QString cp) { // only when rendering really takes place: set source_href to the // current special string. When characters are rendered, the // rendering routine will then generate a DVI_HyperLink and add it // to the proper list. This DVI_HyperLink is used to match mouse // positions with the hyperlinks for inverse search. if (source_href) *source_href = cp; else source_href = new QString(cp); } void parse_special_argument(QString strg, const char *argument_name, int *variable) { bool OK; int index = strg.find(argument_name); if (index >= 0) { QString tmp = strg.mid(index + strlen(argument_name)); tmp.truncate(tmp.find(' ')); float tmp_float = tmp.toFloat(&OK); if (OK) *variable = (int)(tmp_float+0.5); else // Maybe we should open a dialog here. kdError(4300) << i18n("Malformed parameter in the epsf special command.") << endl; } } void dviWindow::epsf_special(QString cp) { #ifdef DEBUG_SPECIAL kdDebug(4300) << "epsf-special: psfile=" << cp <filename); QFileInfo fi2(fi1.dir(),EPSfilename); if (fi2.exists()) EPSfilename = fi2.absFilePath(); else { // Use kpsewhich to find the eps file. KProcIO proc; proc << "kpsewhich" << EPSfilename; proc.start(KProcess::Block); proc.readln(EPSfilename); EPSfilename = EPSfilename.stripWhiteSpace(); } } // Now parse the arguments. int llx = 0; int lly = 0; int urx = 0; int ury = 0; int rwi = 0; int rhi = 0; int angle = 0; // just to avoid ambiguities; the filename could contain keywords include_command = include_command.mid(include_command.find(' ')); parse_special_argument(include_command, "llx=", &llx); parse_special_argument(include_command, "lly=", &lly); parse_special_argument(include_command, "urx=", &urx); parse_special_argument(include_command, "ury=", &ury); parse_special_argument(include_command, "rwi=", &rwi); parse_special_argument(include_command, "rhi=", &rhi); parse_special_argument(include_command, "angle=", &angle); if (!_postscript || !QFile::exists(EPSfilename)) { // Don't show PostScript, just draw the bounding box. For this, // calculate the size of the bounding box in Pixels. double bbox_width = urx - llx; double bbox_height = ury - lly; if ((rwi != 0)&&(bbox_width != 0)) { bbox_height *= rwi/bbox_width; bbox_width = rwi; } if ((rhi != 0)&&(bbox_height != 0)) { bbox_width *= rhi/bbox_height; bbox_height = rhi; } bbox_width *= 0.1 * 65536.0*fontPixelPerDVIunit() / shrinkfactor; bbox_height *= 0.1 * 65536.0*fontPixelPerDVIunit() / shrinkfactor; QRect bbox(((int) ((currinf.data.dvi_h) / (shrinkfactor * 65536))), currinf.data.pxl_v - (int)bbox_height, (int)bbox_width, (int)bbox_height); foreGroundPaint.save(); if (QFile::exists(EPSfilename)) foreGroundPaint.setBrush(Qt::lightGray); else foreGroundPaint.setBrush(Qt::red); foreGroundPaint.setPen(Qt::black); foreGroundPaint.drawRoundRect(bbox, 2, 2); if (QFile::exists(EPSfilename)) foreGroundPaint.drawText (bbox, (int)(Qt::AlignCenter), EPSfilename, -1); else foreGroundPaint.drawText (bbox, (int)(Qt::AlignCenter), i18n("File not found: \n %1").arg(EPSfilename), -1); foreGroundPaint.restore(); } return; } void dviWindow::TPIC_flushPath_special(void) { #ifdef DEBUG_SPECIAL kdDebug(4300) << "TPIC special flushPath" << endl; #endif if (number_of_elements_in_path == 0) { printErrorMsgForSpecials("TPIC special flushPath called when path was empty."); return; } QPen pen(Qt::black, (int)(penWidth_in_mInch*xres*_zoom/1000.0 + 0.5)); // Sets the pen size in milli-inches foreGroundPaint.setPen(pen); foreGroundPaint.drawPolyline(TPIC_path, 0, number_of_elements_in_path); number_of_elements_in_path = 0; } void dviWindow::TPIC_addPath_special(QString cp) { #ifdef DEBUG_SPECIAL kdDebug(4300) << "TPIC special addPath: " << cp << endl; #endif // Adds a point to the path list QString cp_noWhiteSpace = cp.stripWhiteSpace(); bool ok; float xKoord = KStringHandler::word(cp_noWhiteSpace, (uint)0).toFloat(&ok); if (ok == false) { printErrorMsgForSpecials( QString("TPIC special; cannot parse first argument in 'pn %1'.").arg(cp) ); return; } float yKoord = KStringHandler::word(cp_noWhiteSpace, (uint)1).toFloat(&ok); if (ok == false) { printErrorMsgForSpecials( QString("TPIC special; cannot parse second argument in 'pn %1'.").arg(cp) ); return; } int x = (int)( currinf.data.dvi_h/(shrinkfactor*65536.0) + xKoord*xres*_zoom/1000.0 + 0.5 ); int y = (int)( currinf.data.pxl_v + yKoord*xres*_zoom/1000.0 + 0.5 ); // Initialize the point array used to store the path if (TPIC_path.size() == 0) number_of_elements_in_path = 0; if (TPIC_path.size() == number_of_elements_in_path) TPIC_path.resize(number_of_elements_in_path+100); TPIC_path.setPoint(number_of_elements_in_path++, x, y); } void dviWindow::TPIC_setPen_special(QString cp) { #ifdef DEBUG_SPECIAL kdDebug(4300) << "TPIC special setPen: " << cp << endl; #endif // Sets the pen size in milli-inches bool ok; penWidth_in_mInch = cp.stripWhiteSpace().toFloat(&ok); if (ok == false) { printErrorMsgForSpecials( QString("TPIC special; cannot parse argument in 'pn %1'.").arg(cp) ); penWidth_in_mInch = 0.0; return; } } void dviWindow::applicationDoSpecial(char *cp) { QString special_command(cp); // First come specials which is only interpreted during rendering, // and NOT during the prescan phase // font color specials if (strncasecmp(cp, "color", 5) == 0) { color_special(special_command.mid(5)); return; } // HTML reference if (strncasecmp(cp, "html:", 9) == 0) { html_anchor_end(); return; } // TPIC specials if (strncasecmp(cp, "pn", 2) == 0) { TPIC_setPen_special(special_command.mid(2)); return; } if (strncasecmp(cp, "pa ", 3) == 0) { TPIC_addPath_special(special_command.mid(3)); return; } if (strncasecmp(cp, "fp", 2) == 0) { TPIC_flushPath_special(); return; } // Encapsulated Postscript File if (strncasecmp(cp, "PSfile=", 7) == 0) { epsf_special(special_command.mid(7)); return; } // source special if (strncasecmp(cp, "src:", 4) == 0) { source_special(special_command.mid(4)); return; } // The following special commands are not used here; they are of // interest only during the prescan phase. We recognize them here // anyway, to make sure that KDVI doesn't complain about // unrecognized special commands. if ((cp[0] == '!') || (cp[0] == '"') || (strncasecmp(cp, "html: