summaryrefslogtreecommitdiffstats
path: root/src/svg
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/svg/CMakeLists.txt34
-rw-r--r--src/svg/HACKING7
-rw-r--r--src/svg/README8
-rw-r--r--src/svg/css-ostringstream.cpp66
-rw-r--r--src/svg/css-ostringstream.h67
-rw-r--r--src/svg/path-string.cpp167
-rw-r--r--src/svg/path-string.h266
-rw-r--r--src/svg/sp-svg.def27
-rw-r--r--src/svg/stringstream.cpp94
-rw-r--r--src/svg/stringstream.h90
-rw-r--r--src/svg/strip-trailing-zeros.cpp54
-rw-r--r--src/svg/strip-trailing-zeros.h29
-rw-r--r--src/svg/svg-affine-parser.cpp1248
-rw-r--r--src/svg/svg-affine-parser.rl132
-rw-r--r--src/svg/svg-affine.cpp138
-rw-r--r--src/svg/svg-angle.cpp134
-rw-r--r--src/svg/svg-angle.h70
-rw-r--r--src/svg/svg-bool.cpp57
-rw-r--r--src/svg/svg-bool.h42
-rw-r--r--src/svg/svg-color.cpp672
-rw-r--r--src/svg/svg-color.h26
-rw-r--r--src/svg/svg-icc-color.h38
-rw-r--r--src/svg/svg-length.cpp596
-rw-r--r--src/svg/svg-length.h87
-rw-r--r--src/svg/svg-path.cpp139
-rw-r--r--src/svg/svg.h80
26 files changed, 4368 insertions, 0 deletions
diff --git a/src/svg/CMakeLists.txt b/src/svg/CMakeLists.txt
new file mode 100644
index 0000000..0abb9cd
--- /dev/null
+++ b/src/svg/CMakeLists.txt
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+set(svg_SRC
+ css-ostringstream.cpp
+ path-string.cpp
+ # sp-svg.def
+ stringstream.cpp
+ strip-trailing-zeros.cpp
+ svg-affine.cpp
+ svg-affine-parser.cpp
+ svg-color.cpp
+ svg-angle.cpp
+ svg-length.cpp
+ svg-bool.cpp
+ svg-path.cpp
+
+
+ # -------
+ # Headers
+ css-ostringstream.h
+ path-string.h
+ stringstream.h
+ strip-trailing-zeros.h
+ svg-color.h
+ svg-icc-color.h
+ svg-angle.h
+ svg-length.h
+ svg-bool.h
+ svg.h
+
+)
+
+# add_inkscape_lib(svg_LIB "${svg_SRC}")
+add_inkscape_source("${svg_SRC}")
diff --git a/src/svg/HACKING b/src/svg/HACKING
new file mode 100644
index 0000000..e3d8b10
--- /dev/null
+++ b/src/svg/HACKING
@@ -0,0 +1,7 @@
+Here are svg specific functions, i.e. value parsing & creation.
+Most of these are written by Raph Levien for gill. I'll include correct
+copyright notices one day too.
+
+Lauris Kaplinski
+<lauris@ariman.ee>
+
diff --git a/src/svg/README b/src/svg/README
new file mode 100644
index 0000000..777f8b8
--- /dev/null
+++ b/src/svg/README
@@ -0,0 +1,8 @@
+
+
+This directory contains SVG utilities.
+
+To do:
+
+* Move to "util/svg".
+* Clean up.
diff --git a/src/svg/css-ostringstream.cpp b/src/svg/css-ostringstream.cpp
new file mode 100644
index 0000000..96e9abb
--- /dev/null
+++ b/src/svg/css-ostringstream.cpp
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#include "svg/css-ostringstream.h"
+#include "svg/strip-trailing-zeros.h"
+#include "preferences.h"
+
+Inkscape::CSSOStringStream::CSSOStringStream()
+{
+ /* These two are probably unnecessary now that we provide our own operator<< for float and
+ * double. */
+ ostr.imbue(std::locale::classic());
+ ostr.setf(std::ios::showpoint);
+
+ /* This one is (currently) needed though, as we currently use ostr.precision as a sort of
+ variable for storing the desired precision: see our two precision methods and our operator<<
+ methods for float and double. */
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ ostr.precision(prefs->getInt("/options/svgoutput/numericprecision", 8));
+}
+
+Inkscape::CSSOStringStream &Inkscape::CSSOStringStream::operator<<(double d)
+{
+ // Try as integer first
+ long const n = long(d);
+ if (d == n) {
+ *this << n;
+ return *this;
+ }
+
+ char buf[32]; // haven't thought about how much is really required.
+ switch (precision()) {
+ case 9: g_ascii_formatd(buf, sizeof(buf), "%.9f", d); break;
+ case 8: g_ascii_formatd(buf, sizeof(buf), "%.8f", d); break;
+ case 7: g_ascii_formatd(buf, sizeof(buf), "%.7f", d); break;
+ case 6: g_ascii_formatd(buf, sizeof(buf), "%.6f", d); break;
+ case 5: g_ascii_formatd(buf, sizeof(buf), "%.5f", d); break;
+ case 4: g_ascii_formatd(buf, sizeof(buf), "%.4f", d); break;
+ case 3: g_ascii_formatd(buf, sizeof(buf), "%.3f", d); break;
+ case 2: g_ascii_formatd(buf, sizeof(buf), "%.2f", d); break;
+ case 1: g_ascii_formatd(buf, sizeof(buf), "%.1f", d); break;
+ case 0: g_ascii_formatd(buf, sizeof(buf), "%.0f", d); break;
+ case 10: default: g_ascii_formatd(buf, sizeof(buf), "%.10f", d); break;
+ }
+ auto &os = *this;
+ os << strip_trailing_zeros(buf);
+ return os;
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/css-ostringstream.h b/src/svg/css-ostringstream.h
new file mode 100644
index 0000000..5a34b7c
--- /dev/null
+++ b/src/svg/css-ostringstream.h
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2014 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef SVG_CSS_OSTRINGSTREAM_H_INKSCAPE
+#define SVG_CSS_OSTRINGSTREAM_H_INKSCAPE
+
+#include <sstream>
+#include <type_traits>
+
+namespace Inkscape {
+
+/**
+ * A thin wrapper around std::ostringstream, but writing floating point numbers in the format
+ * required by CSS: `.' as decimal separator, no `e' notation, no nan or inf.
+ */
+class CSSOStringStream {
+private:
+ std::ostringstream ostr;
+
+public:
+ CSSOStringStream();
+
+ template <typename T,
+ // disable this template for float and double
+ typename std::enable_if<!std::is_floating_point<T>::value, int>::type = 0>
+ CSSOStringStream &operator<<(T const &arg)
+ {
+ ostr << arg;
+ return *this;
+ }
+
+ CSSOStringStream &operator<<(double);
+
+ std::string str() const {
+ return ostr.str();
+ }
+
+ std::streamsize precision() const {
+ return ostr.precision();
+ }
+
+ std::streamsize precision(std::streamsize p) {
+ return ostr.precision(p);
+ }
+};
+
+}
+
+
+#endif /* !SVG_CSS_OSTRINGSTREAM_H_INKSCAPE */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/path-string.cpp b/src/svg/path-string.cpp
new file mode 100644
index 0000000..516fe35
--- /dev/null
+++ b/src/svg/path-string.cpp
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Inkscape::SVG::PathString - builder for SVG path strings
+ *//*
+ * Authors: see git history
+ *
+ * Copyright 2008 Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
+ * Copyright 2013 Tavmjong Bah <tavmjong@free.fr>
+ * Copyright (C) 2018 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "svg/path-string.h"
+#include "svg/stringstream.h"
+#include "svg/svg.h"
+#include "preferences.h"
+
+// 1<=numericprecision<=16, doubles are only accurate upto (slightly less than) 16 digits (and less than one digit doesn't make sense)
+// Please note that these constants are used to allocate sufficient space to hold serialized numbers
+static int const minprec = 1;
+static int const maxprec = 16;
+
+int Inkscape::SVG::PathString::numericprecision;
+int Inkscape::SVG::PathString::minimumexponent;
+Inkscape::SVG::PATHSTRING_FORMAT Inkscape::SVG::PathString::format;
+
+Inkscape::SVG::PathString::PathString() :
+ force_repeat_commands(!Inkscape::Preferences::get()->getBool("/options/svgoutput/disable_optimizations" ) && Inkscape::Preferences::get()->getBool("/options/svgoutput/forcerepeatcommands"))
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ format = (PATHSTRING_FORMAT)prefs->getIntLimited("/options/svgoutput/pathstring_format", 1, 0, PATHSTRING_FORMAT_SIZE - 1 );
+ numericprecision = std::max<int>(minprec,std::min<int>(maxprec, prefs->getInt("/options/svgoutput/numericprecision", 8)));
+ minimumexponent = prefs->getInt("/options/svgoutput/minimumexponent", -8);
+}
+
+// For absolute and relative paths... the entire path is kept in the "tail".
+// For optimized path, at a switch between absolute and relative, add tail to commonbase.
+void Inkscape::SVG::PathString::_appendOp(char abs_op, char rel_op) {
+ bool abs_op_repeated = _abs_state.prevop == abs_op && !force_repeat_commands;
+ bool rel_op_repeated = _rel_state.prevop == rel_op && !force_repeat_commands;
+
+ // For absolute and relative paths... do nothing.
+ switch (format) {
+ case PATHSTRING_ABSOLUTE:
+ if ( !abs_op_repeated ) _abs_state.appendOp(abs_op);
+ break;
+ case PATHSTRING_RELATIVE:
+ if ( !rel_op_repeated ) _rel_state.appendOp(rel_op);
+ break;
+ case PATHSTRING_OPTIMIZE:
+ {
+ unsigned int const abs_added_size = abs_op_repeated ? 0 : 2;
+ unsigned int const rel_added_size = rel_op_repeated ? 0 : 2;
+ if ( _rel_state.str.size()+2 < _abs_state.str.size()+abs_added_size ) {
+
+ // Store common prefix
+ commonbase += _rel_state.str;
+ _rel_state.str.clear();
+ // Copy rel to abs
+ _abs_state = _rel_state;
+ _abs_state.switches++;
+ abs_op_repeated = false;
+ // We do not have to copy abs to rel:
+ // _rel_state.str.size()+2 < _abs_state.str.size()+abs_added_size
+ // _rel_state.str.size()+rel_added_size < _abs_state.str.size()+2
+ // _abs_state.str.size()+2 > _rel_state.str.size()+rel_added_size
+ } else if ( _abs_state.str.size()+2 < _rel_state.str.size()+rel_added_size ) {
+
+ // Store common prefix
+ commonbase += _abs_state.str;
+ _abs_state.str.clear();
+ // Copy abs to rel
+ _rel_state = _abs_state;
+ _abs_state.switches++;
+ rel_op_repeated = false;
+ }
+ if ( !abs_op_repeated ) _abs_state.appendOp(abs_op);
+ if ( !rel_op_repeated ) _rel_state.appendOp(rel_op);
+ }
+ break;
+ default:
+ std::cout << "Better not be here!" << std::endl;
+ }
+}
+
+void Inkscape::SVG::PathString::State::append(Geom::Coord v) {
+ str += ' ';
+ appendNumber(v);
+}
+
+void Inkscape::SVG::PathString::State::append(Geom::Point p) {
+ str += ' ';
+ appendNumber(p[Geom::X]);
+ str += ',';
+ appendNumber(p[Geom::Y]);
+}
+
+void Inkscape::SVG::PathString::State::append(Geom::Coord v, Geom::Coord& rv) {
+ str += ' ';
+ appendNumber(v, rv);
+}
+
+void Inkscape::SVG::PathString::State::append(Geom::Point p, Geom::Point &rp) {
+ str += ' ';
+ appendNumber(p[Geom::X], rp[Geom::X]);
+ str += ',';
+ appendNumber(p[Geom::Y], rp[Geom::Y]);
+}
+
+// NOTE: The following appendRelativeCoord function will not be exact if the total number of digits needed
+// to represent the difference exceeds the precision of a double. This is not very likely though, and if
+// it does happen the imprecise value is not likely to be chosen (because it will probably be a lot longer
+// than the absolute value).
+
+// NOTE: This assumes v and r are already rounded (this includes flushing to zero if they are < 10^minexp)
+void Inkscape::SVG::PathString::State::appendRelativeCoord(Geom::Coord v, Geom::Coord r) {
+ int const minexp = minimumexponent-numericprecision+1;
+ int const digitsEnd = (int)floor(log10(std::min(fabs(v),fabs(r)))) - numericprecision; // Position just beyond the last significant digit of the smallest (in absolute sense) number
+ double const roundeddiff = floor((v-r)*pow(10.,-digitsEnd-1)+.5);
+ int const numDigits = (int)floor(log10(fabs(roundeddiff)))+1; // Number of digits in roundeddiff
+ if (r == 0) {
+ appendNumber(v, numericprecision, minexp);
+ } else if (v == 0) {
+ appendNumber(-r, numericprecision, minexp);
+ } else if (numDigits>0) {
+ appendNumber(v-r, numDigits, minexp);
+ } else {
+ // This assumes the input numbers are already rounded to 'precision' digits
+ str += '0';
+ }
+}
+
+void Inkscape::SVG::PathString::State::appendRelative(Geom::Point p, Geom::Point r) {
+ str += ' ';
+ appendRelativeCoord(p[Geom::X], r[Geom::X]);
+ str += ',';
+ appendRelativeCoord(p[Geom::Y], r[Geom::Y]);
+}
+
+void Inkscape::SVG::PathString::State::appendRelative(Geom::Coord v, Geom::Coord r) {
+ str += ' ';
+ appendRelativeCoord(v, r);
+}
+
+void Inkscape::SVG::PathString::State::appendNumber(double v, int precision, int minexp) {
+
+ str.append(sp_svg_number_write_de(v, precision, minexp));
+}
+
+void Inkscape::SVG::PathString::State::appendNumber(double v, double &rv, int precision, int minexp) {
+ size_t const oldsize = str.size();
+ appendNumber(v, precision, minexp);
+ char* begin_of_num = const_cast<char*>(str.data()+oldsize); // Slightly evil, I know (but std::string should be storing its data in one big block of memory, so...)
+ sp_svg_number_read_d(begin_of_num, &rv);
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/path-string.h b/src/svg/path-string.h
new file mode 100644
index 0000000..53080ba
--- /dev/null
+++ b/src/svg/path-string.h
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Inkscape::SVG::PathString - builder for SVG path strings
+ *//*
+ * Authors: see git history
+ *
+ * Copyright 2007 MenTaLguY <mental@rydia.net>
+ * Copyright 2008 Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
+ * Copyright 2013 Tavmjong Bah <tavmjong@free.fr>
+ * Copyright (C) 2014 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef SEEN_INKSCAPE_SVG_PATH_STRING_H
+#define SEEN_INKSCAPE_SVG_PATH_STRING_H
+
+#include <2geom/point.h>
+#include <cstdio>
+#include <glibmm/ustring.h>
+#include <string>
+
+namespace Inkscape {
+
+namespace SVG {
+
+// Relative vs. absolute coordinates
+enum PATHSTRING_FORMAT {
+ PATHSTRING_ABSOLUTE, // Use only absolute coordinates
+ PATHSTRING_RELATIVE, // Use only relative coordinates
+ PATHSTRING_OPTIMIZE, // Optimize for path string length
+ PATHSTRING_FORMAT_SIZE
+};
+
+/**
+ * Builder for SVG path strings.
+ */
+class PathString {
+public:
+ PathString();
+
+ // default copy
+ // default assign
+
+ std::string const &string() {
+ std::string const &t = tail();
+ final.reserve(commonbase.size()+t.size());
+ final = commonbase;
+ final += tail();
+ // std::cout << " final: " << final << std::endl;
+ return final;
+ }
+
+ operator std::string const &() {
+ return string();
+ }
+
+ operator Glib::ustring const () const {
+ return commonbase + tail();
+ }
+
+ char const *c_str() {
+ return string().c_str();
+ }
+
+ PathString &moveTo(Geom::Coord x, Geom::Coord y) {
+ return moveTo(Geom::Point(x, y));
+ }
+
+ PathString &moveTo(Geom::Point p) {
+ _appendOp('M','m');
+ _appendPoint(p, true);
+
+ _initial_point = _current_point;
+ return *this;
+ }
+
+ PathString &lineTo(Geom::Coord x, Geom::Coord y) {
+ return lineTo(Geom::Point(x, y));
+ }
+
+ PathString &lineTo(Geom::Point p) {
+ _appendOp('L','l');
+ _appendPoint(p, true);
+ return *this;
+ }
+
+ PathString &horizontalLineTo(Geom::Coord x) {
+ _appendOp('H','h');
+ _appendX(x, true);
+ return *this;
+ }
+
+ PathString &verticalLineTo(Geom::Coord y) {
+ _appendOp('V','v');
+ _appendY(y, true);
+ return *this;
+ }
+
+ PathString &quadTo(Geom::Coord cx, Geom::Coord cy, Geom::Coord x, Geom::Coord y) {
+ return quadTo(Geom::Point(cx, cy), Geom::Point(x, y));
+ }
+
+ PathString &quadTo(Geom::Point c, Geom::Point p) {
+ _appendOp('Q','q');
+ _appendPoint(c, false);
+ _appendPoint(p, true);
+ return *this;
+ }
+
+ PathString &curveTo(Geom::Coord x0, Geom::Coord y0,
+ Geom::Coord x1, Geom::Coord y1,
+ Geom::Coord x, Geom::Coord y)
+ {
+ return curveTo(Geom::Point(x0, y0), Geom::Point(x1, y1), Geom::Point(x, y));
+ }
+
+ PathString &curveTo(Geom::Point c0, Geom::Point c1, Geom::Point p) {
+ _appendOp('C','c');
+ _appendPoint(c0, false);
+ _appendPoint(c1, false);
+ _appendPoint(p, true);
+ return *this;
+ }
+
+ /**
+ * \param rot the angle in degrees
+ */
+ PathString &arcTo(Geom::Coord rx, Geom::Coord ry, Geom::Coord rot,
+ bool large_arc, bool sweep,
+ Geom::Point p)
+ {
+ _appendOp('A','a');
+ _appendValue(Geom::Point(rx,ry));
+ _appendValue(rot);
+ _appendFlag(large_arc);
+ _appendFlag(sweep);
+ _appendPoint(p, true);
+ return *this;
+ }
+
+ PathString &closePath() {
+
+ _abs_state.appendOp('Z');
+ _rel_state.appendOp('z');
+
+ _current_point = _initial_point;
+ return *this;
+ }
+
+private:
+
+ void _appendOp(char abs_op, char rel_op);
+
+ void _appendFlag(bool flag) {
+ _abs_state.append(flag);
+ _rel_state.append(flag);
+ }
+
+ void _appendValue(Geom::Coord v) {
+ _abs_state.append(v);
+ _rel_state.append(v);
+ }
+
+ void _appendValue(Geom::Point p) {
+ _abs_state.append(p);
+ _rel_state.append(p);
+ }
+
+ void _appendX(Geom::Coord x, bool sc) {
+ double rx;
+ _abs_state.append(x, rx);
+ _rel_state.appendRelative(rx, _current_point[Geom::X]);
+ if (sc) _current_point[Geom::X] = rx;
+ }
+
+ void _appendY(Geom::Coord y, bool sc) {
+ double ry;
+ _abs_state.append(y, ry);
+ _rel_state.appendRelative(ry, _current_point[Geom::Y]);
+ if (sc) _current_point[Geom::Y] = ry;
+ }
+
+ void _appendPoint(Geom::Point p, bool sc) {
+ Geom::Point rp;
+ _abs_state.append(p, rp);
+ _rel_state.appendRelative(rp, _current_point);
+ if (sc) _current_point = rp;
+ }
+
+ struct State {
+ State() { prevop = 0; switches = 0; }
+
+ void appendOp(char op) {
+ if (prevop != 0) str += ' ';
+ str += op;
+ prevop = ( op == 'M' ? 'L' : op == 'm' ? 'l' : op );
+ }
+
+ void append(bool flag) {
+ str += ' ';
+ str += ( flag ? '1' : '0' );
+ }
+
+ void append(Geom::Coord v);
+ void append(Geom::Point v);
+ void append(Geom::Coord v, Geom::Coord& rv);
+ void append(Geom::Point p, Geom::Point& rp);
+ void appendRelative(Geom::Coord v, Geom::Coord r);
+ void appendRelative(Geom::Point p, Geom::Point r);
+
+ bool operator<=(const State& s) const {
+ if ( str.size() < s.str.size() ) return true;
+ if ( str.size() > s.str.size() ) return false;
+ if ( switches < s.switches ) return true;
+ if ( switches > s.switches ) return false;
+ return true;
+ }
+
+ // Note: changing this to Glib::ustring might cause problems in path-string.cpp because it assumes that
+ // size() returns the size of the string in BYTES (and Glib::ustring::resize is terribly slow)
+ std::string str;
+ unsigned int switches;
+ char prevop;
+
+ private:
+ void appendNumber(double v, int precision=numericprecision, int minexp=minimumexponent);
+ void appendNumber(double v, double &rv, int precision=numericprecision, int minexp=minimumexponent);
+ void appendRelativeCoord(Geom::Coord v, Geom::Coord r);
+ } _abs_state, _rel_state; // State with the last operator being an absolute/relative operator
+
+ Geom::Point _initial_point;
+ Geom::Point _current_point;
+
+ // If both states have a common prefix it is stored here.
+ // Separating out the common prefix prevents repeated copying between the states
+ // to cause a quadratic time complexity (in the number of characters/operators)
+ std::string commonbase;
+ std::string final;
+ std::string const &tail() const {
+ return ( (format == PATHSTRING_ABSOLUTE) ||
+ (format == PATHSTRING_OPTIMIZE && _abs_state <= _rel_state ) ?
+ _abs_state.str : _rel_state.str );
+ }
+
+ static PATHSTRING_FORMAT format;
+ bool const force_repeat_commands;
+ static int numericprecision;
+ static int minimumexponent;
+};
+
+}
+
+}
+
+#endif
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/sp-svg.def b/src/svg/sp-svg.def
new file mode 100644
index 0000000..6336f9b
--- /dev/null
+++ b/src/svg/sp-svg.def
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+EXPORTS
+ gnome_canvas_bpath_def_art_finish
+ gnome_canvas_bpath_def_closepath
+ gnome_canvas_bpath_def_curveto
+ gnome_canvas_bpath_def_free
+ gnome_canvas_bpath_def_lineto
+ gnome_canvas_bpath_def_moveto
+ gnome_canvas_bpath_def_new
+ gnome_canvas_bpath_def_new_from
+ gnome_canvas_bpath_def_ref
+ sp_svg_length_read
+ sp_svg_length_read_ldd
+ sp_svg_length_unset
+ sp_svg_length_update
+ sp_svg_number_read_d
+ sp_svg_number_read_f
+ sp_svg_number_write_de
+ sp_svg_number_write_f
+ sp_svg_number_write_fe
+ sp_svg_read_color
+ sp_svg_read_path
+ sp_svg_read_percentage
+ sp_svg_transform_read
+ sp_svg_transform_write
+ sp_svg_write_color
+ sp_svg_write_path
diff --git a/src/svg/stringstream.cpp b/src/svg/stringstream.cpp
new file mode 100644
index 0000000..59f84a2
--- /dev/null
+++ b/src/svg/stringstream.cpp
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#include "svg/stringstream.h"
+#include "svg/strip-trailing-zeros.h"
+#include "preferences.h"
+#include <2geom/point.h>
+
+Inkscape::SVGOStringStream::SVGOStringStream()
+{
+ /* These two are probably unnecessary now that we provide our own operator<< for float and
+ * double. */
+ ostr.imbue(std::locale::classic());
+ ostr.setf(std::ios::showpoint);
+
+ /* This one is (currently) needed though, as we currently use ostr.precision as a sort of
+ variable for storing the desired precision: see our two precision methods and our operator<<
+ methods for float and double. */
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ ostr.precision(prefs->getInt("/options/svgoutput/numericprecision", 8));
+}
+
+Inkscape::SVGOStringStream &
+Inkscape::SVGOStringStream::operator<<(double d)
+{
+ auto &os = *this;
+
+ /* Try as integer first. */
+ {
+ int const n = int(d);
+ if (d == n) {
+ os << n;
+ return os;
+ }
+ }
+
+ std::ostringstream s;
+ s.imbue(std::locale::classic());
+ s.flags(os.setf(std::ios::showpoint));
+ s.precision(os.precision());
+ s << d;
+ os << strip_trailing_zeros(s.str());
+ return os;
+}
+
+Inkscape::SVGOStringStream &
+Inkscape::SVGOStringStream::operator<<(Geom::Point const & p)
+{
+ auto &os = *this;
+ os << p[0] << ',' << p[1];
+ return os;
+}
+
+Inkscape::SVGIStringStream::SVGIStringStream():std::istringstream()
+{
+ this->imbue(std::locale::classic());
+ this->setf(std::ios::showpoint);
+
+ /* This one is (currently) needed though, as we currently use ostr.precision as a sort of
+ variable for storing the desired precision: see our two precision methods and our operator<<
+ methods for float and double. */
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ this->precision(prefs->getInt("/options/svgoutput/numericprecision", 8));
+}
+
+Inkscape::SVGIStringStream::SVGIStringStream(const std::string& str):std::istringstream(str)
+{
+ this->imbue(std::locale::classic());
+ this->setf(std::ios::showpoint);
+
+ /* This one is (currently) needed though, as we currently use ostr.precision as a sort of
+ variable for storing the desired precision: see our two precision methods and our operator<<
+ methods for float and double. */
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ this->precision(prefs->getInt("/options/svgoutput/numericprecision", 8));
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/stringstream.h b/src/svg/stringstream.h
new file mode 100644
index 0000000..f5a7e95
--- /dev/null
+++ b/src/svg/stringstream.h
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2014 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef INKSCAPE_STRINGSTREAM_H
+#define INKSCAPE_STRINGSTREAM_H
+
+#include <sstream>
+#include <string>
+#include <type_traits>
+
+#include <2geom/forward.h>
+
+namespace Inkscape {
+
+class SVGOStringStream {
+private:
+ std::ostringstream ostr;
+
+public:
+ SVGOStringStream();
+
+ template <typename T,
+ // disable this template for float and double
+ typename std::enable_if<!std::is_floating_point<T>::value, int>::type = 0>
+ SVGOStringStream &operator<<(T const &arg)
+ {
+ static_assert(!std::is_base_of<Geom::Point, T>::value, "");
+ ostr << arg;
+ return *this;
+ }
+
+ SVGOStringStream &operator<<(double);
+ SVGOStringStream &operator<<(Geom::Point const &);
+
+ std::string str() const {
+ return ostr.str();
+ }
+
+ void str (std::string &s) {
+ ostr.str(s);
+ }
+
+ std::streamsize precision() const {
+ return ostr.precision();
+ }
+
+ std::streamsize precision(std::streamsize p) {
+ return ostr.precision(p);
+ }
+
+ std::ios::fmtflags setf(std::ios::fmtflags fmtfl) {
+ return ostr.setf(fmtfl);
+ }
+
+ std::ios::fmtflags setf(std::ios::fmtflags fmtfl, std::ios::fmtflags mask) {
+ return ostr.setf(fmtfl, mask);
+ }
+
+ void unsetf(std::ios::fmtflags mask) {
+ ostr.unsetf(mask);
+ }
+};
+
+class SVGIStringStream:public std::istringstream {
+
+public:
+ SVGIStringStream();
+ SVGIStringStream(const std::string &str);
+};
+
+}
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/strip-trailing-zeros.cpp b/src/svg/strip-trailing-zeros.cpp
new file mode 100644
index 0000000..8abe4fa
--- /dev/null
+++ b/src/svg/strip-trailing-zeros.cpp
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cstring>
+#include <string>
+#include <glib.h>
+
+#include "svg/strip-trailing-zeros.h"
+
+std::string
+strip_trailing_zeros(std::string str)
+{
+ std::string::size_type p_ix = str.find('.');
+ if (p_ix != std::string::npos) {
+ std::string::size_type e_ix = str.find('e', p_ix);
+ /* N.B. In some contexts (e.g. CSS) it is an error for a number to contain `e'. fixme:
+ * Default to avoiding `e', e.g. using sprintf(str, "%17f", d). Add a new function that
+ * allows use of `e' and use that function only where the spec allows it.
+ */
+ std::string::size_type nz_ix = str.find_last_not_of('0', (e_ix == std::string::npos
+ ? e_ix
+ : e_ix - 1));
+ if (nz_ix == std::string::npos || nz_ix < p_ix || nz_ix >= e_ix) {
+ g_error("have `.' but couldn't find non-0");
+ } else {
+ str.erase(str.begin() + (nz_ix == p_ix
+ ? p_ix
+ : nz_ix + 1),
+ (e_ix == std::string::npos
+ ? str.end()
+ : str.begin() + e_ix));
+ }
+ }
+ return str;
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/strip-trailing-zeros.h b/src/svg/strip-trailing-zeros.h
new file mode 100644
index 0000000..1c5f537
--- /dev/null
+++ b/src/svg/strip-trailing-zeros.h
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2010 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef SVG_STRIP_TRAILING_ZEROS_H_SEEN
+#define SVG_STRIP_TRAILING_ZEROS_H_SEEN
+
+#include <string>
+
+std::string strip_trailing_zeros(std::string str);
+
+
+#endif /* !SVG_STRIP_TRAILING_ZEROS_H_SEEN */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg-affine-parser.cpp b/src/svg/svg-affine-parser.cpp
new file mode 100644
index 0000000..14bfd72
--- /dev/null
+++ b/src/svg/svg-affine-parser.cpp
@@ -0,0 +1,1248 @@
+
+#line 1 "svg-affine-parser.rl"
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SVG data parser
+ *
+ * Authors:
+ * Marc Jeanmougin <marc.jeanmougin@telecom-paris.fr>
+ *
+ * Copyright (C) 2019 Marc Jeanmougin (.rl parser, CSS-transform spec)
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ *
+ * THE CPP FILE IS GENERATED FROM THE RL FILE, DO NOT EDIT THE CPP
+ *
+ * To generate it, run
+ * ragel svg-affine-parser.rl -o svg-affine-parser.cpp
+ * sed -Ei 's/\(1\)/(true)/' svg-affine-parser.cpp
+ */
+
+#include <string>
+#include <glib.h>
+#include <2geom/transforms.h>
+#include "svg.h"
+#include "preferences.h"
+
+
+#line 29 "svg-affine-parser.cpp"
+static const char _svg_transform_actions[] = {
+ 0, 1, 0, 1, 8, 1, 11, 1,
+ 12, 1, 14, 1, 15, 1, 16, 2,
+ 0, 8, 2, 1, 7, 2, 2, 7,
+ 2, 3, 7, 2, 4, 7, 2, 5,
+ 7, 2, 6, 7, 2, 9, 10, 2,
+ 12, 0, 2, 12, 13, 3, 1, 7,
+ 14, 3, 2, 7, 14, 3, 3, 7,
+ 14, 3, 4, 7, 14, 3, 5, 7,
+ 14, 3, 6, 7, 14, 3, 12, 1,
+ 7, 3, 12, 2, 7, 3, 12, 3,
+ 7, 3, 12, 4, 7, 3, 12, 5,
+ 7, 3, 12, 6, 7
+};
+
+static const short _svg_transform_key_offsets[] = {
+ 0, 0, 1, 2, 3, 4, 5, 10,
+ 19, 22, 24, 36, 46, 49, 51, 63,
+ 73, 76, 78, 90, 100, 103, 105, 117,
+ 127, 130, 132, 144, 154, 157, 159, 168,
+ 173, 181, 182, 183, 184, 185, 186, 191,
+ 200, 203, 205, 217, 227, 230, 232, 244,
+ 254, 257, 259, 271, 281, 284, 286, 298,
+ 308, 311, 313, 325, 335, 338, 340, 349,
+ 354, 358, 360, 367, 377, 386, 390, 392,
+ 402, 414, 423, 427, 429, 439, 451, 460,
+ 464, 466, 476, 488, 497, 501, 503, 513,
+ 525, 534, 538, 540, 550, 562, 563, 564,
+ 565, 566, 567, 572, 581, 584, 586, 599,
+ 610, 612, 613, 614, 615, 620, 629, 632,
+ 634, 647, 658, 659, 660, 661, 662, 663,
+ 664, 665, 666, 671, 680, 683, 685, 698,
+ 709, 712, 714, 723, 728, 732, 734, 741,
+ 751, 760, 764, 766, 777, 790, 793, 795,
+ 804, 809, 813, 815, 822, 832, 841, 845,
+ 847, 858, 871, 872, 873, 875, 880, 889,
+ 892, 894, 903, 908, 912, 914, 921, 931,
+ 936, 945, 948, 950, 959, 964, 968, 970,
+ 977, 987, 990, 992, 1004, 1014, 1017, 1019,
+ 1028, 1033, 1037, 1039, 1046, 1056, 1065, 1069,
+ 1071, 1081, 1093, 1102, 1106, 1108, 1119, 1132,
+ 1136, 1138, 1145, 1155, 1164, 1168, 1170, 1180,
+ 1192, 1201, 1205, 1207, 1217, 1229, 1238, 1242,
+ 1244, 1254, 1266, 1275, 1279, 1281, 1291, 1303,
+ 1312, 1316, 1318, 1328, 1340, 1341, 1342, 1343,
+ 1344, 1345, 1350, 1359, 1362, 1364, 1377, 1388,
+ 1391, 1393, 1405, 1415, 1418, 1420, 1429, 1434,
+ 1438, 1440, 1447, 1457, 1466, 1470, 1472, 1482,
+ 1494, 1503, 1507, 1509, 1520, 1533, 1535, 1536,
+ 1537, 1538, 1543, 1552, 1555, 1557, 1570, 1581,
+ 1584, 1586, 1595, 1600, 1604, 1606, 1613, 1623,
+ 1632, 1636, 1638, 1649, 1662, 1663, 1664, 1666,
+ 1671, 1680, 1683, 1685, 1694, 1699, 1703, 1705,
+ 1712, 1722, 1727, 1736, 1739, 1741, 1750, 1755,
+ 1759, 1761, 1768, 1778, 1779, 1780, 1781, 1782,
+ 1783, 1784, 1785, 1786, 1791, 1800, 1803, 1805,
+ 1818, 1829, 1832, 1834, 1843, 1848, 1852, 1854,
+ 1861, 1871, 1880, 1884, 1886, 1897, 1910, 1918,
+ 1926, 1935, 1944, 1953, 1962, 1971, 1980
+};
+
+static const char _svg_transform_trans_keys[] = {
+ 97, 116, 114, 105, 120, 13, 32, 40,
+ 9, 10, 13, 32, 43, 45, 46, 9,
+ 10, 48, 57, 46, 48, 57, 48, 57,
+ 13, 32, 44, 46, 69, 101, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 9, 10, 43, 45, 48, 57, 46, 48,
+ 57, 48, 57, 13, 32, 44, 46, 69,
+ 101, 9, 10, 43, 45, 48, 57, 13,
+ 32, 44, 46, 9, 10, 43, 45, 48,
+ 57, 46, 48, 57, 48, 57, 13, 32,
+ 44, 46, 69, 101, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 9, 10,
+ 43, 45, 48, 57, 46, 48, 57, 48,
+ 57, 13, 32, 44, 46, 69, 101, 9,
+ 10, 43, 45, 48, 57, 13, 32, 44,
+ 46, 9, 10, 43, 45, 48, 57, 46,
+ 48, 57, 48, 57, 13, 32, 44, 46,
+ 69, 101, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 9, 10, 43, 45,
+ 48, 57, 46, 48, 57, 48, 57, 13,
+ 32, 41, 69, 101, 9, 10, 48, 57,
+ 13, 32, 41, 9, 10, 13, 32, 109,
+ 114, 115, 116, 9, 10, 97, 116, 114,
+ 105, 120, 13, 32, 40, 9, 10, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 46, 48, 57, 48, 57, 13, 32, 44,
+ 46, 69, 101, 9, 10, 43, 45, 48,
+ 57, 13, 32, 44, 46, 9, 10, 43,
+ 45, 48, 57, 46, 48, 57, 48, 57,
+ 13, 32, 44, 46, 69, 101, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 9, 10, 43, 45, 48, 57, 46, 48,
+ 57, 48, 57, 13, 32, 44, 46, 69,
+ 101, 9, 10, 43, 45, 48, 57, 13,
+ 32, 44, 46, 9, 10, 43, 45, 48,
+ 57, 46, 48, 57, 48, 57, 13, 32,
+ 44, 46, 69, 101, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 9, 10,
+ 43, 45, 48, 57, 46, 48, 57, 48,
+ 57, 13, 32, 44, 46, 69, 101, 9,
+ 10, 43, 45, 48, 57, 13, 32, 44,
+ 46, 9, 10, 43, 45, 48, 57, 46,
+ 48, 57, 48, 57, 13, 32, 41, 69,
+ 101, 9, 10, 48, 57, 13, 32, 41,
+ 9, 10, 43, 45, 48, 57, 48, 57,
+ 13, 32, 41, 9, 10, 48, 57, 13,
+ 32, 41, 46, 69, 101, 9, 10, 48,
+ 57, 13, 32, 43, 45, 46, 9, 10,
+ 48, 57, 43, 45, 48, 57, 48, 57,
+ 13, 32, 44, 46, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 69, 101,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 43, 45, 46, 9, 10, 48, 57, 43,
+ 45, 48, 57, 48, 57, 13, 32, 44,
+ 46, 9, 10, 43, 45, 48, 57, 13,
+ 32, 44, 46, 69, 101, 9, 10, 43,
+ 45, 48, 57, 13, 32, 43, 45, 46,
+ 9, 10, 48, 57, 43, 45, 48, 57,
+ 48, 57, 13, 32, 44, 46, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 69, 101, 9, 10, 43, 45, 48, 57,
+ 13, 32, 43, 45, 46, 9, 10, 48,
+ 57, 43, 45, 48, 57, 48, 57, 13,
+ 32, 44, 46, 9, 10, 43, 45, 48,
+ 57, 13, 32, 44, 46, 69, 101, 9,
+ 10, 43, 45, 48, 57, 13, 32, 43,
+ 45, 46, 9, 10, 48, 57, 43, 45,
+ 48, 57, 48, 57, 13, 32, 44, 46,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 44, 46, 69, 101, 9, 10, 43, 45,
+ 48, 57, 111, 116, 97, 116, 101, 13,
+ 32, 40, 9, 10, 13, 32, 43, 45,
+ 46, 9, 10, 48, 57, 46, 48, 57,
+ 48, 57, 13, 32, 41, 44, 46, 69,
+ 101, 9, 10, 43, 45, 48, 57, 13,
+ 32, 41, 44, 46, 9, 10, 43, 45,
+ 48, 57, 99, 107, 97, 108, 101, 13,
+ 32, 40, 9, 10, 13, 32, 43, 45,
+ 46, 9, 10, 48, 57, 46, 48, 57,
+ 48, 57, 13, 32, 41, 44, 46, 69,
+ 101, 9, 10, 43, 45, 48, 57, 13,
+ 32, 41, 44, 46, 9, 10, 43, 45,
+ 48, 57, 114, 97, 110, 115, 108, 97,
+ 116, 101, 13, 32, 40, 9, 10, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 46, 48, 57, 48, 57, 13, 32, 41,
+ 44, 46, 69, 101, 9, 10, 43, 45,
+ 48, 57, 13, 32, 41, 44, 46, 9,
+ 10, 43, 45, 48, 57, 46, 48, 57,
+ 48, 57, 13, 32, 41, 69, 101, 9,
+ 10, 48, 57, 13, 32, 41, 9, 10,
+ 43, 45, 48, 57, 48, 57, 13, 32,
+ 41, 9, 10, 48, 57, 13, 32, 41,
+ 46, 69, 101, 9, 10, 48, 57, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 43, 45, 48, 57, 48, 57, 13, 32,
+ 41, 44, 46, 9, 10, 43, 45, 48,
+ 57, 13, 32, 41, 44, 46, 69, 101,
+ 9, 10, 43, 45, 48, 57, 46, 48,
+ 57, 48, 57, 13, 32, 41, 69, 101,
+ 9, 10, 48, 57, 13, 32, 41, 9,
+ 10, 43, 45, 48, 57, 48, 57, 13,
+ 32, 41, 9, 10, 48, 57, 13, 32,
+ 41, 46, 69, 101, 9, 10, 48, 57,
+ 13, 32, 43, 45, 46, 9, 10, 48,
+ 57, 43, 45, 48, 57, 48, 57, 13,
+ 32, 41, 44, 46, 9, 10, 43, 45,
+ 48, 57, 13, 32, 41, 44, 46, 69,
+ 101, 9, 10, 43, 45, 48, 57, 101,
+ 119, 88, 89, 13, 32, 40, 9, 10,
+ 13, 32, 43, 45, 46, 9, 10, 48,
+ 57, 46, 48, 57, 48, 57, 13, 32,
+ 41, 69, 101, 9, 10, 48, 57, 13,
+ 32, 41, 9, 10, 43, 45, 48, 57,
+ 48, 57, 13, 32, 41, 9, 10, 48,
+ 57, 13, 32, 41, 46, 69, 101, 9,
+ 10, 48, 57, 13, 32, 40, 9, 10,
+ 13, 32, 43, 45, 46, 9, 10, 48,
+ 57, 46, 48, 57, 48, 57, 13, 32,
+ 41, 69, 101, 9, 10, 48, 57, 13,
+ 32, 41, 9, 10, 43, 45, 48, 57,
+ 48, 57, 13, 32, 41, 9, 10, 48,
+ 57, 13, 32, 41, 46, 69, 101, 9,
+ 10, 48, 57, 46, 48, 57, 48, 57,
+ 13, 32, 44, 46, 69, 101, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 9, 10, 43, 45, 48, 57, 46, 48,
+ 57, 48, 57, 13, 32, 41, 69, 101,
+ 9, 10, 48, 57, 13, 32, 41, 9,
+ 10, 43, 45, 48, 57, 48, 57, 13,
+ 32, 41, 9, 10, 48, 57, 13, 32,
+ 41, 46, 69, 101, 9, 10, 48, 57,
+ 13, 32, 43, 45, 46, 9, 10, 48,
+ 57, 43, 45, 48, 57, 48, 57, 13,
+ 32, 44, 46, 9, 10, 43, 45, 48,
+ 57, 13, 32, 44, 46, 69, 101, 9,
+ 10, 43, 45, 48, 57, 13, 32, 43,
+ 45, 46, 9, 10, 48, 57, 43, 45,
+ 48, 57, 48, 57, 13, 32, 41, 44,
+ 46, 9, 10, 43, 45, 48, 57, 13,
+ 32, 41, 44, 46, 69, 101, 9, 10,
+ 43, 45, 48, 57, 43, 45, 48, 57,
+ 48, 57, 13, 32, 41, 9, 10, 48,
+ 57, 13, 32, 41, 46, 69, 101, 9,
+ 10, 48, 57, 13, 32, 43, 45, 46,
+ 9, 10, 48, 57, 43, 45, 48, 57,
+ 48, 57, 13, 32, 44, 46, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 69, 101, 9, 10, 43, 45, 48, 57,
+ 13, 32, 43, 45, 46, 9, 10, 48,
+ 57, 43, 45, 48, 57, 48, 57, 13,
+ 32, 44, 46, 9, 10, 43, 45, 48,
+ 57, 13, 32, 44, 46, 69, 101, 9,
+ 10, 43, 45, 48, 57, 13, 32, 43,
+ 45, 46, 9, 10, 48, 57, 43, 45,
+ 48, 57, 48, 57, 13, 32, 44, 46,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 44, 46, 69, 101, 9, 10, 43, 45,
+ 48, 57, 13, 32, 43, 45, 46, 9,
+ 10, 48, 57, 43, 45, 48, 57, 48,
+ 57, 13, 32, 44, 46, 9, 10, 43,
+ 45, 48, 57, 13, 32, 44, 46, 69,
+ 101, 9, 10, 43, 45, 48, 57, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 43, 45, 48, 57, 48, 57, 13, 32,
+ 44, 46, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 69, 101, 9, 10,
+ 43, 45, 48, 57, 111, 116, 97, 116,
+ 101, 13, 32, 40, 9, 10, 13, 32,
+ 43, 45, 46, 9, 10, 48, 57, 46,
+ 48, 57, 48, 57, 13, 32, 41, 44,
+ 46, 69, 101, 9, 10, 43, 45, 48,
+ 57, 13, 32, 41, 44, 46, 9, 10,
+ 43, 45, 48, 57, 46, 48, 57, 48,
+ 57, 13, 32, 44, 46, 69, 101, 9,
+ 10, 43, 45, 48, 57, 13, 32, 44,
+ 46, 9, 10, 43, 45, 48, 57, 46,
+ 48, 57, 48, 57, 13, 32, 41, 69,
+ 101, 9, 10, 48, 57, 13, 32, 41,
+ 9, 10, 43, 45, 48, 57, 48, 57,
+ 13, 32, 41, 9, 10, 48, 57, 13,
+ 32, 41, 46, 69, 101, 9, 10, 48,
+ 57, 13, 32, 43, 45, 46, 9, 10,
+ 48, 57, 43, 45, 48, 57, 48, 57,
+ 13, 32, 44, 46, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 69, 101,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 43, 45, 46, 9, 10, 48, 57, 43,
+ 45, 48, 57, 48, 57, 13, 32, 41,
+ 44, 46, 9, 10, 43, 45, 48, 57,
+ 13, 32, 41, 44, 46, 69, 101, 9,
+ 10, 43, 45, 48, 57, 99, 107, 97,
+ 108, 101, 13, 32, 40, 9, 10, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 46, 48, 57, 48, 57, 13, 32, 41,
+ 44, 46, 69, 101, 9, 10, 43, 45,
+ 48, 57, 13, 32, 41, 44, 46, 9,
+ 10, 43, 45, 48, 57, 46, 48, 57,
+ 48, 57, 13, 32, 41, 69, 101, 9,
+ 10, 48, 57, 13, 32, 41, 9, 10,
+ 43, 45, 48, 57, 48, 57, 13, 32,
+ 41, 9, 10, 48, 57, 13, 32, 41,
+ 46, 69, 101, 9, 10, 48, 57, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 43, 45, 48, 57, 48, 57, 13, 32,
+ 41, 44, 46, 9, 10, 43, 45, 48,
+ 57, 13, 32, 41, 44, 46, 69, 101,
+ 9, 10, 43, 45, 48, 57, 101, 119,
+ 88, 89, 13, 32, 40, 9, 10, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 46, 48, 57, 48, 57, 13, 32, 41,
+ 69, 101, 9, 10, 48, 57, 13, 32,
+ 41, 9, 10, 43, 45, 48, 57, 48,
+ 57, 13, 32, 41, 9, 10, 48, 57,
+ 13, 32, 41, 46, 69, 101, 9, 10,
+ 48, 57, 13, 32, 40, 9, 10, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 46, 48, 57, 48, 57, 13, 32, 41,
+ 69, 101, 9, 10, 48, 57, 13, 32,
+ 41, 9, 10, 43, 45, 48, 57, 48,
+ 57, 13, 32, 41, 9, 10, 48, 57,
+ 13, 32, 41, 46, 69, 101, 9, 10,
+ 48, 57, 114, 97, 110, 115, 108, 97,
+ 116, 101, 13, 32, 40, 9, 10, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 46, 48, 57, 48, 57, 13, 32, 41,
+ 44, 46, 69, 101, 9, 10, 43, 45,
+ 48, 57, 13, 32, 41, 44, 46, 9,
+ 10, 43, 45, 48, 57, 46, 48, 57,
+ 48, 57, 13, 32, 41, 69, 101, 9,
+ 10, 48, 57, 13, 32, 41, 9, 10,
+ 43, 45, 48, 57, 48, 57, 13, 32,
+ 41, 9, 10, 48, 57, 13, 32, 41,
+ 46, 69, 101, 9, 10, 48, 57, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 43, 45, 48, 57, 48, 57, 13, 32,
+ 41, 44, 46, 9, 10, 43, 45, 48,
+ 57, 13, 32, 41, 44, 46, 69, 101,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 109, 114, 115, 116, 9, 10, 13, 32,
+ 109, 114, 115, 116, 9, 10, 13, 32,
+ 44, 109, 114, 115, 116, 9, 10, 13,
+ 32, 44, 109, 114, 115, 116, 9, 10,
+ 13, 32, 44, 109, 114, 115, 116, 9,
+ 10, 13, 32, 44, 109, 114, 115, 116,
+ 9, 10, 13, 32, 44, 109, 114, 115,
+ 116, 9, 10, 13, 32, 44, 109, 114,
+ 115, 116, 9, 10, 13, 32, 44, 109,
+ 114, 115, 116, 9, 10, 0
+};
+
+static const char _svg_transform_single_lengths[] = {
+ 0, 1, 1, 1, 1, 1, 3, 5,
+ 1, 0, 6, 4, 1, 0, 6, 4,
+ 1, 0, 6, 4, 1, 0, 6, 4,
+ 1, 0, 6, 4, 1, 0, 5, 3,
+ 6, 1, 1, 1, 1, 1, 3, 5,
+ 1, 0, 6, 4, 1, 0, 6, 4,
+ 1, 0, 6, 4, 1, 0, 6, 4,
+ 1, 0, 6, 4, 1, 0, 5, 3,
+ 2, 0, 3, 6, 5, 2, 0, 4,
+ 6, 5, 2, 0, 4, 6, 5, 2,
+ 0, 4, 6, 5, 2, 0, 4, 6,
+ 5, 2, 0, 4, 6, 1, 1, 1,
+ 1, 1, 3, 5, 1, 0, 7, 5,
+ 2, 1, 1, 1, 3, 5, 1, 0,
+ 7, 5, 1, 1, 1, 1, 1, 1,
+ 1, 1, 3, 5, 1, 0, 7, 5,
+ 1, 0, 5, 3, 2, 0, 3, 6,
+ 5, 2, 0, 5, 7, 1, 0, 5,
+ 3, 2, 0, 3, 6, 5, 2, 0,
+ 5, 7, 1, 1, 2, 3, 5, 1,
+ 0, 5, 3, 2, 0, 3, 6, 3,
+ 5, 1, 0, 5, 3, 2, 0, 3,
+ 6, 1, 0, 6, 4, 1, 0, 5,
+ 3, 2, 0, 3, 6, 5, 2, 0,
+ 4, 6, 5, 2, 0, 5, 7, 2,
+ 0, 3, 6, 5, 2, 0, 4, 6,
+ 5, 2, 0, 4, 6, 5, 2, 0,
+ 4, 6, 5, 2, 0, 4, 6, 5,
+ 2, 0, 4, 6, 1, 1, 1, 1,
+ 1, 3, 5, 1, 0, 7, 5, 1,
+ 0, 6, 4, 1, 0, 5, 3, 2,
+ 0, 3, 6, 5, 2, 0, 4, 6,
+ 5, 2, 0, 5, 7, 2, 1, 1,
+ 1, 3, 5, 1, 0, 7, 5, 1,
+ 0, 5, 3, 2, 0, 3, 6, 5,
+ 2, 0, 5, 7, 1, 1, 2, 3,
+ 5, 1, 0, 5, 3, 2, 0, 3,
+ 6, 3, 5, 1, 0, 5, 3, 2,
+ 0, 3, 6, 1, 1, 1, 1, 1,
+ 1, 1, 1, 3, 5, 1, 0, 7,
+ 5, 1, 0, 5, 3, 2, 0, 3,
+ 6, 5, 2, 0, 5, 7, 6, 6,
+ 7, 7, 7, 7, 7, 7, 7
+};
+
+static const char _svg_transform_range_lengths[] = {
+ 0, 0, 0, 0, 0, 0, 1, 2,
+ 1, 1, 3, 3, 1, 1, 3, 3,
+ 1, 1, 3, 3, 1, 1, 3, 3,
+ 1, 1, 3, 3, 1, 1, 2, 1,
+ 1, 0, 0, 0, 0, 0, 1, 2,
+ 1, 1, 3, 3, 1, 1, 3, 3,
+ 1, 1, 3, 3, 1, 1, 3, 3,
+ 1, 1, 3, 3, 1, 1, 2, 1,
+ 1, 1, 2, 2, 2, 1, 1, 3,
+ 3, 2, 1, 1, 3, 3, 2, 1,
+ 1, 3, 3, 2, 1, 1, 3, 3,
+ 2, 1, 1, 3, 3, 0, 0, 0,
+ 0, 0, 1, 2, 1, 1, 3, 3,
+ 0, 0, 0, 0, 1, 2, 1, 1,
+ 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 1, 1, 3, 3,
+ 1, 1, 2, 1, 1, 1, 2, 2,
+ 2, 1, 1, 3, 3, 1, 1, 2,
+ 1, 1, 1, 2, 2, 2, 1, 1,
+ 3, 3, 0, 0, 0, 1, 2, 1,
+ 1, 2, 1, 1, 1, 2, 2, 1,
+ 2, 1, 1, 2, 1, 1, 1, 2,
+ 2, 1, 1, 3, 3, 1, 1, 2,
+ 1, 1, 1, 2, 2, 2, 1, 1,
+ 3, 3, 2, 1, 1, 3, 3, 1,
+ 1, 2, 2, 2, 1, 1, 3, 3,
+ 2, 1, 1, 3, 3, 2, 1, 1,
+ 3, 3, 2, 1, 1, 3, 3, 2,
+ 1, 1, 3, 3, 0, 0, 0, 0,
+ 0, 1, 2, 1, 1, 3, 3, 1,
+ 1, 3, 3, 1, 1, 2, 1, 1,
+ 1, 2, 2, 2, 1, 1, 3, 3,
+ 2, 1, 1, 3, 3, 0, 0, 0,
+ 0, 1, 2, 1, 1, 3, 3, 1,
+ 1, 2, 1, 1, 1, 2, 2, 2,
+ 1, 1, 3, 3, 0, 0, 0, 1,
+ 2, 1, 1, 2, 1, 1, 1, 2,
+ 2, 1, 2, 1, 1, 2, 1, 1,
+ 1, 2, 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 2, 1, 1, 3,
+ 3, 1, 1, 2, 1, 1, 1, 2,
+ 2, 2, 1, 1, 3, 3, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1
+};
+
+static const short _svg_transform_index_offsets[] = {
+ 0, 0, 2, 4, 6, 8, 10, 15,
+ 23, 26, 28, 38, 46, 49, 51, 61,
+ 69, 72, 74, 84, 92, 95, 97, 107,
+ 115, 118, 120, 130, 138, 141, 143, 151,
+ 156, 164, 166, 168, 170, 172, 174, 179,
+ 187, 190, 192, 202, 210, 213, 215, 225,
+ 233, 236, 238, 248, 256, 259, 261, 271,
+ 279, 282, 284, 294, 302, 305, 307, 315,
+ 320, 324, 326, 332, 341, 349, 353, 355,
+ 363, 373, 381, 385, 387, 395, 405, 413,
+ 417, 419, 427, 437, 445, 449, 451, 459,
+ 469, 477, 481, 483, 491, 501, 503, 505,
+ 507, 509, 511, 516, 524, 527, 529, 540,
+ 549, 552, 554, 556, 558, 563, 571, 574,
+ 576, 587, 596, 598, 600, 602, 604, 606,
+ 608, 610, 612, 617, 625, 628, 630, 641,
+ 650, 653, 655, 663, 668, 672, 674, 680,
+ 689, 697, 701, 703, 712, 723, 726, 728,
+ 736, 741, 745, 747, 753, 762, 770, 774,
+ 776, 785, 796, 798, 800, 803, 808, 816,
+ 819, 821, 829, 834, 838, 840, 846, 855,
+ 860, 868, 871, 873, 881, 886, 890, 892,
+ 898, 907, 910, 912, 922, 930, 933, 935,
+ 943, 948, 952, 954, 960, 969, 977, 981,
+ 983, 991, 1001, 1009, 1013, 1015, 1024, 1035,
+ 1039, 1041, 1047, 1056, 1064, 1068, 1070, 1078,
+ 1088, 1096, 1100, 1102, 1110, 1120, 1128, 1132,
+ 1134, 1142, 1152, 1160, 1164, 1166, 1174, 1184,
+ 1192, 1196, 1198, 1206, 1216, 1218, 1220, 1222,
+ 1224, 1226, 1231, 1239, 1242, 1244, 1255, 1264,
+ 1267, 1269, 1279, 1287, 1290, 1292, 1300, 1305,
+ 1309, 1311, 1317, 1326, 1334, 1338, 1340, 1348,
+ 1358, 1366, 1370, 1372, 1381, 1392, 1395, 1397,
+ 1399, 1401, 1406, 1414, 1417, 1419, 1430, 1439,
+ 1442, 1444, 1452, 1457, 1461, 1463, 1469, 1478,
+ 1486, 1490, 1492, 1501, 1512, 1514, 1516, 1519,
+ 1524, 1532, 1535, 1537, 1545, 1550, 1554, 1556,
+ 1562, 1571, 1576, 1584, 1587, 1589, 1597, 1602,
+ 1606, 1608, 1614, 1623, 1625, 1627, 1629, 1631,
+ 1633, 1635, 1637, 1639, 1644, 1652, 1655, 1657,
+ 1668, 1677, 1680, 1682, 1690, 1695, 1699, 1701,
+ 1707, 1716, 1724, 1728, 1730, 1739, 1750, 1758,
+ 1766, 1775, 1784, 1793, 1802, 1811, 1820
+};
+
+static const short _svg_transform_indicies[] = {
+ 1, 0, 2, 0, 3, 0, 4, 0,
+ 5, 0, 5, 5, 6, 5, 0, 6,
+ 6, 7, 7, 8, 6, 9, 0, 10,
+ 11, 0, 12, 0, 13, 13, 15, 16,
+ 17, 17, 13, 14, 12, 0, 18, 18,
+ 20, 21, 18, 19, 22, 0, 23, 24,
+ 0, 25, 0, 26, 26, 28, 29, 30,
+ 30, 26, 27, 25, 0, 31, 31, 33,
+ 34, 31, 32, 35, 0, 36, 37, 0,
+ 38, 0, 39, 39, 41, 42, 43, 43,
+ 39, 40, 38, 0, 44, 44, 46, 47,
+ 44, 45, 48, 0, 49, 50, 0, 51,
+ 0, 52, 52, 54, 55, 56, 56, 52,
+ 53, 51, 0, 57, 57, 59, 60, 57,
+ 58, 61, 0, 62, 63, 0, 64, 0,
+ 65, 65, 67, 68, 69, 69, 65, 66,
+ 64, 0, 70, 70, 72, 73, 70, 71,
+ 74, 0, 75, 76, 0, 77, 0, 78,
+ 78, 79, 80, 80, 78, 77, 0, 81,
+ 81, 82, 81, 0, 84, 84, 85, 86,
+ 87, 88, 84, 83, 89, 83, 90, 83,
+ 91, 83, 92, 83, 93, 83, 93, 93,
+ 94, 93, 83, 94, 94, 95, 95, 96,
+ 94, 97, 83, 98, 99, 83, 100, 83,
+ 101, 101, 103, 104, 105, 105, 101, 102,
+ 100, 83, 106, 106, 108, 109, 106, 107,
+ 110, 83, 111, 112, 83, 113, 83, 114,
+ 114, 116, 117, 118, 118, 114, 115, 113,
+ 83, 119, 119, 121, 122, 119, 120, 123,
+ 83, 124, 125, 83, 126, 83, 127, 127,
+ 129, 130, 131, 131, 127, 128, 126, 83,
+ 132, 132, 134, 135, 132, 133, 136, 83,
+ 137, 138, 83, 139, 83, 140, 140, 142,
+ 143, 144, 144, 140, 141, 139, 83, 145,
+ 145, 147, 148, 145, 146, 149, 83, 150,
+ 151, 83, 152, 83, 153, 153, 155, 156,
+ 157, 157, 153, 154, 152, 83, 158, 158,
+ 160, 161, 158, 159, 162, 83, 163, 164,
+ 83, 165, 83, 166, 166, 79, 167, 167,
+ 166, 165, 83, 168, 168, 82, 168, 83,
+ 169, 169, 170, 83, 170, 83, 166, 166,
+ 79, 166, 170, 83, 166, 166, 79, 165,
+ 167, 167, 166, 164, 83, 160, 160, 159,
+ 159, 161, 160, 162, 83, 171, 171, 172,
+ 83, 172, 83, 153, 153, 155, 156, 153,
+ 154, 172, 83, 153, 153, 155, 152, 157,
+ 157, 153, 154, 151, 83, 147, 147, 146,
+ 146, 148, 147, 149, 83, 173, 173, 174,
+ 83, 174, 83, 140, 140, 142, 143, 140,
+ 141, 174, 83, 140, 140, 142, 139, 144,
+ 144, 140, 141, 138, 83, 134, 134, 133,
+ 133, 135, 134, 136, 83, 175, 175, 176,
+ 83, 176, 83, 127, 127, 129, 130, 127,
+ 128, 176, 83, 127, 127, 129, 126, 131,
+ 131, 127, 128, 125, 83, 121, 121, 120,
+ 120, 122, 121, 123, 83, 177, 177, 178,
+ 83, 178, 83, 114, 114, 116, 117, 114,
+ 115, 178, 83, 114, 114, 116, 113, 118,
+ 118, 114, 115, 112, 83, 108, 108, 107,
+ 107, 109, 108, 110, 83, 179, 179, 180,
+ 83, 180, 83, 101, 101, 103, 104, 101,
+ 102, 180, 83, 101, 101, 103, 100, 105,
+ 105, 101, 102, 99, 83, 181, 83, 182,
+ 83, 183, 83, 184, 83, 185, 83, 185,
+ 185, 186, 185, 83, 186, 186, 187, 187,
+ 188, 186, 189, 83, 190, 191, 83, 192,
+ 83, 193, 193, 194, 196, 197, 198, 198,
+ 193, 195, 192, 83, 199, 199, 200, 202,
+ 203, 199, 201, 204, 83, 205, 206, 83,
+ 207, 83, 208, 83, 209, 83, 209, 209,
+ 210, 209, 83, 210, 210, 211, 211, 212,
+ 210, 213, 83, 214, 215, 83, 216, 83,
+ 217, 217, 218, 220, 221, 222, 222, 217,
+ 219, 216, 83, 223, 223, 224, 226, 227,
+ 223, 225, 228, 83, 229, 83, 230, 83,
+ 231, 83, 232, 83, 233, 83, 234, 83,
+ 235, 83, 236, 83, 236, 236, 237, 236,
+ 83, 237, 237, 238, 238, 239, 237, 240,
+ 83, 241, 242, 83, 243, 83, 244, 244,
+ 245, 247, 248, 249, 249, 244, 246, 243,
+ 83, 250, 250, 251, 253, 254, 250, 252,
+ 255, 83, 256, 257, 83, 258, 83, 259,
+ 259, 245, 260, 260, 259, 258, 83, 261,
+ 261, 251, 261, 83, 262, 262, 263, 83,
+ 263, 83, 259, 259, 245, 259, 263, 83,
+ 259, 259, 245, 258, 260, 260, 259, 257,
+ 83, 253, 253, 252, 252, 254, 253, 255,
+ 83, 264, 264, 265, 83, 265, 83, 244,
+ 244, 245, 247, 248, 244, 246, 265, 83,
+ 244, 244, 245, 247, 243, 249, 249, 244,
+ 246, 242, 83, 266, 267, 83, 268, 83,
+ 269, 269, 218, 270, 270, 269, 268, 83,
+ 271, 271, 224, 271, 83, 272, 272, 273,
+ 83, 273, 83, 269, 269, 218, 269, 273,
+ 83, 269, 269, 218, 268, 270, 270, 269,
+ 267, 83, 226, 226, 225, 225, 227, 226,
+ 228, 83, 274, 274, 275, 83, 275, 83,
+ 217, 217, 218, 220, 221, 217, 219, 275,
+ 83, 217, 217, 218, 220, 216, 222, 222,
+ 217, 219, 215, 83, 276, 83, 277, 83,
+ 278, 279, 83, 278, 278, 280, 278, 83,
+ 280, 280, 281, 281, 282, 280, 283, 83,
+ 284, 285, 83, 286, 83, 287, 287, 288,
+ 289, 289, 287, 286, 83, 290, 290, 291,
+ 290, 83, 292, 292, 293, 83, 293, 83,
+ 287, 287, 288, 287, 293, 83, 287, 287,
+ 288, 286, 289, 289, 287, 285, 83, 279,
+ 279, 294, 279, 83, 294, 294, 295, 295,
+ 296, 294, 297, 83, 298, 299, 83, 300,
+ 83, 301, 301, 302, 303, 303, 301, 300,
+ 83, 304, 304, 305, 304, 83, 306, 306,
+ 307, 83, 307, 83, 301, 301, 302, 301,
+ 307, 83, 301, 301, 302, 300, 303, 303,
+ 301, 299, 83, 308, 309, 83, 310, 83,
+ 311, 311, 313, 314, 315, 315, 311, 312,
+ 310, 83, 316, 316, 318, 319, 316, 317,
+ 320, 83, 321, 322, 83, 323, 83, 324,
+ 324, 194, 325, 325, 324, 323, 83, 326,
+ 326, 200, 326, 83, 327, 327, 328, 83,
+ 328, 83, 324, 324, 194, 324, 328, 83,
+ 324, 324, 194, 323, 325, 325, 324, 322,
+ 83, 318, 318, 317, 317, 319, 318, 320,
+ 83, 329, 329, 330, 83, 330, 83, 311,
+ 311, 313, 314, 311, 312, 330, 83, 311,
+ 311, 313, 310, 315, 315, 311, 312, 309,
+ 83, 202, 202, 201, 201, 203, 202, 204,
+ 83, 331, 331, 332, 83, 332, 83, 193,
+ 193, 194, 196, 197, 193, 195, 332, 83,
+ 193, 193, 194, 196, 192, 198, 198, 193,
+ 195, 191, 83, 333, 333, 334, 0, 334,
+ 0, 78, 78, 79, 78, 334, 0, 78,
+ 78, 79, 77, 80, 80, 78, 76, 0,
+ 72, 72, 71, 71, 73, 72, 74, 0,
+ 335, 335, 336, 0, 336, 0, 65, 65,
+ 67, 68, 65, 66, 336, 0, 65, 65,
+ 67, 64, 69, 69, 65, 66, 63, 0,
+ 59, 59, 58, 58, 60, 59, 61, 0,
+ 337, 337, 338, 0, 338, 0, 52, 52,
+ 54, 55, 52, 53, 338, 0, 52, 52,
+ 54, 51, 56, 56, 52, 53, 50, 0,
+ 46, 46, 45, 45, 47, 46, 48, 0,
+ 339, 339, 340, 0, 340, 0, 39, 39,
+ 41, 42, 39, 40, 340, 0, 39, 39,
+ 41, 38, 43, 43, 39, 40, 37, 0,
+ 33, 33, 32, 32, 34, 33, 35, 0,
+ 341, 341, 342, 0, 342, 0, 26, 26,
+ 28, 29, 26, 27, 342, 0, 26, 26,
+ 28, 25, 30, 30, 26, 27, 24, 0,
+ 20, 20, 19, 19, 21, 20, 22, 0,
+ 343, 343, 344, 0, 344, 0, 13, 13,
+ 15, 16, 13, 14, 344, 0, 13, 13,
+ 15, 12, 17, 17, 13, 14, 11, 0,
+ 345, 0, 346, 0, 347, 0, 348, 0,
+ 349, 0, 349, 349, 350, 349, 0, 350,
+ 350, 351, 351, 352, 350, 353, 0, 354,
+ 355, 0, 356, 0, 357, 357, 194, 359,
+ 360, 361, 361, 357, 358, 356, 0, 362,
+ 362, 200, 364, 365, 362, 363, 366, 0,
+ 367, 368, 0, 369, 0, 370, 370, 372,
+ 373, 374, 374, 370, 371, 369, 0, 375,
+ 375, 377, 378, 375, 376, 379, 0, 380,
+ 381, 0, 382, 0, 383, 383, 194, 384,
+ 384, 383, 382, 0, 385, 385, 200, 385,
+ 0, 386, 386, 387, 0, 387, 0, 383,
+ 383, 194, 383, 387, 0, 383, 383, 194,
+ 382, 384, 384, 383, 381, 0, 377, 377,
+ 376, 376, 378, 377, 379, 0, 388, 388,
+ 389, 0, 389, 0, 370, 370, 372, 373,
+ 370, 371, 389, 0, 370, 370, 372, 369,
+ 374, 374, 370, 371, 368, 0, 364, 364,
+ 363, 363, 365, 364, 366, 0, 390, 390,
+ 391, 0, 391, 0, 357, 357, 194, 359,
+ 360, 357, 358, 391, 0, 357, 357, 194,
+ 359, 356, 361, 361, 357, 358, 355, 0,
+ 392, 393, 0, 394, 0, 395, 0, 396,
+ 0, 396, 396, 397, 396, 0, 397, 397,
+ 398, 398, 399, 397, 400, 0, 401, 402,
+ 0, 403, 0, 404, 404, 218, 406, 407,
+ 408, 408, 404, 405, 403, 0, 409, 409,
+ 224, 411, 412, 409, 410, 413, 0, 414,
+ 415, 0, 416, 0, 417, 417, 218, 418,
+ 418, 417, 416, 0, 419, 419, 224, 419,
+ 0, 420, 420, 421, 0, 421, 0, 417,
+ 417, 218, 417, 421, 0, 417, 417, 218,
+ 416, 418, 418, 417, 415, 0, 411, 411,
+ 410, 410, 412, 411, 413, 0, 422, 422,
+ 423, 0, 423, 0, 404, 404, 218, 406,
+ 407, 404, 405, 423, 0, 404, 404, 218,
+ 406, 403, 408, 408, 404, 405, 402, 0,
+ 424, 0, 425, 0, 426, 427, 0, 426,
+ 426, 428, 426, 0, 428, 428, 429, 429,
+ 430, 428, 431, 0, 432, 433, 0, 434,
+ 0, 435, 435, 288, 436, 436, 435, 434,
+ 0, 437, 437, 291, 437, 0, 438, 438,
+ 439, 0, 439, 0, 435, 435, 288, 435,
+ 439, 0, 435, 435, 288, 434, 436, 436,
+ 435, 433, 0, 427, 427, 440, 427, 0,
+ 440, 440, 441, 441, 442, 440, 443, 0,
+ 444, 445, 0, 446, 0, 447, 447, 302,
+ 448, 448, 447, 446, 0, 449, 449, 305,
+ 449, 0, 450, 450, 451, 0, 451, 0,
+ 447, 447, 302, 447, 451, 0, 447, 447,
+ 302, 446, 448, 448, 447, 445, 0, 452,
+ 0, 453, 0, 454, 0, 455, 0, 456,
+ 0, 457, 0, 458, 0, 459, 0, 459,
+ 459, 460, 459, 0, 460, 460, 461, 461,
+ 462, 460, 463, 0, 464, 465, 0, 466,
+ 0, 467, 467, 245, 469, 470, 471, 471,
+ 467, 468, 466, 0, 472, 472, 251, 474,
+ 475, 472, 473, 476, 0, 477, 478, 0,
+ 479, 0, 480, 480, 245, 481, 481, 480,
+ 479, 0, 482, 482, 251, 482, 0, 483,
+ 483, 484, 0, 484, 0, 480, 480, 245,
+ 480, 484, 0, 480, 480, 245, 479, 481,
+ 481, 480, 478, 0, 474, 474, 473, 473,
+ 475, 474, 476, 0, 485, 485, 486, 0,
+ 486, 0, 467, 467, 245, 469, 470, 467,
+ 468, 486, 0, 467, 467, 245, 469, 466,
+ 471, 471, 467, 468, 465, 0, 487, 487,
+ 489, 490, 491, 492, 487, 488, 487, 487,
+ 489, 490, 491, 492, 487, 0, 494, 494,
+ 495, 496, 497, 498, 499, 494, 493, 501,
+ 501, 84, 85, 86, 87, 88, 501, 500,
+ 503, 503, 504, 505, 506, 507, 508, 503,
+ 502, 510, 510, 511, 512, 513, 514, 515,
+ 510, 509, 517, 517, 518, 519, 520, 521,
+ 522, 517, 516, 524, 524, 525, 526, 527,
+ 528, 529, 524, 523, 531, 531, 532, 533,
+ 534, 535, 536, 531, 530, 0
+};
+
+static const short _svg_transform_trans_targs[] = {
+ 334, 2, 3, 4, 5, 6, 7, 8,
+ 9, 227, 9, 227, 10, 11, 12, 223,
+ 13, 224, 11, 12, 223, 13, 222, 13,
+ 222, 14, 15, 16, 218, 17, 219, 15,
+ 16, 218, 17, 217, 17, 217, 18, 19,
+ 20, 213, 21, 214, 19, 20, 213, 21,
+ 212, 21, 212, 22, 23, 24, 208, 25,
+ 209, 23, 24, 208, 25, 207, 25, 207,
+ 26, 27, 28, 203, 29, 204, 27, 28,
+ 203, 29, 202, 29, 202, 30, 31, 336,
+ 199, 31, 336, 334, 32, 33, 93, 104,
+ 114, 34, 35, 36, 37, 38, 39, 40,
+ 41, 92, 41, 92, 42, 43, 44, 88,
+ 45, 89, 43, 44, 88, 45, 87, 45,
+ 87, 46, 47, 48, 83, 49, 84, 47,
+ 48, 83, 49, 82, 49, 82, 50, 51,
+ 52, 78, 53, 79, 51, 52, 78, 53,
+ 77, 53, 77, 54, 55, 56, 73, 57,
+ 74, 55, 56, 73, 57, 72, 57, 72,
+ 58, 59, 60, 68, 61, 69, 59, 60,
+ 68, 61, 67, 61, 67, 62, 63, 64,
+ 63, 65, 66, 70, 71, 75, 76, 80,
+ 81, 85, 86, 90, 91, 94, 95, 96,
+ 97, 98, 99, 100, 101, 198, 101, 198,
+ 102, 103, 338, 177, 194, 178, 195, 103,
+ 338, 177, 194, 178, 193, 105, 154, 106,
+ 107, 108, 109, 110, 111, 153, 111, 153,
+ 112, 113, 339, 141, 149, 142, 150, 113,
+ 339, 141, 149, 142, 148, 115, 116, 117,
+ 118, 119, 120, 121, 122, 123, 124, 125,
+ 140, 125, 140, 126, 127, 340, 128, 136,
+ 129, 137, 127, 340, 128, 136, 129, 135,
+ 129, 135, 130, 131, 132, 131, 133, 134,
+ 138, 139, 142, 148, 143, 144, 145, 144,
+ 146, 147, 151, 152, 155, 156, 157, 167,
+ 158, 159, 160, 166, 160, 166, 161, 162,
+ 341, 163, 162, 341, 164, 165, 168, 169,
+ 170, 176, 170, 176, 171, 172, 342, 173,
+ 172, 342, 174, 175, 178, 193, 179, 180,
+ 181, 189, 182, 190, 180, 181, 189, 182,
+ 188, 182, 188, 183, 184, 185, 184, 186,
+ 187, 191, 192, 196, 197, 200, 201, 205,
+ 206, 210, 211, 215, 216, 220, 221, 225,
+ 226, 229, 230, 231, 232, 233, 234, 235,
+ 236, 260, 236, 260, 237, 238, 239, 256,
+ 240, 257, 238, 239, 256, 240, 255, 240,
+ 255, 241, 242, 243, 251, 244, 252, 242,
+ 243, 251, 244, 250, 244, 250, 245, 246,
+ 247, 246, 248, 249, 253, 254, 258, 259,
+ 262, 284, 263, 264, 265, 266, 267, 268,
+ 283, 268, 283, 269, 270, 271, 279, 272,
+ 280, 270, 271, 279, 272, 278, 272, 278,
+ 273, 274, 275, 274, 276, 277, 281, 282,
+ 285, 286, 287, 297, 288, 289, 290, 296,
+ 290, 296, 291, 292, 293, 292, 294, 295,
+ 298, 299, 300, 306, 300, 306, 301, 302,
+ 303, 302, 304, 305, 308, 309, 310, 311,
+ 312, 313, 314, 315, 316, 317, 318, 333,
+ 318, 333, 319, 320, 321, 329, 322, 330,
+ 320, 321, 329, 322, 328, 322, 328, 323,
+ 324, 325, 324, 326, 327, 331, 332, 335,
+ 0, 1, 228, 261, 307, 334, 337, 32,
+ 33, 93, 104, 114, 334, 337, 334, 337,
+ 32, 33, 93, 104, 114, 334, 337, 32,
+ 33, 93, 104, 114, 334, 337, 32, 33,
+ 93, 104, 114, 334, 337, 32, 33, 93,
+ 104, 114, 334, 337, 32, 33, 93, 104,
+ 114
+};
+
+static const char _svg_transform_trans_actions[] = {
+ 13, 0, 0, 0, 0, 0, 0, 3,
+ 3, 3, 0, 0, 0, 1, 15, 1,
+ 15, 0, 0, 3, 0, 3, 3, 0,
+ 0, 0, 1, 15, 1, 15, 0, 0,
+ 3, 0, 3, 3, 0, 0, 0, 1,
+ 15, 1, 15, 0, 0, 3, 0, 3,
+ 3, 0, 0, 0, 1, 15, 1, 15,
+ 0, 0, 3, 0, 3, 3, 0, 0,
+ 0, 1, 15, 1, 15, 0, 0, 3,
+ 0, 3, 3, 0, 0, 0, 1, 39,
+ 0, 0, 7, 11, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 3,
+ 3, 3, 0, 0, 0, 1, 15, 1,
+ 15, 0, 0, 3, 0, 3, 3, 0,
+ 0, 0, 1, 15, 1, 15, 0, 0,
+ 3, 0, 3, 3, 0, 0, 0, 1,
+ 15, 1, 15, 0, 0, 3, 0, 3,
+ 3, 0, 0, 0, 1, 15, 1, 15,
+ 0, 0, 3, 0, 3, 3, 0, 0,
+ 0, 1, 15, 1, 15, 0, 0, 3,
+ 0, 3, 3, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 3, 3, 0, 0,
+ 0, 1, 39, 15, 1, 15, 0, 0,
+ 7, 3, 0, 3, 3, 0, 0, 0,
+ 0, 0, 0, 3, 3, 3, 0, 0,
+ 0, 1, 39, 15, 1, 15, 0, 0,
+ 7, 3, 0, 3, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 3, 3,
+ 3, 0, 0, 0, 1, 39, 15, 1,
+ 15, 0, 0, 7, 3, 0, 3, 3,
+ 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 0, 0, 0, 1,
+ 39, 0, 0, 7, 0, 0, 0, 3,
+ 3, 3, 0, 0, 0, 1, 39, 0,
+ 0, 7, 0, 0, 0, 0, 0, 1,
+ 15, 1, 15, 0, 0, 3, 0, 3,
+ 3, 0, 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 3,
+ 3, 3, 0, 0, 0, 1, 15, 1,
+ 15, 0, 0, 3, 0, 3, 3, 0,
+ 0, 0, 1, 15, 1, 15, 0, 0,
+ 3, 0, 3, 3, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 3, 3,
+ 3, 0, 0, 0, 1, 15, 1, 15,
+ 0, 0, 3, 0, 3, 3, 0, 0,
+ 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3, 3, 3,
+ 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 3, 3, 3, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3, 3, 3,
+ 0, 0, 0, 1, 15, 1, 15, 0,
+ 0, 3, 0, 3, 3, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 42,
+ 0, 0, 0, 0, 0, 65, 89, 33,
+ 33, 33, 33, 33, 9, 7, 53, 77,
+ 24, 24, 24, 24, 24, 49, 73, 21,
+ 21, 21, 21, 21, 45, 69, 18, 18,
+ 18, 18, 18, 57, 81, 27, 27, 27,
+ 27, 27, 61, 85, 30, 30, 30, 30,
+ 30
+};
+
+static const char _svg_transform_to_state_actions[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 36, 0,
+ 0, 0, 0, 0, 0, 0, 0
+};
+
+static const char _svg_transform_from_state_actions[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0, 0
+};
+
+static const short _svg_transform_eof_trans[] = {
+ 0, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 0, 1,
+ 494, 501, 503, 510, 517, 524, 531
+};
+
+static const int svg_transform_start = 334;
+static const int svg_transform_first_final = 334;
+
+static const int svg_transform_en_main = 334;
+
+
+#line 28 "svg-affine-parser.rl"
+
+
+// https://www.w3.org/TR/css-transforms-1/#svg-syntax
+bool sp_svg_transform_read(gchar const *str, Geom::Affine *transform)
+{
+ if (str == nullptr)
+ return false;
+
+ std::vector<double> params;
+ Geom::Affine final_transform (Geom::identity());
+ *transform = final_transform;
+ Geom::Affine tmp_transform (Geom::identity());
+ int cs;
+ const char *p = str;
+ const char *pe = p + strlen(p) + 1;
+ const char *eof = pe;
+ char const *start_num = 0;
+ char const *ts = p;
+ char const *te = pe;
+ int act = 0;
+ if (pe == p+1) return true; // ""
+
+
+#line 1023 "svg-affine-parser.cpp"
+ {
+ cs = svg_transform_start;
+ ts = 0;
+ te = 0;
+ act = 0;
+ }
+
+#line 1031 "svg-affine-parser.cpp"
+ {
+ int _klen;
+ unsigned int _trans;
+ const char *_acts;
+ unsigned int _nacts;
+ const char *_keys;
+
+ if ( p == pe )
+ goto _test_eof;
+ if ( cs == 0 )
+ goto _out;
+_resume:
+ _acts = _svg_transform_actions + _svg_transform_from_state_actions[cs];
+ _nacts = (unsigned int) *_acts++;
+ while ( _nacts-- > 0 ) {
+ switch ( *_acts++ ) {
+ case 11:
+#line 1 "NONE"
+ {ts = p;}
+ break;
+#line 1052 "svg-affine-parser.cpp"
+ }
+ }
+
+ _keys = _svg_transform_trans_keys + _svg_transform_key_offsets[cs];
+ _trans = _svg_transform_index_offsets[cs];
+
+ _klen = _svg_transform_single_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + _klen - 1;
+ while (true) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + ((_upper-_lower) >> 1);
+ if ( (*p) < *_mid )
+ _upper = _mid - 1;
+ else if ( (*p) > *_mid )
+ _lower = _mid + 1;
+ else {
+ _trans += (unsigned int)(_mid - _keys);
+ goto _match;
+ }
+ }
+ _keys += _klen;
+ _trans += _klen;
+ }
+
+ _klen = _svg_transform_range_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + (_klen<<1) - 2;
+ while (true) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + (((_upper-_lower) >> 1) & ~1);
+ if ( (*p) < _mid[0] )
+ _upper = _mid - 2;
+ else if ( (*p) > _mid[1] )
+ _lower = _mid + 2;
+ else {
+ _trans += (unsigned int)((_mid - _keys)>>1);
+ goto _match;
+ }
+ }
+ _trans += _klen;
+ }
+
+_match:
+ _trans = _svg_transform_indicies[_trans];
+_eof_trans:
+ cs = _svg_transform_trans_targs[_trans];
+
+ if ( _svg_transform_trans_actions[_trans] == 0 )
+ goto _again;
+
+ _acts = _svg_transform_actions + _svg_transform_trans_actions[_trans];
+ _nacts = (unsigned int) *_acts++;
+ while ( _nacts-- > 0 )
+ {
+ switch ( *_acts++ )
+ {
+ case 0:
+#line 53 "svg-affine-parser.rl"
+ {
+ std::string buf(start_num, p);
+ params.push_back(g_ascii_strtod(buf.c_str(), NULL));
+ }
+ break;
+ case 1:
+#line 58 "svg-affine-parser.rl"
+ { tmp_transform = Geom::Translate(params[0], params.size() == 1 ? 0 : params[1]); }
+ break;
+ case 2:
+#line 59 "svg-affine-parser.rl"
+ { tmp_transform = Geom::Scale(params[0], params.size() == 1 ? params[0] : params[1]); }
+ break;
+ case 3:
+#line 60 "svg-affine-parser.rl"
+ {
+ if (params.size() == 1)
+ tmp_transform = Geom::Rotate(Geom::rad_from_deg(params[0]));
+ else {
+ tmp_transform = Geom::Translate(-params[1], -params[2]) *
+ Geom::Rotate(Geom::rad_from_deg(params[0])) *
+ Geom::Translate(params[1], params[2]);
+ }
+ }
+ break;
+ case 4:
+#line 69 "svg-affine-parser.rl"
+ { tmp_transform = Geom::Affine(1, 0, tan(params[0] * M_PI / 180.0), 1, 0, 0); }
+ break;
+ case 5:
+#line 70 "svg-affine-parser.rl"
+ { tmp_transform = Geom::Affine(1, tan(params[0] * M_PI / 180.0), 0, 1, 0, 0); }
+ break;
+ case 6:
+#line 71 "svg-affine-parser.rl"
+ { tmp_transform = Geom::Affine(params[0], params[1], params[2], params[3], params[4], params[5]);}
+ break;
+ case 7:
+#line 72 "svg-affine-parser.rl"
+ {params.clear(); final_transform = tmp_transform * final_transform ;}
+ break;
+ case 8:
+#line 87 "svg-affine-parser.rl"
+ {start_num = p;}
+ break;
+ case 12:
+#line 1 "NONE"
+ {te = p+1;}
+ break;
+ case 13:
+#line 73 "svg-affine-parser.rl"
+ {act = 1;}
+ break;
+ case 14:
+#line 73 "svg-affine-parser.rl"
+ {te = p;p--;{ *transform = final_transform; /*printf("%p %p %p %p
+%d\n",p, pe, ts, te, cs);*/ return (te+1 == pe);}}
+ break;
+ case 15:
+#line 73 "svg-affine-parser.rl"
+ {{p = ((te))-1;}{ *transform = final_transform; /*printf("%p %p %p %p
+%d\n",p, pe, ts, te, cs);*/ return (te+1 == pe);}}
+ break;
+ case 16:
+#line 1 "NONE"
+ { switch( act ) {
+ case 0:
+ {{cs = 0;goto _again;}}
+ break;
+ case 1:
+ {{p = ((te))-1;} *transform = final_transform; /*printf("%p %p %p %p
+%d\n",p, pe, ts, te, cs);*/ return (te+1 == pe);}
+ break;
+ }
+ }
+ break;
+#line 1196 "svg-affine-parser.cpp"
+ }
+ }
+
+_again:
+ _acts = _svg_transform_actions + _svg_transform_to_state_actions[cs];
+ _nacts = (unsigned int) *_acts++;
+ while ( _nacts-- > 0 ) {
+ switch ( *_acts++ ) {
+ case 9:
+#line 1 "NONE"
+ {ts = 0;}
+ break;
+ case 10:
+#line 1 "NONE"
+ {act = 0;}
+ break;
+#line 1213 "svg-affine-parser.cpp"
+ }
+ }
+
+ if ( cs == 0 )
+ goto _out;
+ if ( ++p != pe )
+ goto _resume;
+ _test_eof: {}
+ if ( p == eof )
+ {
+ if ( _svg_transform_eof_trans[cs] > 0 ) {
+ _trans = _svg_transform_eof_trans[cs] - 1;
+ goto _eof_trans;
+ }
+ }
+
+ _out: {}
+ }
+
+#line 117 "svg-affine-parser.rl"
+
+ g_warning("could not parse transform attribute");
+
+ return false;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=ragel:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg-affine-parser.rl b/src/svg/svg-affine-parser.rl
new file mode 100644
index 0000000..616ff44
--- /dev/null
+++ b/src/svg/svg-affine-parser.rl
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SVG data parser
+ *
+ * Authors:
+ * Marc Jeanmougin <marc.jeanmougin@telecom-paris.fr>
+ *
+ * Copyright (C) 2019 Marc Jeanmougin (.rl parser, CSS-transform spec)
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ *
+ * THE CPP FILE IS GENERATED FROM THE RL FILE, DO NOT EDIT THE CPP
+ *
+ * To generate it, run
+ * ragel svg-affine-parser.rl -o svg-affine-parser.cpp
+ * sed -Ei 's/\(1\)/(true)/' svg-affine-parser.cpp
+ */
+
+#include <string>
+#include <glib.h>
+#include <2geom/transforms.h>
+#include "svg.h"
+#include "preferences.h"
+
+%%{
+ machine svg_transform;
+ write data noerror;
+}%%
+
+// https://www.w3.org/TR/css-transforms-1/#svg-syntax
+bool sp_svg_transform_read(gchar const *str, Geom::Affine *transform)
+{
+ if (str == nullptr)
+ return false;
+
+ std::vector<double> params;
+ Geom::Affine final_transform (Geom::identity());
+ *transform = final_transform;
+ Geom::Affine tmp_transform (Geom::identity());
+ int cs;
+ const char *p = str;
+ const char *pe = p + strlen(p) + 1;
+ const char *eof = pe;
+ char const *start_num = 0;
+ char const *ts = p;
+ char const *te = pe;
+ int act = 0;
+ if (pe == p+1) return true; // ""
+
+%%{
+ write init;
+
+ action number {
+ std::string buf(start_num, p);
+ params.push_back(g_ascii_strtod(buf.c_str(), NULL));
+ }
+
+ action translate { tmp_transform = Geom::Translate(params[0], params.size() == 1 ? 0 : params[1]); }
+ action scale { tmp_transform = Geom::Scale(params[0], params.size() == 1 ? params[0] : params[1]); }
+ action rotate {
+ if (params.size() == 1)
+ tmp_transform = Geom::Rotate(Geom::rad_from_deg(params[0]));
+ else {
+ tmp_transform = Geom::Translate(-params[1], -params[2]) *
+ Geom::Rotate(Geom::rad_from_deg(params[0])) *
+ Geom::Translate(params[1], params[2]);
+ }
+ }
+ action skewX { tmp_transform = Geom::Affine(1, 0, tan(params[0] * M_PI / 180.0), 1, 0, 0); }
+ action skewY { tmp_transform = Geom::Affine(1, tan(params[0] * M_PI / 180.0), 0, 1, 0, 0); }
+ action matrix { tmp_transform = Geom::Affine(params[0], params[1], params[2], params[3], params[4], params[5]);}
+ action transform {params.clear(); final_transform = tmp_transform * final_transform ;}
+ action transformlist { *transform = final_transform; /*printf("%p %p %p %p
+%d\n",p, pe, ts, te, cs);*/ return (te+1 == pe);}
+
+ comma = ',';
+ wsp = ( 10 | 13 | 9 | ' ');
+ sign = ('+' | '-');
+ digit_sequence = digit+;
+ exponent = ('e' | 'E') sign? digit_sequence;
+ fractional_constant = digit_sequence? '.' digit_sequence
+ | digit_sequence '.';
+ floating_point_constant = fractional_constant exponent?
+ | digit_sequence exponent;
+ integer_constant = digit_sequence;
+ number = ( sign? integer_constant | sign? floating_point_constant )
+ > {start_num = p;}
+ %number;
+
+ commawsp = (wsp+ comma? wsp*) | (comma wsp*);
+ translate = "translate" wsp* "(" wsp* number <: ( commawsp? number )? wsp* ")"
+ %translate;
+ scale = "scale" wsp* "(" wsp* number <: ( commawsp? number )? wsp* ")"
+ %scale;
+ rotate = "rotate" wsp* "(" wsp* number <: ( commawsp? number <: commawsp? number )? wsp* ")"
+ %rotate;
+ skewX = "skewX" wsp* "(" wsp* number wsp* ")"
+ %skewX;
+ skewY = "skewY" wsp* "(" wsp* number wsp* ")"
+ %skewY;
+ matrix = "matrix" wsp* "(" wsp*
+ number <: commawsp?
+ number <: commawsp?
+ number <: commawsp?
+ number <: commawsp?
+ number <: commawsp?
+ number wsp* ")"
+ %matrix;
+ transform = (matrix | translate | scale | rotate | skewX | skewY)
+ %transform;
+ transforms = transform ( commawsp? transform )**;
+ transformlist = wsp* transforms? wsp*;
+ main := |*
+ transformlist => transformlist;
+ *|;
+ write exec;
+}%%
+ g_warning("could not parse transform attribute");
+
+ return false;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=ragel:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg-affine.cpp b/src/svg/svg-affine.cpp
new file mode 100644
index 0000000..336eeca
--- /dev/null
+++ b/src/svg/svg-affine.cpp
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SVG data parser
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Raph Levien <raph@acm.org>
+ *
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ * Copyright (C) 1999 Raph Levien
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cstring>
+#include <string>
+#include <cstdlib>
+#include <cstdio>
+#include <glib.h>
+#include <2geom/transforms.h>
+#include "svg.h"
+#include "preferences.h"
+
+std::string
+sp_svg_transform_write(Geom::Affine const &transform)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+ // this must be a bit grater than EPSILON
+ double e = 1e-5 * transform.descrim();
+ int prec = prefs->getInt("/options/svgoutput/numericprecision", 8);
+ int min_exp = prefs->getInt("/options/svgoutput/minimumexponent", -8);
+
+ // Special case: when all fields of the affine are zero,
+ // the optimized transformation is scale(0)
+ if (transform[0] == 0 && transform[1] == 0 && transform[2] == 0 &&
+ transform[3] == 0 && transform[4] == 0 && transform[5] == 0)
+ {
+ return "scale(0)";
+ }
+
+
+ std::stringstream c(""); // string buffer
+
+ if (transform.isIdentity()) {
+ // We are more or less identity, so no transform attribute needed:
+ return {};
+ } else if (transform.isScale()) {
+ // We are more or less a uniform scale
+ c << "scale(";
+ c << sp_svg_number_write_de(transform[0], prec, min_exp);
+ if (Geom::are_near(transform[0], transform[3], e)) {
+ c << ")";
+ } else {
+ c << ",";
+ c << sp_svg_number_write_de(transform[3], prec, min_exp);
+ c << ")";
+ }
+ } else if (transform.isTranslation()) {
+ // We are more or less a pure translation
+ c << "translate(";
+ c << sp_svg_number_write_de(transform[4], prec, min_exp);
+ if (Geom::are_near(transform[5], 0.0, e)) {
+ c << ")";
+ } else {
+ c << ",";
+ c << sp_svg_number_write_de(transform[5], prec, min_exp);
+ c << ")";
+ }
+ } else if (transform.isRotation()) {
+ // We are more or less a pure rotation
+ c << "rotate(";
+ double angle = std::atan2(transform[1], transform[0]) * (180 / M_PI);
+ c << sp_svg_number_write_de(angle, prec, min_exp);
+ c << ")";
+ } else if (transform.withoutTranslation().isRotation()) {
+ // Solution found by Johan Engelen
+ // Refer to the matrix in svg-affine-test.h
+
+ // We are a rotation about a special axis
+ c << "rotate(";
+ double angle = std::atan2(transform[1], transform[0]) * (180 / M_PI);
+ c << sp_svg_number_write_de(angle, prec, min_exp);
+ c << ",";
+
+ Geom::Affine const& m = transform;
+ double tx = (m[2]*m[5]+m[4]-m[4]*m[3]) / (1-m[3]-m[0]+m[0]*m[3]-m[2]*m[1]);
+
+ c << sp_svg_number_write_de(tx, prec, min_exp);
+ c << ",";
+
+ double ty = (m[1]*tx + m[5]) / (1 - m[3]);
+ c << sp_svg_number_write_de(ty, prec, min_exp);
+ c << ")";
+ } else if (transform.isHShear()) {
+ // We are more or less a pure skewX
+ c << "skewX(";
+ double angle = atan(transform[2]) * (180 / M_PI);
+ c << sp_svg_number_write_de(angle, prec, min_exp);
+ c << ")";
+ } else if (transform.isVShear()) {
+ // We are more or less a pure skewY
+ c << "skewY(";
+ double angle = atan(transform[1]) * (180 / M_PI);
+
+ c << sp_svg_number_write_de(angle, prec, min_exp);
+ c << ")";
+ } else {
+ c << "matrix(";
+ c << sp_svg_number_write_de(transform[0], prec, min_exp);
+ c << ",";
+ c << sp_svg_number_write_de(transform[1], prec, min_exp);
+ c << ",";
+ c << sp_svg_number_write_de(transform[2], prec, min_exp);
+ c << ",";
+ c << sp_svg_number_write_de(transform[3], prec, min_exp);
+ c << ",";
+ c << sp_svg_number_write_de(transform[4], prec, min_exp);
+ c << ",";
+ c << sp_svg_number_write_de(transform[5], prec, min_exp);
+ c << ")";
+ }
+
+ assert(c.str().length() <= 256);
+ return c.str();
+
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg-angle.cpp b/src/svg/svg-angle.cpp
new file mode 100644
index 0000000..0e4af4f
--- /dev/null
+++ b/src/svg/svg-angle.cpp
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * \file src/svg/svg-angle.cpp
+ * \brief SVG angle type
+ */
+/*
+ * Authors:
+ * Tomasz Boczkowski <penginsbacon@gmail.com>
+ *
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cstring>
+#include <string>
+#include <glib.h>
+
+#include "svg/svg-angle.h"
+#include "util/units.h"
+
+
+static bool sp_svg_angle_read_lff(gchar const *str, SVGAngle::Unit &unit, float &val, float &computed);
+
+SVGAngle::SVGAngle()
+ : _set(false)
+ , unit(Unit::NONE)
+ , value(0)
+ , computed(0)
+{
+}
+
+/* Angle */
+
+bool SVGAngle::read(gchar const *str)
+{
+ if (!str) {
+ return false;
+ }
+
+ SVGAngle::Unit u;
+ float v;
+ float c;
+ if (!sp_svg_angle_read_lff(str, u, v, c)) {
+ return false;
+ }
+
+ _set = true;
+ unit = u;
+ value = v;
+ computed = c;
+
+ return true;
+}
+
+void SVGAngle::unset(SVGAngle::Unit u, float v, float c) {
+ _set = false;
+ unit = u;
+ value = v;
+ computed = c;
+}
+
+void SVGAngle::readOrUnset(gchar const *str, Unit u, float v, float c) {
+ if (!read(str)) {
+ unset(u, v, c);
+ }
+}
+
+static bool sp_svg_angle_read_lff(gchar const *str, SVGAngle::Unit &unit, float &val, float &computed)
+{
+ if (!str) {
+ return false;
+ }
+
+ gchar const *e;
+ float const v = g_ascii_strtod(str, (char **) &e);
+ if (e == str) {
+ return false;
+ }
+
+ if (!e[0]) {
+ /* Unitless (defaults to degrees)*/
+ unit = SVGAngle::Unit::NONE;
+ val = v;
+ computed = v;
+ return true;
+ } else if (!g_ascii_isalnum(e[0])) {
+ if (g_ascii_isspace(e[0]) && e[1] && g_ascii_isalpha(e[1])) {
+ return false; // spaces between value and unit are not allowed
+ } else {
+ /* Unitless (defaults to degrees)*/
+ unit = SVGAngle::Unit::NONE;
+ val = v;
+ computed = v;
+ return true;
+ }
+ } else {
+ if (strncmp(e, "deg", 3) == 0) {
+ unit = SVGAngle::Unit::DEG;
+ val = v;
+ computed = v;
+ } else if (strncmp(e, "grad", 4) == 0) {
+ unit = SVGAngle::Unit::GRAD;
+ val = v;
+ computed = Inkscape::Util::Quantity::convert(v, "grad", "°");
+ } else if (strncmp(e, "rad", 3) == 0) {
+ unit = SVGAngle::Unit::RAD;
+ val = v;
+ computed = Inkscape::Util::Quantity::convert(v, "rad", "°");
+ } else if (strncmp(e, "turn", 4) == 0) {
+ unit = SVGAngle::Unit::TURN;
+ val = v;
+ computed = Inkscape::Util::Quantity::convert(v, "turn", "°");
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ /* Invalid */
+ return false;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg-angle.h b/src/svg/svg-angle.h
new file mode 100644
index 0000000..e8d0379
--- /dev/null
+++ b/src/svg/svg-angle.h
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef SEEN_SP_SVG_ANGLE_H
+#define SEEN_SP_SVG_ANGLE_H
+
+/**
+ * \file src/svg/svg-angle.h
+ * \brief SVG angle type
+ */
+/*
+ * Authors:
+ * Tomasz Boczkowski <penginsbacon@gmail.com>
+ *
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glib.h>
+
+class SVGAngle
+{
+public:
+ SVGAngle();
+
+ enum class Unit {
+ NONE,
+ DEG,
+ GRAD,
+ RAD,
+ TURN,
+ LAST_UNIT = TURN
+ };
+
+ // The object's value is valid / exists in SVG.
+ bool _set;
+
+ // The unit of value.
+ Unit unit;
+
+ // The value of this SVGAngle as found in the SVG.
+ float value;
+
+ // The value in degrees.
+ float computed;
+
+ float operator=(float v) {
+ _set = true;
+ unit = Unit::NONE;
+ value = computed = v;
+ return v;
+ }
+
+ bool read(gchar const *str);
+ void unset(Unit u = Unit::NONE, float v = 0, float c = 0);
+ void readOrUnset(gchar const *str, Unit u = Unit::NONE, float v = 0, float c = 0);
+};
+
+#endif // SEEN_SP_SVG_ANGLE_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg-bool.cpp b/src/svg/svg-bool.cpp
new file mode 100644
index 0000000..9a2ceaa
--- /dev/null
+++ b/src/svg/svg-bool.cpp
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Authors:
+ * Martin Owens <doctormo@geek-2.com>
+ *
+ * Copyright (C) 2021 Martin Owens
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cstring>
+#include <string>
+#include <glib.h>
+
+#include "svg/svg-bool.h"
+
+/**
+ * This boolean is not an SVG specification type, but it is something
+ * Inkscape uses for many of it's internal values.
+ */
+SVGBool::SVGBool(bool default_value)
+ : _default(default_value)
+{}
+
+bool SVGBool::read(gchar const *str)
+{
+ if (!str) return false;
+
+ _is_set = true;
+ _value = !g_ascii_strcasecmp(str, "true") ||
+ !g_ascii_strcasecmp(str, "yes") ||
+ !g_ascii_strcasecmp(str, "y") ||
+ (atoi(str) != 0);
+
+ return true;
+}
+
+void SVGBool::unset() {
+ _is_set = false;
+}
+
+void SVGBool::readOrUnset(gchar const *str) {
+ if (!read(str)) {
+ unset();
+ }
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg-bool.h b/src/svg/svg-bool.h
new file mode 100644
index 0000000..64297ff
--- /dev/null
+++ b/src/svg/svg-bool.h
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef SEEN_SP_SVG_TYPES_H
+#define SEEN_SP_SVG_TYPES_H
+/*
+ * Authors:
+ * Martin Owens <doctormo@geek-2.com>
+ *
+ * Copyright (C) 2021 Martin Owens
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glib.h>
+
+class SVGBool {
+public:
+ SVGBool(bool default_value);
+
+ operator bool() const { return _is_set ? _value : _default; }
+
+ bool read(gchar const *str);
+ void unset();
+ void readOrUnset(gchar const *str);
+
+private:
+ bool _is_set = false;
+ bool _value = false;
+ bool _default = false;
+};
+
+#endif // SEEN_SP_SVG_ANGLE_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg-color.cpp b/src/svg/svg-color.cpp
new file mode 100644
index 0000000..998d955
--- /dev/null
+++ b/src/svg/svg-color.cpp
@@ -0,0 +1,672 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * \file
+ * Reading \& writing of SVG/CSS colors.
+ */
+/*
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h" // only include where actually required!
+#endif
+
+#include <cstdlib>
+#include <cstdio> // sprintf
+#include <cstring>
+#include <string>
+#include <cmath>
+#include <glib.h> // g_assert
+#include <cerrno>
+
+#include <map>
+
+#include "colorspace.h"
+#include "strneq.h"
+#include "preferences.h"
+#include "svg-color.h"
+#include "svg-icc-color.h"
+
+#include "color.h"
+
+#include <vector>
+#include "object/color-profile.h"
+
+#include "document.h"
+#include "inkscape.h"
+#include "profile-manager.h"
+
+#include "cms-system.h"
+
+struct SPSVGColor {
+ unsigned long rgb;
+ const std::string name;
+};
+
+/*
+ * These are the colors defined in the SVG standard
+ */
+static SPSVGColor const sp_svg_color_named[] = {
+ { 0xF0F8FF, "aliceblue" },
+ { 0xFAEBD7, "antiquewhite" },
+ { 0x00FFFF, "aqua" },
+ { 0x7FFFD4, "aquamarine" },
+ { 0xF0FFFF, "azure" },
+ { 0xF5F5DC, "beige" },
+ { 0xFFE4C4, "bisque" },
+ { 0x000000, "black" },
+ { 0xFFEBCD, "blanchedalmond" },
+ { 0x0000FF, "blue" },
+ { 0x8A2BE2, "blueviolet" },
+ { 0xA52A2A, "brown" },
+ { 0xDEB887, "burlywood" },
+ { 0x5F9EA0, "cadetblue" },
+ { 0x7FFF00, "chartreuse" },
+ { 0xD2691E, "chocolate" },
+ { 0xFF7F50, "coral" },
+ { 0x6495ED, "cornflowerblue" },
+ { 0xFFF8DC, "cornsilk" },
+ { 0xDC143C, "crimson" },
+ { 0x00FFFF, "cyan" },
+ { 0x00008B, "darkblue" },
+ { 0x008B8B, "darkcyan" },
+ { 0xB8860B, "darkgoldenrod" },
+ { 0xA9A9A9, "darkgray" },
+ { 0x006400, "darkgreen" },
+ { 0xA9A9A9, "darkgrey" },
+ { 0xBDB76B, "darkkhaki" },
+ { 0x8B008B, "darkmagenta" },
+ { 0x556B2F, "darkolivegreen" },
+ { 0xFF8C00, "darkorange" },
+ { 0x9932CC, "darkorchid" },
+ { 0x8B0000, "darkred" },
+ { 0xE9967A, "darksalmon" },
+ { 0x8FBC8F, "darkseagreen" },
+ { 0x483D8B, "darkslateblue" },
+ { 0x2F4F4F, "darkslategray" },
+ { 0x2F4F4F, "darkslategrey" },
+ { 0x00CED1, "darkturquoise" },
+ { 0x9400D3, "darkviolet" },
+ { 0xFF1493, "deeppink" },
+ { 0x00BFFF, "deepskyblue" },
+ { 0x696969, "dimgray" },
+ { 0x696969, "dimgrey" },
+ { 0x1E90FF, "dodgerblue" },
+ { 0xB22222, "firebrick" },
+ { 0xFFFAF0, "floralwhite" },
+ { 0x228B22, "forestgreen" },
+ { 0xFF00FF, "fuchsia" },
+ { 0xDCDCDC, "gainsboro" },
+ { 0xF8F8FF, "ghostwhite" },
+ { 0xFFD700, "gold" },
+ { 0xDAA520, "goldenrod" },
+ { 0x808080, "gray" },
+ { 0x808080, "grey" },
+ { 0x008000, "green" },
+ { 0xADFF2F, "greenyellow" },
+ { 0xF0FFF0, "honeydew" },
+ { 0xFF69B4, "hotpink" },
+ { 0xCD5C5C, "indianred" },
+ { 0x4B0082, "indigo" },
+ { 0xFFFFF0, "ivory" },
+ { 0xF0E68C, "khaki" },
+ { 0xE6E6FA, "lavender" },
+ { 0xFFF0F5, "lavenderblush" },
+ { 0x7CFC00, "lawngreen" },
+ { 0xFFFACD, "lemonchiffon" },
+ { 0xADD8E6, "lightblue" },
+ { 0xF08080, "lightcoral" },
+ { 0xE0FFFF, "lightcyan" },
+ { 0xFAFAD2, "lightgoldenrodyellow" },
+ { 0xD3D3D3, "lightgray" },
+ { 0x90EE90, "lightgreen" },
+ { 0xD3D3D3, "lightgrey" },
+ { 0xFFB6C1, "lightpink" },
+ { 0xFFA07A, "lightsalmon" },
+ { 0x20B2AA, "lightseagreen" },
+ { 0x87CEFA, "lightskyblue" },
+ { 0x778899, "lightslategray" },
+ { 0x778899, "lightslategrey" },
+ { 0xB0C4DE, "lightsteelblue" },
+ { 0xFFFFE0, "lightyellow" },
+ { 0x00FF00, "lime" },
+ { 0x32CD32, "limegreen" },
+ { 0xFAF0E6, "linen" },
+ { 0xFF00FF, "magenta" },
+ { 0x800000, "maroon" },
+ { 0x66CDAA, "mediumaquamarine" },
+ { 0x0000CD, "mediumblue" },
+ { 0xBA55D3, "mediumorchid" },
+ { 0x9370DB, "mediumpurple" },
+ { 0x3CB371, "mediumseagreen" },
+ { 0x7B68EE, "mediumslateblue" },
+ { 0x00FA9A, "mediumspringgreen" },
+ { 0x48D1CC, "mediumturquoise" },
+ { 0xC71585, "mediumvioletred" },
+ { 0x191970, "midnightblue" },
+ { 0xF5FFFA, "mintcream" },
+ { 0xFFE4E1, "mistyrose" },
+ { 0xFFE4B5, "moccasin" },
+ { 0xFFDEAD, "navajowhite" },
+ { 0x000080, "navy" },
+ { 0xFDF5E6, "oldlace" },
+ { 0x808000, "olive" },
+ { 0x6B8E23, "olivedrab" },
+ { 0xFFA500, "orange" },
+ { 0xFF4500, "orangered" },
+ { 0xDA70D6, "orchid" },
+ { 0xEEE8AA, "palegoldenrod" },
+ { 0x98FB98, "palegreen" },
+ { 0xAFEEEE, "paleturquoise" },
+ { 0xDB7093, "palevioletred" },
+ { 0xFFEFD5, "papayawhip" },
+ { 0xFFDAB9, "peachpuff" },
+ { 0xCD853F, "peru" },
+ { 0xFFC0CB, "pink" },
+ { 0xDDA0DD, "plum" },
+ { 0xB0E0E6, "powderblue" },
+ { 0x800080, "purple" },
+ { 0x663399, "rebeccapurple" },
+ { 0xFF0000, "red" },
+ { 0xBC8F8F, "rosybrown" },
+ { 0x4169E1, "royalblue" },
+ { 0x8B4513, "saddlebrown" },
+ { 0xFA8072, "salmon" },
+ { 0xF4A460, "sandybrown" },
+ { 0x2E8B57, "seagreen" },
+ { 0xFFF5EE, "seashell" },
+ { 0xA0522D, "sienna" },
+ { 0xC0C0C0, "silver" },
+ { 0x87CEEB, "skyblue" },
+ { 0x6A5ACD, "slateblue" },
+ { 0x708090, "slategray" },
+ { 0x708090, "slategrey" },
+ { 0xFFFAFA, "snow" },
+ { 0x00FF7F, "springgreen" },
+ { 0x4682B4, "steelblue" },
+ { 0xD2B48C, "tan" },
+ { 0x008080, "teal" },
+ { 0xD8BFD8, "thistle" },
+ { 0xFF6347, "tomato" },
+ { 0x40E0D0, "turquoise" },
+ { 0xEE82EE, "violet" },
+ { 0xF5DEB3, "wheat" },
+ { 0xFFFFFF, "white" },
+ { 0xF5F5F5, "whitesmoke" },
+ { 0xFFFF00, "yellow" },
+ { 0x9ACD32, "yellowgreen" }
+};
+
+static std::map<std::string, unsigned long> sp_svg_create_color_hash();
+
+guint32 sp_svg_read_color(gchar const *str, guint32 const dfl)
+{
+ return sp_svg_read_color(str, nullptr, dfl);
+}
+
+static guint32 internal_sp_svg_read_color(gchar const *str, gchar const **end_ptr, guint32 def)
+{
+ static std::map<std::string, unsigned long> colors;
+ guint32 val = 0;
+
+ if (str == nullptr) return def;
+ while ((*str <= ' ') && *str) str++;
+ if (!*str) return def;
+
+ if (str[0] == '#') {
+ gint i;
+ for (i = 1; str[i]; i++) {
+ int hexval;
+ if (str[i] >= '0' && str[i] <= '9')
+ hexval = str[i] - '0';
+ else if (str[i] >= 'A' && str[i] <= 'F')
+ hexval = str[i] - 'A' + 10;
+ else if (str[i] >= 'a' && str[i] <= 'f')
+ hexval = str[i] - 'a' + 10;
+ else
+ break;
+ val = (val << 4) + hexval;
+ }
+ /* handle #rgb case */
+ if (i == 1 + 3) {
+ val = ((val & 0xf00) << 8) |
+ ((val & 0x0f0) << 4) |
+ (val & 0x00f);
+ val |= val << 4;
+ } else if (i != 1 + 6) {
+ /* must be either 3 or 6 digits. */
+ return def;
+ }
+ if (end_ptr) {
+ *end_ptr = str + i;
+ }
+ } else if (strneq(str, "rgb(", 4)) {
+ bool hasp, hasd;
+ gchar *s, *e;
+ gdouble r, g, b;
+
+ s = (gchar *) str + 4;
+ hasp = false;
+ hasd = false;
+
+ r = g_ascii_strtod(s, &e);
+ if (s == e) return def;
+ s = e;
+ if (*s == '%') {
+ hasp = true;
+ s += 1;
+ } else {
+ hasd = true;
+ }
+ while (*s && g_ascii_isspace(*s)) s += 1;
+ if (*s != ',') return def;
+ s += 1;
+ while (*s && g_ascii_isspace(*s)) s += 1;
+ g = g_ascii_strtod(s, &e);
+ if (s == e) return def;
+ s = e;
+ if (*s == '%') {
+ hasp = true;
+ s += 1;
+ } else {
+ hasd = true;
+ }
+ while (*s && g_ascii_isspace(*s)) s += 1;
+ if (*s != ',') return def;
+ s += 1;
+ while (*s && g_ascii_isspace(*s)) s += 1;
+ b = g_ascii_strtod(s, &e);
+ if (s == e) return def;
+ s = e;
+ if (*s == '%') {
+ hasp = true;
+ s += 1;
+ } else {
+ hasd = true;
+ }
+ while(*s && g_ascii_isspace(*s)) s += 1;
+ if (*s != ')') {
+ return def;
+ }
+ ++s;
+ if (hasp && hasd) return def;
+ if (hasp) {
+ val = static_cast<guint>(floor(CLAMP(r, 0.0, 100.0) * 2.559999)) << 24;
+ val |= (static_cast<guint>(floor(CLAMP(g, 0.0, 100.0) * 2.559999)) << 16);
+ val |= (static_cast<guint>(floor(CLAMP(b, 0.0, 100.0) * 2.559999)) << 8);
+ } else {
+ val = static_cast<guint>(CLAMP(r, 0, 255)) << 24;
+ val |= (static_cast<guint>(CLAMP(g, 0, 255)) << 16);
+ val |= (static_cast<guint>(CLAMP(b, 0, 255)) << 8);
+ }
+ if (end_ptr) {
+ *end_ptr = s;
+ }
+ return val;
+ } else if (strneq(str, "hsl(", 4)) {
+
+ gchar *ptr = (gchar *) str + 4;
+
+ gchar *e; // ptr after read
+
+ double h = g_ascii_strtod(ptr, &e); // Read h (0-360)
+ if (ptr == e) return def; // Read failed
+ ptr = e;
+
+ while (*ptr && g_ascii_isspace(*ptr)) ptr += 1; // Remove any white space
+ if (*ptr != ',') return def; // Need comma
+ ptr += 1;
+ while (*ptr && g_ascii_isspace(*ptr)) ptr += 1; // Remove any white space
+
+ double s = g_ascii_strtod(ptr, &e); // Read s (percent)
+ if (ptr == e) return def; // Read failed
+ ptr = e;
+ while (*ptr && g_ascii_isspace(*ptr)) ptr += 1; // Remove any white space
+ if (*ptr != '%') return def; // Need %
+ ptr += 1;
+
+ while (*ptr && g_ascii_isspace(*ptr)) ptr += 1; // Remove any white space
+ if (*ptr != ',') return def; // Need comma
+ ptr += 1;
+ while (*ptr && g_ascii_isspace(*ptr)) ptr += 1; // Remove any white space
+
+ double l = g_ascii_strtod(ptr, &e); // Read l (percent)
+ if (ptr == e) return def; // Read failed
+ ptr = e;
+ while (*ptr && g_ascii_isspace(*ptr)) ptr += 1; // Remove any white space
+ if (*ptr != '%') return def; // Need %
+ ptr += 1;
+
+ if (end_ptr) {
+ *end_ptr = ptr;
+ }
+
+
+ // Normalize to 0..1
+ h /= 360.0;
+ s /= 100.0;
+ l /= 100.0;
+
+ gfloat rgb[3];
+
+ SPColor::hsl_to_rgb_floatv( rgb, h, s, l );
+
+ val = static_cast<guint>(floor(CLAMP(rgb[0], 0.0, 1.0) * 255.9999)) << 24;
+ val |= (static_cast<guint>(floor(CLAMP(rgb[1], 0.0, 1.0) * 255.9999)) << 16);
+ val |= (static_cast<guint>(floor(CLAMP(rgb[2], 0.0, 1.0) * 255.9999)) << 8);
+ return val;
+
+ } else {
+ gint i;
+ if (colors.empty()) {
+ colors = sp_svg_create_color_hash();
+ }
+ gchar c[32];
+ for (i = 0; i < 31; i++) {
+ if (str[i] == ';' || g_ascii_isspace(str[i])) {
+ c[i] = '\0';
+ break;
+ }
+ c[i] = g_ascii_tolower(str[i]);
+ if (!str[i]) break;
+ }
+ c[31] = '\0';
+
+ if (colors.count(std::string(c))) {
+ val = colors[std::string(c)];
+ }
+ else {
+ return def;
+ }
+ if (end_ptr) {
+ *end_ptr = str + i;
+ }
+ }
+
+ return (val << 8);
+}
+
+guint32 sp_svg_read_color(gchar const *str, gchar const **end_ptr, guint32 dfl)
+{
+ /* I've been rather hurried in editing the above to add support for end_ptr, so I'm adding
+ * this check wrapper. */
+ gchar const *end = str;
+ guint32 const ret = internal_sp_svg_read_color(str, &end, dfl);
+ g_assert(((ret == dfl) && (end == str))
+ || (((ret & 0xff) == 0)
+ && (str < end)));
+ if (str < end) {
+ gchar *buf = (gchar *) g_malloc(end + 1 - str);
+ memcpy(buf, str, end - str);
+ buf[end - str] = '\0';
+ gchar const *buf_end = buf;
+ guint32 const check = internal_sp_svg_read_color(buf, &buf_end, 1);
+ g_assert(check == ret
+ && buf_end - buf == end - str);
+ g_free(buf);
+
+ if ( end_ptr ) {
+ *end_ptr = end;
+ }
+ }
+ return ret;
+}
+
+
+/**
+ * Converts an RGB colour expressed in form 0x00rrggbb to a CSS/SVG representation of that colour.
+ * The result is valid even in SVG Tiny or non-SVG CSS.
+ */
+static void rgb24_to_css(char *const buf, unsigned const rgb24)
+{
+ g_assert(rgb24 < (1u << 24));
+
+ /* SVG 1.1 Full allows additional colour names not supported by SVG Tiny, but we don't bother
+ * with them: it's good for these colours to be copyable to non-SVG CSS stylesheets and for
+ * documents to be more viewable in SVG Tiny/Basic viewers; and some of the SVG Full names are
+ * less meaningful than hex equivalents anyway. And it's easier for a person to map from the
+ * restricted set because the only component values are {00,80,ff}, other than silver 0xc0c0c0.
+ */
+
+ char const *src = nullptr;
+ switch (rgb24) {
+ /* Extracted mechanically from the table at
+ * http://www.w3.org/TR/REC-html40/types.html#h-6.5 .*/
+ case 0x000000: src = "black"; break;
+ case 0xc0c0c0: src = "silver"; break;
+ case 0x808080: src = "gray"; break;
+ case 0xffffff: src = "white"; break;
+ case 0x800000: src = "maroon"; break;
+ case 0xff0000: src = "red"; break;
+ case 0x800080: src = "purple"; break;
+ case 0xff00ff: src = "fuchsia"; break;
+ case 0x008000: src = "green"; break;
+ case 0x00ff00: src = "lime"; break;
+ case 0x808000: src = "olive"; break;
+ case 0xffff00: src = "yellow"; break;
+ case 0x000080: src = "navy"; break;
+ case 0x0000ff: src = "blue"; break;
+ case 0x008080: src = "teal"; break;
+ case 0x00ffff: src = "aqua"; break;
+
+ default: {
+ if ((rgb24 & 0xf0f0f) * 0x11 == rgb24) {
+ /* Can use the shorter three-digit form #rgb instead of #rrggbb. */
+ std::sprintf(buf, "#%x%x%x",
+ (rgb24 >> 16) & 0xf,
+ (rgb24 >> 8) & 0xf,
+ rgb24 & 0xf);
+ } else {
+ std::sprintf(buf, "#%06x", rgb24);
+ }
+ break;
+ }
+ }
+ if (src) {
+ strcpy(buf, src);
+ }
+
+ // assert(sp_svg_read_color(buf, 0xff) == (rgb24 << 8));
+}
+
+/**
+ * Converts an RGBA32 colour to a CSS/SVG representation of the RGB portion of that colour. The
+ * result is valid even in SVG Tiny or non-SVG CSS.
+ *
+ * \param rgba32 Colour expressed in form 0xrrggbbaa.
+ * \pre buflen \>= 8.
+ */
+void sp_svg_write_color(gchar *buf, unsigned const buflen, guint32 const rgba32)
+{
+ g_assert(8 <= buflen);
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ unsigned const rgb24 = rgba32 >> 8;
+ if ( prefs->getBool("/options/svgoutput/usenamedcolors") &&
+ !prefs->getBool("/options/svgoutput/disable_optimizations" )) {
+ rgb24_to_css(buf, rgb24);
+ } else {
+ g_snprintf(buf, buflen, "#%06x", rgb24);
+ }
+}
+
+static std::map<std::string, unsigned long>
+sp_svg_create_color_hash()
+{
+ std::map<std::string, unsigned long> colors;
+
+ for (const auto & i : sp_svg_color_named) {
+ colors[i.name] = i.rgb;
+ }
+ return colors;
+}
+
+
+void icc_color_to_sRGB(SVGICCColor* icc, guchar* r, guchar* g, guchar* b)
+{
+ if (icc) {
+ g_message("profile name: %s", icc->colorProfile.c_str());
+ Inkscape::ColorProfile* prof = SP_ACTIVE_DOCUMENT->getProfileManager()->find(icc->colorProfile.c_str());
+ if ( prof ) {
+ guchar color_out[4] = {0,0,0,0};
+ cmsHTRANSFORM trans = prof->getTransfToSRGB8();
+ if ( trans ) {
+ std::vector<colorspace::Component> comps = colorspace::getColorSpaceInfo( prof );
+
+ size_t count = Inkscape::CMSSystem::getChannelCount( prof );
+ size_t cap = std::min(count, comps.size());
+ guchar color_in[4];
+ for (size_t i = 0; i < cap; i++) {
+ color_in[i] = static_cast<guchar>((((gdouble)icc->colors[i]) * 256.0) * (gdouble)comps[i].scale);
+ g_message("input[%d]: %d", (int)i, (int)color_in[i]);
+ }
+
+ Inkscape::CMSSystem::doTransform( trans, color_in, color_out, 1 );
+g_message("transform to sRGB done");
+ }
+ *r = color_out[0];
+ *g = color_out[1];
+ *b = color_out[2];
+ }
+ }
+}
+
+/*
+ * Some discussion at http://markmail.org/message/bhfvdfptt25kgtmj
+ * Allowed ASCII first characters: ':', 'A'-'Z', '_', 'a'-'z'
+ * Allowed ASCII remaining chars add: '-', '.', '0'-'9',
+ */
+bool sp_svg_read_icc_color( gchar const *str, gchar const **end_ptr, SVGICCColor* dest )
+{
+ bool good = true;
+
+ if ( end_ptr ) {
+ *end_ptr = str;
+ }
+ if ( dest ) {
+ dest->colorProfile.clear();
+ dest->colors.clear();
+ }
+
+ if ( !str ) {
+ // invalid input
+ good = false;
+ } else {
+ while ( g_ascii_isspace(*str) ) {
+ str++;
+ }
+
+ good = strneq( str, "icc-color(", 10 );
+
+ if ( good ) {
+ str += 10;
+ while ( g_ascii_isspace(*str) ) {
+ str++;
+ }
+
+ if ( !g_ascii_isalpha(*str)
+ && ( !(0x080 & *str) )
+ && (*str != '_')
+ && (*str != ':') ) {
+ // Name must start with a certain type of character
+ good = false;
+ } else {
+ while ( g_ascii_isdigit(*str) || g_ascii_isalpha(*str)
+ || (*str == '-') || (*str == ':') || (*str == '_') || (*str == '.') ) {
+ if ( dest ) {
+ dest->colorProfile += *str;
+ }
+ str++;
+ }
+ while ( g_ascii_isspace(*str) || *str == ',' ) {
+ str++;
+ }
+ }
+ }
+
+ if ( good ) {
+ while ( *str && *str != ')' ) {
+ if ( g_ascii_isdigit(*str) || *str == '.' || *str == '-' || *str == '+') {
+ gchar* endPtr = nullptr;
+ gdouble dbl = g_ascii_strtod( str, &endPtr );
+ if ( !errno ) {
+ if ( dest ) {
+ dest->colors.push_back( dbl );
+ }
+ str = endPtr;
+ } else {
+ good = false;
+ break;
+ }
+
+ while ( g_ascii_isspace(*str) || *str == ',' ) {
+ str++;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ // We need to have ended on a closing parenthesis
+ if ( good ) {
+ while ( g_ascii_isspace(*str) ) {
+ str++;
+ }
+ good &= (*str == ')');
+ }
+ }
+
+ if ( good ) {
+ if ( end_ptr ) {
+ *end_ptr = str;
+ }
+ } else {
+ if ( dest ) {
+ dest->colorProfile.clear();
+ dest->colors.clear();
+ }
+ }
+
+ return good;
+}
+
+
+bool sp_svg_read_icc_color( gchar const *str, SVGICCColor* dest )
+{
+ return sp_svg_read_icc_color(str, nullptr, dest);
+}
+
+/**
+ * Reading inkscape colors, for things like namedviews, guides, etc.
+ * Non-CSS / SVG specification formatted. Usually just a number.
+ */
+bool sp_ink_read_opacity(char const *str, guint32 *color, guint32 default_color)
+{
+ *color = (*color & 0xffffff00) | (default_color & 0xff);
+ if (!str) return false;
+
+ gchar *check;
+ gdouble value = g_ascii_strtod(str, &check);
+ if (!check) return false;
+
+ value = CLAMP(value, 0.0, 1.0);
+ *color = (*color & 0xffffff00) | (guint32) floor(value * 255.9999);
+ return true;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg-color.h b/src/svg/svg-color.h
new file mode 100644
index 0000000..b68d860
--- /dev/null
+++ b/src/svg/svg-color.h
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2014 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef SVG_SVG_COLOR_H_SEEN
+#define SVG_SVG_COLOR_H_SEEN
+
+typedef unsigned int guint32;
+struct SVGICCColor;
+
+guint32 sp_svg_read_color(char const *str, unsigned int dfl);
+guint32 sp_svg_read_color(char const *str, char const **end_ptr, guint32 def);
+void sp_svg_write_color(char *buf, unsigned int buflen, unsigned int rgba32);
+
+bool sp_svg_read_icc_color( char const *str, char const **end_ptr, SVGICCColor* dest );
+bool sp_svg_read_icc_color( char const *str, SVGICCColor* dest );
+void icc_color_to_sRGB(SVGICCColor* dest, unsigned char* r, unsigned char* g, unsigned char* b);
+
+bool sp_ink_read_opacity(char const *str, guint32 *color, guint32 default_color);
+
+#endif /* !SVG_SVG_COLOR_H_SEEN */
diff --git a/src/svg/svg-icc-color.h b/src/svg/svg-icc-color.h
new file mode 100644
index 0000000..2a1f89e
--- /dev/null
+++ b/src/svg/svg-icc-color.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2010 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef SVG_ICC_COLOR_H_SEEN
+#define SVG_ICC_COLOR_H_SEEN
+
+#include <string>
+#include <vector>
+
+/**
+ * An icc-color specification. Corresponds to the DOM interface of the same name.
+ *
+ * Referenced by SPIPaint.
+ */
+struct SVGICCColor {
+ std::string colorProfile;
+ std::vector<double> colors;
+};
+
+
+#endif /* !SVG_ICC_COLOR_H_SEEN */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg-length.cpp b/src/svg/svg-length.cpp
new file mode 100644
index 0000000..c3baba7
--- /dev/null
+++ b/src/svg/svg-length.cpp
@@ -0,0 +1,596 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * SVG data parser
+ *//*
+ * Authors: see git history
+
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cmath>
+#include <cstring>
+#include <string>
+#include <glib.h>
+#include <iostream>
+#include <vector>
+
+#include "svg.h"
+#include "stringstream.h"
+#include "util/units.h"
+
+using std::pow;
+
+static unsigned sp_svg_length_read_lff(gchar const *str, SVGLength::Unit *unit, float *val, float *computed, char **next);
+
+#ifndef MAX
+# define MAX(a,b) ((a < b) ? (b) : (a))
+#endif
+
+unsigned int sp_svg_number_read_f(gchar const *str, float *val)
+{
+ if (!str) {
+ return 0;
+ }
+
+ char *e;
+ float const v = g_ascii_strtod(str, &e);
+ if ((gchar const *) e == str) {
+ return 0;
+ }
+
+ *val = v;
+ return 1;
+}
+
+unsigned int sp_svg_number_read_d(gchar const *str, double *val)
+{
+ if (!str) {
+ return 0;
+ }
+
+ char *e;
+ double const v = g_ascii_strtod(str, &e);
+ if ((gchar const *) e == str) {
+ return 0;
+ }
+
+ *val = v;
+ return 1;
+}
+
+static std::string sp_svg_number_write_d( double val, unsigned int tprec, unsigned int fprec)
+{
+
+ std::string buf;
+ /* Process sign */
+ int i = 0;
+ if (val < 0.0) {
+ buf.append("-");
+ val = fabs(val);
+ }
+
+ /* Determine number of integral digits */
+ int idigits = 0;
+ if (val >= 1.0) {
+ idigits = (int) floor(log10(val)) + 1;
+ }
+
+ /* Determine the actual number of fractional digits */
+ fprec = MAX(static_cast<int>(fprec), static_cast<int>(tprec) - idigits);
+ /* Round value */
+ val += 0.5 / pow(10.0, fprec);
+ /* Extract integral and fractional parts */
+ double dival = floor(val);
+ double fval = val - dival;
+ /* Write integra */
+ if (idigits > (int)tprec) {
+ buf.append(std::to_string((unsigned int)floor(dival/pow(10.0, idigits-tprec) + .5)));
+ for(unsigned int j=0; j<(unsigned int)idigits-tprec; j++) {
+ buf.append("0");
+ }
+ i += idigits-tprec;
+ } else {
+ buf.append(std::to_string((unsigned int)dival));
+ }
+
+ if (fprec > 0 && fval > 0.0) {
+ std::string s(".");
+ do {
+ fval *= 10.0;
+ dival = floor(fval);
+ fval -= dival;
+ int const int_dival = (int) dival;
+ s.append(std::to_string(int_dival));
+ if(int_dival != 0){
+ buf.append(s);
+ s="";
+ }
+ fprec -= 1;
+ } while(fprec > 0 && fval > 0.0);
+ }
+ return buf;
+}
+
+std::string sp_svg_number_write_de(double val, unsigned int tprec, int min_exp)
+{
+ std::string buf;
+ int eval = (int)floor(log10(fabs(val)));
+ if (val == 0.0 || eval < min_exp) {
+ buf.append("0");
+ return buf;
+ }
+ unsigned int maxnumdigitsWithoutExp = // This doesn't include the sign because it is included in either representation
+ eval<0?tprec+(unsigned int)-eval+1:
+ eval+1<(int)tprec?tprec+1:
+ (unsigned int)eval+1;
+ unsigned int maxnumdigitsWithExp = tprec + ( eval<0 ? 4 : 3 ); // It's not necessary to take larger exponents into account, because then maxnumdigitsWithoutExp is DEFINITELY larger
+ if (maxnumdigitsWithoutExp <= maxnumdigitsWithExp) {
+ buf.append(sp_svg_number_write_d(val, tprec, 0));
+ } else {
+ val = eval < 0 ? val * pow(10.0, -eval) : val / pow(10.0, eval);
+ buf.append(sp_svg_number_write_d(val, tprec, 0));
+ buf.append("e");
+ buf.append(std::to_string(eval));
+ }
+ return buf;
+
+}
+
+SVGLength::SVGLength()
+ : _set(false)
+ , unit(NONE)
+ , value(0)
+ , computed(0)
+{
+}
+
+/* Length */
+
+bool SVGLength::read(gchar const *str)
+{
+ if (!str) {
+ return false;
+ }
+
+ SVGLength::Unit u;
+ float v;
+ float c;
+ if (!sp_svg_length_read_lff(str, &u, &v, &c, nullptr)) {
+ return false;
+ }
+
+ if (!std::isfinite(v)) {
+ return false;
+ }
+
+ _set = true;
+ unit = u;
+ value = v;
+ computed = c;
+
+ return true;
+}
+
+bool SVGLength::readAbsolute(gchar const *str)
+{
+ if (!str) {
+ return false;
+ }
+
+ SVGLength::Unit u;
+ float v;
+ float c;
+ if (!sp_svg_length_read_lff(str, &u, &v, &c, nullptr)) {
+ return false;
+ }
+
+ if (svg_length_absolute_unit(u) == false) {
+ return false;
+ }
+
+ _set = true;
+ unit = u;
+ value = v;
+ computed = c;
+
+ return true;
+}
+
+/**
+ * Returns the unit used as a string.
+ *
+ * @returns unit string
+ */
+std::string SVGLength::getUnit() const
+{
+ return sp_svg_length_get_css_units(unit);
+}
+
+/**
+ * Is this length an absolute value (uses an absolute unit).
+ *
+ * @returns true if unit is not NONE and not a relative unit (percent etc)
+ */
+bool SVGLength::isAbsolute()
+{
+ return unit && svg_length_absolute_unit(unit);
+}
+
+unsigned int sp_svg_length_read_computed_absolute(gchar const *str, float *length)
+{
+ if (!str) {
+ return 0;
+ }
+
+ SVGLength::Unit unit;
+ float computed;
+ if (!sp_svg_length_read_lff(str, &unit, nullptr, &computed, nullptr)) {
+ // failed to read
+ return 0;
+ }
+
+ if (svg_length_absolute_unit(unit) == false) {
+ return 0;
+ }
+
+ *length = computed;
+
+ return 1;
+}
+
+std::vector<SVGLength> sp_svg_length_list_read(gchar const *str)
+{
+ if (!str) {
+ return std::vector<SVGLength>();
+ }
+
+ SVGLength::Unit unit;
+ float value;
+ float computed;
+ char *next = (char *) str;
+ std::vector<SVGLength> list;
+
+ while (sp_svg_length_read_lff(next, &unit, &value, &computed, &next)) {
+
+ SVGLength length;
+ length.set(unit, value, computed);
+ list.push_back(length);
+
+ while (next && *next &&
+ (*next == ',' || *next == ' ' || *next == '\n' || *next == '\r' || *next == '\t')) {
+ // the list can be comma- or space-separated, but we will be generous and accept
+ // a mix, including newlines and tabs
+ next++;
+ }
+
+ if (!next || !*next) {
+ break;
+ }
+ }
+
+ return list;
+}
+
+
+#define UVAL(a,b) (((unsigned int) (a) << 8) | (unsigned int) (b))
+
+static unsigned sp_svg_length_read_lff(gchar const *str, SVGLength::Unit *unit, float *val, float *computed, char **next)
+{
+/* note: this function is sometimes fed a string with several consecutive numbers, e.g. by sp_svg_length_list_read.
+So after the number, the string does not necessarily have a \0 or a unit, it might also contain a space or comma and then the next number!
+*/
+
+ if (!str) {
+ return 0;
+ }
+
+ gchar const *e;
+ float const v = g_ascii_strtod(str, (char **) &e);
+ if (e == str) {
+ return 0;
+ }
+
+ if (!e[0]) {
+ /* Unitless */
+ if (unit) {
+ *unit = SVGLength::NONE;
+ }
+ if (val) {
+ *val = v;
+ }
+ if (computed) {
+ *computed = v;
+ }
+ if (next) {
+ *next = nullptr; // no more values
+ }
+ return 1;
+ } else if (!g_ascii_isalnum(e[0])) {
+ /* Unitless or percent */
+ if (e[0] == '%') {
+ /* Percent */
+ if (e[1] && g_ascii_isalnum(e[1])) {
+ return 0;
+ }
+ if (unit) {
+ *unit = SVGLength::PERCENT;
+ }
+ if (val) {
+ *val = v * 0.01;
+ }
+ if (computed) {
+ *computed = v * 0.01;
+ }
+ if (next) {
+ *next = (char *) e + 1;
+ }
+ return 1;
+ } else if (g_ascii_isspace(e[0]) && e[1] && g_ascii_isalpha(e[1])) {
+ return 0; // spaces between value and unit are not allowed
+ } else {
+ /* Unitless */
+ if (unit) {
+ *unit = SVGLength::NONE;
+ }
+ if (val) {
+ *val = v;
+ }
+ if (computed) {
+ *computed = v;
+ }
+ if (next) {
+ *next = (char *) e;
+ }
+ return 1;
+ }
+ } else if (e[1] && !g_ascii_isalnum(e[2])) {
+ /* TODO: Allow the number of px per inch to vary (document preferences, X server
+ * or whatever). E.g. don't fill in computed here, do it at the same time as
+ * percentage units are done. */
+ unsigned int const uval = UVAL(e[0], e[1]);
+ switch (uval) {
+ case UVAL('p','x'):
+ if (unit) {
+ *unit = SVGLength::PX;
+ }
+ if (computed) {
+ *computed = v;
+ }
+ break;
+ case UVAL('p','t'):
+ if (unit) {
+ *unit = SVGLength::PT;
+ }
+ if (computed) {
+ *computed = Inkscape::Util::Quantity::convert(v, "pt", "px");
+ }
+ break;
+ case UVAL('p','c'):
+ if (unit) {
+ *unit = SVGLength::PC;
+ }
+ if (computed) {
+ *computed = Inkscape::Util::Quantity::convert(v, "pc", "px");
+ }
+ break;
+ case UVAL('m','m'):
+ if (unit) {
+ *unit = SVGLength::MM;
+ }
+ if (computed) {
+ *computed = Inkscape::Util::Quantity::convert(v, "mm", "px");
+ }
+ break;
+ case UVAL('c','m'):
+ if (unit) {
+ *unit = SVGLength::CM;
+ }
+ if (computed) {
+ *computed = Inkscape::Util::Quantity::convert(v, "cm", "px");
+ }
+ break;
+ case UVAL('i','n'):
+ if (unit) {
+ *unit = SVGLength::INCH;
+ }
+ if (computed) {
+ *computed = Inkscape::Util::Quantity::convert(v, "in", "px");
+ }
+ break;
+ case UVAL('e','m'):
+ if (unit) {
+ *unit = SVGLength::EM;
+ }
+ break;
+ case UVAL('e','x'):
+ if (unit) {
+ *unit = SVGLength::EX;
+ }
+ break;
+ default:
+ /* Invalid */
+ return 0;
+ break;
+ }
+ if (val) {
+ *val = v;
+ }
+ if (next) {
+ *next = (char *) e + 2;
+ }
+ return 1;
+ }
+
+ /* Invalid */
+ return 0;
+}
+
+unsigned int sp_svg_length_read_ldd(gchar const *str, SVGLength::Unit *unit, double *value, double *computed)
+{
+ float a;
+ float b;
+ unsigned int r = sp_svg_length_read_lff(str, unit, &a, &b, nullptr);
+ if (r) {
+ if (value) {
+ *value = a;
+ }
+ if (computed) {
+ *computed = b;
+ }
+ }
+ return r;
+}
+
+std::string SVGLength::write() const
+{
+ return sp_svg_length_write_with_units(*this);
+}
+
+void SVGLength::set(SVGLength::Unit u, float v)
+{
+ _set = true;
+ unit = u;
+ Glib::ustring hack("px");
+ switch( unit ) {
+ case NONE:
+ case PX:
+ case EM:
+ case EX:
+ case PERCENT:
+ break;
+ case PT:
+ hack = "pt";
+ break;
+ case PC:
+ hack = "pc";
+ break;
+ case MM:
+ hack = "pt";
+ break;
+ case CM:
+ hack = "pt";
+ break;
+ case INCH:
+ hack = "pt";
+ break;
+ default:
+ break;
+ }
+ value = v;
+ computed = Inkscape::Util::Quantity::convert(v, hack, "px");
+}
+
+void SVGLength::set(SVGLength::Unit u, float v, float c)
+{
+ _set = true;
+ unit = u;
+ value = v;
+ computed = c;
+}
+
+void SVGLength::unset(SVGLength::Unit u, float v, float c)
+{
+ _set = false;
+ unit = u;
+ value = v;
+ computed = c;
+}
+
+void SVGLength::scale(double scale)
+{
+ value *= scale;
+ computed *= scale;
+}
+
+void SVGLength::update(double em, double ex, double scale)
+{
+ if (unit == EM) {
+ computed = value * em;
+ } else if (unit == EX) {
+ computed = value * ex;
+ } else if (unit == PERCENT) {
+ computed = value * scale;
+ }
+}
+
+double sp_svg_read_percentage(char const *str, double def)
+{
+ if (str == nullptr) {
+ return def;
+ }
+
+ char *u;
+ double v = g_ascii_strtod(str, &u);
+ while (isspace(*u)) {
+ if (*u == '\0') {
+ return v;
+ }
+ u++;
+ }
+ if (*u == '%') {
+ v /= 100.0;
+ }
+
+ return v;
+}
+
+gchar const *sp_svg_length_get_css_units(SVGLength::Unit unit)
+{
+ switch (unit) {
+ case SVGLength::NONE: return "";
+ case SVGLength::PX: return "";
+ case SVGLength::PT: return "pt";
+ case SVGLength::PC: return "pc";
+ case SVGLength::MM: return "mm";
+ case SVGLength::CM: return "cm";
+ case SVGLength::INCH: return "in";
+ case SVGLength::EM: return "em";
+ case SVGLength::EX: return "ex";
+ case SVGLength::PERCENT: return "%";
+ }
+ return "";
+}
+
+bool svg_length_absolute_unit(SVGLength::Unit u)
+{
+ return (u != SVGLength::EM && u != SVGLength::EX && u != SVGLength::PERCENT);
+}
+
+/**
+ * N.B.\ This routine will sometimes return strings with `e' notation, so is unsuitable for CSS
+ * lengths (which don't allow scientific `e' notation).
+ */
+std::string sp_svg_length_write_with_units(SVGLength const &length)
+{
+ Inkscape::SVGOStringStream os;
+ if (length.unit == SVGLength::PERCENT) {
+ os << 100*length.value << sp_svg_length_get_css_units(length.unit);
+ } else {
+ os << length.value << sp_svg_length_get_css_units(length.unit);
+ }
+ return os.str();
+}
+
+
+void SVGLength::readOrUnset(gchar const *str, Unit u, float v, float c)
+{
+ if (!read(str)) {
+ unset(u, v, c);
+ }
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 :
diff --git a/src/svg/svg-length.h b/src/svg/svg-length.h
new file mode 100644
index 0000000..27f534f
--- /dev/null
+++ b/src/svg/svg-length.h
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Carl Hetherington <inkscape@carlh.net>
+ *
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef SEEN_SP_SVG_LENGTH_H
+#define SEEN_SP_SVG_LENGTH_H
+
+#include <string>
+
+/**
+ * SVG length type
+ */
+class SVGLength {
+public:
+ SVGLength();
+
+ enum Unit {
+ NONE,
+ PX,
+ PT,
+ PC,
+ MM,
+ CM,
+ INCH,
+ EM,
+ EX,
+ PERCENT,
+ LAST_UNIT = PERCENT
+ };
+
+ // The object's value is valid / exists in SVG.
+ bool _set;
+
+ // The unit of value.
+ Unit unit;
+
+ // The value of this SVGLength as found in the SVG.
+ float value;
+
+ // The value in pixels (value * pixels/unit).
+ float computed;
+
+ float operator=(float v) {
+ _set = true;
+ unit = NONE;
+ value = computed = v;
+ return v;
+ }
+
+ bool read(char const *str);
+ void readOrUnset(char const *str, Unit u = NONE, float v = 0, float c = 0);
+ bool readAbsolute(char const *str);
+ std::string getUnit() const;
+ bool isAbsolute();
+
+ std::string write() const;
+ // To set 'v' use '='
+ void set(Unit u, float v); // Sets computed value based on u and v.
+ void set(Unit u, float v, float c); // Sets all three values.
+ void unset(Unit u = NONE, float v = 0, float c = 0);
+ void scale(double scale); // Scales length (value, computed), leaving unit alone.
+ void update(double em, double ex, double scale); // Updates computed value
+};
+
+char const *sp_svg_length_get_css_units(SVGLength::Unit unit);
+bool svg_length_absolute_unit(SVGLength::Unit unit);
+
+#endif // SEEN_SP_SVG_LENGTH_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 :
diff --git a/src/svg/svg-path.cpp b/src/svg/svg-path.cpp
new file mode 100644
index 0000000..555b2c2
--- /dev/null
+++ b/src/svg/svg-path.cpp
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * svg-path.cpp: Parse SVG path element data into bezier path.
+ * Authors:
+ * Johan Engelen
+ * (old nartbpath code that has been deleted: Raph Levien <raph@artofcode.com>)
+ * (old nartbpath code that has been deleted: Lauris Kaplinski <lauris@ximian.com>)
+ *
+ * Copyright (C) 2000 Eazel, Inc.
+ * Copyright (C) 2000 Lauris Kaplinski
+ * Copyright (C) 2001 Ximian, Inc.
+ * Copyright (C) 2008 Johan Engelen
+ *
+ * Copyright (C) 2000-2008 authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cstring>
+#include <string>
+#include <glib.h> // g_assert()
+
+#include <2geom/pathvector.h>
+#include <2geom/curves.h>
+#include <2geom/sbasis-to-bezier.h>
+#include <2geom/path-sink.h>
+#include <2geom/svg-path-parser.h>
+
+#include "svg/svg.h"
+#include "svg/path-string.h"
+
+/*
+ * Parses the path in str. When an error is found in the pathstring, this method
+ * returns a truncated path up to where the error was found in the pathstring.
+ * Returns an empty PathVector when str==NULL
+ */
+Geom::PathVector sp_svg_read_pathv(char const * str)
+{
+ Geom::PathVector pathv;
+ if (!str)
+ return pathv; // return empty pathvector when str == NULL
+
+ Geom::PathBuilder builder(pathv);
+ Geom::SVGPathParser parser(builder);
+ parser.setZSnapThreshold(Geom::EPSILON);
+
+ try {
+ parser.parse(str);
+ }
+ catch (Geom::SVGPathParseError &e) {
+ builder.flush();
+ // This warning is extremely annoying when testing
+ g_warning(
+ "Malformed SVG path, truncated path up to where error was found.\n Input path=\"%s\"\n Parsed path=\"%s\"",
+ str, sp_svg_write_path(pathv).c_str());
+ }
+
+ return pathv;
+}
+
+static void sp_svg_write_curve(Inkscape::SVG::PathString & str, Geom::Curve const * c) {
+ // TODO: this code needs to removed and replaced by appropriate path sink
+ if(Geom::LineSegment const *line_segment = dynamic_cast<Geom::LineSegment const *>(c)) {
+ // don't serialize stitch segments
+ if (!dynamic_cast<Geom::Path::StitchSegment const *>(c)) {
+ if (line_segment->initialPoint()[Geom::X] == line_segment->finalPoint()[Geom::X]) {
+ str.verticalLineTo( line_segment->finalPoint()[Geom::Y] );
+ } else if (line_segment->initialPoint()[Geom::Y] == line_segment->finalPoint()[Geom::Y]) {
+ str.horizontalLineTo( line_segment->finalPoint()[Geom::X] );
+ } else {
+ str.lineTo( (*line_segment)[1][0], (*line_segment)[1][1] );
+ }
+ }
+ }
+ else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast<Geom::QuadraticBezier const *>(c)) {
+ str.quadTo( (*quadratic_bezier)[1][0], (*quadratic_bezier)[1][1],
+ (*quadratic_bezier)[2][0], (*quadratic_bezier)[2][1] );
+ }
+ else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const *>(c)) {
+ str.curveTo( (*cubic_bezier)[1][0], (*cubic_bezier)[1][1],
+ (*cubic_bezier)[2][0], (*cubic_bezier)[2][1],
+ (*cubic_bezier)[3][0], (*cubic_bezier)[3][1] );
+ }
+ else if(Geom::EllipticalArc const *elliptical_arc = dynamic_cast<Geom::EllipticalArc const *>(c)) {
+ str.arcTo( elliptical_arc->ray(Geom::X), elliptical_arc->ray(Geom::Y),
+ Geom::deg_from_rad(elliptical_arc->rotationAngle()),
+ elliptical_arc->largeArc(), elliptical_arc->sweep(),
+ elliptical_arc->finalPoint() );
+ } else {
+ //this case handles sbasis as well as all other curve types
+ Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c->toSBasis(), 0.1);
+
+ //recurse to convert the new path resulting from the sbasis to svgd
+ for(const auto & iter : sbasis_path) {
+ sp_svg_write_curve(str, &iter);
+ }
+ }
+}
+
+static void sp_svg_write_path(Inkscape::SVG::PathString & str, Geom::Path const & p) {
+ str.moveTo( p.initialPoint()[0], p.initialPoint()[1] );
+
+ for(Geom::Path::const_iterator cit = p.begin(); cit != p.end_open(); ++cit) {
+ sp_svg_write_curve(str, &(*cit));
+ }
+
+ if (p.closed()) {
+ str.closePath();
+ }
+}
+
+std::string sp_svg_write_path(Geom::PathVector const &p) {
+ Inkscape::SVG::PathString str;
+
+ for(const auto & pit : p) {
+ sp_svg_write_path(str, pit);
+ }
+
+ return str;
+}
+
+std::string sp_svg_write_path(Geom::Path const &p) {
+ Inkscape::SVG::PathString str;
+
+ sp_svg_write_path(str, p);
+
+ return str;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 :
diff --git a/src/svg/svg.h b/src/svg/svg.h
new file mode 100644
index 0000000..3725043
--- /dev/null
+++ b/src/svg/svg.h
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef SEEN_SP_SVG_H
+#define SEEN_SP_SVG_H
+
+/*
+ * SVG data parser
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <vector>
+#include <cstring>
+#include <string>
+
+#include "svg/svg-length.h"
+#include <2geom/forward.h>
+
+/* Generic */
+
+/*
+ * These are very-very simple:
+ * - they accept everything libc strtod accepts
+ * - no valid end character checking
+ * Return FALSE and let val untouched on error
+ */
+
+unsigned int sp_svg_number_read_f( const char *str, float *val );
+unsigned int sp_svg_number_read_d( const char *str, double *val );
+
+/*
+ * No buffer overflow checking is done, so better wrap them if needed
+ */
+std::string sp_svg_number_write_de( double val, unsigned int tprec, int min_exp );
+
+/* Length */
+
+/*
+ * Parse number with optional unit specifier:
+ * - for px, pt, pc, mm, cm, computed is final value according to SVG spec
+ * - for em, ex, and % computed is left untouched
+ * - % is divided by 100 (i.e. 100% is 1.0)
+ * !isalnum check is done at the end
+ * Any return value pointer can be NULL
+ */
+
+unsigned int sp_svg_length_read_computed_absolute( const char *str, float *length );
+std::vector<SVGLength> sp_svg_length_list_read( const char *str );
+unsigned int sp_svg_length_read_ldd( const char *str, SVGLength::Unit *unit, double *value, double *computed );
+
+std::string sp_svg_length_write_with_units(SVGLength const &length);
+
+bool sp_svg_transform_read(char const *str, Geom::Affine *transform);
+
+std::string sp_svg_transform_write(Geom::Affine const &transform);
+
+double sp_svg_read_percentage( const char * str, double def );
+
+/* NB! As paths can be long, we use here dynamic string */
+
+Geom::PathVector sp_svg_read_pathv( char const * str );
+std::string sp_svg_write_path(Geom::PathVector const &p);
+std::string sp_svg_write_path(Geom::Path const &p);
+
+#endif // SEEN_SP_SVG_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :