#define __SP_SVG_LENGTH_C__ /* * SVG data parser * * Authors: * Lauris Kaplinski * bulia byak * * Copyright (C) 1999-2002 Lauris Kaplinski * * This code is in public domain */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #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 - 1); /* 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, int min_exp, 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))); if ((int) eval < min_exp) { return sp_svg_number_write_d(buf, 0, tprec, 0, padf); } else { 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 sp_svg_length_list_read(gchar const *str) { if (!str) { return std::vector(); } SVGLength::Unit unit; float value; float computed; char *next = (char *) str; std::vector 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 :