/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* vim: set sw=4 sts=4 ts=4 expandtab: */ /* rsvg-css.c: Parse CSS basic data types. Copyright (C) 2000 Eazel, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library 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: Dom Lachowicz Raph Levien */ #include "config.h" #include "rsvg-css.h" #include "rsvg-private.h" #include "rsvg-styles.h" #include #include #include #include #ifdef HAVE_STRINGS_H #include #endif #include #include #include #include #define POINTS_PER_INCH (72.0) #define CM_PER_INCH (2.54) #define MM_PER_INCH (25.4) #define PICA_PER_INCH (6.0) #define SETINHERIT() G_STMT_START {if (inherit != NULL) *inherit = TRUE;} G_STMT_END #define UNSETINHERIT() G_STMT_START {if (inherit != NULL) *inherit = FALSE;} G_STMT_END /** * rsvg_css_parse_vbox: * @vbox: The CSS viewBox * @x : The X output * @y: The Y output * @w: The Width output * @h: The Height output * * Returns: */ RsvgViewBox rsvg_css_parse_vbox (const char *vbox) { RsvgViewBox vb; gdouble *list; guint list_len; vb.active = FALSE; vb.rect.x = vb.rect.y = 0; vb.rect.width = vb.rect.height = 0; list = rsvg_css_parse_number_list (vbox, &list_len); if (!(list && list_len)) return vb; else if (list_len != 4) { g_free (list); return vb; } else { vb.rect.x = list[0]; vb.rect.y = list[1]; vb.rect.width = list[2]; vb.rect.height = list[3]; vb.active = TRUE; g_free (list); return vb; } } typedef enum _RelativeSize { RELATIVE_SIZE_NORMAL, RELATIVE_SIZE_SMALLER, RELATIVE_SIZE_LARGER } RelativeSize; static double rsvg_css_parse_raw_length (const char *str, gboolean * in, gboolean * percent, gboolean * em, gboolean * ex, RelativeSize * relative_size) { double length = 0.0; char *p = NULL; /* * The supported CSS length unit specifiers are: * em, ex, px, pt, pc, cm, mm, in, and % */ *percent = FALSE; *em = FALSE; *ex = FALSE; *relative_size = RELATIVE_SIZE_NORMAL; length = g_ascii_strtod (str, &p); if ((length == -HUGE_VAL || length == HUGE_VAL) && (ERANGE == errno)) { /* todo: error condition - figure out how to best represent it */ return 0.0; } /* test for either pixels or no unit, which is assumed to be pixels */ if (p && *p && (strcmp (p, "px") != 0)) { if (!strcmp (p, "pt")) { length /= POINTS_PER_INCH; *in = TRUE; } else if (!strcmp (p, "in")) *in = TRUE; else if (!strcmp (p, "cm")) { length /= CM_PER_INCH; *in = TRUE; } else if (!strcmp (p, "mm")) { length /= MM_PER_INCH; *in = TRUE; } else if (!strcmp (p, "pc")) { length /= PICA_PER_INCH; *in = TRUE; } else if (!strcmp (p, "em")) *em = TRUE; else if (!strcmp (p, "ex")) *ex = TRUE; else if (!strcmp (p, "%")) { *percent = TRUE; length *= 0.01; } else { double pow_factor = 0.0; if (!g_ascii_strcasecmp (p, "larger")) { *relative_size = RELATIVE_SIZE_LARGER; return 0.0; } else if (!g_ascii_strcasecmp (p, "smaller")) { *relative_size = RELATIVE_SIZE_SMALLER; return 0.0; } else if (!g_ascii_strcasecmp (p, "xx-small")) { pow_factor = -3.0; } else if (!g_ascii_strcasecmp (p, "x-small")) { pow_factor = -2.0; } else if (!g_ascii_strcasecmp (p, "small")) { pow_factor = -1.0; } else if (!g_ascii_strcasecmp (p, "medium")) { pow_factor = 0.0; } else if (!g_ascii_strcasecmp (p, "large")) { pow_factor = 1.0; } else if (!g_ascii_strcasecmp (p, "x-large")) { pow_factor = 2.0; } else if (!g_ascii_strcasecmp (p, "xx-large")) { pow_factor = 3.0; } else { return 0.0; } length = 12.0 * pow (1.2, pow_factor) / POINTS_PER_INCH; *in = TRUE; } } return length; } RsvgLength _rsvg_css_parse_length (const char *str) { RsvgLength out; gboolean percent, em, ex, in; RelativeSize relative_size = RELATIVE_SIZE_NORMAL; percent = em = ex = in = FALSE; out.length = rsvg_css_parse_raw_length (str, &in, &percent, &em, &ex, &relative_size); if (percent) out.factor = 'p'; else if (em) out.factor = 'm'; else if (ex) out.factor = 'x'; else if (in) out.factor = 'i'; else if (relative_size == RELATIVE_SIZE_LARGER) out.factor = 'l'; else if (relative_size == RELATIVE_SIZE_SMALLER) out.factor = 's'; else out.factor = '\0'; return out; } /* Recursive evaluation of all parent elements regarding absolute font size */ double _rsvg_css_normalize_font_size (RsvgState * state, RsvgDrawingCtx * ctx) { RsvgState *parent; switch (state->font_size.factor) { case 'p': case 'm': case 'x': parent = rsvg_state_parent (state); if (parent) { double parent_size; parent_size = _rsvg_css_normalize_font_size (parent, ctx); return state->font_size.length * parent_size; } break; default: return _rsvg_css_normalize_length (&state->font_size, ctx, 'v'); break; } return 12.; } double _rsvg_css_normalize_length (const RsvgLength * in, RsvgDrawingCtx * ctx, char dir) { if (in->factor == '\0') return in->length; else if (in->factor == 'p') { if (dir == 'h') return in->length * ctx->vb.rect.width; if (dir == 'v') return in->length * ctx->vb.rect.height; if (dir == 'o') return in->length * rsvg_viewport_percentage (ctx->vb.rect.width, ctx->vb.rect.height); } else if (in->factor == 'm' || in->factor == 'x') { double font = _rsvg_css_normalize_font_size (rsvg_current_state (ctx), ctx); if (in->factor == 'm') return in->length * font; else return in->length * font / 2.; } else if (in->factor == 'i') { if (dir == 'h') return in->length * ctx->dpi_x; if (dir == 'v') return in->length * ctx->dpi_y; if (dir == 'o') return in->length * rsvg_viewport_percentage (ctx->dpi_x, ctx->dpi_y); } else if (in->factor == 'l') { /* todo: "larger" */ } else if (in->factor == 's') { /* todo: "smaller" */ } return 0; } /* Recursive evaluation of all parent elements regarding basline-shift */ double _rsvg_css_accumulate_baseline_shift (RsvgState * state, RsvgDrawingCtx * ctx) { RsvgState *parent; double shift = 0.; parent = rsvg_state_parent (state); if (parent) { if (state->has_baseline_shift) { double parent_font_size; parent_font_size = _rsvg_css_normalize_font_size (parent, ctx); /* font size from here */ shift = parent_font_size * state->baseline_shift; } shift += _rsvg_css_accumulate_baseline_shift (parent, ctx); /* baseline-shift for parent element */ } return shift; } double _rsvg_css_hand_normalize_length (const RsvgLength * in, gdouble pixels_per_inch, gdouble width_or_height, gdouble font_size) { if (in->factor == '\0') return in->length; else if (in->factor == 'p') return in->length * width_or_height; else if (in->factor == 'm') return in->length * font_size; else if (in->factor == 'x') return in->length * font_size / 2.; else if (in->factor == 'i') return in->length * pixels_per_inch; return 0; } static gint rsvg_css_clip_rgb_percent (const char *s, double max) { double value; char *end; value = g_ascii_strtod (s, &end); if (*end == '%') { value = CLAMP (value, 0, 100) / 100.0; } else { value = CLAMP (value, 0, max) / max; } return (gint) floor (value * 255 + 0.5); } /* pack 3 [0,255] ints into one 32 bit one */ #define PACK_RGBA(r,g,b,a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) #define PACK_RGB(r,g,b) PACK_RGBA(r, g, b, 255) /** * rsvg_css_parse_color: * @str: string to parse * @inherit: whether to inherit * * Parse a CSS2 color specifier, return RGB value * * Returns: and RGB value */ guint32 rsvg_css_parse_color (const char *str, gboolean * inherit) { gint val = 0; SETINHERIT (); if (str[0] == '#') { int 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 == 4) { val = ((val & 0xf00) << 8) | ((val & 0x0f0) << 4) | (val & 0x00f); val |= val << 4; } val |= 0xff000000; /* opaque */ } else if (g_str_has_prefix (str, "rgb")) { gint r, g, b, a; gboolean has_alpha; guint nb_toks; char **toks; r = g = b = 0; a = 255; if (str[3] == 'a') { /* "rgba" */ has_alpha = TRUE; str += 4; } else { /* "rgb" */ has_alpha = FALSE; str += 3; } str = strchr (str, '('); if (str == NULL) return val; toks = rsvg_css_parse_list (str + 1, &nb_toks); if (toks) { if (nb_toks == (has_alpha ? 4 : 3)) { r = rsvg_css_clip_rgb_percent (toks[0], 255.0); g = rsvg_css_clip_rgb_percent (toks[1], 255.0); b = rsvg_css_clip_rgb_percent (toks[2], 255.0); if (has_alpha) a = rsvg_css_clip_rgb_percent (toks[3], 1.0); else a = 255; } g_strfreev (toks); } val = PACK_RGBA (r, g, b, a); } else if (!strcmp (str, "inherit")) UNSETINHERIT (); else { CRRgb rgb; if (cr_rgb_set_from_name (&rgb, (const guchar *) str) == CR_OK) { val = PACK_RGB (rgb.red, rgb.green, rgb.blue); } else { /* default to opaque black on failed lookup */ UNSETINHERIT (); val = PACK_RGB (0, 0, 0); } } return val; } #undef PACK_RGB #undef PACK_RGBA guint rsvg_css_parse_opacity (const char *str) { char *end_ptr = NULL; double opacity; opacity = g_ascii_strtod (str, &end_ptr); if (((opacity == -HUGE_VAL || opacity == HUGE_VAL) && (ERANGE == errno)) || *end_ptr != '\0') opacity = 1.; opacity = CLAMP (opacity, 0., 1.); return (guint) floor (opacity * 255. + 0.5); } /* : An angle value is a optionally followed immediately with an angle unit identifier. Angle unit identifiers are: * deg: degrees * grad: grads * rad: radians For properties defined in [CSS2], an angle unit identifier must be provided. For SVG-specific attributes and properties, the angle unit identifier is optional. If not provided, the angle value is assumed to be in degrees. */ double rsvg_css_parse_angle (const char *str) { double degrees; char *end_ptr; degrees = g_ascii_strtod (str, &end_ptr); /* todo: error condition - figure out how to best represent it */ if ((degrees == -HUGE_VAL || degrees == HUGE_VAL) && (ERANGE == errno)) return 0.0; if (end_ptr) { if (!strcmp (end_ptr, "rad")) return degrees * 180. / G_PI; else if (!strcmp (end_ptr, "grad")) return degrees * 360. / 400.; } return degrees; } /* : Frequency values are used with aural properties. The normative definition of frequency values can be found in [CSS2-AURAL]. A frequency value is a immediately followed by a frequency unit identifier. Frequency unit identifiers are: * Hz: Hertz * kHz: kilo Hertz Frequency values may not be negative. */ double rsvg_css_parse_frequency (const char *str) { double f_hz; char *end_ptr; f_hz = g_ascii_strtod (str, &end_ptr); /* todo: error condition - figure out how to best represent it */ if ((f_hz == -HUGE_VAL || f_hz == HUGE_VAL) && (ERANGE == errno)) return 0.0; if (end_ptr && !strcmp (end_ptr, "kHz")) return f_hz * 1000.; return f_hz; } /*