summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvisvgm/dvisvgm-1.11/src/GraphicPath.h
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/dvisvgm/dvisvgm-1.11/src/GraphicPath.h')
-rw-r--r--Build/source/texk/dvisvgm/dvisvgm-1.11/src/GraphicPath.h395
1 files changed, 395 insertions, 0 deletions
diff --git a/Build/source/texk/dvisvgm/dvisvgm-1.11/src/GraphicPath.h b/Build/source/texk/dvisvgm/dvisvgm-1.11/src/GraphicPath.h
new file mode 100644
index 00000000000..6d377eb8a59
--- /dev/null
+++ b/Build/source/texk/dvisvgm/dvisvgm-1.11/src/GraphicPath.h
@@ -0,0 +1,395 @@
+/*************************************************************************
+** GraphicPath.h **
+** **
+** This file is part of dvisvgm -- the DVI to SVG converter **
+** Copyright (C) 2005-2015 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/>. **
+*************************************************************************/
+
+#ifndef DVISVGM_GRAPHICPATH_H
+#define DVISVGM_GRAPHICPATH_H
+
+#include <cctype>
+#include <list>
+#include <ostream>
+#include <vector>
+#include "BoundingBox.h"
+#include "Matrix.h"
+#include "Pair.h"
+#include "XMLString.h"
+
+
+template <typename T>
+class GraphicPath
+{
+ friend class PathClipper;
+ public:
+ enum WindingRule {WR_EVEN_ODD, WR_NON_ZERO};
+ typedef Pair<T> Point;
+
+ struct Command {
+ enum Type {MOVETO, LINETO, CONICTO, CUBICTO, CLOSEPATH};
+
+ Command (Type t) : type(t) {}
+
+ Command (Type t, const Point &p) : type(t) {
+ params[0] = p;
+ }
+
+ Command (Type t, const Point &p1, const Point &p2) : type(t) {
+ params[0] = p1;
+ params[1] = p2;
+ }
+
+ Command (Type t, const Point &p1, const Point &p2, const Point &p3) : type(t) {
+ params[0] = p1;
+ params[1] = p2;
+ params[2] = p3;
+ }
+
+ int numParams () const {
+ switch (type) {
+ case CLOSEPATH : return 0;
+ case MOVETO :
+ case LINETO : return 1;
+ case CONICTO : return 2;
+ case CUBICTO : return 3;
+ }
+ return 0;
+ }
+
+ void transform (const Matrix &matrix) {
+ for (int i=0; i < numParams(); i++)
+ params[i] = matrix * params[i];
+ }
+
+ Type type;
+ Point params[3];
+ };
+
+ struct Actions
+ {
+ virtual ~Actions () {}
+ virtual void moveto (const Point &p) {}
+ virtual void lineto (const Point &p) {}
+ virtual void hlineto (const T &y) {}
+ virtual void vlineto (const T &x) {}
+ virtual void sconicto (const Point &p) {}
+ virtual void conicto (const Point &p1, const Point &p2) {}
+ virtual void scubicto (const Point &p1, const Point &p2) {}
+ virtual void cubicto (const Point &p1, const Point &p2, const Point &p3) {}
+ virtual void closepath () {}
+ virtual void draw (char cmd, const Point *points, int n) {}
+ virtual bool quit () {return false;}
+ virtual void finished () {}
+ };
+
+ typedef typename std::vector<Command>::iterator Iterator;
+ typedef typename std::vector<Command>::const_iterator ConstIterator;
+ typedef typename std::vector<Command>::const_reverse_iterator ConstRevIterator;
+
+ public:
+ GraphicPath (WindingRule wr=WR_NON_ZERO) : _windingRule(wr) {}
+
+ void setWindingRule (WindingRule wr) {_windingRule = wr;}
+ WindingRule windingRule () const {return _windingRule;}
+
+ void clear () {
+ _commands.clear();
+ }
+
+ /// Returns true if the path is empty, i.e. there is nothing to draw
+ bool empty () const {
+ return _commands.empty();
+ }
+
+ /// Returns the number of path commands used to describe the path.
+ size_t size () const {
+ return _commands.size();
+ }
+
+ /// Insert another path at the beginning of this one.
+ void prepend (const GraphicPath &path) {
+ _commands.insert(_commands.begin(), path._commands.begin(), path._commands.end());
+ }
+
+ void moveto (const T &x, const T &y) {
+ moveto(Point(x, y));
+ }
+
+ void moveto (const Point &p) {
+ // avoid sequences of several MOVETOs; always use latest
+ if (_commands.empty() || _commands.back().type != Command::MOVETO)
+ _commands.push_back(Command(Command::MOVETO, p));
+ else
+ _commands.back().params[0] = p;
+ }
+
+ void lineto (const T &x, const T &y) {
+ lineto(Point(x, y));
+ }
+
+ void lineto (const Point &p) {
+ _commands.push_back(Command(Command::LINETO, p));
+ }
+
+ void conicto (const T &x1, const T &y1, const T &x2, const T &y2) {
+ conicto(Point(x1, y1), Point(x2, y2));
+ }
+
+ void conicto (const Point &p1, const Point &p2) {
+ _commands.push_back(Command(Command::CONICTO, p1, p2));
+ }
+
+ void cubicto (const T &x1, const T &y1, const T &x2, const T &y2, const T &x3, const T &y3) {
+ cubicto(Point(x1, y1), Point(x2, y2), Point(x3, y3));
+ }
+
+ void cubicto (const Point &p1, const Point &p2, const Point &p3) {
+ _commands.push_back(Command(Command::CUBICTO, p1, p2, p3));
+ }
+
+ void closepath () {
+ _commands.push_back(Command(Command::CLOSEPATH));
+ }
+
+
+ const std::vector<Command>& commands () const {
+ return _commands;
+ }
+
+
+ /** Detects all open subpaths and closes them by adding a closePath command.
+ * Most font formats only support closed outline paths so there are no explicit closePath statements
+ * in the glyph's outline description. All open paths are automatically closed by the renderer.
+ * This method detects all open paths and adds the missing closePath statement. */
+ void closeOpenSubPaths () {
+ Command *prevCommand = 0;
+ FORALL(_commands, Iterator, it) {
+ if (it->type == Command::MOVETO && prevCommand && prevCommand->type != Command::CLOSEPATH) {
+ prevCommand = &(*it);
+ it = _commands.insert(it, Command(Command::CLOSEPATH))+1;
+// ++it; // skip inserted closePath command in next iteration step
+ }
+ else
+ prevCommand = &(*it);
+ }
+ if (!_commands.empty() && _commands.back().type != Command::CLOSEPATH)
+ closepath();
+ }
+
+
+ /** Writes the path data as SVG path drawing command to a given output stream.
+ * @param[in] os output stream used to write the SVG commands to
+ * @param[in] relative if true, create relative rather than absolute coordinate values
+ * @param[in] sx horizontal scale factor
+ * @param[in] sy vertical scale factor
+ * @param[in] dx horizontal translation in PS point units
+ * @param[in] dy vertical translation in PS point units */
+ void writeSVG (std::ostream &os, bool relative, double sx=1.0, double sy=1.0, double dx=0.0, double dy=0.0) const {
+ struct WriteActions : Actions {
+ WriteActions (std::ostream &os, bool relative, double sx, double sy, double dx, double dy)
+ : _os(os), _relative(relative), _sx(sx), _sy(sy), _dx(dx), _dy(dy) {}
+
+ void draw (char cmd, const Point *points, int n) {
+ if (_relative)
+ cmd = tolower(cmd);
+ _os << cmd;
+ switch (cmd) {
+ case 'h': _os << XMLString(_sx*(points->x()-_currentPoint.x())+_dx); break;
+ case 'v': _os << XMLString(_sy*(points->y()-_currentPoint.y())+_dy); break;
+ case 'z': _currentPoint = _startPoint; break;
+ case 'H': _os << XMLString(_sx*points->x()+_dx); break;
+ case 'V': _os << XMLString(_sy*points->y()+_dy); break;
+ default :
+ for (int i=0; i < n; i++) {
+ if (i > 0)
+ _os << ' ';
+ Point p = points[i];
+ if (_relative)
+ p -= _currentPoint;
+ _os << XMLString(_sx*p.x()+_dx) << ' ' << XMLString(_sy*p.y()+_dy);
+ }
+ }
+ if (cmd == 'm')
+ _startPoint = points[0];
+ if (islower(cmd) && n > 0)
+ _currentPoint = points[n-1];
+ }
+ std::ostream &_os;
+ bool _relative;
+ double _sx, _sy, _dx, _dy;
+ Point _startPoint, _currentPoint;
+ } actions(os, relative, sx, sy, dx, dy);
+ iterate(actions, true);
+ }
+
+#if 0
+ void writePS (std::ostream &os, double sx=1.0, double sy=1.0, double dx=0.0, double dy=0.0) const {
+ struct WriteActions : Actions {
+ WriteActions (std::ostream &os, double sx, double sy, double dx, double dy)
+ : _os(os), _sx(sx), _sy(sy), _dx(dx), _dy(dy) {}
+ void draw (char cmd, const Point *points, int n) {
+ for (int i=0; i < n; i++)
+ _os << _sx*points[i].x()+_dx << ' ' << _sy*points[i].y()+_dy << ' ';
+ switch (cmd) {
+ case 'M': _os << "moveto"; break;
+ case 'L': _os << "lineto"; break;
+ case 'C': _os << "curveto"; break;
+ case 'Z': _os << "closepath"; break;
+ default: ;
+ }
+ _os << '\n';
+ }
+ std::ostream &_os;
+ bool _relative;
+ double _sx, _sy, _dx, _dy;
+ } actions(os, sx, sy, dx, dy);
+ iterate(actions, false);
+ }
+#endif
+
+
+ /** Computes the bounding box of the current path.
+ * @param[out] bbox the computed bounding box */
+ void computeBBox (BoundingBox &bbox) const {
+ struct BBoxActions : Actions {
+ BBoxActions (BoundingBox &bb) : bbox(bb) {}
+ void moveto (const Point &p) {bbox.embed(p);}
+ void lineto (const Point &p) {bbox.embed(p);}
+ void conicto (const Point &p1, const Point &p2) {bbox.embed(p1); bbox.embed(p2);}
+ void cubicto (const Point &p1, const Point &p2, const Point &p3) {bbox.embed(p1); bbox.embed(p2); bbox.embed(p3);}
+ BoundingBox &bbox;
+ } actions(bbox);
+ iterate(actions, false);
+ }
+
+
+ /** Checks whether the current path describes a dot/point only (with no extent).
+ * @param[out] p coordinates of the point if path describes a dot
+ * @return true if path is a dot/point */
+ bool isDot (Point &p) const {
+ struct DotActions : Actions {
+ DotActions () : differs(false) {}
+ void moveto (const Point &p) {point = p;}
+ void lineto (const Point &p) {differs = (p != point);}
+ void conicto (const Point &p1, const Point &p2) {differs = (point != p1 || point != p2);}
+ void cubicto (const Point &p1, const Point &p2, const Point &p3) {differs = (point != p1 || point != p2 || point != p3);}
+ bool quit () {return differs;}
+ Point point;
+ bool differs;
+ } actions;
+ iterate(actions, false);
+ p = actions.point;
+ return !actions.differs;
+ }
+
+
+ /** Transforms the path according to a given Matrix.
+ * @param[in] matrix Matrix describing the affine transformation */
+ void transform (const Matrix &matrix) {
+ FORALL(_commands, Iterator, it)
+ it->transform(matrix);
+ }
+
+ void iterate (Actions &actions, bool optimize) const;
+
+ private:
+ std::vector<Command> _commands;
+ WindingRule _windingRule;
+};
+
+
+/** Iterates over all commands defining this path and calls the corresponding template methods.
+ * In the case of successive bezier curve sequences, control points or tangent slopes are often
+ * identical so that the path description contains redundant information. SVG provides shorthand
+ * curve commands that require less parameters. If 'optimize' is true, this method detects such
+ * command sequences.
+ * @param[in] actions template methods called by each iteration step
+ * @param[in] optimize if true, shorthand drawing commands (sconicto, scubicto,...) are considered */
+template <typename T>
+void GraphicPath<T>::iterate (Actions &actions, bool optimize) const {
+ ConstIterator prev = _commands.end(); // pointer to preceding command
+ Point fp; // first point of current path
+ Point cp; // current point
+ Point pstore[2];
+ for (ConstIterator it=_commands.begin(); it != _commands.end() && !actions.quit(); ++it) {
+ const Point *params = it->params;
+ switch (it->type) {
+ case Command::MOVETO:
+ actions.moveto(params[0]);
+ actions.draw('M', params, 1);
+ fp = params[0];
+ break;
+ case Command::LINETO:
+ if (optimize) {
+ if (cp.x() == params[0].x()) {
+ actions.vlineto(params[0].y());
+ actions.draw('V', params, 1);
+ }
+ else if (cp.y() == params[0].y()) {
+ actions.hlineto(params[0].x());
+ actions.draw('H', params, 1);
+ }
+ else {
+ actions.lineto(params[0]);
+ actions.draw('L', params, 1);
+ }
+ }
+ else {
+ actions.lineto(params[0]);
+ actions.draw('L', params, 1);
+ }
+ break;
+ case Command::CONICTO:
+ if (optimize && prev != _commands.end() && prev->type == Command::CONICTO && params[0] == pstore[1]*T(2)-pstore[0]) {
+ actions.sconicto(params[1]);
+ actions.draw('T', params+1, 1);
+ }
+ else {
+ actions.conicto(params[0], params[1]);
+ actions.draw('Q', params, 2);
+ }
+ pstore[0] = params[0]; // store control point and
+ pstore[1] = params[1]; // curve endpoint
+ break;
+ case Command::CUBICTO:
+ // is first control point reflection of preceding second control point?
+ if (optimize && prev != _commands.end() && prev->type == Command::CUBICTO && params[0] == pstore[1]*T(2)-pstore[0]) {
+ actions.scubicto(params[1], params[2]);
+ actions.draw('S', params+1, 2);
+ }
+ else {
+ actions.cubicto(params[0], params[1], params[2]);
+ actions.draw('C', params, 3);
+ }
+ pstore[0] = params[1]; // store second control point and
+ pstore[1] = params[2]; // curve endpoint
+ break;
+ case Command::CLOSEPATH:
+ actions.closepath();
+ actions.draw('Z', params, 0);
+ cp = fp;
+ }
+ // update current point
+ const int np = it->numParams();
+ if (np > 0)
+ cp = it->params[np-1];
+ prev = it;
+ }
+ actions.finished();
+}
+
+#endif