summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvisvgm/dvisvgm-1.7/src/DvisvgmSpecialHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/dvisvgm/dvisvgm-1.7/src/DvisvgmSpecialHandler.cpp')
-rw-r--r--Build/source/texk/dvisvgm/dvisvgm-1.7/src/DvisvgmSpecialHandler.cpp354
1 files changed, 354 insertions, 0 deletions
diff --git a/Build/source/texk/dvisvgm/dvisvgm-1.7/src/DvisvgmSpecialHandler.cpp b/Build/source/texk/dvisvgm/dvisvgm-1.7/src/DvisvgmSpecialHandler.cpp
new file mode 100644
index 00000000000..a3b2233e5b2
--- /dev/null
+++ b/Build/source/texk/dvisvgm/dvisvgm-1.7/src/DvisvgmSpecialHandler.cpp
@@ -0,0 +1,354 @@
+/*************************************************************************
+** DvisvgmSpecialHandler.cpp **
+** **
+** This file is part of dvisvgm -- the DVI to SVG converter **
+** Copyright (C) 2005-2014 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 <config.h>
+#include <cstring>
+#include <utility>
+#include "DvisvgmSpecialHandler.h"
+#include "InputBuffer.h"
+#include "InputReader.h"
+#include "SpecialActions.h"
+#include "XMLNode.h"
+#include "XMLString.h"
+
+using namespace std;
+
+
+DvisvgmSpecialHandler::DvisvgmSpecialHandler ()
+ : _currentMacro(_macros.end()), _nestingLevel(0)
+{
+}
+
+
+void DvisvgmSpecialHandler::preprocess (const char*, istream &is, SpecialActions*) {
+ struct Command {
+ const char *name;
+ void (DvisvgmSpecialHandler::*handler)(InputReader&);
+ } commands[] = {
+ {"raw", &DvisvgmSpecialHandler::preprocessRaw},
+ {"rawdef", &DvisvgmSpecialHandler::preprocessRawDef},
+ {"rawset", &DvisvgmSpecialHandler::preprocessRawSet},
+ {"endrawset", &DvisvgmSpecialHandler::preprocessEndRawSet},
+ {"rawput", &DvisvgmSpecialHandler::preprocessRawPut},
+ };
+
+ StreamInputReader ir(is);
+ string cmd = ir.getWord();
+ for (size_t i=0; i < sizeof(commands)/sizeof(Command); i++) {
+ if (commands[i].name == cmd) {
+ ir.skipSpace();
+ (this->*commands[i].handler)(ir);
+ return;
+ }
+ }
+}
+
+
+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.insert(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.push_back(string("P")+str);
+}
+
+
+void DvisvgmSpecialHandler::preprocessRawDef (InputReader &ir) {
+ if (_currentMacro == _macros.end())
+ return;
+ string str = ir.getLine();
+ if (!str.empty())
+ _currentMacro->second.push_back(string("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 char *prefix, istream &is, SpecialActions *actions) {
+ if (!actions)
+ return true;
+
+ struct Command {
+ const char *name;
+ void (DvisvgmSpecialHandler::*handler)(InputReader&, SpecialActions*);
+ } 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);
+ string cmd = ir.getWord();
+ for (size_t i=0; i < sizeof(commands)/sizeof(Command); i++) {
+ if (commands[i].name == cmd) {
+ ir.skipSpace();
+ (this->*commands[i].handler)(ir, actions);
+ return true;
+ }
+ }
+ 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) {
+ struct Constant {
+ const char *name;
+ string val;
+ }
+ constants[] = {
+ {"x", XMLString(actions->getX())},
+ {"y", XMLString(actions->getY())},
+ {"color", actions->getColor().rgbString()},
+ {"nl", "\n"},
+ {0, ""}
+ };
+ bool repl_bbox = true;
+ while (repl_bbox) {
+ size_t pos = str.find(string("{?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;
+ }
+ }
+ for (const Constant *p=constants; p->name; p++) {
+ const string pattern = string("{?")+p->name+"}";
+ size_t pos = str.find(pattern);
+ while (pos != string::npos) {
+ str.replace(pos, strlen(p->name)+3, p->val);
+ pos = str.find(pattern, pos+p->val.length()); // look for further matches
+ }
+ }
+}
+
+
+void DvisvgmSpecialHandler::processRaw (InputReader &ir, SpecialActions *actions) {
+ if (_nestingLevel == 0) {
+ string str = ir.getLine();
+ if (!str.empty()) {
+ expand_constants(str, actions);
+ actions->appendToPage(new XMLTextNode(str));
+ }
+ }
+}
+
+
+void DvisvgmSpecialHandler::processRawDef (InputReader &ir, SpecialActions *actions) {
+ if (_nestingLevel == 0) {
+ string str = ir.getLine();
+ if (!str.empty()) {
+ expand_constants(str, actions);
+ actions->appendToDefs(new XMLTextNode(str));
+ }
+ }
+}
+
+
+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();
+ MacroMap::iterator it = _macros.find(id);
+ if (it == _macros.end())
+ throw SpecialException("undefined SVG fragment '" + id + "' referenced");
+
+ StringVector &defs = it->second;
+ for (StringVector::iterator defs_it=defs.begin(); defs_it != defs.end(); ++defs_it) {
+ char &type = defs_it->at(0);
+ string def = defs_it->substr(1);
+ if ((type == 'P' || type == 'D') && !def.empty()) {
+ expand_constants(def, actions);
+ if (type == 'P')
+ actions->appendToPage(new XMLTextNode(def));
+ else { // type == 'D'
+ actions->appendToDefs(new XMLTextNode(def));
+ 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] actions object providing the actions that can be performed by the SpecialHandler */
+static void update_bbox (double w, double h, double d, SpecialActions *actions) {
+ double x = actions->getX();
+ double y = actions->getY();
+ actions->embed(BoundingBox(x, y, x+w, y-h));
+ actions->embed(BoundingBox(x, y, x+w, y+d));
+}
+
+
+/** Evaluates the special dvisvgm:bbox.
+ * variant 1: dvisvgm:bbox [r[el]] <width> <height> [<depth>]
+ * variant 2: dvisvgm:bbox a[bs] <x1> <y1> <x2> <y2>
+ * variant 3: dvisvgm:bbox f[ix] <x1> <y1> <x2> <y2>
+ * variant 4: dvisvgm:bbox n[ew] <name> */
+void DvisvgmSpecialHandler::processBBox (InputReader &ir, SpecialActions *actions) {
+ const double pt2bp = 72/72.27;
+ ir.skipSpace();
+ int c = ir.peek();
+ if (isalpha(c)) {
+ while (!isspace(ir.peek())) // skip trailing characters
+ ir.get();
+ if (c == 'n') {
+ 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') {
+ double p[4];
+ for (int i=0; i < 4; i++)
+ p[i] = ir.getDouble()*pt2bp;
+ BoundingBox b(p[0], p[1], p[2], p[3]);
+ if (c == 'a')
+ actions->embed(b);
+ else {
+ actions->bbox() = b;
+ actions->bbox().lock();
+ }
+ }
+ }
+ else
+ c = 'r'; // no mode specifier => relative box parameters
+
+ if (c == 'r') {
+ double w = ir.getDouble()*pt2bp;
+ double h = ir.getDouble()*pt2bp;
+ double d = ir.getDouble()*pt2bp;
+ update_bbox(w, h, d, actions);
+ }
+}
+
+
+void DvisvgmSpecialHandler::processImg (InputReader &ir, SpecialActions *actions) {
+ if (actions) {
+ const double pt2bp = 72/72.27;
+ double w = ir.getDouble()*pt2bp;
+ double h = ir.getDouble()*pt2bp;
+ string f = ir.getString();
+ update_bbox(w, h, 0, actions);
+ XMLElementNode *img = new XMLElementNode("image");
+ img->addAttribute("x", actions->getX());
+ img->addAttribute("y", actions->getY());
+ img->addAttribute("width", w);
+ img->addAttribute("height", h);
+ img->addAttribute("xlink:href", f);
+ if (!actions->getMatrix().isIdentity())
+ img->addAttribute("transform", actions->getMatrix().getSVG());
+ actions->appendToPage(img);
+ }
+}
+
+
+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) {
+ for (MacroMap::iterator map_it=_macros.begin(); map_it != _macros.end(); ++map_it) {
+ StringVector &vec = map_it->second;
+ for (StringVector::iterator str_it=vec.begin(); str_it != vec.end(); ++str_it) {
+ // activate locked parts of a pattern again
+ if (str_it->at(0) == 'L')
+ str_it->at(0) = 'D';
+ }
+ }
+}
+
+
+const char** DvisvgmSpecialHandler::prefixes () const {
+ static const char *pfx[] = {"dvisvgm:", 0};
+ return pfx;
+}