diff options
Diffstat (limited to 'Build/source/texk/dvisvgm/dvisvgm-src/src/DvisvgmSpecialHandler.cpp')
-rw-r--r-- | Build/source/texk/dvisvgm/dvisvgm-src/src/DvisvgmSpecialHandler.cpp | 579 |
1 files changed, 0 insertions, 579 deletions
diff --git a/Build/source/texk/dvisvgm/dvisvgm-src/src/DvisvgmSpecialHandler.cpp b/Build/source/texk/dvisvgm/dvisvgm-src/src/DvisvgmSpecialHandler.cpp deleted file mode 100644 index 67463a4c597..00000000000 --- a/Build/source/texk/dvisvgm/dvisvgm-src/src/DvisvgmSpecialHandler.cpp +++ /dev/null @@ -1,579 +0,0 @@ -/************************************************************************* -** DvisvgmSpecialHandler.cpp ** -** ** -** This file is part of dvisvgm -- a fast DVI to SVG converter ** -** Copyright (C) 2005-2021 Martin Gieseking <martin.gieseking@uos.de> ** -** ** -** 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 <http://www.gnu.org/licenses/>. ** -*************************************************************************/ - -#include <array> -#include <cstring> -#include <utility> -#include "Calculator.hpp" -#include "DvisvgmSpecialHandler.hpp" -#include "InputBuffer.hpp" -#include "InputReader.hpp" -#include "Length.hpp" -#include "SpecialActions.hpp" -#include "SVGTree.hpp" -#include "utility.hpp" -#include "XMLNode.hpp" -#include "XMLString.hpp" - -using namespace std; - - -DvisvgmSpecialHandler::DvisvgmSpecialHandler () : - _currentMacro(_macros.end()), - _defsParser(&SVGTree::appendToDefs, &SVGTree::pushDefsContext, &SVGTree::popDefsContext), - _pageParser(&SVGTree::appendToPage, &SVGTree::pushPageContext, &SVGTree::popPageContext) -{ -} - - -void DvisvgmSpecialHandler::preprocess (const string&, istream &is, SpecialActions&) { - struct Command { - const char *name; - void (DvisvgmSpecialHandler::*handler)(InputReader&); - }; - constexpr array<Command, 5> commands {{ - {"raw", &DvisvgmSpecialHandler::preprocessRaw}, - {"rawdef", &DvisvgmSpecialHandler::preprocessRawDef}, - {"rawset", &DvisvgmSpecialHandler::preprocessRawSet}, - {"endrawset", &DvisvgmSpecialHandler::preprocessEndRawSet}, - {"rawput", &DvisvgmSpecialHandler::preprocessRawPut} - }}; - - StreamInputReader ir(is); - const string cmdstr = ir.getWord(); - auto it = find_if(commands.begin(), commands.end(), [&](const Command &cmd) { - return cmd.name == cmdstr; - }); - if (it != commands.end()) { - ir.skipSpace(); - (this->*it->handler)(ir); - } -} - - -void DvisvgmSpecialHandler::preprocessRawSet (InputReader &ir) { - _nestingLevel++; - string id = ir.getString(); - if (id.empty()) - throw SpecialException("definition of unnamed SVG fragment"); - if (_nestingLevel > 1) - throw SpecialException("nested definition of SVG fragment '" + id + "'"); - - _currentMacro = _macros.find(id); - if (_currentMacro != _macros.end()) { - _currentMacro = _macros.end(); - throw SpecialException("redefinition of SVG fragment '" + id + "'"); - } - pair<string, StringVector> entry(id, StringVector()); - pair<MacroMap::iterator, bool> state = _macros.emplace(move(entry)); - _currentMacro = state.first; -} - - -void DvisvgmSpecialHandler::preprocessEndRawSet (InputReader&) { - if (_nestingLevel > 0 && --_nestingLevel == 0) - _currentMacro = _macros.end(); -} - - -void DvisvgmSpecialHandler::preprocessRaw (InputReader &ir) { - if (_currentMacro == _macros.end()) - return; - string str = ir.getLine(); - if (!str.empty()) - _currentMacro->second.emplace_back("P"+str); -} - - -void DvisvgmSpecialHandler::preprocessRawDef (InputReader &ir) { - if (_currentMacro == _macros.end()) - return; - string str = ir.getLine(); - if (!str.empty()) - _currentMacro->second.emplace_back("D"+str); -} - - -void DvisvgmSpecialHandler::preprocessRawPut (InputReader &ir) { - if (_currentMacro != _macros.end()) - throw SpecialException("dvisvgm:rawput not allowed inside rawset/endrawset"); -} - - -/** Evaluates and executes a dvisvgm special statement. - * @param[in] prefix special prefix read by the SpecialManager - * @param[in] is the special statement is read from this stream - * @param[in] actions object providing the actions that can be performed by the SpecialHandler */ -bool DvisvgmSpecialHandler::process (const string &prefix, istream &is, SpecialActions &actions) { - struct Command { - const char *name; - void (DvisvgmSpecialHandler::*handler)(InputReader&, SpecialActions&); - }; - constexpr array<Command, 7> commands {{ - {"raw", &DvisvgmSpecialHandler::processRaw}, - {"rawdef", &DvisvgmSpecialHandler::processRawDef}, - {"rawset", &DvisvgmSpecialHandler::processRawSet}, - {"endrawset", &DvisvgmSpecialHandler::processEndRawSet}, - {"rawput", &DvisvgmSpecialHandler::processRawPut}, - {"bbox", &DvisvgmSpecialHandler::processBBox}, - {"img", &DvisvgmSpecialHandler::processImg} - }}; - StreamInputReader ir(is); - const string cmdstr = ir.getWord(); - auto it = find_if(commands.begin(), commands.end(), [&](const Command &cmd) { - return cmd.name == cmdstr; - }); - if (it != commands.end()) { - ir.skipSpace(); - (this->*it->handler)(ir, actions); - } - return true; -} - - -/** Replaces constants of the form {?name} by their corresponding value. - * @param[in,out] str text to expand - * @param[in] actions interfcae to the world outside the special handler */ -static void expand_constants (string &str, SpecialActions &actions) { - bool repl_bbox = true; - while (repl_bbox) { - size_t pos = str.find("{?bbox "); - if (pos == string::npos) - repl_bbox = false; - else { - size_t endpos = pos+7; - while (endpos < str.length() && isalnum(str[endpos])) - ++endpos; - if (str[endpos] == '}') { - BoundingBox &box=actions.bbox(str.substr(pos+7, endpos-pos-7)); - str.replace(pos, endpos-pos+1, box.toSVGViewBox()); - } - else - repl_bbox = false; - } - } - struct Constant { - const char *name; - string val; - }; - const array<Constant, 5> constants {{ - {"x", XMLString(actions.getX())}, - {"y", XMLString(actions.getY())}, - {"color", actions.getColor().svgColorString()}, - {"matrix", actions.getMatrix().toSVG()}, - {"nl", "\n"}, - }}; - for (const Constant &constant : constants) { - const string pattern = string("{?")+constant.name+"}"; - size_t pos = str.find(pattern); - while (pos != string::npos) { - str.replace(pos, strlen(constant.name)+3, constant.val); - pos = str.find(pattern, pos+constant.val.length()); // look for further matches - } - } -} - - -/** Evaluates substrings of the form {?(expr)} where 'expr' is a math expression, - * and replaces the substring by the computed value. - * @param[in,out] str string to scan for expressions */ -static void evaluate_expressions (string &str, SpecialActions &actions) { - size_t left = str.find("{?("); // start position of expression macro - while (left != string::npos) { - size_t right = str.find(")}", left+2); // end position of expression macro - if (right == string::npos) - break; - Calculator calc; - calc.setVariable("x", actions.getX()); - calc.setVariable("y", actions.getY()); - string expr = str.substr(left+3, right-left-3); // math expression to evaluate - if (util::normalize_space(expr).empty()) // no expression given, e.g. {?( )} - str.erase(left, right-left+2); // => replace with empty string - else { - try { - double val = calc.eval(expr); - XMLString valstr(val); - str.replace(left, right-left+2, valstr); - right = left+valstr.length()-1; - } - catch (CalculatorException &e) { - throw SpecialException(string(e.what())+" in '{?("+expr+")}'"); - } - } - left = str.find("{?(", right+1); // find next expression macro - } -} - - -/** Processes raw SVG fragments from the input stream. The SVG data must represent - * a single or multiple syntactically complete XML parts, like opening/closing tags, - * comments, or CDATA blocks. These must not be split and distributed over several - * 'raw' statements. Elements can be split but element tags can't. - * Example: "<g transform=" is invalid, "<g transform='scale(2,3)'>" is ok. */ -void DvisvgmSpecialHandler::processRaw (InputReader &ir, SpecialActions &actions) { - if (_nestingLevel == 0) { - string xml = ir.getLine(); - if (!xml.empty()) { - evaluate_expressions(xml, actions); - expand_constants(xml, actions); - _pageParser.parse(xml, actions); - } - } -} - - -void DvisvgmSpecialHandler::processRawDef (InputReader &ir, SpecialActions &actions) { - if (_nestingLevel == 0) { - string xml = ir.getLine(); - if (!xml.empty()) { - expand_constants(xml, actions); - _defsParser.parse(xml, actions); - } - } -} - - -void DvisvgmSpecialHandler::processRawSet (InputReader&, SpecialActions&) { - _nestingLevel++; -} - - -void DvisvgmSpecialHandler::processEndRawSet (InputReader&, SpecialActions&) { - if (_nestingLevel > 0) - _nestingLevel--; -} - - -void DvisvgmSpecialHandler::processRawPut (InputReader &ir, SpecialActions &actions) { - if (_nestingLevel > 0) - return; - string id = ir.getString(); - auto it = _macros.find(id); - if (it == _macros.end()) - throw SpecialException("undefined SVG fragment '" + id + "' referenced"); - - StringVector &defvector = it->second; - for (string &defstr : defvector) { - char &type = defstr[0]; - string def = defstr.substr(1); - if ((type == 'P' || type == 'D') && !def.empty()) { - expand_constants(def, actions); - if (type == 'P') - _pageParser.parse(def, actions); - else { // type == 'D' - _defsParser.parse(def, actions); - type = 'L'; // locked - } - } - } -} - - -/** Embeds the virtual rectangle (x, y ,w , h) into the current bounding box, - * where (x,y) is the lower left vertex composed of the current DVI position. - * @param[in] w width of the rectangle in PS point units - * @param[in] h height of the rectangle in PS point units - * @param[in] d depth of the rectangle in PS point units - * @param[in] transform if true, apply the current transformation matrix to the rectangle - * @param[in] actions object providing the actions that can be performed by the SpecialHandler */ -static void update_bbox (Length w, Length h, Length d, bool transform, SpecialActions &actions) { - double x = actions.getX(); - double y = actions.getY(); - BoundingBox bbox1(x, y, x+w.bp(), y-h.bp()); - BoundingBox bbox2(x, y, x+w.bp(), y+d.bp()); - if (transform) { - bbox1.transform(actions.getMatrix()); - bbox2.transform(actions.getMatrix()); - } - actions.embed(bbox1); - actions.embed(bbox2); -} - - -/** Reads a length value including a trailing unit specifier and returns it. */ -static Length read_length (InputReader &ir) { - Length length; - ir.skipSpace(); - if (!isalpha(ir.peek())) { - double val = ir.getDouble(); - string unit = isalpha(ir.peek()) ? ir.getString(2) : "pt"; - length = Length(val, unit); - } - return length; -} - - -/** Evaluates the special dvisvgm:bbox. - * variant 1: dvisvgm:bbox [r[el]] <width> <height> [<depth>] [transform] - * variant 2: dvisvgm:bbox a[bs] <x1> <y1> <x2> <y2> [transform] - * variant 3: dvisvgm:bbox f[ix] <x1> <y1> <x2> <y2> [transform] - * variant 4: dvisvgm:bbox n[ew] <name> - * variant 5: dvisvgm:bbox lock | unlock */ -void DvisvgmSpecialHandler::processBBox (InputReader &ir, SpecialActions &actions) { - ir.skipSpace(); - if (ir.check("lock")) - actions.bbox().lock(); - else if (ir.check("unlock")) - actions.bbox().unlock(); - else { - int c = ir.peek(); - try { - if (!isalpha(c)) - c = 'r'; // no mode specifier => relative box parameters - else { - while (!isspace(ir.peek())) // skip trailing characters - ir.get(); - if (c == 'n') { // "new": create new local bounding box - ir.skipSpace(); - string name; - while (isalnum(ir.peek())) - name += char(ir.get()); - ir.skipSpace(); - if (!name.empty() && ir.eof()) - actions.bbox(name, true); // create new user box - } - else if (c == 'a' || c == 'f') { // "abs" or "fix" - Length lengths[4]; - for (Length &len : lengths) - len = read_length(ir); - BoundingBox b(lengths[0], lengths[1], lengths[2], lengths[3]); - ir.skipSpace(); - if (ir.check("transform")) - b.transform(actions.getMatrix()); - if (c == 'a') - actions.embed(b); - else { - actions.bbox() = b; - actions.bbox().lock(); - } - } - } - if (c == 'r') { - Length w = read_length(ir); - Length h = read_length(ir); - Length d = read_length(ir); - ir.skipSpace(); - update_bbox(w, h, d, ir.check("transform"), actions); - } - } - catch (const UnitException &e) { - throw SpecialException(string("dvisvgm:bbox: ") + e.what()); - } - } -} - - -void DvisvgmSpecialHandler::processImg (InputReader &ir, SpecialActions &actions) { - try { - Length w = read_length(ir); - Length h = read_length(ir); - string f = ir.getString(); - update_bbox(w, h, Length(0), false, actions); - auto img = util::make_unique<XMLElement>("image"); - img->addAttribute("x", actions.getX()); - img->addAttribute("y", actions.getY()); - img->addAttribute("width", w.bp()); - img->addAttribute("height", h.bp()); - img->addAttribute("xlink:href", f); - if (!actions.getMatrix().isIdentity()) - img->addAttribute("transform", actions.getMatrix().toSVG()); - actions.svgTree().appendToPage(std::move(img)); - } - catch (const UnitException &e) { - throw SpecialException(string("dvisvgm:img: ") + e.what()); - } -} - - -void DvisvgmSpecialHandler::dviPreprocessingFinished () { - string id; - if (_currentMacro != _macros.end()) - id = _currentMacro->first; - // ensure all pattern definitions are closed after pre-processing the whole DVI file - _currentMacro = _macros.end(); - _nestingLevel = 0; - if (!id.empty()) - throw SpecialException("missing dvisvgm:endrawset for SVG fragment '" + id + "'"); -} - - -void DvisvgmSpecialHandler::dviEndPage (unsigned, SpecialActions &actions) { - _defsParser.finish(actions); - _pageParser.finish(actions); - actions.bbox().unlock(); - for (auto &strvecpair : _macros) { - StringVector &vec = strvecpair.second; - for (string &str : vec) { - // activate locked parts of a pattern again - if (str[0] == 'L') - str[0] = 'D'; - } - } -} - - -vector<const char*> DvisvgmSpecialHandler::prefixes() const { - vector<const char*> pfx {"dvisvgm:"}; - return pfx; -} - -//////////////////////////////////////////////////////////////////////////////// - -/** Parses a fragment of XML code, creates corresponding XML nodes and adds them - * to the SVG tree. The code may be split and processed by several calls of this - * function. Incomplete chunks that can't be processed yet are stored and picked - * up again together with the next incoming XML fragment. If a call of this function - * is supposed to finish the parsing of an XML subtree, parameter 'finish' must be set. - * @param[in] xml XML fragment to parse - * @param[in] actions object providing the SVG tree functions - * @param[in] finish if true, no more XML is expected and parsing is finished */ -void DvisvgmSpecialHandler::XMLParser::parse (const string &xml, SpecialActions &actions, bool finish) { - // collect/extract an XML fragment that only contains complete tags - // incomplete tags are held back - _xmlbuf += xml; - size_t left=0, right; - try { - while (left != string::npos) { - right = _xmlbuf.find('<', left); - if (left < right && left < _xmlbuf.length()) // plain text found? - (actions.svgTree().*_append)(util::make_unique<XMLText>(_xmlbuf.substr(left, right-left))); - if (right != string::npos) { - left = right; - if (_xmlbuf.compare(left, 9, "<![CDATA[") == 0) { - right = _xmlbuf.find("]]>", left+9); - if (right == string::npos) { - if (finish) throw SpecialException("expected ']]>' at end of CDATA block"); - break; - } - (actions.svgTree().*_append)(util::make_unique<XMLCData>(_xmlbuf.substr(left+9, right-left-9))); - right += 2; - } - else if (_xmlbuf.compare(left, 4, "<!--") == 0) { - right = _xmlbuf.find("-->", left+4); - if (right == string::npos) { - if (finish) throw SpecialException("expected '-->' at end of comment"); - break; - } - (actions.svgTree().*_append)(util::make_unique<XMLComment>(_xmlbuf.substr(left+4, right-left-4))); - right += 2; - } - else if (_xmlbuf.compare(left, 2, "<?") == 0) { - right = _xmlbuf.find("?>", left+2); - if (right == string::npos) { - if (finish) throw SpecialException("expected '?>' at end of processing instruction"); - break; - } - (actions.svgTree().*_append)(util::make_unique<XMLText>(_xmlbuf.substr(left, right-left+2))); - right++; - } - else if (_xmlbuf.compare(left, 2, "</") == 0) { - right = _xmlbuf.find('>', left+2); - if (right == string::npos) { - if (finish) throw SpecialException("missing '>' at end of closing XML tag"); - break; - } - closeElement(_xmlbuf.substr(left+2, right-left-2), actions); - } - else { - right = _xmlbuf.find('>', left+1); - if (right == string::npos) { - if (finish) throw SpecialException("missing '>' or '/>' at end of opening XML tag"); - break; - } - openElement(_xmlbuf.substr(left+1, right-left-1), actions); - } - } - left = right; - if (right != string::npos) - left++; - } - } - catch (const SpecialException &e) { - _error = true; - throw; - } - if (left == string::npos) - _xmlbuf.clear(); - else - _xmlbuf.erase(0, left); -} - - -/** Processes an opening element tag. - * @param[in] tag tag without leading and trailing angle brackets */ -void DvisvgmSpecialHandler::XMLParser::openElement (const string &tag, SpecialActions &actions) { - StringInputBuffer ib(tag); - BufferInputReader ir(ib); - string name = ir.getString("/ \t\n\r"); - ir.skipSpace(); - auto elemNode = util::make_unique<XMLElement>(name); - map<string, string> attribs; - if (ir.parseAttributes(attribs, true, "\"'")) { - for (const auto &attrpair : attribs) - elemNode->addAttribute(attrpair.first, attrpair.second); - } - ir.skipSpace(); - if (ir.peek() == '/') // end of empty element tag - (actions.svgTree().*_append)(std::move(elemNode)); - else if (ir.peek() < 0) { // end of opening tag - _nameStack.push_back(name); - (actions.svgTree().*_pushContext)(std::move(elemNode)); - } - else - throw SpecialException("'>' or '/>' expected at end of opening tag <"+name); -} - - -/** Processes a closing element tag. - * @param[in] tag tag without leading and trailing angle brackets */ -void DvisvgmSpecialHandler::XMLParser::closeElement (const string &tag, SpecialActions &actions) { - StringInputBuffer ib(tag); - BufferInputReader ir(ib); - string name = ir.getString(" \t\n\r"); - ir.skipSpace(); - if (ir.peek() >= 0) - throw SpecialException("'>' expected at end of closing tag </"+name); - if (_nameStack.empty()) - throw SpecialException("spurious closing tag </" + name + ">"); - if (_nameStack.back() != name) - throw SpecialException("expected </" + _nameStack.back() + "> but found </" + name + ">"); - (actions.svgTree().*_popContext)(); - _nameStack.pop_back(); -} - - -/** Processes any remaining XML fragments, checks for missing closing tags, - * and resets the parser state. */ -void DvisvgmSpecialHandler::XMLParser::finish (SpecialActions &actions) { - if (!_xmlbuf.empty()) { - if (!_error) - parse("", actions, true); - _xmlbuf.clear(); - } - string tags; - while (!_nameStack.empty()) { - tags += "</"+_nameStack.back()+">, "; - _nameStack.pop_back(); - } - if (!tags.empty() && !_error) { - tags.resize(tags.length()-2); - throw SpecialException("missing closing tag(s): "+tags); - } -} |