/************************************************************************* ** PSInterpreter.cpp ** ** ** ** This file is part of dvisvgm -- the DVI to SVG converter ** ** Copyright (C) 2005-2013 Martin Gieseking ** ** ** ** This program is free software; you can redistribute it and/or ** ** modify it under the terms of the GNU General Public License as ** ** published by the Free Software Foundation; either version 3 of ** ** the License, or (at your option) any later version. ** ** ** ** This program is distributed in the hope that it will be useful, but ** ** WITHOUT ANY WARRANTY; without even the implied warranty of ** ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** ** GNU General Public License for more details. ** ** ** ** You should have received a copy of the GNU General Public License ** ** along with this program; if not, see . ** *************************************************************************/ #include #include #include #include #include "FileFinder.h" #include "InputReader.h" #include "Message.h" #include "PSFilter.h" #include "PSInterpreter.h" #include "SignalHandler.h" using namespace std; const char *PSInterpreter::GSARGS[] = { "gs", // dummy name "-q", // be quiet, suppress gs banner "-dSAFER", // disallow writing of files "-dNODISPLAY", // we don't need a display device "-dNOPAUSE", // keep going "-dWRITESYSTEMDICT", // leave systemdict writable as some operators must be replaced "-dNOPROMPT", // "-dNOBIND", }; /** Constructs a new PSInterpreter object. * @param[in] actions template methods to be executed after recognizing the corresponding PS operator. */ PSInterpreter::PSInterpreter (PSActions *actions) : _mode(PS_NONE), _actions(actions), _filter(0), _bytesToRead(0), _inError(false), _initialized(false) { } void PSInterpreter::init () { if (!_initialized) { _gs.init(sizeof(GSARGS)/sizeof(char*), GSARGS, this); _gs.set_stdio(input, output, error); _initialized = true; // Before executing any random PS code redefine some operators and run // initializing PS code. This cannot be done in the constructor because we // need the completely initialized PSInterpreter object here. execute(PSDEFS); } } PSActions* PSInterpreter::setActions (PSActions *actions) { PSActions *old_actions = _actions; _actions = actions; return old_actions; } /** Checks if the given status value returned by Ghostscript indicates an error. * @param[in] status status value returned by Ghostscript after the execution of a PS snippet * @throw PSException if the status value indicates a PostScript error */ void PSInterpreter::checkStatus (int status) { if (status < 0) { _mode = PS_QUIT; if (status < -100) throw PSException("fatal error"); if (_errorMessage.empty()) throw PSException(_gs.error_name(status)); throw PSException(_errorMessage); } } /** Executes a chunk of PostScript code. * @param[in] str buffer containing the code * @param[in] len number of characters in buffer * @param[in] flush If true, a final 'flush' is sent which forces the output buffer to be written immediately. * @return true if the assigned number of bytes have been read */ bool PSInterpreter::execute (const char *str, size_t len, bool flush) { init(); if (_mode == PS_QUIT) return false; int status=0; if (_mode == PS_NONE) { _gs.run_string_begin(0, &status); _mode = PS_RUNNING; } checkStatus(status); bool complete=false; if (_bytesToRead > 0 && len >= _bytesToRead) { len = _bytesToRead; complete = true; } if (_filter && _filter->active()) { PSFilter *filter = _filter; _filter = 0; // prevent recursion when filter calls execute() filter->execute(str, len); if (filter->active()) // filter still active after execution? _filter = filter; return complete; } // feed Ghostscript with code chunks that are not larger than 64KB // => see documentation of gsapi_run_string_foo() const char *p=str; while (PS_RUNNING && len > 0) { SignalHandler::instance().check(); size_t chunksize = min(len, (size_t)0xffff); _gs.run_string_continue(p, chunksize, 0, &status); p += chunksize; len -= chunksize; if (_bytesToRead > 0) _bytesToRead -= chunksize; if (status == -101) // e_Quit _mode = PS_QUIT; else checkStatus(status); } if (flush) { // force writing contents of output buffer _gs.run_string_continue("\nflush ", 7, 0, &status); } return complete; } /** Executes a chunk of PostScript code read from a stream. The method returns on EOF. * @param[in] is the input stream * @return true if the assigned number of bytes have been read */ bool PSInterpreter::execute (istream &is, bool flush) { char buf[4096]; bool finished = false; while (is && !is.eof() && !finished) { is.read(buf, 4096); finished = execute(buf, is.gcount(), false); } execute("\n", 1, flush); return finished; } bool PSInterpreter::executeRaw (const string &str, int n) { _rawData.clear(); ostringstream oss; oss << str << ' ' << n << " (raw) prcmd\n"; execute(oss.str()); return !_rawData.empty(); } /** This callback function handles input from stdin to Ghostscript. Currently not needed. * @param[in] inst pointer to calling instance of PSInterpreter * @param[in] buf takes the read characters * @param[in] len size of buffer buf * @return number of characters read */ int GSDLLCALL PSInterpreter::input (void *inst, char *buf, int len) { return 0; } /** This callback function handles output from Ghostscript to stdout. It looks for * emitted commands staring with "dvi." and executes them by calling method callActions. * Ghostscript sends the text in chunks by several calls of this function. * Unfortunately, the PostScript specification wants error messages also to be sent to stdout * instead of stderr. Thus, we must collect and concatenate the chunks until an evaluable text * snippet is completely received. Furthermore, error messages have to be recognized and to be * filtered out. * @param[in] inst pointer to calling instance of PSInterpreter * @param[in] buf contains the characters to be output * @param[in] len number of characters in buf * @return number of processed characters (equals 'len') */ int GSDLLCALL PSInterpreter::output (void *inst, const char *buf, int len) { PSInterpreter *self = static_cast(inst); if (self && self->_actions) { const size_t MAXLEN = 512; // maximal line length (longer lines are of no interest) const char *end = buf+len-1; // last position of buf for (const char *first=buf, *last=buf; first <= end; last++, first=last) { // move first and last to begin and end of the next line, respectively while (last < end && *last != '\n') last++; size_t linelength = last-first+1; if (linelength > MAXLEN) // skip long lines since they don't contain any relevant information continue; vector &linebuf = self->_linebuf; // just a shorter name... if ((*last == '\n' || !self->active()) || self->_inError) { if (linelength + linebuf.size() > 1) { // prefix "dvi." plus final newline SplittedCharInputBuffer ib(linebuf.empty() ? 0 : &linebuf[0], linebuf.size(), first, linelength); BufferInputReader in(ib); if (self->_inError) self->_errorMessage += string(first, linelength); else { in.skipSpace(); if (in.check("Unrecoverable error: ")) { self->_errorMessage.clear(); while (!in.eof()) self->_errorMessage += in.get(); self->_inError = true; } else if (in.check("dvi.")) self->callActions(in); } } linebuf.clear(); } else { // no line end found => // save remaining characters and prepend them to the next incoming chunk of characters if (linebuf.size() + linelength > MAXLEN) linebuf.clear(); // don't care for long lines else { size_t currsize = linebuf.size(); linebuf.resize(currsize+linelength); memcpy(&linebuf[currsize], first, linelength); } } } } return len; } /** Converts a vector of strings to a vector of doubles. * @param[in] str the strings to be converted * @param[out] d the resulting doubles */ static void str2double (const vector &str, vector &d) { for (size_t i=0; i < str.size(); i++) { istringstream iss(str[i]); iss >> d[i]; } } /** Evaluates a command emitted by Ghostscript and invokes the corresponding * method of interface class PSActions. * @param[in] in reader pointing to the next command */ void PSInterpreter::callActions (InputReader &in) { // array of currently supported operators (must be ascendingly sorted) static const struct Operator { const char *name; // name of operator int pcount; // number of parameters (< 0 : variable number of parameters) void (PSActions::*op)(vector &p); // operation handler } operators [] = { {"applyscalevals", 3, &PSActions::applyscalevals}, {"clip", 0, &PSActions::clip}, {"closepath", 0, &PSActions::closepath}, {"curveto", 6, &PSActions::curveto}, {"eoclip", 0, &PSActions::eoclip}, {"eofill", 0, &PSActions::eofill}, {"fill", 0, &PSActions::fill}, {"grestore", 0, &PSActions::grestore}, {"grestoreall", 0, &PSActions::grestoreall}, {"gsave", 0, &PSActions::gsave}, {"initclip", 0, &PSActions::initclip}, {"lineto", 2, &PSActions::lineto}, {"makepattern", -1, &PSActions::makepattern}, {"moveto", 2, &PSActions::moveto}, {"newpath", 0, &PSActions::newpath}, {"querypos", 2, &PSActions::querypos}, {"raw", -1, 0}, {"restore", 1, &PSActions::restore}, {"rotate", 1, &PSActions::rotate}, {"save", 1, &PSActions::save}, {"scale", 2, &PSActions::scale}, {"setcmykcolor", 4, &PSActions::setcmykcolor}, {"setdash", -1, &PSActions::setdash}, {"setgray", 1, &PSActions::setgray}, {"sethsbcolor", 3, &PSActions::sethsbcolor}, {"setlinecap", 1, &PSActions::setlinecap}, {"setlinejoin", 1, &PSActions::setlinejoin}, {"setlinewidth", 1, &PSActions::setlinewidth}, {"setmatrix", 6, &PSActions::setmatrix}, {"setmiterlimit", 1, &PSActions::setmiterlimit}, {"setopacityalpha", 1, &PSActions::setopacityalpha}, {"setpattern", -1, &PSActions::setpattern}, {"setrgbcolor", 3, &PSActions::setrgbcolor}, {"stroke", 0, &PSActions::stroke}, {"translate", 2, &PSActions::translate}, }; if (_actions) { in.skipSpace(); // binary search int first=0, last=sizeof(operators)/sizeof(Operator)-1; while (first <= last) { int mid = first+(last-first)/2; int cmp = in.compare(operators[mid].name); if (cmp > 0) last = mid-1; else if (cmp < 0) first = mid+1; else { if (!operators[mid].op) { // raw string data received _rawData.clear(); in.skipSpace(); while (!in.eof()) { _rawData.push_back(in.getString()); in.skipSpace(); } } else { // collect parameters and call handler vector params; int pcount = operators[mid].pcount; if (pcount < 0) { // variable number of parameters? in.skipSpace(); while (!in.eof()) { // read all available parameters params.push_back(in.getString()); in.skipSpace(); } } else { // fix number of parameters for (int i=0; i < pcount; i++) { in.skipSpace(); params.push_back(in.getString()); } } vector v(params.size()); str2double(params, v); (_actions->*operators[mid].op)(v); _actions->executed(); } break; } } } } /** This callback function handles output from Ghostscript to stderr. * @param[in] inst pointer to calling instance of PSInterpreter * @param[in] buf contains the characters to be output * @param[in] len number of chars in buf * @return number of processed characters */ int GSDLLCALL PSInterpreter::error (void *inst, const char *buf, int len) { return len; }