/************************************************************************* ** DvisvgmSpecialHandler.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 "DvisvgmSpecialHandler.h" #include "InputBuffer.h" #include "InputReader.h" #include "SpecialActions.h" #include "XMLNode.h" #include "XMLString.h" using namespace std; /** 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 } } } /** 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 TeX point units * @param[in] h height of the rectangle in TeX point units * @param[in] d depth of the rectangle in TeX point units */ 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)); } /** Inserts raw text into the SVG tree. * @param[in,out] in the raw text is read from this input buffer * @param[in] actions interfcae to the world outside the special handler * @param[in] group true if the text should be wrapped by a group element */ static void raw (InputReader &in, SpecialActions *actions, bool group=false) { string str; while (!in.eof()) { int c = in.get(); if (isprint(c)) str += char(c); } expand_constants(str, actions); if (!str.empty()) { XMLNode *node = new XMLTextNode(str); if (group) { XMLElementNode *g = new XMLElementNode("g"); g->addAttribute("x", actions->getX()); g->addAttribute("y", actions->getY()); if (actions->getColor() != Color::BLACK) g->addAttribute("fill", actions->getColor().rgbString()); g->append(node); node = g; } actions->appendToPage(node); } } /** Evaluates the special dvisvgm:bbox. * variant 1: dvisvgm:bbox [r[el]] [] * variant 2: dvisvgm:bbox a[bs] * variant 3: dvisvgm:bbox f[ix] * variant 4: dvisvgm:bbox n[ew] */ static void bbox (InputReader &in, SpecialActions *actions) { in.skipSpace(); int c = in.peek(); if (isalpha(c)) { while (!isspace(in.peek())) // skip trailing characters in.get(); if (c == 'n') { in.skipSpace(); string name; while (isalnum(in.peek())) name += char(in.get()); in.skipSpace(); if (!name.empty() && in.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] = in.getDouble(); 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 = in.getDouble(); double h = in.getDouble(); double d = in.getDouble(); update_bbox(w, h, d, actions); } } static void img (InputReader &in, SpecialActions *actions) { double w = in.getDouble(); double h = in.getDouble(); string f = in.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 && !actions->getMatrix().isIdentity()) img->addAttribute("transform", actions->getMatrix().getSVG()); actions->appendToPage(img); } /** 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,out] in the raw text is read from this input buffer */ bool DvisvgmSpecialHandler::process (const char *prefix, istream &is, SpecialActions *actions) { if (actions) { StreamInputBuffer ib(is); BufferInputReader in(ib); string cmd = in.getWord(); if (cmd == "raw") // raw raw(in, actions); else if (cmd == "bbox") // bbox [r] or bbox a bbox(in, actions); else if (cmd == "img") { // img img(in, actions); } } return true; } const char** DvisvgmSpecialHandler::prefixes () const { static const char *pfx[] = {"dvisvgm:", 0}; return pfx; }