summaryrefslogtreecommitdiffstats
path: root/src/svg
diff options
context:
space:
mode:
Diffstat (limited to 'src/svg')
-rw-r--r--src/svg/.cvsignore5
-rw-r--r--src/svg/HACKING7
-rw-r--r--src/svg/Makefile_insert58
-rw-r--r--src/svg/css-ostringstream-test.h73
-rw-r--r--src/svg/css-ostringstream.cpp76
-rw-r--r--src/svg/css-ostringstream.h80
-rw-r--r--src/svg/ftos.cpp485
-rw-r--r--src/svg/ftos.h54
-rw-r--r--src/svg/gnome-canvas-bpath-util.cpp179
-rw-r--r--src/svg/gnome-canvas-bpath-util.h48
-rw-r--r--src/svg/itos.cpp77
-rw-r--r--src/svg/makefile.in17
-rw-r--r--src/svg/round.cpp47
-rw-r--r--src/svg/sp-svg.def26
-rw-r--r--src/svg/stringstream-test.h70
-rw-r--r--src/svg/stringstream.cpp69
-rw-r--r--src/svg/stringstream.h76
-rw-r--r--src/svg/strip-trailing-zeros.cpp42
-rw-r--r--src/svg/strip-trailing-zeros.h20
-rw-r--r--src/svg/svg-affine.cpp265
-rw-r--r--src/svg/svg-color.cpp324
-rw-r--r--src/svg/svg-length.cpp515
-rw-r--r--src/svg/svg-length.h66
-rw-r--r--src/svg/svg-path.cpp710
-rw-r--r--src/svg/svg.h85
25 files changed, 3474 insertions, 0 deletions
diff --git a/src/svg/.cvsignore b/src/svg/.cvsignore
new file mode 100644
index 000000000..e8014d011
--- /dev/null
+++ b/src/svg/.cvsignore
@@ -0,0 +1,5 @@
+Makefile
+Makefile.in
+.deps
+makefile
+.dirstamp
diff --git a/src/svg/HACKING b/src/svg/HACKING
new file mode 100644
index 000000000..e3d8b1053
--- /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/Makefile_insert b/src/svg/Makefile_insert
new file mode 100644
index 000000000..da81537ad
--- /dev/null
+++ b/src/svg/Makefile_insert
@@ -0,0 +1,58 @@
+## Makefile.am fragment sourced by src/Makefile.am.
+
+#
+# Sodipodi svg wrapper
+# Author: Lauris Kaplinski <lauris@ariman.ee>
+# Most code is actually written by Raph Levien
+#
+# Implements svg interpretation of xml tree
+#
+
+svg/all: svg/libspsvg.a
+
+svg/clean:
+ rm -f svg/libspsvg.a $(svg_libspsvg_a_OBJECTS)
+
+svg_libspsvg_a_SOURCES = \
+ svg/css-ostringstream.h \
+ svg/css-ostringstream.cpp \
+ svg/gnome-canvas-bpath-util.cpp \
+ svg/gnome-canvas-bpath-util.h \
+ svg/itos.cpp \
+ svg/round.cpp \
+ svg/stringstream.h \
+ svg/stringstream.cpp \
+ svg/strip-trailing-zeros.h \
+ svg/strip-trailing-zeros.cpp \
+ svg/svg-affine.cpp \
+ svg/svg-color.cpp \
+ svg/svg-length.cpp \
+ svg/svg-path.cpp \
+ svg/svg-types.h \
+ svg/svg.h
+
+
+# This CxxTest stuff is adapted blindly from libnr/Makefile_insert.
+# It would be nice to reduce the amount of boilerplate / copy&paste here.
+svg/test-svg-main.cpp: svg/test-svg.cpp
+ $(top_srcdir)/cxxtest/cxxtestgen.pl --error-printer -root -o svg/test-svg-main.cpp $(svg_test_svg_includes)
+
+svg/test-svg.cpp: $(svg_test_svg_includes) svg/Makefile_insert
+ $(top_srcdir)/cxxtest/cxxtestgen.pl -part -o svg/test-svg.cpp $(svg_test_svg_includes)
+
+svg_test_svg_includes = \
+ $(srcdir)/svg/css-ostringstream-test.h \
+ $(srcdir)/svg/stringstream-test.h
+
+svg_libtest_svg_a_SOURCES = \
+ svg/test-svg.cpp \
+ $(svg_test_svg_includes)
+
+svg_test_svg_SOURCES = \
+ svg/test-svg-main.cpp \
+ $(svg_test_svg_includes)
+
+svg_test_svg_LDADD = \
+ svg/libspsvg.a \
+ svg/libtest-svg.a \
+ -lglib-2.0
diff --git a/src/svg/css-ostringstream-test.h b/src/svg/css-ostringstream-test.h
new file mode 100644
index 000000000..1d407b0e2
--- /dev/null
+++ b/src/svg/css-ostringstream-test.h
@@ -0,0 +1,73 @@
+#include <cxxtest/TestSuite.h>
+#include "svg/css-ostringstream.h"
+
+template<typename T>
+static void
+css_test_datum(T const x, std::string const &exp_str)
+{
+ Inkscape::CSSOStringStream s;
+ s << x;
+ TS_ASSERT_EQUALS(s.str(), exp_str);
+}
+
+static void
+css_test_float(float const x, std::string const &exp_str)
+{
+ css_test_datum(x, exp_str);
+ css_test_datum((double) x, exp_str);
+}
+
+class CSSOStringStreamTest : public CxxTest::TestSuite
+{
+public:
+ void testFloats()
+ {
+ css_test_float(4.5, "4.5");
+ css_test_float(4.0, "4");
+ css_test_float(0.0, "0");
+ css_test_float(-3.75, "-3.75");
+ css_test_float(-2.0625, "-2.0625");
+ css_test_float(-0.0625, "-0.0625");
+ css_test_float(30.0, "30");
+ css_test_float(12345678.0, "12345678");
+ css_test_float(3e9, "3000000000");
+ css_test_float(-3.5e9, "-3500000000");
+ css_test_float(3e-7, "0.0000003");
+ css_test_float(3e-8, "0.00000003");
+ css_test_float(3e-9, "0");
+ css_test_float(32768e9, "32768000000000");
+ css_test_float(-10.5, "-10.5");
+ }
+
+ void testOtherTypes()
+ {
+ css_test_datum('3', "3");
+ css_test_datum('x', "x");
+ css_test_datum((unsigned char) '$', "$");
+ css_test_datum((signed char) 'Z', "Z");
+ css_test_datum(" my string ", " my string ");
+ css_test_datum((signed char const *) "023", "023");
+ css_test_datum((unsigned char const *) "023", "023");
+ }
+
+ void testConcat()
+ {
+ Inkscape::CSSOStringStream s;
+ s << "hello, ";
+ s << -53.5;
+ TS_ASSERT_EQUALS(s.str(), std::string("hello, -53.5"));
+ }
+
+};
+
+
+/*
+ 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:encoding=utf-8:textwidth=99 :
diff --git a/src/svg/css-ostringstream.cpp b/src/svg/css-ostringstream.cpp
new file mode 100644
index 000000000..91b1cfcfb
--- /dev/null
+++ b/src/svg/css-ostringstream.cpp
@@ -0,0 +1,76 @@
+#include "svg/css-ostringstream.h"
+#include "svg/strip-trailing-zeros.h"
+#include <glib/gmessages.h>
+#include <glib/gstrfuncs.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. */
+ ostr.precision(8);
+}
+
+static void
+write_num(Inkscape::CSSOStringStream &os, unsigned const prec, double const d)
+{
+ char buf[32]; // haven't thought about how much is really required.
+ if (prec != 8) {
+ static bool warned;
+ if (!warned) {
+ g_warning("Using precision of 8 instead of the requested %u. Won't re-warn.", prec);
+ warned = true;
+ }
+ }
+ g_ascii_formatd(buf, sizeof(buf), "%.8f", d);
+ os << strip_trailing_zeros(buf);
+}
+
+Inkscape::CSSOStringStream &
+operator<<(Inkscape::CSSOStringStream &os, float const d)
+{
+ /* Try as integer first. */
+ {
+ long const n = long(d);
+ if (d == n) {
+ os << n;
+ return os;
+ }
+ }
+
+ write_num(os, os.precision(), d);
+ return os;
+}
+
+Inkscape::CSSOStringStream &
+operator<<(Inkscape::CSSOStringStream &os, double const d)
+{
+ /* Try as integer first. */
+ {
+ long const n = long(d);
+ if (d == n) {
+ os << n;
+ return os;
+ }
+ }
+
+ write_num(os, os.precision(), d);
+ 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:encoding=utf-8:textwidth=99 :
diff --git a/src/svg/css-ostringstream.h b/src/svg/css-ostringstream.h
new file mode 100644
index 000000000..48ecf2992
--- /dev/null
+++ b/src/svg/css-ostringstream.h
@@ -0,0 +1,80 @@
+#ifndef SVG_CSS_OSTRINGSTREAM_H_INKSCAPE
+#define SVG_CSS_OSTRINGSTREAM_H_INKSCAPE
+
+#include <glib/gtypes.h>
+#include <sstream>
+
+namespace Inkscape {
+
+typedef std::ios_base &(*std_oct_type)(std::ios_base &);
+
+/**
+ * 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();
+
+#define INK_CSS_STR_OP(_t) \
+ CSSOStringStream &operator<<(_t arg) { \
+ ostr << arg; \
+ return *this; \
+ }
+
+ INK_CSS_STR_OP(char)
+ INK_CSS_STR_OP(signed char)
+ INK_CSS_STR_OP(unsigned char)
+ INK_CSS_STR_OP(short)
+ INK_CSS_STR_OP(unsigned short)
+ INK_CSS_STR_OP(int)
+ INK_CSS_STR_OP(unsigned int)
+ INK_CSS_STR_OP(long)
+ INK_CSS_STR_OP(unsigned long)
+ INK_CSS_STR_OP(char const *)
+ INK_CSS_STR_OP(signed char const *)
+ INK_CSS_STR_OP(unsigned char const *)
+ INK_CSS_STR_OP(std::string const &)
+ INK_CSS_STR_OP(std_oct_type)
+
+#undef INK_CSS_STR_OP
+
+ gchar const *gcharp() const {
+ return reinterpret_cast<gchar const *>(ostr.str().c_str());
+ }
+
+ std::string str() const {
+ return ostr.str();
+ }
+
+ std::streamsize precision() const {
+ return ostr.precision();
+ }
+
+ std::streamsize precision(std::streamsize p) {
+ return ostr.precision(p);
+ }
+};
+
+}
+
+Inkscape::CSSOStringStream &operator<<(Inkscape::CSSOStringStream &os, float d);
+
+Inkscape::CSSOStringStream &operator<<(Inkscape::CSSOStringStream &os, double d);
+
+
+#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:encoding=utf-8:textwidth=99 :
diff --git a/src/svg/ftos.cpp b/src/svg/ftos.cpp
new file mode 100644
index 000000000..c468b4c63
--- /dev/null
+++ b/src/svg/ftos.cpp
@@ -0,0 +1,485 @@
+/* //////////////////////////////////////////////////////////////////////
+// ftos.cc
+//
+// Copyright (c) 1996-2003 Bryce W. Harrington [bryce at osdl dot org]
+//
+//-----------------------------------------------------------------------
+// License: This code may be used by anyone for any purpose
+// so long as the copyright notices and this license
+// statement remains attached.
+//-----------------------------------------------------------------------
+//
+// string ftos(double val[, char mode[, int sigfig[, int precision[, int options]]]])
+//
+// DESCRIPTION
+// This routine is intended to replace the typical use of sprintf for
+// converting floating point numbers into strings.
+//
+// To one-up sprintf, an additional mode was created - 'h' mode -
+// which produces numbers in 'engineering notation' - exponents are
+// always shown in multiples of 3. To non-engineers this mode is
+// probably irrelevant, but for engineers (and scientists) it is SOP.
+//
+// One other new feature is an option to use 'x10^' instead of the
+// conventional 'E' for exponental notation. This is entirely for
+// aesthetics since numbers in the 'x10^' form cannot be used as
+// inputs for most programs.
+//
+// For most cases, the routine can simply be used with the defaults
+// and acceptable results will be produced. No fill zeros or trailing
+// zeros are shown, and exponential notation is only used for numbers
+// greater than 1e6 or less than 1e-3.
+//
+// The one area where sprintf may surpass this routine is in width control.
+// No provisions are made in this routine to restrict a number to a
+// certain number of digits (thus allowing the number to be constrained
+// to an 8 space column, for instance.) Along with this, it does not
+// support pre-padding a number with zeros (e.g., '5' -> '0005') and will
+// not post-pad a number with spaces (i.e., allow left-justification.)
+//
+// If width control is this important, then the user will probably want to
+// use the stdio routines, which really is well suited for outputting
+// columns of data with a brief amount of code.
+//
+// PARAMETERS
+// val - number to be converted
+// mode - can be one of four possible values. Default is 'g'
+//
+// e: Produces numbers in scientific notation. One digit
+// is shown on the left side of the decimal, the rest
+// on the right, and the exponential is always shown.
+// EXAMPLE: 1.04e-4
+//
+// f: Produces numbers with fixed format. Number is shown
+// exact, with no exponent.
+// EXAMPLE: 0.000104
+//
+// g: If val is greater than 1e6 or less than 1e-3 it will
+// be shown in 'e' format, otherwise 'f' format will be
+// used.
+//
+// h: Produces numbers in engineering format. Result is
+// identical to 'f' format for numbers between 1 and
+// 1e3, otherwise, the number is shown such that it
+// always begins with a nonzero digit on the left side
+// (unless number equals zero), and the exponential is
+// a multiple of 3.
+// EXAMPLE: 104e-6
+//
+// If the mode is expressed as a capital letter (e.g., 'F')
+// then the exponential part of the number will also be
+// capitalized (e.g., '1E6' or '1X10^6'.)
+//
+// sigfig - the number of significant figures. These are the digits
+// that are "retained". For example, the following numbers
+// all have four sigfigs:
+// 1234 12.34 0.0001234 1.234e-10
+// the last digit shown will be rounded in the standard
+// manner (down if the next digit is less than 5, up otherwise.)
+//
+// precision - the number of digits to show to the right of the decimal.
+// For example, all of the following numbers have precisions
+// of 2:
+// 1234.00 12.34 0.00 1.23e-10 123.40e-12
+//
+// options - several options are allowed to control the look of the
+// output.
+//
+// FORCE_DECIMAL - require the decimal point to be shown for
+// numbers that do not have any fractional digits (or that
+// have a precision set to zero)
+// EXAMPLE: 1.e6
+// FORCE_EXP_ZERO - pad the 10's zero in exponent if necessary
+// EXAMPLE: 1e06
+// FORCE_HUNDRED_EXP_ZERO - pad the 100's zero in exponent if
+// necessary. Also pads 10's zero in exponent if necessary.
+// EXAMPLE: 1e006
+// FORCE_EXP_PLUS - show the '+' in the exponent if exponent
+// is used.
+// EXAMPLE: 1e+6
+// FORCE_EXP - force the output to display the exponent
+// EXAMPLE: 0e0
+// FORCE_X10 - use x10^ instead of E
+// EXAMPLE: 1x10^6
+// FORCE_PLUS - force output of the '+' for positive numbers
+// EXAMPLE: +1e6
+//
+// Options can be combined using the usual OR method. For
+// example,
+//
+// ftos(123.456, 'f', -1, -1, FORCE_PLUS | FORCE_X10 | FORCE_EXP)
+//
+// gives "+123.456x10^0"
+//
+// RETURN VALUE
+// The string representation of the number is returned from the routine.
+// The ANSI C++ Standard "string" class was used for several important
+// reasons. First, because the string class manages it's own space, the
+// ftos routine does not need to concern itself with writing to unallocated
+// areas of memory or with handling memory reallocation internally. Second,
+// it allows return of an object, not a pointer to an object; this may not
+// be as efficient, but it is cleaner and safer than the alternative. Third,
+// the routine's return value can be directly assigned to a variable, i.e.
+// string var = ftos(3.1415);
+// which makes code much easier to comprehend and modify.
+//
+// Internally, the ftos routine uses fairly typical string operators (=, +=,
+// +, etc.) which pretty much any other flavor of string class will define as
+// well. Thus if one does not have access to the ANSI C++ Standard string
+// class, the user can substitute another with little difficulty. (If the
+// alternate class is not named "string" then redefine "string" to whatever
+// you wish to use. For example,
+// #define string CString
+//
+// November 1996 - Bryce Harrington
+// Created ftoa and ftos
+//
+// December 1996 - Bryce Harrington
+// Added engineering notation mode, added sigfig capability, added
+// significant debug code, added options, thoroughly debugged and
+// tested the code.
+//
+//
+// June 1999 - Bryce Harrington
+// Modified to run on Linux for WorldForge
+//
+// March 2003 - Bryce Harrington
+// Removed DTAG() macros - use of fprintf(stderr,...) instead
+// Broke out round/itos/ftos into separate files
+// Removed curses bits
+//
+/////////////////////////////////////////////////////////////////////// */
+
+#include <string>
+
+// This is the routine used for converting a floating point into a string
+// This may be included in stdlib.h on some systems and may conflict.
+// Let me know your system & etc. so I can properly #ifdef this, but
+// try commenting the following four lines out if you run into conflicts.
+// extern "C" {
+// char*
+// ecvt (double val, size_t ndigit, int *decpt, int *sign);
+// }
+
+using namespace std;
+
+#ifndef HAS_ECVT
+#include <cstdio>
+#include <glib.h>
+#endif
+
+
+#include "ftos.h"
+
+#include <iostream>
+
+// This routine counts from the end of a string like '10229000' to find the index
+// of the first non-'0' character (5 would be returned for the above number.)
+int countDigs(char *p)
+{
+ int length =0;
+ while (*(p+length)!='\0') length++; // Count total length
+ while (length>0 && *(p+length-1)=='0') length--; // Scan backwards for a non-'0'
+ return length;
+}
+
+// This routine determines how many digits make up the left hand
+// side of the number if the abs value of the number is greater than 1, or the
+// digits that make up the right hand side if the abs value of the number
+// is between 0 and 1. Returns 1 if v==0. Return value is positive for numbers
+// greater than or equal to 1, negative for numbers less than 0.1, and zero for
+// numbers between 0.1 and 1.
+int countLhsDigits(double v)
+{
+ if (v<0) v = -v; // Take abs value
+ else if (v==0) return 1; // Special case if v==0
+
+ int n=0;
+ for (; v<0.1; v*=10) // Count digits on right hand side (l.t. 0.1)
+ { n--; }
+ for (; v>=1; v/=10) // Count digits on left hand side (g.e. 1.0)
+ { n++; }
+ return n;
+}
+
+// This is the routine that does the work of converting the number into a string.
+string ftos(double val, char mode, int sigfig, int precision, int options)
+{
+ // Parse the options to a more usable form
+ // These options allow the user to control some of the ornaments on the
+ // number that is output. By default they are all false. Turning them
+ // on helps to "fix" the format of the number so it lines up in columns
+ // better.
+ // - require the decimal point to be shown for numbers that do not have
+ // any fractional digits (or that have a precision set to zero
+ bool forceDecimal = (options & FORCE_DECIMAL);
+ // - show the 10's and 100's zero in exponent
+ bool forceExpZero = (options & FORCE_EXP_ZERO);
+ bool forceHundredExpZero = (options & FORCE_HUNDRED_EXP_ZERO);
+ // - show the '+' in the exponent if exponent is used
+ bool forceExpPlus = (options & FORCE_EXP_PLUS);
+ // - force the output to display the exponent
+ bool forceExponent = (options & FORCE_EXP);
+ // - use x10^ instead of E
+ bool forcex10 = (options & FORCE_X10);
+ // - force output of the '+' for positive numbers
+ bool forcePlus = (options & FORCE_PLUS);
+
+#ifdef DEBUG
+ fprintf(stderr, "Options: ");
+ fprintf(stderr, " %4s = %s ", "x10", (forcex10 ? "on" : "off" ));
+ fprintf(stderr, " %4s = %s ", ".", (forceDecimal ? "on" : "off" ));
+ fprintf(stderr, " %4s = %s ", "e0", (forceExpZero ? "on" : "off" ));
+ fprintf(stderr, " %4s = %s ", "e00", (forceHundredExpZero ? "on" : "off" ));
+ fprintf(stderr, " %4s = %s ", "e+", (forceExpPlus ? "on" : "off" ));
+ fprintf(stderr, " %4s = %s ", "e", (forceExponent ? "on" : "off" ));
+ fprintf(stderr, " %4s = %s \n", "+#", (forcePlus ? "on" : "off" ));
+#endif
+
+ // - exponent usage
+ bool useExponent = false;
+
+ // Determine the case for the 'e' (if used)
+ char E = (forcex10)? 'x' : 'e';
+ if (g_ascii_isupper(mode)) {
+ E = g_ascii_toupper(E);
+ mode = g_ascii_tolower(mode);
+ }
+
+ // Determine how many decimals we're interested in
+ int L = countLhsDigits(val);
+
+#ifdef DEBUG
+ fprintf(stderr, "*** L is %s\n", itos(L).c_str());
+#endif
+
+ int count = 0;
+ if (sigfig==0) // bad input - don't want any sigfigs??!!
+ return "";
+ else if (precision>=0) { // Use fixed number of decimal places
+ count = precision;
+ if (mode == 'e') count += 1;
+ else if (mode == 'f') count += L;
+ else if (mode == 'g') count += (L>6 || L<-3)? 1 : L;
+ else if (mode == 'h') count += (L>0)? ((L-1)%3+1) : (L%3+3);
+ if (sigfig>0) count = (sigfig > count)? count : sigfig; // Use sigfig # if it means more decimal places
+ }
+ else if (sigfig>0) // Just use sigfigs
+ count = sigfig;
+ else // prec < 0 and sigfig < 0
+ count = 10;
+#ifdef DEBUG
+ fprintf(stderr, "*** count is %s\n", itos(count).c_str());
+#endif
+
+ // Get number's string rep, sign, and exponent
+ int sign = 0;
+ int decimal=0;
+
+#ifdef HAS_ECVT
+ char *p = ecvt(val, count, &decimal, &sign);
+#else
+ char *p = (char *) g_strdup_printf("%.0f", val);
+ // asprintf(&p, "%.0f", val);
+#endif
+
+#ifdef DEBUG
+ fprintf(stderr, "*** string rep is %s\n", p);
+ fprintf(stderr, "*** decimal is %s\n", itos(decimal).c_str());
+ fprintf(stderr, "*** sign is %s\n", itos(sign).c_str());
+#endif
+
+ // Count the number of relevant digits in the resultant number
+ int dig = countDigs(p);
+ if (dig < sigfig) dig = sigfig;
+
+#ifdef DEBUG
+ fprintf(stderr, "*** digs is %s\n", itos(dig).c_str());
+#endif
+
+ // Determine number of digits to put on left side of the decimal point
+ int lhs=0;
+ // For 'g' mode, decide whether to use 'e' or 'f' format.
+ if (mode=='g') mode = (decimal>6 || decimal<-3)? 'e' : 'f';
+ switch (mode) {
+ case 'e':
+ lhs = 1; // only need one char on left side
+ useExponent = true; // force exponent use
+ break;
+
+ case 'f':
+ lhs = (decimal<1)? 1 : decimal;
+ // use one char on left for num < 1,
+ // otherwise, use the number of decimal places.
+ useExponent = false; // don't want exponent for 'f' format
+ break;
+
+ case 'h':
+ if (val==0.0) // special case for if value is zero exactly.
+ lhs = 0; // this prevents code from returning '000.0'
+ else
+ lhs = (decimal<=0)? (decimal)%3 + 3 : (decimal-1)%3+1;
+ useExponent = !(lhs==decimal); // only use exponent if we really need it
+ break;
+
+ default:
+ return "**bad mode**";
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "*** lhs is %s\n", itos(lhs).c_str());
+#endif
+
+ // Figure out the number of digits to show in the right hand side
+ int rhs=0;
+ if (precision>=0)
+ rhs = precision;
+ else if (val == 0.0)
+ rhs = 0;
+ else if (useExponent || decimal>0)
+ rhs = dig-lhs;
+ else
+ rhs = dig-decimal;
+
+ // can't use a negative rhs value, so turn it to zero if that is the case
+ if (rhs<0) rhs = 0;
+
+#ifdef DEBUG
+ fprintf(stderr, "*** rhs is", itos(rhs).c_str());
+#endif
+
+ // Determine the exponent
+ int exponent = decimal - lhs;
+ if (val==0.0) exponent=0; // prevent zero from getting an exponent
+#ifdef DEBUG
+ fprintf(stderr, "*** exponent is %s\n", itos(exponent).c_str());
+#endif
+
+ string ascii;
+
+ // output the sign
+ if (sign) ascii += "-";
+ else if (forcePlus) ascii += "+";
+
+ // output the left hand side
+ if (!useExponent && decimal<=0) // if fraction, put the 0 out front
+ ascii += '0';
+ else // is either exponential or >= 1, so write the lhs
+ for (; lhs>0; lhs--)
+ ascii += (*p)? *p++ : int('0'); // now fill in the numbers before decimal
+
+#ifdef DEBUG
+ fprintf(stderr, "*** ascii + sign + lhs is %s\n", ascii.c_str());
+#endif
+
+ // output the decimal point
+ if (forceDecimal || rhs>0)
+ ascii += '.';
+
+ // output the right hand side
+ if (!useExponent && rhs>0) // first fill in zeros after dp and before numbers
+ while (decimal++ <0 && rhs-->0)
+ ascii += '0';
+ for (; rhs>0 ; rhs--) // now fill in the numbers after decimal
+ ascii += (*p)? *p++ : int('0');
+
+#ifdef DEBUG
+ fprintf(stderr, "*** ascii + . + rhs is %s\n", ascii.c_str());
+#endif
+
+ if (forceExponent || useExponent) // output the entire exponent if required
+ {
+ ascii += E; // output the E or X
+ if (forcex10) ascii += "10^"; // if using 'x10^' format, output the '10^' part
+
+ // output the exponent's sign
+ if (exponent < 0) { // Negative exponent
+ exponent = -exponent; // make exponent positive if needed
+ ascii += '-'; // output negative sign
+ }
+ else if (forceExpPlus) // We only want the '+' if it is asked for explicitly
+ ascii += '+';
+
+ // output the exponent
+ if (forceHundredExpZero || exponent >= 100)
+ ascii += ( (exponent/100) % 10 + '0' );
+ if (forceHundredExpZero || forceExpZero || exponent >= 10)
+ ascii += ( (exponent/10) % 10 + '0' );
+ ascii += ( exponent % 10 + '0' );
+
+#ifdef DEBUG
+ fprintf(stderr, "*** ascii + exp is %s\n", ascii.c_str());
+#endif
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "*** End of ftos with ascii = ", ascii.c_str());
+#endif
+ /* finally, we can return */
+ return ascii;
+}
+
+#ifdef TESTFTOS
+
+int main()
+{
+ cout << "Normal (g): " << endl;
+ cout << "1.0 = " << ftos(1.0) << endl;
+ cout << "42 = " << ftos(42) << endl;
+ cout << "3.141 = " << ftos(3.141) << endl;
+ cout << "0.01 = " << ftos(0.01) << endl;
+ cout << "1.0e7 = " << ftos(1.0e7) << endl;
+ cout << endl;
+
+ cout << "Scientific (e): " << endl;
+ cout << "1.0 = " << ftos(1.0, 'e') << endl;
+ cout << "42 = " << ftos(42, 'e') << endl;
+ cout << "3.141 = " << ftos(3.141, 'e') << endl;
+ cout << "0.01 = " << ftos(0.01, 'e') << endl;
+ cout << "1.0e7 = " << ftos(1.0e7, 'e') << endl;
+ cout << endl;
+
+ cout << "Fixed (f): " << endl;
+ cout << "1.0 = " << ftos(1.0, 'f') << endl;
+ cout << "42 = " << ftos(42, 'f') << endl;
+ cout << "3.141 = " << ftos(3.141, 'f') << endl;
+ cout << "0.01 = " << ftos(0.01, 'f') << endl;
+ cout << "1.0e7 = " << ftos(1.0e7, 'f') << endl;
+ cout << endl;
+
+ cout << "Engineering (h): " << endl;
+ cout << "1.0 = " << ftos(1.0, 'h') << endl;
+ cout << "42 = " << ftos(42, 'h') << endl;
+ cout << "3.141 = " << ftos(3.141, 'h') << endl;
+ cout << "0.01 = " << ftos(0.01, 'h') << endl;
+ cout << "1.0e7 = " << ftos(1.0e7, 'h') << endl;
+ cout << endl;
+
+ cout << "Sigfigs: " << endl;
+ cout << "2 sf = " << ftos(1234, 'g', 2) << " "
+ << ftos(12.34, 'g', 2) << " "
+ << ftos(0, 'g', 2) << " "
+ << ftos(123.4e-11, 'g', 2) << endl;
+ cout << "4 sf = " << ftos(1234, 'g', 4) << " "
+ << ftos(12.34, 'g', 4) << " "
+ << ftos(0, 'g', 4) << " "
+ << ftos(123.4e-11, 'g', 4) << endl;
+ cout << "8 sf = " << ftos(1234, 'g', 8) << " "
+ << ftos(12.34, 'g', 8) << " "
+ << ftos(0, 'g', 8) << " "
+ << ftos(123.4e-11, 'g', 8) << endl;
+ cout << endl;
+
+ cout << "x10 mode: " << endl;
+ cout << "1234 = " << ftos(1234, 'e', 4, -1, FORCE_X10 | FORCE_EXP) << endl;
+ cout << "1.01e10 = " << ftos(1.01e10, 'h', -1, -1, FORCE_X10 | FORCE_EXP) << endl;
+ cout << endl;
+
+ cout << "itos tests..." << endl;
+ cout << "42 = " << itos(42) << endl;
+ cout << endl;
+
+ return 0;
+}
+
+#endif // TESTFTOS
diff --git a/src/svg/ftos.h b/src/svg/ftos.h
new file mode 100644
index 000000000..888def639
--- /dev/null
+++ b/src/svg/ftos.h
@@ -0,0 +1,54 @@
+/////////////////////////////////////////////////////////////////////////
+// ftos.h
+//
+// Copyright (c) 1996 Bryce W. Harrington - bryce@neptune.net
+//
+//-----------------------------------------------------------------------
+// License: This code may be used by anyone for any purpose
+// so long as the copyright notices and this license
+// statement remains attached.
+//-----------------------------------------------------------------------
+// Description of file contents
+// 1993 - Bryce Harrington
+// Created initial ftoa routine
+//
+// October 1996 - Bryce Harrington
+// Created itos from code taken from Kernighan & Ritchie
+// _The C Programming Language_ 2nd edition
+//
+// November 1996 - Bryce Harrington
+// Created new ftoa and ftos
+//
+// July 1999 - Bryce Harrington
+// Ported to Linux for use in WorldForge
+//
+// January 2000 - Karsten Laux klaux@rhrk.uni-kl.de
+// added ultos - convering ulong to string
+//
+/////////////////////////////////////////////////////////////////////////
+#ifndef ftos_h
+#define ftos_h
+
+#include <string>
+
+// ftos routine
+const int FORCE_X10 = (1 << 0);
+const int FORCE_DECIMAL = (1 << 1);
+const int FORCE_EXP_ZERO = (1 << 2);
+const int FORCE_HUNDRED_EXP_ZERO = (1 << 3);
+const int FORCE_EXP_PLUS = (1 << 4);
+const int FORCE_EXP = (1 << 5);
+const int FORCE_PLUS = (1 << 6);
+
+///
+std::string ftos(double val, char mode='g', int sigfig=-1, int precision=-1, int options=0);
+///
+std::string itos(int n);
+///
+std::string ultos(unsigned long n);
+///
+double rround(double x); // use rounding rule -> x to nearest int.
+///
+double rround(double x, int k); // round to the kth place
+
+#endif // ftos_h
diff --git a/src/svg/gnome-canvas-bpath-util.cpp b/src/svg/gnome-canvas-bpath-util.cpp
new file mode 100644
index 000000000..b583e1a66
--- /dev/null
+++ b/src/svg/gnome-canvas-bpath-util.cpp
@@ -0,0 +1,179 @@
+/* GnomeCanvas Bezier polyline paths & segments
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Lauris Kaplinski <lauris@ariman.ee>
+ * Raph Levien <raph@acm.org>
+ */
+
+#include <glib/gmem.h>
+#include <glib/gmessages.h>
+
+#include "libnr/n-art-bpath.h"
+#include "gnome-canvas-bpath-util.h"
+
+GnomeCanvasBpathDef *
+gnome_canvas_bpath_def_new (void)
+{
+ GnomeCanvasBpathDef *bpd;
+
+ bpd = g_new (GnomeCanvasBpathDef, 1);
+ bpd->n_bpath = 0;
+ bpd->n_bpath_max = 16;
+ bpd->moveto_idx = -1;
+ bpd->bpath = g_new (NArtBpath, bpd->n_bpath_max);
+ bpd->ref_count = 1;
+
+ return bpd;
+}
+
+GnomeCanvasBpathDef *
+gnome_canvas_bpath_def_new_from (NArtBpath *path)
+{
+ GnomeCanvasBpathDef *bpd;
+ int i;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ bpd = g_new (GnomeCanvasBpathDef, 1);
+
+ for (i = 0; path [i].code != NR_END; i++)
+ ;
+ bpd->n_bpath = i;
+ bpd->n_bpath_max = i;
+ bpd->moveto_idx = -1;
+ bpd->ref_count = 1;
+ bpd->bpath = g_new (NArtBpath, i);
+
+ memcpy (bpd->bpath, path, i * sizeof (NArtBpath));
+ return bpd;
+}
+
+GnomeCanvasBpathDef *
+gnome_canvas_bpath_def_ref (GnomeCanvasBpathDef *bpd)
+{
+ g_return_val_if_fail (bpd != NULL, NULL);
+
+ bpd->ref_count += 1;
+ return bpd;
+}
+
+void
+gnome_canvas_bpath_def_free (GnomeCanvasBpathDef *bpd)
+{
+ g_return_if_fail (bpd != NULL);
+
+ bpd->ref_count -= 1;
+ if (bpd->ref_count == 0) {
+ g_free (bpd->bpath);
+ g_free (bpd);
+ }
+}
+
+void
+gnome_canvas_bpath_def_moveto (GnomeCanvasBpathDef *bpd, double x, double y)
+{
+ NArtBpath *bpath;
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+
+ n_bpath = bpd->n_bpath++;
+
+ if (n_bpath == bpd->n_bpath_max)
+ bpd->bpath = (NArtBpath*)g_realloc (bpd->bpath,
+ (bpd->n_bpath_max <<= 1) * sizeof (NArtBpath));
+ bpath = bpd->bpath;
+ bpath[n_bpath].code = NR_MOVETO_OPEN;
+ bpath[n_bpath].x3 = x;
+ bpath[n_bpath].y3 = y;
+ bpd->moveto_idx = n_bpath;
+}
+
+void
+gnome_canvas_bpath_def_lineto (GnomeCanvasBpathDef *bpd, double x, double y)
+{
+ NArtBpath *bpath;
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+ g_return_if_fail (bpd->moveto_idx >= 0);
+
+ n_bpath = bpd->n_bpath++;
+
+ if (n_bpath == bpd->n_bpath_max)
+ bpd->bpath = (NArtBpath*)g_realloc (bpd->bpath,
+ (bpd->n_bpath_max <<= 1) * sizeof (NArtBpath));
+ bpath = bpd->bpath;
+ bpath[n_bpath].code = NR_LINETO;
+ bpath[n_bpath].x3 = x;
+ bpath[n_bpath].y3 = y;
+}
+
+void
+gnome_canvas_bpath_def_curveto (GnomeCanvasBpathDef *bpd, double x1, double y1, double x2, double y2, double x3, double y3)
+{
+ NArtBpath *bpath;
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+ g_return_if_fail (bpd->moveto_idx >= 0);
+
+ n_bpath = bpd->n_bpath++;
+
+ if (n_bpath == bpd->n_bpath_max)
+ bpd->bpath = (NArtBpath*)g_realloc (bpd->bpath,
+ (bpd->n_bpath_max <<= 1) * sizeof (NArtBpath));
+ bpath = bpd->bpath;
+ bpath[n_bpath].code = NR_CURVETO;
+ bpath[n_bpath].x1 = x1;
+ bpath[n_bpath].y1 = y1;
+ bpath[n_bpath].x2 = x2;
+ bpath[n_bpath].y2 = y2;
+ bpath[n_bpath].x3 = x3;
+ bpath[n_bpath].y3 = y3;
+}
+
+void
+gnome_canvas_bpath_def_closepath (GnomeCanvasBpathDef *bpd)
+{
+ NArtBpath *bpath;
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+ g_return_if_fail (bpd->moveto_idx >= 0);
+ g_return_if_fail (bpd->n_bpath > 0);
+
+ bpath = bpd->bpath;
+ n_bpath = bpd->n_bpath;
+
+ /* Add closing vector if we need it. */
+ if (bpath[n_bpath - 1].x3 != bpath[bpd->moveto_idx].x3 ||
+ bpath[n_bpath - 1].y3 != bpath[bpd->moveto_idx].y3) {
+ gnome_canvas_bpath_def_lineto (bpd, bpath[bpd->moveto_idx].x3,
+ bpath[bpd->moveto_idx].y3);
+ bpath = bpd->bpath;
+ }
+ bpath[bpd->moveto_idx].code = NR_MOVETO;
+ bpd->moveto_idx = -1;
+}
+
+void
+gnome_canvas_bpath_def_art_finish (GnomeCanvasBpathDef *bpd)
+{
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+
+ n_bpath = bpd->n_bpath++;
+
+ if (n_bpath == bpd->n_bpath_max)
+ bpd->bpath = (NArtBpath*)g_realloc (bpd->bpath,
+ (bpd->n_bpath_max <<= 1) * sizeof (NArtBpath));
+ bpd->bpath [n_bpath].code = NR_END;
+}
+
diff --git a/src/svg/gnome-canvas-bpath-util.h b/src/svg/gnome-canvas-bpath-util.h
new file mode 100644
index 000000000..62d47c178
--- /dev/null
+++ b/src/svg/gnome-canvas-bpath-util.h
@@ -0,0 +1,48 @@
+/* GnomeCanvas Bezier polyline paths & segments
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Lauris Kaplinski <lauris@ariman.ee>
+ * Raph Levien <raph@acm.org>
+ */
+
+#ifndef PATH_UTIL_H
+#define PATH_UTIL_H
+
+struct NArtBpath;
+
+struct GnomeCanvasBpathDef {
+ int ref_count;
+ NArtBpath *bpath;
+ int n_bpath;
+ int n_bpath_max;
+ int moveto_idx;
+};
+
+
+GnomeCanvasBpathDef *gnome_canvas_bpath_def_new (void);
+GnomeCanvasBpathDef *gnome_canvas_bpath_def_new_from (NArtBpath *bpath);
+GnomeCanvasBpathDef *gnome_canvas_bpath_def_ref (GnomeCanvasBpathDef *bpd);
+
+#define gnome_canvas_bpath_def_unref gnome_canvas_bpath_def_free
+void gnome_canvas_bpath_def_free (GnomeCanvasBpathDef *bpd);
+
+void gnome_canvas_bpath_def_moveto (GnomeCanvasBpathDef *bpd,
+ double x, double y);
+void gnome_canvas_bpath_def_lineto (GnomeCanvasBpathDef *bpd,
+ double x, double y);
+void gnome_canvas_bpath_def_curveto (GnomeCanvasBpathDef *bpd,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3);
+void gnome_canvas_bpath_def_closepath (GnomeCanvasBpathDef *bpd);
+
+void gnome_canvas_bpath_def_art_finish (GnomeCanvasBpathDef *bpd);
+
+
+
+#endif
diff --git a/src/svg/itos.cpp b/src/svg/itos.cpp
new file mode 100644
index 000000000..9b1232130
--- /dev/null
+++ b/src/svg/itos.cpp
@@ -0,0 +1,77 @@
+/////////////////////////////////////////////////////////////////////////
+// ftoa.cpp
+//
+// Copyright (c) 1996-2003 Bryce W. Harrington [bryce at osdl dot org]
+//
+//-----------------------------------------------------------------------
+// License: This code may be used by anyone for any purpose
+// so long as the copyright notices and this license
+// statement remains attached.
+//-----------------------------------------------------------------------
+//
+// This routine converts an integer into a string
+//
+/////////////////////////////////////////////////////////////////////////
+
+// Standard include files
+#include <string> // for string
+
+using namespace std;
+
+string itos(int n)
+{
+ int sign;
+ string s;
+
+ if ((sign = n) < 0) // record sign
+ n = -n; // make n positive
+ do { // generate digits in reverse order
+ s += (char(n % 10) + '0'); // get next digit
+ } while ((n/=10) > 0); // delete it
+
+ if (sign < 0)
+ s += '-';
+
+ reverse(s.begin(), s.end()); // This is what the code should look like
+ // if the string class is compatible with
+ // the standard C++ string class
+#ifdef DUMB_OS_LIKE_WINDOWS
+ // In Windows, we'll use this hack...
+ for (int i=0, j=s.GetLength()-1; i<j; i++, j--)
+ {
+ char c = s[i];
+// s[i] = s[j];
+// s[j] = c;
+ s.SetAt(i, s[j]);
+ s.SetAt(j, c);
+ }
+#endif
+
+ return s;
+}
+
+string ultos(unsigned long n)
+{
+ string s;
+
+ do { // generate digits in reverse order
+ s += (char(n % 10) + '0'); // get next digit
+ } while ((n/=10) > 0); // delete it
+
+ reverse(s.begin(), s.end()); // This is what the code should look like
+ // if the string class is compatible with
+ // the standard C++ string class
+#ifdef DUMB_OS_LIKE_WINDOWS
+ // In Windows, we'll use this hack...
+ for (int i=0, j=s.GetLength()-1; i<j; i++, j--)
+ {
+ char c = s[i];
+// s[i] = s[j];
+// s[j] = c;
+ s.SetAt(i, s[j]);
+ s.SetAt(j, c);
+ }
+#endif
+
+ return s;
+}
diff --git a/src/svg/makefile.in b/src/svg/makefile.in
new file mode 100644
index 000000000..edb8b7100
--- /dev/null
+++ b/src/svg/makefile.in
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.
+
+@SET_MAKE@
+
+# Explicit so that it's the default rule.
+all:
+ cd .. && $(MAKE) svg/all
+
+clean %.a %.o:
+ cd .. && $(MAKE) svg/$@
+
+.PHONY: all clean
+
+OBJEXT = @OBJEXT@
+
+.SUFFIXES:
+.SUFFIXES: .a .$(OBJEXT)
diff --git a/src/svg/round.cpp b/src/svg/round.cpp
new file mode 100644
index 000000000..9e7b91e4e
--- /dev/null
+++ b/src/svg/round.cpp
@@ -0,0 +1,47 @@
+/////////////////////////////////////////////////////////////////////////
+// ftos.cc
+//
+// Copyright (c) 1996-2003 Bryce W. Harrington [bryce at osdl dot org]
+//
+//-----------------------------------------------------------------------
+// License: This code may be used by anyone for any purpose
+// so long as the copyright notices and this license
+// statement remains attached.
+//-----------------------------------------------------------------------
+// This routine rounds a double using the "rounding rule", as expressed
+// in _Advanced Engineering Mathematics_ by Erwin Kreyszig, 6th ed.,
+// John Wiley & Sons, Inc., 1988, page 945.
+//
+// Discard the (k+1)th and all subsequent decimals.
+// (a) If the number thus discarded is less than half a unit in the
+// kth place, leave the kth decimal unchanged ("rounding down")
+// (b) If it is greater than half a unit in the kth place, add one
+// to the kth decimal ("rounding up")
+// (c) If it is exactly half a unit, round off to the nearest *even*
+// decimal.
+// Example: Rounding off 3.45 and 3.55 by one decimal gives 3.4 and
+// 3.6, respectively.
+// Rule (c) is to ensure that in discarding exactly half a decimal,
+// rounding up and rounding down happens about equally often,
+// on the average.
+///////////////////////////////////////////////////////////////////////
+#include <math.h>
+
+double rround(double x)
+{
+ double xlow = floor(x);
+ if (x - xlow != 0.5000)
+ return floor(x + 0.5000);
+ else if ( floor(x/2.0) == xlow/2.0)
+ return xlow;
+ else
+ return xlow++;
+}
+
+// This version allows rounding to a specific digit
+double rround(double x, int k)
+{
+ if (k==0) return rround(x);
+ else return rround(x*pow(10,k)) / pow(10,k);
+}
+
diff --git a/src/svg/sp-svg.def b/src/svg/sp-svg.def
new file mode 100644
index 000000000..2f013f218
--- /dev/null
+++ b/src/svg/sp-svg.def
@@ -0,0 +1,26 @@
+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-test.h b/src/svg/stringstream-test.h
new file mode 100644
index 000000000..6612f3fd2
--- /dev/null
+++ b/src/svg/stringstream-test.h
@@ -0,0 +1,70 @@
+#include <cxxtest/TestSuite.h>
+#include "svg/stringstream.h"
+
+template<typename T>
+static void
+svg_test_datum(T const x, std::string const &exp_str)
+{
+ Inkscape::SVGOStringStream s;
+ s << x;
+ TS_ASSERT_EQUALS(s.str(), exp_str);
+}
+
+static void
+svg_test_float(float const x, std::string const &exp_str)
+{
+ svg_test_datum(x, exp_str);
+ svg_test_datum((double) x, exp_str);
+}
+
+class StringStreamTest : public CxxTest::TestSuite
+{
+public:
+ void testFloats()
+ {
+ svg_test_float(4.5, "4.5");
+ svg_test_float(4.0, "4");
+ svg_test_float(0.0, "0");
+ svg_test_float(-3.75, "-3.75");
+ svg_test_float(-2.0625, "-2.0625");
+ svg_test_float(-0.0625, "-0.0625");
+ svg_test_float(30.0, "30");
+ svg_test_float(12345678.0, "12345678");
+ svg_test_float(3e9, "3e+09");
+ svg_test_float(-3.5e9, "-3.5e+09");
+ svg_test_float(32768e9, "3.2768e+13");
+ svg_test_float(-10.5, "-10.5");
+ }
+
+ void testOtherTypes()
+ {
+ svg_test_datum('3', "3");
+ svg_test_datum('x', "x");
+ svg_test_datum((unsigned char) '$', "$");
+ svg_test_datum((signed char) 'Z', "Z");
+ svg_test_datum(" my string ", " my string ");
+ svg_test_datum((signed char const *) "023", "023");
+ svg_test_datum((unsigned char const *) "023", "023");
+ }
+
+ void testConcat()
+ {
+ Inkscape::SVGOStringStream s;
+ s << "hello, ";
+ s << -53.5;
+ TS_ASSERT_EQUALS(s.str(), std::string("hello, -53.5"));
+ }
+
+};
+
+
+/*
+ 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:encoding=utf-8:textwidth=99 :
diff --git a/src/svg/stringstream.cpp b/src/svg/stringstream.cpp
new file mode 100644
index 000000000..3cbfcd86b
--- /dev/null
+++ b/src/svg/stringstream.cpp
@@ -0,0 +1,69 @@
+#include "svg/stringstream.h"
+#include "svg/strip-trailing-zeros.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. */
+ ostr.precision(8);
+}
+
+Inkscape::SVGOStringStream &
+operator<<(Inkscape::SVGOStringStream &os, float d)
+{
+ /* Try as integer first. */
+ {
+ long const n = long(d);
+ if (d == n) {
+ os << n;
+ return os;
+ }
+ }
+
+ std::ostringstream s;
+ s.imbue(std::locale::classic());
+ s.setf(std::ios::showpoint);
+ s.precision(os.precision());
+ s << d;
+ os << strip_trailing_zeros(s.str());
+ return os;
+}
+
+Inkscape::SVGOStringStream &
+operator<<(Inkscape::SVGOStringStream &os, double d)
+{
+ /* Try as integer first. */
+ {
+ long const n = long(d);
+ if (d == n) {
+ os << n;
+ return os;
+ }
+ }
+
+ std::ostringstream s;
+ s.imbue(std::locale::classic());
+ s.setf(std::ios::showpoint);
+ s.precision(os.precision());
+ s << d;
+ os << strip_trailing_zeros(s.str());
+ 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:encoding=utf-8:textwidth=99 :
diff --git a/src/svg/stringstream.h b/src/svg/stringstream.h
new file mode 100644
index 000000000..34cbdc2bd
--- /dev/null
+++ b/src/svg/stringstream.h
@@ -0,0 +1,76 @@
+#ifndef INKSCAPE_STRINGSTREAM_H
+#define INKSCAPE_STRINGSTREAM_H
+
+#include <glib/gtypes.h>
+#include <sstream>
+
+namespace Inkscape {
+
+typedef std::ios_base &(*std_oct_type)(std::ios_base &);
+
+class SVGOStringStream {
+private:
+ std::ostringstream ostr;
+
+public:
+ SVGOStringStream();
+
+#define INK_SVG_STR_OP(_t) \
+ SVGOStringStream &operator<<(_t arg) { \
+ ostr << arg; \
+ return *this; \
+ }
+
+ INK_SVG_STR_OP(char)
+ INK_SVG_STR_OP(signed char)
+ INK_SVG_STR_OP(unsigned char)
+ INK_SVG_STR_OP(short)
+ INK_SVG_STR_OP(unsigned short)
+ INK_SVG_STR_OP(int)
+ INK_SVG_STR_OP(unsigned int)
+ INK_SVG_STR_OP(long)
+ INK_SVG_STR_OP(unsigned long)
+ INK_SVG_STR_OP(char const *)
+ INK_SVG_STR_OP(signed char const *)
+ INK_SVG_STR_OP(unsigned char const *)
+ INK_SVG_STR_OP(std::string const &)
+ INK_SVG_STR_OP(std_oct_type)
+
+#undef INK_SVG_STR_OP
+
+ gchar const *gcharp() const {
+ return reinterpret_cast<gchar const *>(ostr.str().c_str());
+ }
+
+ std::string str() const {
+ return ostr.str();
+ }
+
+ std::streamsize precision() const {
+ return ostr.precision();
+ }
+
+ std::streamsize precision(std::streamsize p) {
+ return ostr.precision(p);
+ }
+};
+
+}
+
+Inkscape::SVGOStringStream &operator<<(Inkscape::SVGOStringStream &os, float d);
+
+Inkscape::SVGOStringStream &operator<<(Inkscape::SVGOStringStream &os, double d);
+
+
+#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:encoding=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 000000000..79b266b42
--- /dev/null
+++ b/src/svg/strip-trailing-zeros.cpp
@@ -0,0 +1,42 @@
+#include "svg/strip-trailing-zeros.h"
+#include <glib/gmessages.h>
+using std::string;
+
+string
+strip_trailing_zeros(string str)
+{
+ string::size_type p_ix = str.find('.');
+ if (p_ix != string::npos) {
+ 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.
+ */
+ string::size_type nz_ix = str.find_last_not_of('0', (e_ix == string::npos
+ ? e_ix
+ : e_ix - 1));
+ if (nz_ix == 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 == 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:encoding=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 000000000..53f6395a2
--- /dev/null
+++ b/src/svg/strip-trailing-zeros.h
@@ -0,0 +1,20 @@
+#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:encoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg-affine.cpp b/src/svg/svg-affine.cpp
new file mode 100644
index 000000000..660809452
--- /dev/null
+++ b/src/svg/svg-affine.cpp
@@ -0,0 +1,265 @@
+#define __SP_SVG_AFFINE_C__
+
+/*
+ * 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, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib/gstrfuncs.h>
+#include <libnr/nr-matrix-fns.h>
+#include <libnr/nr-matrix-ops.h>
+#include <libnr/nr-matrix-translate-ops.h>
+#include <libnr/nr-rotate-fns.h>
+#include <libnr/nr-rotate-matrix-ops.h>
+#include <libnr/nr-scale-matrix-ops.h>
+#include <libnr/nr-translate-matrix-ops.h>
+#include <libnr/nr-translate-rotate-ops.h>
+#include "svg.h"
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846
+#endif
+
+bool
+sp_svg_transform_read(gchar const *str, NR::Matrix *transform)
+{
+ int idx;
+ char keyword[32];
+ double args[6];
+ int n_args;
+ size_t key_len;
+
+ if (str == NULL) return false;
+
+ NR::Matrix a(NR::identity());
+
+ idx = 0;
+ while (str[idx]) {
+ /* skip initial whitespace */
+ while (g_ascii_isspace (str[idx])) idx++;
+
+ /* parse keyword */
+ for (key_len = 0; key_len < sizeof (keyword); key_len++) {
+ char c;
+
+ c = str[idx];
+ if (g_ascii_isalpha (c) || c == '-') {
+ keyword[key_len] = str[idx++];
+ } else {
+ break;
+ }
+ }
+ if (key_len >= sizeof (keyword)) return false;
+ keyword[key_len] = '\0';
+
+ /* skip whitespace */
+ while (g_ascii_isspace (str[idx])) idx++;
+
+ if (str[idx] != '(') return false;
+ idx++;
+
+ for (n_args = 0; ; n_args++) {
+ char c;
+ char *end_ptr;
+
+ /* skip whitespace */
+ while (g_ascii_isspace (str[idx])) idx++;
+ c = str[idx];
+ if (g_ascii_isdigit (c) || c == '+' || c == '-' || c == '.') {
+ if (n_args == sizeof (args) / sizeof (args[0])) return false; /* Too many args */
+ args[n_args] = g_ascii_strtod (str + idx, &end_ptr);
+
+ //printf("took %d chars from '%s' to make %f\n",
+ // end_ptr-(str+idx),
+ // str+idx,
+ // args[n_args]);
+
+ idx = end_ptr - (char *) str;
+
+ while (g_ascii_isspace (str[idx])) idx++;
+
+ /* skip optional comma */
+ if (str[idx] == ',') idx++;
+ } else if (c == ')') {
+ break;
+ } else {
+ return false;
+ }
+ }
+ idx++;
+
+ /* ok, have parsed keyword and args, now modify the transform */
+ if (!strcmp (keyword, "matrix")) {
+ if (n_args != 6) return false;
+ a = NR_MATRIX_D_FROM_DOUBLE(args) * a;
+ } else if (!strcmp (keyword, "translate")) {
+ if (n_args == 1) {
+ args[1] = 0;
+ } else if (n_args != 2) {
+ return false;
+ }
+ a = NR::translate(args[0], args[1]) * a;
+ } else if (!strcmp (keyword, "scale")) {
+ if (n_args == 1) {
+ args[1] = args[0];
+ } else if (n_args != 2) {
+ return false;
+ }
+ a = NR::scale(args[0], args[1]) * a;
+ } else if (!strcmp (keyword, "rotate")) {
+ if (n_args != 1 && n_args != 3) {
+ return false;
+ }
+ NR::rotate const rot(rotate_degrees(args[0]));
+ if (n_args == 3) {
+ a = ( NR::translate(-args[1], -args[2])
+ * rot
+ * NR::translate(args[1], args[2])
+ * a );
+ } else {
+ a = rot * a;
+ }
+ } else if (!strcmp (keyword, "skewX")) {
+ if (n_args != 1) return false;
+ a = ( NR::Matrix(1, 0,
+ tan(args[0] * M_PI / 180.0), 1,
+ 0, 0)
+ * a );
+ } else if (!strcmp (keyword, "skewY")) {
+ if (n_args != 1) return false;
+ a = ( NR::Matrix(1, tan(args[0] * M_PI / 180.0),
+ 0, 1,
+ 0, 0)
+ * a );
+ } else {
+ return false; /* unknown keyword */
+ }
+ /* Skip trailing whitespace */
+ while (g_ascii_isspace (str[idx])) idx++;
+ }
+
+ *transform = a;
+ return true;
+}
+
+#define EQ(a,b) (fabs ((a) - (b)) < 1e-9)
+
+unsigned
+sp_svg_transform_write(gchar str[], unsigned const size, NR::Matrix const &transform)
+{
+ NRMatrix const t(transform);
+ return sp_svg_transform_write(str, size, &t);
+}
+
+unsigned
+sp_svg_transform_write(gchar str[], unsigned const size, NRMatrix const *transform)
+{
+ double e;
+
+ if (!transform) {
+ *str = 0;
+ return 0;
+ }
+
+ e = 0.000001 * NR_MATRIX_DF_EXPANSION (transform);
+
+ /* fixme: We could use t1 * t1 + t2 * t2 here instead */
+ if (NR_DF_TEST_CLOSE (transform->c[1], 0.0, e) && NR_DF_TEST_CLOSE (transform->c[2], 0.0, e)) {
+ if (NR_DF_TEST_CLOSE (transform->c[4], 0.0, e) && NR_DF_TEST_CLOSE (transform->c[5], 0.0, e)) {
+ if (NR_DF_TEST_CLOSE (transform->c[0], 1.0, e) && NR_DF_TEST_CLOSE (transform->c[3], 1.0, e)) {
+ /* We are more or less identity */
+ *str = 0;
+ return 0;
+ } else {
+ /* We are more or less scale */
+ gchar c[256];
+ unsigned p = 0;
+ strcpy (c + p, "scale(");
+ p += 6;
+ p += sp_svg_number_write_de (c + p, transform->c[0], 6, FALSE);
+ c[p++] = ',';
+ p += sp_svg_number_write_de (c + p, transform->c[3], 6, FALSE);
+ c[p++] = ')';
+ g_assert( p <= sizeof(c) );
+ p = MIN (p, size - 1 );
+ memcpy (str, c, p);
+ str[p] = 0;
+ return p;
+ }
+ } else {
+ if (NR_DF_TEST_CLOSE (transform->c[0], 1.0, e) && NR_DF_TEST_CLOSE (transform->c[3], 1.0, e)) {
+ /* We are more or less translate */
+ gchar c[256];
+ unsigned p = 0;
+ strcpy (c + p, "translate(");
+ p += 10;
+ p += sp_svg_number_write_de (c + p, transform->c[4], 6, FALSE);
+ c[p++] = ',';
+ p += sp_svg_number_write_de (c + p, transform->c[5], 6, FALSE);
+ c[p++] = ')';
+ g_assert( p <= sizeof(c) );
+ p = MIN(p, size - 1);
+ memcpy (str, c, p);
+ str[p] = 0;
+ return p;
+ } else {
+ gchar c[256];
+ unsigned p = 0;
+ strcpy (c + p, "matrix(");
+ p += 7;
+ p += sp_svg_number_write_de (c + p, transform->c[0], 6, FALSE);
+ c[p++] = ',';
+ p += sp_svg_number_write_de (c + p, transform->c[1], 6, FALSE);
+ c[p++] = ',';
+ p += sp_svg_number_write_de (c + p, transform->c[2], 6, FALSE);
+ c[p++] = ',';
+ p += sp_svg_number_write_de (c + p, transform->c[3], 6, FALSE);
+ c[p++] = ',';
+ p += sp_svg_number_write_de (c + p, transform->c[4], 6, FALSE);
+ c[p++] = ',';
+ p += sp_svg_number_write_de (c + p, transform->c[5], 6, FALSE);
+ c[p++] = ')';
+ g_assert( p <= sizeof(c) );
+ p = MIN(p, size - 1);
+ memcpy (str, c, p);
+ str[p] = 0;
+ return p;
+ }
+ }
+ } else {
+ gchar c[256];
+ unsigned p = 0;
+ strcpy (c + p, "matrix(");
+ p += 7;
+ p += sp_svg_number_write_de (c + p, transform->c[0], 6, FALSE);
+ c[p++] = ',';
+ p += sp_svg_number_write_de (c + p, transform->c[1], 6, FALSE);
+ c[p++] = ',';
+ p += sp_svg_number_write_de (c + p, transform->c[2], 6, FALSE);
+ c[p++] = ',';
+ p += sp_svg_number_write_de (c + p, transform->c[3], 6, FALSE);
+ c[p++] = ',';
+ p += sp_svg_number_write_de (c + p, transform->c[4], 6, FALSE);
+ c[p++] = ',';
+ p += sp_svg_number_write_de (c + p, transform->c[5], 6, FALSE);
+ c[p++] = ')';
+ g_assert( p <= sizeof(c) );
+ p = MIN(p, size - 1);
+ memcpy (str, c, p);
+ str[p] = 0;
+ return p;
+ }
+}
+
diff --git a/src/svg/svg-color.cpp b/src/svg/svg-color.cpp
new file mode 100644
index 000000000..58f206422
--- /dev/null
+++ b/src/svg/svg-color.cpp
@@ -0,0 +1,324 @@
+#define __SP_SVG_COLOR_C__
+
+/*
+ * SVG data parser
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <math.h>
+#include <glib/gstrfuncs.h>
+#include <glib/ghash.h>
+#include <glib/gprintf.h>
+#include "svg.h"
+
+struct SPSVGColor {
+ unsigned long rgb;
+ char const *name;
+};
+
+/*
+ * These are the colors defined in the SVG standard
+ */
+static SPSVGColor 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" },
+ { 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" }
+};
+
+#define SP_SVG_NUMCOLORS (sizeof (sp_svg_color_named) / sizeof (sp_svg_color_named[0]))
+
+static GHashTable *sp_svg_create_color_hash (void);
+
+guint32
+sp_svg_read_color (const gchar *str, guint32 def)
+{
+ static GHashTable *colors = NULL;
+ gchar c[32];
+ guint32 val = 0;
+
+ /*
+ * todo: handle the rgb (r, g, b) and rgb ( r%, g%, b%), syntax
+ * defined in http://www.w3.org/TR/REC-CSS2/syndata.html#color-units
+ */
+
+ if (str == NULL) 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;
+ }
+ } else if (!strncmp (str, "rgb(", 4)) {
+ gboolean 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;
+ } else {
+ hasd = TRUE;
+ }
+ if (hasp && hasd) return def;
+ if (hasp) {
+ val = (guint) floor (CLAMP (r, 0.0, 100.0) * 2.559999) << 24;
+ val |= ((guint) floor (CLAMP (g, 0.0, 100.0) * 2.559999) << 16);
+ val |= ((guint) floor (CLAMP (b, 0.0, 100.0) * 2.559999) << 8);
+ } else {
+ val = (guint) CLAMP (r, 0, 255) << 24;
+ val |= ((guint) CLAMP (g, 0, 255) << 16);
+ val |= ((guint) CLAMP (b, 0, 255) << 8);
+ }
+ return val;
+ } else {
+ gint i;
+ if (!colors) {
+ colors = sp_svg_create_color_hash ();
+ }
+ for (i = 0; i < 31; i++) {
+ if (str[i] == ';') {
+ c[i] = '\0';
+ break;
+ }
+ c[i] = g_ascii_tolower (str[i]);
+ if (!str[i]) break;
+ }
+ c[31] = '\0';
+
+ gpointer const rgb_ptr = g_hash_table_lookup(colors, c);
+ if (rgb_ptr) {
+ val = *(static_cast<unsigned long *>(rgb_ptr));
+ } else {
+ return def;
+ }
+ }
+
+ return (val << 8);
+}
+
+gint
+sp_svg_write_color (gchar * buf, gint buflen, guint32 color)
+{
+ return g_snprintf (buf, buflen, "#%06x", color >> 8);
+}
+
+static GHashTable *
+sp_svg_create_color_hash (void)
+{
+ GHashTable * colors = g_hash_table_new (g_str_hash, g_str_equal);
+
+ for (unsigned i = 0 ; i < SP_SVG_NUMCOLORS ; i++) {
+ void const *name = sp_svg_color_named[i].name;
+ gpointer val = &(sp_svg_color_named[i].rgb);
+ g_hash_table_insert(colors, const_cast<gpointer>(name), val);
+ }
+
+ return colors;
+}
diff --git a/src/svg/svg-length.cpp b/src/svg/svg-length.cpp
new file mode 100644
index 000000000..102d7b74c
--- /dev/null
+++ b/src/svg/svg-length.cpp
@@ -0,0 +1,515 @@
+#define __SP_SVG_LENGTH_C__
+
+/*
+ * SVG data parser
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ *
+ * This code is in public domain
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <math.h>
+#include <glib/gstrfuncs.h>
+#include "svg.h"
+#include "stringstream.h"
+#include "../unit-constants.h"
+
+
+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 unsigned int sp_svg_number_write_i(gchar *buf, int val)
+{
+ int p = 0;
+ if (val < 0) {
+ buf[p++] = '-';
+ val = -val;
+ }
+
+ int i = 0;
+ char c[32];
+ do {
+ c[32 - (++i)] = '0' + (val % 10);
+ val /= 10;
+ } while (val > 0);
+
+ memcpy(buf + p, &c[32 - i], i);
+ p += i;
+ buf[p] = 0;
+
+ return p;
+}
+
+static unsigned sp_svg_number_write_d(gchar *buf, double val, unsigned int tprec, unsigned int fprec, unsigned int padf)
+{
+ /* Process sign */
+ int i = 0;
+ if (val < 0.0) {
+ buf[i++] = '-';
+ val = fabs(val);
+ }
+
+ /* Determine number of integral digits */
+ int idigits = 0;
+ if (val >= 1.0) {
+ idigits = (int) floor(log10(val));
+ }
+
+ /* Determine the actual number of fractional digits */
+ fprec = MAX(fprec, tprec - idigits);
+ /* Round value */
+ val += 0.5 * pow(10.0, - ((double) fprec));
+ /* Extract integral and fractional parts */
+ double dival = floor(val);
+ int ival = (int) dival;
+ double fval = val - dival;
+ /* Write integra */
+ i += sp_svg_number_write_i(buf + i, ival);
+ int end_i = i;
+ if (fprec > 0 && (padf || fval > 0.0)) {
+ buf[i++] = '.';
+ while ((fprec > 0) && (padf || (fval > 0.0))) {
+ fval *= 10.0;
+ dival = floor(fval);
+ fval -= dival;
+ int const int_dival = (int) dival;
+ buf[i++] = '0' + int_dival;
+ if (int_dival != 0) {
+ end_i = i;
+ }
+ fprec -= 1;
+ }
+ }
+ buf[end_i] = 0;
+ return end_i;
+}
+
+unsigned int sp_svg_number_write_de(gchar *buf, double val, unsigned int tprec, unsigned int padf)
+{
+ if (val == 0.0 || (fabs(val) >= 0.1 && fabs(val) < 10000000)) {
+ return sp_svg_number_write_d(buf, val, tprec, 0, padf);
+ } else {
+ double eval = floor(log10(fabs(val)));
+ val = val / pow(10.0, eval);
+ int p = sp_svg_number_write_d(buf, val, tprec, 0, padf);
+ buf[p++] = 'e';
+ p += sp_svg_number_write_i(buf + p, (int) eval);
+ return p;
+ }
+}
+
+/* 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, NULL)) {
+ return false;
+ }
+
+ _set = true;
+ unit = u;
+ value = v;
+ computed = c;
+
+ return true;
+}
+
+static bool svg_length_absolute_unit(SVGLength::Unit u)
+{
+ return (u != SVGLength::EM && u != SVGLength::EX && u != SVGLength::PERCENT);
+}
+
+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, NULL)) {
+ return false;
+ }
+
+ if (svg_length_absolute_unit(u) == false) {
+ return false;
+ }
+
+ _set = true;
+ unit = u;
+ value = v;
+ computed = c;
+
+ return true;
+}
+
+
+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, NULL, &computed, NULL)) {
+ // 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)
+{
+ 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 = NULL; // 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 {
+ /* 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 = v * PX_PER_PT;
+ }
+ break;
+ case UVAL('p','c'):
+ if (unit) {
+ *unit = SVGLength::PC;
+ }
+ if (computed) {
+ *computed = v * 12 * PX_PER_PT;
+ }
+ break;
+ case UVAL('m','m'):
+ if (unit) {
+ *unit = SVGLength::MM;
+ }
+ if (computed) {
+ *computed = v * PX_PER_MM;
+ }
+ break;
+ case UVAL('c','m'):
+ if (unit) {
+ *unit = SVGLength::CM;
+ }
+ if (computed) {
+ *computed = v * PX_PER_CM;
+ }
+ break;
+ case UVAL('i','n'):
+ if (unit) {
+ *unit = SVGLength::INCH;
+ }
+ if (computed) {
+ *computed = v * PX_PER_IN;
+ }
+ 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, NULL);
+ if (r) {
+ if (value) {
+ *value = a;
+ }
+ if (computed) {
+ *computed = b;
+ }
+ }
+ return r;
+}
+
+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::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 == NULL) {
+ 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 "";
+}
+
+/**
+ * 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:encoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg-length.h b/src/svg/svg-length.h
new file mode 100644
index 000000000..883657494
--- /dev/null
+++ b/src/svg/svg-length.h
@@ -0,0 +1,66 @@
+#ifndef __SP_SVG_LENGTH_H__
+#define __SP_SVG_LENGTH_H__
+
+/**
+ * \file src/svg/svg-length.h
+ * \brief SVG length type
+ *
+ * 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, read the file 'COPYING' for more information
+ */
+
+class SVGLength
+{
+public:
+
+ enum Unit {
+ NONE,
+ PX,
+ PT,
+ PC,
+ MM,
+ CM,
+ INCH,
+ EM,
+ EX,
+ PERCENT
+ };
+
+ bool _set;
+ Unit unit;
+ float value;
+ float computed;
+
+ float operator=(float v) {
+ _set = true;
+ unit = NONE;
+ value = computed = v;
+ return v;
+ }
+
+ bool read(gchar const *str);
+ void readOrUnset(gchar const *str, Unit u = NONE, float v = 0, float c = 0);
+ bool readAbsolute(gchar const *str);
+ void set(Unit u, float v, float c);
+ void unset(Unit u = NONE, float v = 0, float c = 0);
+ void update(double em, double ex, double scale);
+};
+
+#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:encoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg-path.cpp b/src/svg/svg-path.cpp
new file mode 100644
index 000000000..6598a5731
--- /dev/null
+++ b/src/svg/svg-path.cpp
@@ -0,0 +1,710 @@
+#define __SP_SVG_PARSE_C__
+/*
+ svg-path.c: Parse SVG path element data into bezier path.
+
+ Copyright (C) 2000 Eazel, Inc.
+ Copyright (C) 2000 Lauris Kaplinski
+ Copyright (C) 2001 Ximian, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Authors:
+ Raph Levien <raph@artofcode.com>
+ Lauris Kaplinski <lauris@ximian.com>
+*/
+
+#include <cassert>
+#include <glib/gmem.h>
+#include <glib/gmessages.h>
+#include <glib/gstrfuncs.h>
+
+#include "libnr/n-art-bpath.h"
+#include "gnome-canvas-bpath-util.h"
+#include "stringstream.h"
+
+
+/* This module parses an SVG path element into an RsvgBpathDef.
+
+ At present, there is no support for <marker> or any other contextual
+ information from the SVG file. The API will need to change rather
+ significantly to support these.
+
+ Reference: SVG working draft 3 March 2000, section 8.
+*/
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif /* M_PI */
+
+/* We are lazy ;-) (Lauris) */
+#define rsvg_bpath_def_new gnome_canvas_bpath_def_new
+#define rsvg_bpath_def_moveto gnome_canvas_bpath_def_moveto
+#define rsvg_bpath_def_lineto gnome_canvas_bpath_def_lineto
+#define rsvg_bpath_def_curveto gnome_canvas_bpath_def_curveto
+#define rsvg_bpath_def_closepath gnome_canvas_bpath_def_closepath
+
+struct RSVGParsePathCtx {
+ GnomeCanvasBpathDef *bpath;
+ double cpx, cpy; /* current point */
+ double rpx, rpy; /* reflection point (for 's' and 't' commands) */
+ double spx, spy; /* beginning of current subpath point */
+ char cmd; /* current command (lowercase) */
+ int param; /* parameter number */
+ gboolean rel; /* true if relative coords */
+ double params[7]; /* parameters that have been parsed */
+};
+
+static void rsvg_path_arc_segment(RSVGParsePathCtx *ctx,
+ double xc, double yc,
+ double th0, double th1,
+ double rx, double ry, double x_axis_rotation)
+{
+ double sin_th, cos_th;
+ double a00, a01, a10, a11;
+ double x1, y1, x2, y2, x3, y3;
+ double t;
+ double th_half;
+
+ sin_th = sin (x_axis_rotation * (M_PI / 180.0));
+ cos_th = cos (x_axis_rotation * (M_PI / 180.0));
+ /* inverse transform compared with rsvg_path_arc */
+ a00 = cos_th * rx;
+ a01 = -sin_th * ry;
+ a10 = sin_th * rx;
+ a11 = cos_th * ry;
+
+ th_half = 0.5 * (th1 - th0);
+ t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
+ x1 = xc + cos (th0) - t * sin (th0);
+ y1 = yc + sin (th0) + t * cos (th0);
+ x3 = xc + cos (th1);
+ y3 = yc + sin (th1);
+ x2 = x3 + t * sin (th1);
+ y2 = y3 - t * cos (th1);
+ rsvg_bpath_def_curveto(ctx->bpath,
+ a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
+ a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
+ a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
+}
+
+/**
+ * rsvg_path_arc: Add an RSVG arc to the path context.
+ * @ctx: Path context.
+ * @rx: Radius in x direction (before rotation).
+ * @ry: Radius in y direction (before rotation).
+ * @x_axis_rotation: Rotation angle for axes.
+ * @large_arc_flag: 0 for arc length <= 180, 1 for arc >= 180.
+ * @sweep: 0 for "negative angle", 1 for "positive angle".
+ * @x: New x coordinate.
+ * @y: New y coordinate.
+ *
+ **/
+static void rsvg_path_arc (RSVGParsePathCtx *ctx,
+ double rx, double ry, double x_axis_rotation,
+ int large_arc_flag, int sweep_flag,
+ double x, double y)
+{
+ double sin_th, cos_th;
+ double a00, a01, a10, a11;
+ double x0, y0, x1, y1, xc, yc;
+ double d, sfactor, sfactor_sq;
+ double th0, th1, th_arc;
+ double px, py, pl;
+ int i, n_segs;
+
+ sin_th = sin (x_axis_rotation * (M_PI / 180.0));
+ cos_th = cos (x_axis_rotation * (M_PI / 180.0));
+
+ /*
+ Correction of out-of-range radii as described in Appendix F.6.6:
+
+ 1. Ensure radii are non-zero (Done?).
+ 2. Ensure that radii are positive.
+ 3. Ensure that radii are large enough.
+ */
+
+ if(rx < 0.0) rx = -rx;
+ if(ry < 0.0) ry = -ry;
+
+ px = cos_th * (ctx->cpx - x) * 0.5 + sin_th * (ctx->cpy - y) * 0.5;
+ py = cos_th * (ctx->cpy - y) * 0.5 - sin_th * (ctx->cpx - x) * 0.5;
+ pl = (px * px) / (rx * rx) + (py * py) / (ry * ry);
+
+ if(pl > 1.0)
+ {
+ pl = sqrt(pl);
+ rx *= pl;
+ ry *= pl;
+ }
+
+ /* Proceed with computations as described in Appendix F.6.5 */
+
+ a00 = cos_th / rx;
+ a01 = sin_th / rx;
+ a10 = -sin_th / ry;
+ a11 = cos_th / ry;
+ x0 = a00 * ctx->cpx + a01 * ctx->cpy;
+ y0 = a10 * ctx->cpx + a11 * ctx->cpy;
+ x1 = a00 * x + a01 * y;
+ y1 = a10 * x + a11 * y;
+ /* (x0, y0) is current point in transformed coordinate space.
+ (x1, y1) is new point in transformed coordinate space.
+
+ The arc fits a unit-radius circle in this space.
+ */
+ d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
+ sfactor_sq = 1.0 / d - 0.25;
+ if (sfactor_sq < 0) sfactor_sq = 0;
+ sfactor = sqrt (sfactor_sq);
+ if (sweep_flag == large_arc_flag) sfactor = -sfactor;
+ xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
+ yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
+ /* (xc, yc) is center of the circle. */
+
+ th0 = atan2 (y0 - yc, x0 - xc);
+ th1 = atan2 (y1 - yc, x1 - xc);
+
+ th_arc = th1 - th0;
+ if (th_arc < 0 && sweep_flag)
+ th_arc += 2 * M_PI;
+ else if (th_arc > 0 && !sweep_flag)
+ th_arc -= 2 * M_PI;
+
+ n_segs = (int) ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
+
+ for (i = 0; i < n_segs; i++) {
+ rsvg_path_arc_segment(ctx, xc, yc,
+ th0 + i * th_arc / n_segs,
+ th0 + (i + 1) * th_arc / n_segs,
+ rx, ry, x_axis_rotation);
+ }
+
+ ctx->cpx = x;
+ ctx->cpy = y;
+}
+
+
+/* supply defaults for missing parameters, assuming relative coordinates
+ are to be interpreted as x,y */
+static void rsvg_parse_path_default_xy(RSVGParsePathCtx *ctx, int n_params)
+{
+ int i;
+
+ if (ctx->rel) {
+ for (i = ctx->param; i < n_params; i++) {
+ if (i > 2)
+ ctx->params[i] = ctx->params[i - 2];
+ else if (i == 1)
+ ctx->params[i] = ctx->cpy;
+ else if (i == 0)
+ /* we shouldn't get here (usually ctx->param > 0 as
+ precondition) */
+ ctx->params[i] = ctx->cpx;
+ }
+ } else {
+ for (i = ctx->param; i < n_params; i++) {
+ ctx->params[i] = 0.0;
+ }
+ }
+}
+
+static void rsvg_parse_path_do_cmd(RSVGParsePathCtx *ctx, gboolean final)
+{
+ double x1, y1, x2, y2, x3, y3;
+
+#ifdef VERBOSE
+ int i;
+
+ g_print ("parse_path %c:", ctx->cmd);
+ for (i = 0; i < ctx->param; i++) {
+ g_print(" %f", ctx->params[i]);
+ }
+ g_print (final ? ".\n" : "\n");
+#endif
+
+ switch (ctx->cmd) {
+ case 'm':
+ /* moveto */
+ if (ctx->param == 2
+ || final)
+ {
+ rsvg_parse_path_default_xy (ctx, 2);
+#ifdef VERBOSE
+ g_print ("'m' moveto %g,%g\n",
+ ctx->params[0], ctx->params[1]);
+#endif
+ rsvg_bpath_def_moveto (ctx->bpath,
+ ctx->params[0], ctx->params[1]);
+ ctx->cpx = ctx->rpx = ctx->spx = ctx->params[0];
+ ctx->cpy = ctx->rpy = ctx->spy = ctx->params[1];
+ ctx->param = 0;
+ ctx->cmd = 'l';
+ }
+ break;
+ case 'l':
+ /* lineto */
+ if (ctx->param == 2
+ || final)
+ {
+ rsvg_parse_path_default_xy (ctx, 2);
+#ifdef VERBOSE
+ g_print ("'l' lineto %g,%g\n",
+ ctx->params[0], ctx->params[1]);
+#endif
+ rsvg_bpath_def_lineto (ctx->bpath,
+ ctx->params[0], ctx->params[1]);
+ ctx->cpx = ctx->rpx = ctx->params[0];
+ ctx->cpy = ctx->rpy = ctx->params[1];
+ ctx->param = 0;
+ }
+ break;
+ case 'c':
+ /* curveto */
+ if ( ( ctx->param == 6 )
+ || final )
+ {
+ rsvg_parse_path_default_xy (ctx, 6);
+ x1 = ctx->params[0];
+ y1 = ctx->params[1];
+ x2 = ctx->params[2];
+ y2 = ctx->params[3];
+ x3 = ctx->params[4];
+ y3 = ctx->params[5];
+#ifdef VERBOSE
+ g_print ("'c' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto (ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = x2;
+ ctx->rpy = y2;
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ ctx->param = 0;
+ }
+ break;
+ case 's':
+ /* smooth curveto */
+ if ( ( ctx->param == 4 )
+ || final )
+ {
+ rsvg_parse_path_default_xy (ctx, 4);
+ x1 = 2 * ctx->cpx - ctx->rpx;
+ y1 = 2 * ctx->cpy - ctx->rpy;
+ x2 = ctx->params[0];
+ y2 = ctx->params[1];
+ x3 = ctx->params[2];
+ y3 = ctx->params[3];
+#ifdef VERBOSE
+ g_print ("'s' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto (ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = x2;
+ ctx->rpy = y2;
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ ctx->param = 0;
+ }
+ break;
+ case 'h':
+ /* horizontal lineto */
+ if (ctx->param == 1) {
+#ifdef VERBOSE
+ g_print ("'h' lineto %g,%g\n",
+ ctx->params[0], ctx->cpy);
+#endif
+ rsvg_bpath_def_lineto (ctx->bpath,
+ ctx->params[0], ctx->cpy);
+ ctx->cpx = ctx->rpx = ctx->params[0];
+ ctx->param = 0;
+ }
+ break;
+ case 'v':
+ /* vertical lineto */
+ if (ctx->param == 1) {
+#ifdef VERBOSE
+ g_print ("'v' lineto %g,%g\n",
+ ctx->cpx, ctx->params[0]);
+#endif
+ rsvg_bpath_def_lineto (ctx->bpath,
+ ctx->cpx, ctx->params[0]);
+ ctx->cpy = ctx->rpy = ctx->params[0];
+ ctx->param = 0;
+ }
+ break;
+ case 'q':
+ /* quadratic bezier curveto */
+
+ /* non-normative reference:
+ http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/beziers.html
+ */
+ if (ctx->param == 4 || final)
+ {
+ rsvg_parse_path_default_xy (ctx, 4);
+ /* raise quadratic bezier to cubic */
+ x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
+ y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
+ x3 = ctx->params[2];
+ y3 = ctx->params[3];
+ x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
+ y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
+#ifdef VERBOSE
+ g_print("'q' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto(ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = ctx->params[0];
+ ctx->rpy = ctx->params[1];
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ ctx->param = 0;
+ }
+ break;
+ case 't':
+ /* Truetype quadratic bezier curveto */
+ if (ctx->param == 2 || final) {
+ double xc, yc; /* quadratic control point */
+
+ xc = 2 * ctx->cpx - ctx->rpx;
+ yc = 2 * ctx->cpy - ctx->rpy;
+ /* generate a quadratic bezier with control point = xc, yc */
+ x1 = (ctx->cpx + 2 * xc) * (1.0 / 3.0);
+ y1 = (ctx->cpy + 2 * yc) * (1.0 / 3.0);
+ x3 = ctx->params[0];
+ y3 = ctx->params[1];
+ x2 = (x3 + 2 * xc) * (1.0 / 3.0);
+ y2 = (y3 + 2 * yc) * (1.0 / 3.0);
+#ifdef VERBOSE
+ g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto (ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = xc;
+ ctx->rpy = yc;
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ ctx->param = 0;
+ } else if (final) {
+ if (ctx->param > 2) {
+ rsvg_parse_path_default_xy(ctx, 4);
+ /* raise quadratic bezier to cubic */
+ x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
+ y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
+ x3 = ctx->params[2];
+ y3 = ctx->params[3];
+ x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
+ y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
+#ifdef VERBOSE
+ g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto (ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = x2;
+ ctx->rpy = y2;
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ } else {
+ rsvg_parse_path_default_xy(ctx, 2);
+#ifdef VERBOSE
+ g_print ("'t' lineto %g,%g\n",
+ ctx->params[0], ctx->params[1]);
+#endif
+ rsvg_bpath_def_lineto(ctx->bpath,
+ ctx->params[0], ctx->params[1]);
+ ctx->cpx = ctx->rpx = ctx->params[0];
+ ctx->cpy = ctx->rpy = ctx->params[1];
+ }
+ ctx->param = 0;
+ }
+ break;
+ case 'a':
+ if (ctx->param == 7 || final)
+ {
+ rsvg_path_arc(ctx,
+ ctx->params[0], ctx->params[1], ctx->params[2],
+ (int) ctx->params[3], (int) ctx->params[4],
+ ctx->params[5], ctx->params[6]);
+ ctx->param = 0;
+ }
+ break;
+ default:
+ ctx->param = 0;
+ }
+}
+
+static void rsvg_parse_path_data(RSVGParsePathCtx *ctx, const char *data)
+{
+ int i = 0;
+ double val = 0;
+ char c = 0;
+ gboolean in_num = FALSE;
+ gboolean in_frac = FALSE;
+ gboolean in_exp = FALSE;
+ gboolean exp_wait_sign = FALSE;
+ int sign = 0;
+ int exp = 0;
+ int exp_sign = 0;
+ double frac = 0.0;
+
+ /* fixme: Do better error processing: e.g. at least stop parsing as soon as we find an error.
+ * At some point we'll need to do all of
+ * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing.
+ */
+ for (i = 0; ; i++)
+ {
+ c = data[i];
+ if (c >= '0' && c <= '9')
+ {
+ /* digit */
+ if (in_num)
+ {
+ if (in_exp)
+ {
+ exp = (exp * 10) + c - '0';
+ exp_wait_sign = FALSE;
+ }
+ else if (in_frac)
+ val += (frac *= 0.1) * (c - '0');
+ else
+ val = (val * 10) + c - '0';
+ }
+ else
+ {
+ in_num = TRUE;
+ assert(!in_frac && !in_exp);
+ exp = 0;
+ exp_sign = 1;
+ exp_wait_sign = FALSE;
+ val = c - '0';
+ sign = 1;
+ }
+ }
+ else if (c == '.' && !(in_frac || in_exp))
+ {
+ if (!in_num)
+ {
+ in_num = TRUE;
+ assert(!in_exp);
+ exp = 0;
+ exp_sign = 1;
+ exp_wait_sign = FALSE;
+ val = 0;
+ sign = 1;
+ }
+ in_frac = TRUE;
+ frac = 1;
+ }
+ else if ((c == 'E' || c == 'e') && in_num)
+ {
+ /* fixme: Should we add `&& !in_exp' to the above condition?
+ * It looks like the current code will parse `1e3e4' (as 1e4). */
+ in_exp = TRUE;
+ exp_wait_sign = TRUE;
+ exp = 0;
+ exp_sign = 1;
+ }
+ else if ((c == '+' || c == '-') && in_exp)
+ {
+ exp_sign = c == '+' ? 1 : -1;
+ }
+ else if (in_num)
+ {
+ /* end of number */
+
+ val *= sign * pow (10, exp_sign * exp);
+ if (ctx->rel)
+ {
+ /* Handle relative coordinates. This switch statement attempts
+ to determine _what_ the coords are relative to. This is
+ underspecified in the 12 Apr working draft. */
+ switch (ctx->cmd)
+ {
+ case 'l':
+ case 'm':
+ case 'c':
+ case 's':
+ case 'q':
+ case 't':
+ if ( ctx->param & 1 ) {
+ val += ctx->cpy; /* odd param, y */
+ } else {
+ val += ctx->cpx; /* even param, x */
+ }
+ break;
+ case 'a':
+ /* rule: sixth and seventh are x and y, rest are not
+ relative */
+ if (ctx->param == 5)
+ val += ctx->cpx;
+ else if (ctx->param == 6)
+ val += ctx->cpy;
+ break;
+ case 'h':
+ /* rule: x-relative */
+ val += ctx->cpx;
+ break;
+ case 'v':
+ /* rule: y-relative */
+ val += ctx->cpy;
+ break;
+ }
+ }
+ ctx->params[ctx->param++] = val;
+ rsvg_parse_path_do_cmd (ctx, FALSE);
+ if (c=='.') {
+ in_num = TRUE;
+ val = 0;
+ in_frac = TRUE;
+ in_exp = FALSE;
+ frac = 1;
+ }
+ else {
+ in_num = FALSE;
+ in_frac = FALSE;
+ in_exp = FALSE;
+ }
+ }
+
+ if (c == '\0')
+ break;
+ else if ((c == '+' || c == '-') && !exp_wait_sign)
+ {
+ sign = c == '+' ? 1 : -1;;
+ val = 0;
+ in_num = TRUE;
+ in_frac = FALSE;
+ in_exp = FALSE;
+ exp = 0;
+ exp_sign = 1;
+ exp_wait_sign = FALSE;
+ }
+ else if (c == 'z' || c == 'Z')
+ {
+ if (ctx->param)
+ rsvg_parse_path_do_cmd (ctx, TRUE);
+ rsvg_bpath_def_closepath (ctx->bpath);
+
+ ctx->cmd = 'm';
+ ctx->params[0] = ctx->cpx = ctx->rpx = ctx->spx;
+ ctx->params[1] = ctx->cpy = ctx->rpy = ctx->spy;
+ ctx->param = 2;
+ }
+ else if (c >= 'A' && c <= 'Z' && c != 'E')
+ {
+ if (ctx->param)
+ rsvg_parse_path_do_cmd (ctx, TRUE);
+ ctx->cmd = c + 'a' - 'A';
+ ctx->rel = FALSE;
+ }
+ else if (c >= 'a' && c <= 'z' && c != 'e')
+ {
+ if (ctx->param)
+ rsvg_parse_path_do_cmd (ctx, TRUE);
+ ctx->cmd = c;
+ ctx->rel = TRUE;
+ }
+ /* else c _should_ be whitespace or , */
+ }
+}
+
+
+NArtBpath *sp_svg_read_path(gchar const *str)
+{
+ RSVGParsePathCtx ctx;
+ NArtBpath *bpath;
+
+ ctx.bpath = gnome_canvas_bpath_def_new ();
+ ctx.cpx = 0.0;
+ ctx.cpy = 0.0;
+ ctx.cmd = 0;
+ ctx.param = 0;
+
+ rsvg_parse_path_data (&ctx, str);
+
+ if (ctx.param && ctx.cmd != 'm') {
+ rsvg_parse_path_do_cmd (&ctx, TRUE);
+ }
+
+ gnome_canvas_bpath_def_art_finish (ctx.bpath);
+
+ bpath = g_new (NArtBpath, ctx.bpath->n_bpath);
+ memcpy (bpath, ctx.bpath->bpath, ctx.bpath->n_bpath * sizeof (NArtBpath));
+ g_assert ((bpath + ctx.bpath->n_bpath - 1)->code == NR_END);
+ gnome_canvas_bpath_def_unref (ctx.bpath);
+
+ return bpath;
+}
+
+gchar *sp_svg_write_path(NArtBpath const *bpath)
+{
+ Inkscape::SVGOStringStream os;
+ bool closed=false;
+
+ g_return_val_if_fail (bpath != NULL, NULL);
+
+ for (int i = 0; bpath[i].code != NR_END; i++){
+ if (i) {
+ os << " ";
+ }
+ switch (bpath [i].code){
+ case NR_LINETO:
+ os << "L " << bpath[i].x3 << "," << bpath[i].y3;
+ break;
+
+ case NR_CURVETO:
+ os << "C " << bpath[i].x1 << "," << bpath[i].y1
+ << " " << bpath[i].x2 << "," << bpath[i].y2
+ << " " << bpath[i].x3 << "," << bpath[i].y3;
+ break;
+
+ case NR_MOVETO_OPEN:
+ case NR_MOVETO:
+ if (closed) {
+ os << "z ";
+ }
+ closed = ( bpath[i].code == NR_MOVETO );
+ os << "M " << bpath[i].x3 << "," << bpath[i].y3;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+ if (closed) {
+ os << " z ";
+ }
+
+// std::string s = os.str();
+// gchar *ret = g_strdup(s.c_str());
+// delete (s);
+// return ret;
+ return g_strdup (os.str().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:encoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg.h b/src/svg/svg.h
new file mode 100644
index 000000000..601c0b112
--- /dev/null
+++ b/src/svg/svg.h
@@ -0,0 +1,85 @@
+#ifndef __SP_SVG_H__
+#define __SP_SVG_H__
+
+/*
+ * SVG data parser
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+#include <glib/gtypes.h>
+#include "svg/svg-length.h"
+#include <vector>
+
+struct NArtBpath;
+struct NRMatrix;
+namespace NR {
+ class Matrix;
+}
+
+/* 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 gchar *str, float *val);
+unsigned int sp_svg_number_read_d (const gchar *str, double *val);
+
+/*
+ * No buffer overflow checking is done, so better wrap them if needed
+ */
+unsigned int sp_svg_number_write_de (gchar *buf, double val, unsigned int tprec, unsigned int padf);
+
+/* Length */
+
+/*
+ * Parse number with optional unit specifier:
+ * - for px, pt, pc, mm, cm, computed is final value accrding 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 gchar *str, float *length);
+std::vector<SVGLength> sp_svg_length_list_read (const gchar *str);
+unsigned int sp_svg_length_read_ldd (const gchar *str, SVGLength::Unit *unit, double *value, double *computed);
+
+std::string sp_svg_length_write_with_units(SVGLength const &length);
+
+bool sp_svg_transform_read(gchar const *str, NR::Matrix *transform);
+
+unsigned sp_svg_transform_write(gchar str[], unsigned size, NR::Matrix const &transform);
+unsigned sp_svg_transform_write(gchar str[], unsigned size, NRMatrix const *transform);
+
+double sp_svg_read_percentage (const char * str, double def);
+
+unsigned int sp_svg_read_color (const gchar * str, unsigned int def);
+int sp_svg_write_color (char * buf, int buflen, unsigned int color);
+
+/* NB! As paths can be long, we use here dynamic string */
+
+NArtBpath * sp_svg_read_path (const char * str);
+char * sp_svg_write_path (const NArtBpath * bpath);
+
+
+#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:encoding=utf-8:textwidth=99 :