diff options
Diffstat (limited to 'src/svg')
-rw-r--r-- | src/svg/CMakeLists.txt | 34 | ||||
-rw-r--r-- | src/svg/HACKING | 7 | ||||
-rw-r--r-- | src/svg/README | 8 | ||||
-rw-r--r-- | src/svg/css-ostringstream.cpp | 66 | ||||
-rw-r--r-- | src/svg/css-ostringstream.h | 67 | ||||
-rw-r--r-- | src/svg/path-string.cpp | 167 | ||||
-rw-r--r-- | src/svg/path-string.h | 266 | ||||
-rw-r--r-- | src/svg/sp-svg.def | 27 | ||||
-rw-r--r-- | src/svg/stringstream.cpp | 94 | ||||
-rw-r--r-- | src/svg/stringstream.h | 90 | ||||
-rw-r--r-- | src/svg/strip-trailing-zeros.cpp | 54 | ||||
-rw-r--r-- | src/svg/strip-trailing-zeros.h | 29 | ||||
-rw-r--r-- | src/svg/svg-affine-parser.cpp | 1248 | ||||
-rw-r--r-- | src/svg/svg-affine-parser.rl | 132 | ||||
-rw-r--r-- | src/svg/svg-affine.cpp | 138 | ||||
-rw-r--r-- | src/svg/svg-angle.cpp | 134 | ||||
-rw-r--r-- | src/svg/svg-angle.h | 70 | ||||
-rw-r--r-- | src/svg/svg-bool.cpp | 57 | ||||
-rw-r--r-- | src/svg/svg-bool.h | 42 | ||||
-rw-r--r-- | src/svg/svg-color.cpp | 672 | ||||
-rw-r--r-- | src/svg/svg-color.h | 26 | ||||
-rw-r--r-- | src/svg/svg-icc-color.h | 38 | ||||
-rw-r--r-- | src/svg/svg-length.cpp | 596 | ||||
-rw-r--r-- | src/svg/svg-length.h | 87 | ||||
-rw-r--r-- | src/svg/svg-path.cpp | 139 | ||||
-rw-r--r-- | src/svg/svg.h | 80 |
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 : |