diff options
Diffstat (limited to 'src/svg')
| -rw-r--r-- | src/svg/.cvsignore | 5 | ||||
| -rw-r--r-- | src/svg/HACKING | 7 | ||||
| -rw-r--r-- | src/svg/Makefile_insert | 58 | ||||
| -rw-r--r-- | src/svg/css-ostringstream-test.h | 73 | ||||
| -rw-r--r-- | src/svg/css-ostringstream.cpp | 76 | ||||
| -rw-r--r-- | src/svg/css-ostringstream.h | 80 | ||||
| -rw-r--r-- | src/svg/ftos.cpp | 485 | ||||
| -rw-r--r-- | src/svg/ftos.h | 54 | ||||
| -rw-r--r-- | src/svg/gnome-canvas-bpath-util.cpp | 179 | ||||
| -rw-r--r-- | src/svg/gnome-canvas-bpath-util.h | 48 | ||||
| -rw-r--r-- | src/svg/itos.cpp | 77 | ||||
| -rw-r--r-- | src/svg/makefile.in | 17 | ||||
| -rw-r--r-- | src/svg/round.cpp | 47 | ||||
| -rw-r--r-- | src/svg/sp-svg.def | 26 | ||||
| -rw-r--r-- | src/svg/stringstream-test.h | 70 | ||||
| -rw-r--r-- | src/svg/stringstream.cpp | 69 | ||||
| -rw-r--r-- | src/svg/stringstream.h | 76 | ||||
| -rw-r--r-- | src/svg/strip-trailing-zeros.cpp | 42 | ||||
| -rw-r--r-- | src/svg/strip-trailing-zeros.h | 20 | ||||
| -rw-r--r-- | src/svg/svg-affine.cpp | 265 | ||||
| -rw-r--r-- | src/svg/svg-color.cpp | 324 | ||||
| -rw-r--r-- | src/svg/svg-length.cpp | 515 | ||||
| -rw-r--r-- | src/svg/svg-length.h | 66 | ||||
| -rw-r--r-- | src/svg/svg-path.cpp | 710 | ||||
| -rw-r--r-- | src/svg/svg.h | 85 |
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 : |
