summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvisvgm/dvisvgm-src/src/XMLParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/dvisvgm/dvisvgm-src/src/XMLParser.cpp')
-rw-r--r--Build/source/texk/dvisvgm/dvisvgm-src/src/XMLParser.cpp175
1 files changed, 125 insertions, 50 deletions
diff --git a/Build/source/texk/dvisvgm/dvisvgm-src/src/XMLParser.cpp b/Build/source/texk/dvisvgm/dvisvgm-src/src/XMLParser.cpp
index 166a49e0cf3..c1da5373a6b 100644
--- a/Build/source/texk/dvisvgm/dvisvgm-src/src/XMLParser.cpp
+++ b/Build/source/texk/dvisvgm/dvisvgm-src/src/XMLParser.cpp
@@ -2,7 +2,7 @@
** XMLParser.cpp **
** **
** This file is part of dvisvgm -- a fast DVI to SVG converter **
-** Copyright (C) 2005-2022 Martin Gieseking <martin.gieseking@uos.de> **
+** Copyright (C) 2005-2023 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 **
@@ -19,29 +19,90 @@
*************************************************************************/
#include "InputReader.hpp"
-#include "GraphicsPathParser.hpp"
#include "XMLParser.hpp"
using namespace std;
+XMLElement* XMLParser::setRootElement (XMLElement *root) {
+ _elementStack.clear();
+ if (root)
+ _elementStack.push_back(root);
+ else {
+ _root = util::make_unique<XMLElement>("root");
+ _elementStack.push_back(_root.get());
+ }
+ return _elementStack.back();
+}
+
+
+/** Parses an XML fragment from an input stream. */
+void XMLParser::parse (istream &is) {
+ while (is) {
+ string buf;
+ buf.resize(1024);
+ is.read(&buf[0], 1024);
+ buf.resize(is.gcount());
+ parse(std::move(buf));
+ }
+ finish();
+}
+
+
+static string::size_type find_end_of_tag (const string &str, string::size_type startpos) {
+ char attrval_delim = 0;
+ bool expect_attrval = false;
+ for (auto i=startpos; i < str.length(); i++) {
+ if (attrval_delim) { // inside attrubute value?
+ if (str[i] == attrval_delim) // end of attribute value?
+ attrval_delim = 0;
+ }
+ else if (str[i] == '>')
+ return i;
+ else if (str[i] == '"' || str[i] == '\'') { // start of attribute value?
+ if (expect_attrval)
+ attrval_delim = str[i];
+ else {
+ ostringstream oss;
+ oss << "misplaced " << str[i] << " inside tag";
+ throw XMLParserException(oss.str());
+ }
+ }
+ else if (str[i] == '=') {
+ expect_attrval = true;
+ continue;
+ }
+ else if (str[i] == '<')
+ throw XMLParserException("invalid '<' inside tag");
+ expect_attrval = false;
+ }
+ return string::npos;
+}
+
+
/** Parses a fragment of XML code, creates corresponding XML nodes and adds them
* to an 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] svgTree the parsed nodes are added to this SVG tree
* @param[in] finish if true, no more XML is expected and parsing is finished */
-void XMLParser::parse (const string &xml, SVGTree &svgTree, bool finish) {
+void XMLParser::parse (string xml, bool finish) {
+ if (_elementStack.empty()) // no root element set?
+ return;
// collect/extract an XML fragment that only contains complete tags
// incomplete tags are held back
- _xmlbuf += xml;
+ if (_xmlbuf.empty())
+ _xmlbuf.assign(std::move(xml));
+ else
+ _xmlbuf.append(xml);
string::size_type left=0;
try {
while (left != string::npos) {
auto right = _xmlbuf.find('<', left);
- if (left < right && left < _xmlbuf.length()) // plain text found?
- (svgTree.*_append)(util::make_unique<XMLText>(_xmlbuf.substr(left, right - left)));
+ if (left < right && left < _xmlbuf.length()) {// plain text found?
+ string text = (right == string::npos ? _xmlbuf.substr(left) : _xmlbuf.substr(left, right-left));
+ appendNode(util::make_unique<XMLText>(std::move(text)));
+ }
if (right != string::npos) {
left = right;
if (_xmlbuf.compare(left, 9, "<![CDATA[") == 0) {
@@ -50,7 +111,7 @@ void XMLParser::parse (const string &xml, SVGTree &svgTree, bool finish) {
if (finish) throw XMLParserException("expected ']]>' at end of CDATA block");
break;
}
- (svgTree.*_append)(util::make_unique<XMLCData>(_xmlbuf.substr(left + 9, right - left - 9)));
+ appendNode(util::make_unique<XMLCData>(_xmlbuf.substr(left+9, right-left-9)));
right += 2;
}
else if (_xmlbuf.compare(left, 4, "<!--") == 0) {
@@ -59,7 +120,7 @@ void XMLParser::parse (const string &xml, SVGTree &svgTree, bool finish) {
if (finish) throw XMLParserException("expected '-->' at end of comment");
break;
}
- (svgTree.*_append)(util::make_unique<XMLComment>(_xmlbuf.substr(left + 4, right - left - 4)));
+ appendNode(util::make_unique<XMLComment>(_xmlbuf.substr(left+4, right-left-4)));
right += 2;
}
else if (_xmlbuf.compare(left, 2, "<?") == 0) {
@@ -68,7 +129,7 @@ void XMLParser::parse (const string &xml, SVGTree &svgTree, bool finish) {
if (finish) throw XMLParserException("expected '?>' at end of processing instruction");
break;
}
- (svgTree.*_append)(util::make_unique<XMLText>(_xmlbuf.substr(left, right - left + 2)));
+ appendNode(util::make_unique<XMLText>(_xmlbuf.substr(left, right-left+2)));
right++;
}
else if (_xmlbuf.compare(left, 2, "</") == 0) {
@@ -77,15 +138,15 @@ void XMLParser::parse (const string &xml, SVGTree &svgTree, bool finish) {
if (finish) throw XMLParserException("missing '>' at end of closing XML tag");
break;
}
- closeElement(_xmlbuf.substr(left+2, right-left-2), svgTree);
+ closeElement(_xmlbuf.substr(left+2, right-left-2));
}
else {
- right = _xmlbuf.find('>', left+1);
+ right = find_end_of_tag(_xmlbuf, left+1);
if (right == string::npos) {
if (finish) throw XMLParserException("missing '>' or '/>' at end of opening XML tag");
break;
}
- openElement(_xmlbuf.substr(left+1, right-left-1), svgTree);
+ openElement(_xmlbuf.substr(left+1, right-left-1));
}
}
left = right;
@@ -104,79 +165,93 @@ void XMLParser::parse (const string &xml, SVGTree &svgTree, bool finish) {
}
+void XMLParser::appendNode (unique_ptr<XMLNode> node) {
+ context()->append(std::move(node));
+}
+
+
+XMLElement* XMLParser::finishPushContext (unique_ptr<XMLElement> elem) {
+ XMLElement *elemPtr = elem.get();
+ context()->append(std::move(elem));
+ return elemPtr;
+}
+
+
/** Processes an opening element tag.
* @param[in] tag tag without leading and trailing angle brackets */
-void XMLParser::openElement (const string &tag, SVGTree &svgTree) {
+XMLElement* XMLParser::openElement (const string &tag) {
StringInputBuffer ib(tag);
BufferInputReader ir(ib);
string name = ir.getString("/ \t\n\r");
- bool isPathElement = (name == "path" || name == "svg:path");
ir.skipSpace();
- auto elemNode = util::make_unique<SVGElement>(name);
+ unique_ptr<XMLElement> elemNode{createElementPtr(name)};
map<string, string> attribs;
if (ir.parseAttributes(attribs, true, "\"'")) {
- for (const auto &attrpair : attribs) {
- if (!isPathElement || attrpair.first != "d")
- elemNode->addAttribute(attrpair.first, attrpair.second);
- else {
- try {
- // parse and reformat path definition
- auto path = GraphicsPathParser<double>().parse(attrpair.second);
- ostringstream oss;
- path.writeSVG(oss, SVGTree::RELATIVE_PATH_CMDS);
- elemNode->addAttribute("d", oss.str());
- }
- catch (const GraphicsPathParserException &e) {
- throw XMLParserException(string("error in path data: ")+e.what());
- }
- }
- }
+ for (const auto &attrpair : attribs)
+ elemNode->addAttribute(attrpair.first, attrpair.second);
}
ir.skipSpace();
+ XMLElement *elemPtr = elemNode.get();
if (ir.peek() == '/') // end of empty element tag
- (svgTree.*_append)(std::move(elemNode));
- else if (ir.peek() < 0) { // end of opening tag
- _nameStack.push_back(name);
- (svgTree.*_pushContext)(std::move(elemNode));
- }
+ appendNode(std::move(elemNode));
+ else if (ir.peek() < 0) // end of opening tag
+ _elementStack.push_back(finishPushContext(std::move(elemNode)));
else
throw XMLParserException("'>' or '/>' expected at end of opening tag <"+name);
+ if (_notifyElementOpened)
+ _notifyElementOpened(elemPtr);
+ if (ir.peek() == '/' && _notifyElementClosed)
+ _notifyElementClosed(elemPtr);
+ return elemPtr;
}
/** Processes a closing element tag.
* @param[in] tag tag without leading and trailing angle brackets */
-void XMLParser::closeElement (const string &tag, SVGTree &svgTree) {
+void XMLParser::closeElement (const string &tag) {
StringInputBuffer ib(tag);
BufferInputReader ir(ib);
string name = ir.getString(" \t\n\r");
ir.skipSpace();
if (ir.peek() >= 0)
throw XMLParserException("'>' expected at end of closing tag </"+name);
- if (_nameStack.empty())
+ if (_elementStack.size() <= 1)
throw XMLParserException("spurious closing tag </" + name + ">");
- if (_nameStack.back() != name)
- throw XMLParserException("expected </" + _nameStack.back() + "> but found </" + name + ">");
- (svgTree.*_popContext)();
- _nameStack.pop_back();
+ if (_elementStack.back()->name() != name)
+ throw XMLParserException("expected </" + _elementStack.back()->name() + "> but found </" + name + ">");
+ finishPopContext();
+ if (_notifyElementClosed)
+ _notifyElementClosed(_elementStack.back());
+ _elementStack.pop_back();
}
/** Processes any remaining XML fragments, checks for missing closing tags,
* and resets the parser state. */
-void XMLParser::finish (SVGTree &svgTree) {
+void XMLParser::finish () {
if (!_xmlbuf.empty()) {
if (!_error)
- parse("", svgTree, true);
+ parse("", true);
_xmlbuf.clear();
}
string tags;
- while (!_nameStack.empty()) {
- tags += "</"+_nameStack.back()+">, ";
- _nameStack.pop_back();
+ while (_elementStack.size() > 1) {
+ tags += "</" + _elementStack.back()->name() + ">, ";
+ _elementStack.pop_back();
}
if (!tags.empty() && !_error) {
- tags.resize(tags.length()-2);
- throw XMLParserException("missing closing tag(s): "+tags);
+ tags.resize(tags.length()-2); // drop trailing ", "
+ throw XMLParserException("missing closing tag(s): " + tags);
}
}
+
+
+XMLElement* XMLParser::createElementPtr (std::string name) const {
+ return new XMLElement(std::move(name));
+}
+
+
+void XMLParser::setNotifyFuncs (NotifyFunc notifyElementOpened, NotifyFunc notifyElementClosed) {
+ _notifyElementOpened = std::move(notifyElementOpened);
+ _notifyElementClosed = std::move(notifyElementClosed);
+}