summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvisvgm/dvisvgm-src/src/DvisvgmSpecialHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/dvisvgm/dvisvgm-src/src/DvisvgmSpecialHandler.cpp')
-rw-r--r--Build/source/texk/dvisvgm/dvisvgm-src/src/DvisvgmSpecialHandler.cpp579
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);
- }
-}