rsvg-styles.c 58.9 KB
Newer Older
1 2
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set sw=4 sts=4 expandtab: */
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
   rsvg-styles.c: Handle SVG styles

   Copyright (C) 2000 Eazel, Inc.
   Copyright (C) 2002 Dom Lachowicz <cinamod@hotmail.com>

   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.

   Author: Raph Levien <raph@artofcode.com>
*/
Christian Persch's avatar
Christian Persch committed
26 27
#include "config.h"

28
#include <errno.h>
29
#include <string.h>
Dom Lachowicz's avatar
Dom Lachowicz committed
30
#include <math.h>
31

32 33
#include "rsvg-private.h"
#include "rsvg-filter.h"
34 35
#include "rsvg-css.h"
#include "rsvg-styles.h"
36
#include "rsvg-shapes.h"
37
#include "rsvg-mask.h"
38
#include "rsvg-marker.h"
39

40 41
#include <libcroco/libcroco.h>

42 43
#define RSVG_DEFAULT_FONT "Times New Roman"

44 45 46 47 48 49 50 51 52 53 54 55 56 57
enum {
  SHAPE_RENDERING_AUTO = CAIRO_ANTIALIAS_DEFAULT,
  SHAPE_RENDERING_OPTIMIZE_SPEED = CAIRO_ANTIALIAS_NONE,
  SHAPE_RENDERING_CRISP_EDGES = CAIRO_ANTIALIAS_NONE,
  SHAPE_RENDERING_GEOMETRIC_PRECISION = CAIRO_ANTIALIAS_DEFAULT
};

enum {
  TEXT_RENDERING_AUTO = CAIRO_ANTIALIAS_DEFAULT,
  TEXT_RENDERING_OPTIMIZE_SPEED = CAIRO_ANTIALIAS_NONE,
  TEXT_RENDERING_OPTIMIZE_LEGIBILITY = CAIRO_ANTIALIAS_DEFAULT,
  TEXT_RENDERING_GEOMETRIC_PRECISION = CAIRO_ANTIALIAS_DEFAULT
};

58 59 60 61 62 63 64 65 66 67
typedef struct _StyleValueData {
    gchar *value;
    gboolean important;
} StyleValueData;

static StyleValueData *
style_value_data_new (const gchar *value, gboolean important)
{
    StyleValueData *ret;

68
    ret = g_new0 (StyleValueData, 1);
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
    ret->value = g_strdup (value);
    ret->important = important;

    return ret;
}

static void
style_value_data_free (StyleValueData *value)
{
    if (!value)
        return;
    g_free (value->value);
    g_free (value);
}

84
static void
85
rsvg_state_init (RsvgState * state)
86
{
87 88
    memset (state, 0, sizeof (RsvgState));

89
    state->parent = NULL;
Christian Persch's avatar
Christian Persch committed
90 91
    cairo_matrix_init_identity (&state->affine);
    cairo_matrix_init_identity (&state->personal_affine);
92 93
    state->mask = NULL;
    state->opacity = 0xff;
94
    state->baseline_shift = 0.;
95 96 97 98
    state->current_color = 0xff000000; /* See bgo#764808; we don't inherit CSS
                                        * from the public API, so start off with
                                        * opaque black instead of transparent.
                                        */
99
    state->fill = rsvg_paint_server_parse (NULL, "#000");
100 101
    state->fill_opacity = 0xff;
    state->stroke_opacity = 0xff;
102
    state->stroke_width = rsvg_length_parse ("1", LENGTH_DIR_BOTH);
103
    state->miter_limit = 4;
104
    state->cap = CAIRO_LINE_CAP_BUTT;
105
    state->join = CAIRO_LINE_JOIN_MITER;
106 107 108 109 110 111 112 113 114

    /* The following two start as INHERIT, even though has_stop_color and
     * has_stop_opacity get initialized to FALSE below.  This is so that the
     * first pass of rsvg_state_inherit_run(), called from
     * rsvg_state_reconstruct() from the "stop" element code, will correctly
     * initialize the destination state from the toplevel element.
     *
     */
    state->stop_color.kind = RSVG_CSS_COLOR_SPEC_INHERIT;
115
    state->stop_opacity.kind = RSVG_OPACITY_INHERIT;
116

117 118
    state->fill_rule = CAIRO_FILL_RULE_WINDING;
    state->clip_rule = CAIRO_FILL_RULE_WINDING;
119
    state->enable_background = RSVG_ENABLE_BACKGROUND_ACCUMULATE;
120
    state->comp_op = CAIRO_OPERATOR_OVER;
121 122 123 124 125
    state->overflow = FALSE;
    state->flood_color = 0;
    state->flood_opacity = 255;

    state->font_family = g_strdup (RSVG_DEFAULT_FONT);
126
    state->font_size = rsvg_length_parse ("12.0", LENGTH_DIR_BOTH);
127 128 129 130 131
    state->font_style = PANGO_STYLE_NORMAL;
    state->font_variant = PANGO_VARIANT_NORMAL;
    state->font_weight = PANGO_WEIGHT_NORMAL;
    state->font_stretch = PANGO_STRETCH_NORMAL;
    state->text_dir = PANGO_DIRECTION_LTR;
132
    state->text_gravity = PANGO_GRAVITY_SOUTH;
133 134
    state->unicode_bidi = UNICODE_BIDI_NORMAL;
    state->text_anchor = TEXT_ANCHOR_START;
135
    state->letter_spacing = rsvg_length_parse ("0.0", LENGTH_DIR_HORIZONTAL);
136 137 138
    state->visible = TRUE;
    state->cond_true = TRUE;
    state->filter = NULL;
139
    state->clip_path = NULL;
140 141 142 143
    state->startMarker = NULL;
    state->middleMarker = NULL;
    state->endMarker = NULL;

144
    state->has_baseline_shift = FALSE;
145 146 147 148 149 150 151 152 153 154 155 156 157 158
    state->has_current_color = FALSE;
    state->has_flood_color = FALSE;
    state->has_flood_opacity = FALSE;
    state->has_fill_server = FALSE;
    state->has_fill_opacity = FALSE;
    state->has_fill_rule = FALSE;
    state->has_clip_rule = FALSE;
    state->has_stroke_server = FALSE;
    state->has_stroke_opacity = FALSE;
    state->has_stroke_width = FALSE;
    state->has_miter_limit = FALSE;
    state->has_cap = FALSE;
    state->has_join = FALSE;
    state->has_dash = FALSE;
159
    state->has_dashoffset = FALSE;
160 161 162 163 164 165 166 167 168 169 170 171 172
    state->has_visible = FALSE;
    state->has_cond = FALSE;
    state->has_stop_color = FALSE;
    state->has_stop_opacity = FALSE;
    state->has_font_size = FALSE;
    state->has_font_family = FALSE;
    state->has_lang = FALSE;
    state->has_font_style = FALSE;
    state->has_font_variant = FALSE;
    state->has_font_weight = FALSE;
    state->has_font_stretch = FALSE;
    state->has_font_decor = FALSE;
    state->has_text_dir = FALSE;
173
    state->has_text_gravity = FALSE;
174 175
    state->has_unicode_bidi = FALSE;
    state->has_text_anchor = FALSE;
176
    state->has_letter_spacing = FALSE;
177 178 179 180
    state->has_startMarker = FALSE;
    state->has_middleMarker = FALSE;
    state->has_endMarker = FALSE;
    state->has_overflow = FALSE;
181

182 183 184 185
    state->shape_rendering_type = SHAPE_RENDERING_AUTO;
    state->has_shape_rendering_type = FALSE;
    state->text_rendering_type = TEXT_RENDERING_AUTO;
    state->has_text_rendering_type = FALSE;
186 187 188

    state->styles = g_hash_table_new_full (g_str_hash, g_str_equal,
                                           g_free, (GDestroyNotify) style_value_data_free);
189 190
}

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
RsvgState *
rsvg_state_new (void)
{
    RsvgState *state;

    state = g_slice_new (RsvgState);
    rsvg_state_init (state);

    return state;
}

static void
rsvg_state_finalize (RsvgState * state)
{
    g_free (state->filter);
    state->filter = NULL;

    g_free (state->mask);
    state->mask = NULL;

    g_free (state->clip_path);
    state->clip_path = NULL;

    g_free (state->font_family);
    state->font_family = NULL;

    g_free (state->lang);
    state->lang = NULL;

    g_free (state->startMarker);
    state->startMarker = NULL;

    g_free (state->middleMarker);
    state->middleMarker = NULL;

    g_free (state->endMarker);
    state->endMarker = NULL;

    rsvg_paint_server_unref (state->fill);
    state->fill = NULL;

    rsvg_paint_server_unref (state->stroke);
    state->stroke = NULL;

    if (state->dash.n_dash != 0) {
        g_free (state->dash.dash);
        state->dash.n_dash = 0;
        state->dash.dash = NULL;
    }

    if (state->styles) {
        g_hash_table_unref (state->styles);
        state->styles = NULL;
    }
}

void
rsvg_state_free (RsvgState *state)
{
    g_assert (state != NULL);

    rsvg_state_finalize (state);
    g_slice_free (RsvgState, state);
}

256 257 258 259 260 261 262 263 264
void
rsvg_state_reinit (RsvgState * state)
{
    RsvgState *parent = state->parent;
    rsvg_state_finalize (state);
    rsvg_state_init (state);
    state->parent = parent;
}

265 266
typedef int (*InheritanceFunction) (int dst, int src);

267
void
268
rsvg_state_clone (RsvgState * dst, const RsvgState * src)
269
{
270
    gint i;
271
    RsvgState *parent = dst->parent;
272 273 274 275

    rsvg_state_finalize (dst);

    *dst = *src;
276
    dst->parent = parent;
277
    dst->filter = g_strdup (src->filter);
278
    dst->mask = g_strdup (src->mask);
279
    dst->clip_path = g_strdup (src->clip_path);
280 281
    dst->font_family = g_strdup (src->font_family);
    dst->lang = g_strdup (src->lang);
Benjamin Otte's avatar
Benjamin Otte committed
282 283 284
    dst->startMarker = g_strdup (src->startMarker);
    dst->middleMarker = g_strdup (src->middleMarker);
    dst->endMarker = g_strdup (src->endMarker);
285 286 287
    rsvg_paint_server_ref (dst->fill);
    rsvg_paint_server_ref (dst->stroke);

288 289
    dst->styles = g_hash_table_ref (src->styles);

290
    if (src->dash.n_dash > 0) {
291
        dst->dash.dash = g_new0 (gdouble, src->dash.n_dash);
292 293 294
        for (i = 0; i < src->dash.n_dash; i++)
            dst->dash.dash[i] = src->dash.dash[i];
    }
295 296
}

297 298 299 300 301 302 303 304
/*
  This function is where all inheritance takes place. It is given a 
  base and a modifier state, as well as a function to determine
  how the base is modified and a flag as to whether things that can
  not be inherited are copied streight over, or ignored.
*/

static void
305 306
rsvg_state_inherit_run (RsvgState * dst, const RsvgState * src,
                        const InheritanceFunction function, const gboolean inherituninheritables)
307
{
308 309
    gint i;

310 311
    if (function (dst->has_baseline_shift, src->has_baseline_shift))
        dst->baseline_shift = src->baseline_shift;
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
    if (function (dst->has_current_color, src->has_current_color))
        dst->current_color = src->current_color;
    if (function (dst->has_flood_color, src->has_flood_color))
        dst->flood_color = src->flood_color;
    if (function (dst->has_flood_opacity, src->has_flood_opacity))
        dst->flood_opacity = src->flood_opacity;
    if (function (dst->has_fill_server, src->has_fill_server)) {
        rsvg_paint_server_ref (src->fill);
        if (dst->fill)
            rsvg_paint_server_unref (dst->fill);
        dst->fill = src->fill;
    }
    if (function (dst->has_fill_opacity, src->has_fill_opacity))
        dst->fill_opacity = src->fill_opacity;
    if (function (dst->has_fill_rule, src->has_fill_rule))
        dst->fill_rule = src->fill_rule;
    if (function (dst->has_clip_rule, src->has_clip_rule))
        dst->clip_rule = src->clip_rule;
    if (function (dst->overflow, src->overflow))
        dst->overflow = src->overflow;
    if (function (dst->has_stroke_server, src->has_stroke_server)) {
        rsvg_paint_server_ref (src->stroke);
        if (dst->stroke)
            rsvg_paint_server_unref (dst->stroke);
        dst->stroke = src->stroke;
    }
    if (function (dst->has_stroke_opacity, src->has_stroke_opacity))
        dst->stroke_opacity = src->stroke_opacity;
    if (function (dst->has_stroke_width, src->has_stroke_width))
        dst->stroke_width = src->stroke_width;
    if (function (dst->has_miter_limit, src->has_miter_limit))
        dst->miter_limit = src->miter_limit;
    if (function (dst->has_cap, src->has_cap))
        dst->cap = src->cap;
    if (function (dst->has_join, src->has_join))
        dst->join = src->join;
348
    if (function (dst->has_stop_color, src->has_stop_color)) {
349
        if (dst->stop_color.kind == RSVG_CSS_COLOR_SPEC_INHERIT) {
350
            dst->has_stop_color = TRUE;
351 352
            dst->stop_color = src->stop_color;
        }
353 354
    }
    if (function (dst->has_stop_opacity, src->has_stop_opacity)) {
355
        if (dst->stop_opacity.kind == RSVG_OPACITY_INHERIT) {
356
            dst->has_stop_opacity = TRUE;
357 358
            dst->stop_opacity = src->stop_opacity;
        }
359
    }
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
    if (function (dst->has_cond, src->has_cond))
        dst->cond_true = src->cond_true;
    if (function (dst->has_font_size, src->has_font_size))
        dst->font_size = src->font_size;
    if (function (dst->has_font_style, src->has_font_style))
        dst->font_style = src->font_style;
    if (function (dst->has_font_variant, src->has_font_variant))
        dst->font_variant = src->font_variant;
    if (function (dst->has_font_weight, src->has_font_weight))
        dst->font_weight = src->font_weight;
    if (function (dst->has_font_stretch, src->has_font_stretch))
        dst->font_stretch = src->font_stretch;
    if (function (dst->has_font_decor, src->has_font_decor))
        dst->font_decor = src->font_decor;
    if (function (dst->has_text_dir, src->has_text_dir))
        dst->text_dir = src->text_dir;
376 377
    if (function (dst->has_text_gravity, src->has_text_gravity))
        dst->text_gravity = src->text_gravity;
378 379 380 381
    if (function (dst->has_unicode_bidi, src->has_unicode_bidi))
        dst->unicode_bidi = src->unicode_bidi;
    if (function (dst->has_text_anchor, src->has_text_anchor))
        dst->text_anchor = src->text_anchor;
382
    if (function (dst->has_letter_spacing, src->has_letter_spacing))
383
        dst->letter_spacing = src->letter_spacing;
Benjamin Otte's avatar
Benjamin Otte committed
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
    if (function (dst->has_startMarker, src->has_startMarker)) {
        g_free (dst->startMarker);
        dst->startMarker = g_strdup (src->startMarker);
    }
    if (function (dst->has_middleMarker, src->has_middleMarker)) {
        g_free (dst->middleMarker);
        dst->middleMarker = g_strdup (src->middleMarker);
    }
    if (function (dst->has_endMarker, src->has_endMarker)) {
        g_free (dst->endMarker);
        dst->endMarker = g_strdup (src->endMarker);
    }
    if (function (dst->has_shape_rendering_type, src->has_shape_rendering_type))
            dst->shape_rendering_type = src->shape_rendering_type;
    if (function (dst->has_text_rendering_type, src->has_text_rendering_type))
            dst->text_rendering_type = src->text_rendering_type;
400 401 402 403 404 405

    if (function (dst->has_font_family, src->has_font_family)) {
        g_free (dst->font_family);      /* font_family is always set to something */
        dst->font_family = g_strdup (src->font_family);
    }

406
    if (function (dst->has_space_preserve, src->has_space_preserve))
407
        dst->space_preserve = src->space_preserve;
408

409
    if (function (dst->has_visible, src->has_visible))
410
        dst->visible = src->visible;
411

412 413 414 415 416 417 418 419 420 421
    if (function (dst->has_lang, src->has_lang)) {
        if (dst->has_lang)
            g_free (dst->lang);
        dst->lang = g_strdup (src->lang);
    }

    if (src->dash.n_dash > 0 && (function (dst->has_dash, src->has_dash))) {
        if (dst->has_dash)
            g_free (dst->dash.dash);

422
        dst->dash.dash = g_new0 (gdouble, src->dash.n_dash);
423 424 425 426 427
        dst->dash.n_dash = src->dash.n_dash;
        for (i = 0; i < src->dash.n_dash; i++)
            dst->dash.dash[i] = src->dash.dash[i];
    }

428 429 430 431
    if (function (dst->has_dashoffset, src->has_dashoffset)) {
        dst->dash.offset = src->dash.offset;
    }

432
    if (inherituninheritables) {
433 434
        g_free (dst->clip_path);
        dst->clip_path = g_strdup (src->clip_path);
435 436
        g_free (dst->mask);
        dst->mask = g_strdup (src->mask);
437 438
        g_free (dst->filter);
        dst->filter = g_strdup (src->filter);
439 440 441 442
        dst->enable_background = src->enable_background;
        dst->opacity = src->opacity;
        dst->comp_op = src->comp_op;
    }
443 444 445 446 447 448 449 450 451 452 453
}

/*
  reinherit is given dst which is the top of the state stack
  and src which is the layer before in the state stack from
  which it should be inherited from 
*/

static int
reinheritfunction (int dst, int src)
{
454 455 456
    if (!dst)
        return 1;
    return 0;
457 458 459
}

void
460
rsvg_state_reinherit (RsvgState * dst, const RsvgState * src)
461
{
462
    rsvg_state_inherit_run (dst, src, reinheritfunction, 0);
463 464 465 466 467 468 469 470 471 472 473 474 475 476
}

/*
  dominate is given dst which is the top of the state stack
  and src which is the layer before in the state stack from
  which it should be inherited from, however if anything is
  directly specified in src (the second last layer) it will
  override anything on the top layer, this is for overrides
  in use tags 
*/

static int
dominatefunction (int dst, int src)
{
477 478 479
    if (!dst || src)
        return 1;
    return 0;
480 481
}

482 483
static void
state_dominate (RsvgState * dst, const RsvgState * src)
484
{
485
    rsvg_state_inherit_run (dst, src, dominatefunction, 0);
486
}
487

488
/* copy everything inheritable from the src to the dst */
489

490 491 492
static int
clonefunction (int dst, int src)
{
493
    return 1;
494
}
495

496 497
static void
state_override (RsvgState * dst, const RsvgState * src)
498
{
499
    rsvg_state_inherit_run (dst, src, clonefunction, 0);
500
}
Caleb Michael Moore's avatar
Caleb Michael Moore committed
501

502 503 504 505 506 507 508 509 510 511
/*
  put something new on the inheritance stack, dst is the top of the stack, 
  src is the state to be integrated, this is essentially the opposite of
  reinherit, because it is being given stuff to be integrated on the top, 
  rather than the context underneath.
*/

static int
inheritfunction (int dst, int src)
{
512
    return src;
513 514 515
}

void
516
rsvg_state_inherit (RsvgState * dst, const RsvgState * src)
517
{
518
    rsvg_state_inherit_run (dst, src, inheritfunction, 1);
519 520
}

521 522
/* Parse a CSS2 style argument, setting the SVG context attributes. */
static void
523
rsvg_parse_style_pair (RsvgState * state,
524 525 526
                       const gchar * name,
                       const gchar * value,
                       gboolean important)
527
{
528 529 530 531 532 533
    StyleValueData *data;

    data = g_hash_table_lookup (state->styles, name);
    if (data && data->important && !important)
        return;

Kurosawa Takeshi's avatar
Kurosawa Takeshi committed
534 535 536
    if (name == NULL || value == NULL)
        return;

537 538 539 540
    g_hash_table_insert (state->styles,
                         (gpointer) g_strdup (name),
                         (gpointer) style_value_data_new (value, important));

541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
    if (g_str_equal (name, "color")) {
        RsvgCssColorSpec spec;

        spec = rsvg_css_parse_color (value, ALLOW_INHERIT_YES, ALLOW_CURRENT_COLOR_NO);
        switch (spec.kind) {
        case RSVG_CSS_COLOR_SPEC_INHERIT:
            /* FIXME: we should inherit; see how stop-color is handled in rsvg-styles.c */
            state->has_current_color = FALSE;
            break;

        case RSVG_CSS_COLOR_SPEC_ARGB:
            state->current_color = spec.argb;
            state->has_current_color = TRUE;
            break;

        case RSVG_CSS_COLOR_PARSE_ERROR:
            /* FIXME: no error handling */
            state->has_current_color = FALSE;
            break;

        default:
            g_assert_not_reached ();
        }
564 565 566 567 568 569 570 571 572 573 574
    } else if (g_str_equal (name, "opacity")) {
        RsvgOpacitySpec spec;

        spec = rsvg_css_parse_opacity (value);
        if (spec.kind == RSVG_OPACITY_SPECIFIED) {
            state->opacity = spec.opacity;
        } else {
            state->opacity = 0;
            /* FIXME: handle INHERIT and PARSE_ERROR */
        }
    } else if (g_str_equal (name, "flood-color")) {
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
        RsvgCssColorSpec spec;

        spec = rsvg_css_parse_color (value, ALLOW_INHERIT_YES, ALLOW_CURRENT_COLOR_YES);
        switch (spec.kind) {
        case RSVG_CSS_COLOR_SPEC_INHERIT:
            /* FIXME: we should inherit; see how stop-color is handled in rsvg-styles.c */
            state->has_current_color = FALSE;
            break;

        case RSVG_CSS_COLOR_SPEC_CURRENT_COLOR:
            /* FIXME: in the caller, fix up the current color */
            state->has_flood_color = FALSE;
            break;

        case RSVG_CSS_COLOR_SPEC_ARGB:
            state->flood_color = spec.argb;
            state->has_flood_color = TRUE;
            break;

        case RSVG_CSS_COLOR_PARSE_ERROR:
            /* FIXME: no error handling */
            state->has_current_color = FALSE;
            break;

        default:
            g_assert_not_reached ();
        }
    } else if (g_str_equal (name, "flood-opacity")) {
603 604 605 606 607 608 609 610 611 612
        RsvgOpacitySpec spec;

        spec = rsvg_css_parse_opacity (value);
        if (spec.kind == RSVG_OPACITY_SPECIFIED) {
            state->flood_opacity = spec.opacity;
        } else {
            state->flood_opacity = 0;
            /* FIXME: handle INHERIT and PARSE_ERROR */
        }

613
        state->has_flood_opacity = TRUE;
614 615
    } else if (g_str_equal (name, "filter")) {
        g_free (state->filter);
616
        state->filter = rsvg_get_url_string (value, NULL);
617 618
    } else if (g_str_equal (name, "mask")) {
        g_free (state->mask);
619
        state->mask = rsvg_get_url_string (value, NULL);
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
    } else if (g_str_equal (name, "baseline-shift")) {
        /* These values come from Inkscape's SP_CSS_BASELINE_SHIFT_(SUB/SUPER/BASELINE);
         * see sp_style_merge_baseline_shift_from_parent()
         */
        if (g_str_equal (value, "sub")) {
           state->has_baseline_shift = TRUE;
           state->baseline_shift = -0.2;
        } else if (g_str_equal (value, "super")) {
           state->has_baseline_shift = TRUE;
           state->baseline_shift = 0.4;
        } else if (g_str_equal (value, "baseline")) {
           state->has_baseline_shift = TRUE;
           state->baseline_shift = 0.;
        } else {
          g_warning ("value \'%s\' for attribute \'baseline-shift\' is not supported; only 'sub', 'super', and 'baseline' are supported\n", value);
        }
636
    } else if (g_str_equal (name, "clip-path")) {
637
        g_free (state->clip_path);
638
        state->clip_path = rsvg_get_url_string (value, NULL);
639 640 641
    } else if (g_str_equal (name, "overflow")) {
        if (!g_str_equal (value, "inherit")) {
            state->overflow = rsvg_css_parse_overflow (value, &state->has_overflow);
642
        }
643 644
    } else if (g_str_equal (name, "enable-background")) {
        if (g_str_equal (value, "new"))
645 646 647
            state->enable_background = RSVG_ENABLE_BACKGROUND_NEW;
        else
            state->enable_background = RSVG_ENABLE_BACKGROUND_ACCUMULATE;
648 649
    } else if (g_str_equal (name, "comp-op")) {
        if (g_str_equal (value, "clear"))
650
            state->comp_op = CAIRO_OPERATOR_CLEAR;
651
        else if (g_str_equal (value, "src"))
652
            state->comp_op = CAIRO_OPERATOR_SOURCE;
653
        else if (g_str_equal (value, "dst"))
654
            state->comp_op = CAIRO_OPERATOR_DEST;
655
        else if (g_str_equal (value, "src-over"))
656
            state->comp_op = CAIRO_OPERATOR_OVER;
657
        else if (g_str_equal (value, "dst-over"))
658
            state->comp_op = CAIRO_OPERATOR_DEST_OVER;
659
        else if (g_str_equal (value, "src-in"))
660
            state->comp_op = CAIRO_OPERATOR_IN;
661
        else if (g_str_equal (value, "dst-in"))
662
            state->comp_op = CAIRO_OPERATOR_DEST_IN;
663
        else if (g_str_equal (value, "src-out"))
664
            state->comp_op = CAIRO_OPERATOR_OUT;
665
        else if (g_str_equal (value, "dst-out"))
666
            state->comp_op = CAIRO_OPERATOR_DEST_OUT;
667
        else if (g_str_equal (value, "src-atop"))
668
            state->comp_op = CAIRO_OPERATOR_ATOP;
669
        else if (g_str_equal (value, "dst-atop"))
670
            state->comp_op = CAIRO_OPERATOR_DEST_ATOP;
671
        else if (g_str_equal (value, "xor"))
672
            state->comp_op = CAIRO_OPERATOR_XOR;
673
        else if (g_str_equal (value, "plus"))
674
            state->comp_op = CAIRO_OPERATOR_ADD;
675
        else if (g_str_equal (value, "multiply"))
676
            state->comp_op = CAIRO_OPERATOR_MULTIPLY;
677
        else if (g_str_equal (value, "screen"))
678
            state->comp_op = CAIRO_OPERATOR_SCREEN;
679
        else if (g_str_equal (value, "overlay"))
680
            state->comp_op = CAIRO_OPERATOR_OVERLAY;
681
        else if (g_str_equal (value, "darken"))
682
            state->comp_op = CAIRO_OPERATOR_DARKEN;
683
        else if (g_str_equal (value, "lighten"))
684
            state->comp_op = CAIRO_OPERATOR_LIGHTEN;
685
        else if (g_str_equal (value, "color-dodge"))
686
            state->comp_op = CAIRO_OPERATOR_COLOR_DODGE;
687
        else if (g_str_equal (value, "color-burn"))
688
            state->comp_op = CAIRO_OPERATOR_COLOR_BURN;
689
        else if (g_str_equal (value, "hard-light"))
690
            state->comp_op = CAIRO_OPERATOR_HARD_LIGHT;
691
        else if (g_str_equal (value, "soft-light"))
692
            state->comp_op = CAIRO_OPERATOR_SOFT_LIGHT;
693
        else if (g_str_equal (value, "difference"))
694
            state->comp_op = CAIRO_OPERATOR_DIFFERENCE;
695
        else if (g_str_equal (value, "exclusion"))
696
            state->comp_op = CAIRO_OPERATOR_EXCLUSION;
697
        else
698
            state->comp_op = CAIRO_OPERATOR_OVER;
699
    } else if (g_str_equal (name, "display")) {
700
        state->has_visible = TRUE;
701
        if (g_str_equal (value, "none"))
702
            state->visible = FALSE;
703
        else if (!g_str_equal (value, "inherit") != 0)
704 705 706
            state->visible = TRUE;
        else
            state->has_visible = FALSE;
707
	} else if (g_str_equal (name, "xml:space")) {
708
        state->has_space_preserve = TRUE;
709
        if (g_str_equal (value, "default"))
710
            state->space_preserve = FALSE;
711
        else if (!g_str_equal (value, "preserve") == 0)
712 713 714
            state->space_preserve = TRUE;
        else
            state->space_preserve = FALSE;
715
    } else if (g_str_equal (name, "visibility")) {
716
        state->has_visible = TRUE;
717
        if (g_str_equal (value, "visible"))
718
            state->visible = TRUE;
719
        else if (!g_str_equal (value, "inherit") != 0)
720 721 722
            state->visible = FALSE;     /* collapse or hidden */
        else
            state->has_visible = FALSE;
723
    } else if (g_str_equal (name, "fill")) {
724 725
        RsvgPaintServer *fill = state->fill;
        state->fill =
726
            rsvg_paint_server_parse (&state->has_fill_server, value);
727
        rsvg_paint_server_unref (fill);
728
    } else if (g_str_equal (name, "fill-opacity")) {
729 730 731 732 733 734 735 736 737 738
        RsvgOpacitySpec spec;

        spec = rsvg_css_parse_opacity (value);
        if (spec.kind == RSVG_OPACITY_SPECIFIED) {
            state->fill_opacity = spec.opacity;
        } else {
            state->fill_opacity = 0;
            /* FIXME: handle INHERIT and PARSE_ERROR */
        }

739
        state->has_fill_opacity = TRUE;
740
    } else if (g_str_equal (name, "fill-rule")) {
741
        state->has_fill_rule = TRUE;
742
        if (g_str_equal (value, "nonzero"))
743
            state->fill_rule = CAIRO_FILL_RULE_WINDING;
744
        else if (g_str_equal (value, "evenodd"))
745
            state->fill_rule = CAIRO_FILL_RULE_EVEN_ODD;
746 747
        else
            state->has_fill_rule = FALSE;
748
    } else if (g_str_equal (name, "clip-rule")) {
749
        state->has_clip_rule = TRUE;
750
        if (g_str_equal (value, "nonzero"))
751
            state->clip_rule = CAIRO_FILL_RULE_WINDING;
752
        else if (g_str_equal (value, "evenodd"))
753
            state->clip_rule = CAIRO_FILL_RULE_EVEN_ODD;
754 755
        else
            state->has_clip_rule = FALSE;
756
    } else if (g_str_equal (name, "stroke")) {
757 758 759
        RsvgPaintServer *stroke = state->stroke;

        state->stroke =
760
            rsvg_paint_server_parse (&state->has_stroke_server, value);
761 762

        rsvg_paint_server_unref (stroke);
763
    } else if (g_str_equal (name, "stroke-width")) {
764
        state->stroke_width = rsvg_length_parse (value, LENGTH_DIR_BOTH);
765
        state->has_stroke_width = TRUE;
766
    } else if (g_str_equal (name, "stroke-linecap")) {
767
        state->has_cap = TRUE;
768
        if (g_str_equal (value, "butt"))
769
            state->cap = CAIRO_LINE_CAP_BUTT;
770
        else if (g_str_equal (value, "round"))
771
            state->cap = CAIRO_LINE_CAP_ROUND;
772
        else if (g_str_equal (value, "square"))
773
            state->cap = CAIRO_LINE_CAP_SQUARE;
774
        else
775 776
            g_warning (_("unknown line cap style %s\n"), value);
    } else if (g_str_equal (name, "stroke-opacity")) {
777 778 779 780 781 782 783 784 785 786
        RsvgOpacitySpec spec;

        spec = rsvg_css_parse_opacity (value);
        if (spec.kind == RSVG_OPACITY_SPECIFIED) {
            state->stroke_opacity = spec.opacity;
        } else {
            state->stroke_opacity = 0;
            /* FIXME: handle INHERIT and PARSE_ERROR */
        }

787
        state->has_stroke_opacity = TRUE;
788
    } else if (g_str_equal (name, "stroke-linejoin")) {
789
        state->has_join = TRUE;
790
        if (g_str_equal (value, "miter"))
791
            state->join = CAIRO_LINE_JOIN_MITER;
792
        else if (g_str_equal (value, "round"))
793
            state->join = CAIRO_LINE_JOIN_ROUND;
794
        else if (g_str_equal (value, "bevel"))
795
            state->join = CAIRO_LINE_JOIN_BEVEL;
796
        else
797 798
            g_warning (_("unknown line join style %s\n"), value);
    } else if (g_str_equal (name, "font-size")) {
799
        state->font_size = rsvg_length_parse (value, LENGTH_DIR_BOTH);
800
        state->has_font_size = TRUE;
801 802
    } else if (g_str_equal (name, "font-family")) {
        char *save = g_strdup (rsvg_css_parse_font_family (value, &state->has_font_family));
803 804
        g_free (state->font_family);
        state->font_family = save;
805 806
    } else if (g_str_equal (name, "xml:lang")) {
        char *save = g_strdup (value);
807 808 809
        g_free (state->lang);
        state->lang = save;
        state->has_lang = TRUE;
810 811 812 813 814 815 816 817 818 819
    } else if (g_str_equal (name, "font-style")) {
        state->font_style = rsvg_css_parse_font_style (value, &state->has_font_style);
    } else if (g_str_equal (name, "font-variant")) {
        state->font_variant = rsvg_css_parse_font_variant (value, &state->has_font_variant);
    } else if (g_str_equal (name, "font-weight")) {
        state->font_weight = rsvg_css_parse_font_weight (value, &state->has_font_weight);
    } else if (g_str_equal (name, "font-stretch")) {
        state->font_stretch = rsvg_css_parse_font_stretch (value, &state->has_font_stretch);
    } else if (g_str_equal (name, "text-decoration")) {
        if (g_str_equal (value, "inherit")) {
820 821 822
            state->has_font_decor = FALSE;
            state->font_decor = TEXT_NORMAL;
        } else {
823
            if (strstr (value, "underline"))
824
                state->font_decor |= TEXT_UNDERLINE;
825
            if (strstr (value, "overline"))
826
                state->font_decor |= TEXT_OVERLINE;
827
            if (strstr (value, "strike") || strstr (value, "line-through"))     /* strike though or line-through */
828 829 830
                state->font_decor |= TEXT_STRIKE;
            state->has_font_decor = TRUE;
        }
831
    } else if (g_str_equal (name, "direction")) {
832
        state->has_text_dir = TRUE;
833
        if (g_str_equal (value, "inherit")) {
834 835
            state->text_dir = PANGO_DIRECTION_LTR;
            state->has_text_dir = FALSE;
836
        } else if (g_str_equal (value, "rtl"))
837 838 839
            state->text_dir = PANGO_DIRECTION_RTL;
        else                    /* ltr */
            state->text_dir = PANGO_DIRECTION_LTR;
840
    } else if (g_str_equal (name, "unicode-bidi")) {
841
        state->has_unicode_bidi = TRUE;
842
        if (g_str_equal (value, "inherit")) {
843
            state->unicode_bidi = UNICODE_BIDI_NORMAL;
844
            state->has_unicode_bidi = FALSE;
845
        } else if (g_str_equal (value, "embed"))
846
            state->unicode_bidi = UNICODE_BIDI_EMBED;
847
        else if (g_str_equal (value, "bidi-override"))
848 849 850
            state->unicode_bidi = UNICODE_BIDI_OVERRIDE;
        else                    /* normal */
            state->unicode_bidi = UNICODE_BIDI_NORMAL;
851
    } else if (g_str_equal (name, "writing-mode")) {
852 853 854
        /* TODO: these aren't quite right... */

        state->has_text_dir = TRUE;
855
        state->has_text_gravity = TRUE;
856
        if (g_str_equal (value, "inherit")) {
857 858
            state->text_dir = PANGO_DIRECTION_LTR;
            state->has_text_dir = FALSE;
859 860 861 862 863 864
            state->text_gravity = PANGO_GRAVITY_SOUTH;
            state->has_text_gravity = FALSE;
        } else if (g_str_equal (value, "lr-tb") || g_str_equal (value, "lr")) {
            state->text_dir = PANGO_DIRECTION_LTR;
            state->text_gravity = PANGO_GRAVITY_SOUTH;
        } else if (g_str_equal (value, "rl-tb") || g_str_equal (value, "rl")) {
865
            state->text_dir = PANGO_DIRECTION_RTL;
866 867
            state->text_gravity = PANGO_GRAVITY_SOUTH;
        } else if (g_str_equal (value, "tb-rl") || g_str_equal (value, "tb")) {
868
            state->text_dir = PANGO_DIRECTION_LTR;
869 870
            state->text_gravity = PANGO_GRAVITY_EAST;
        }
871
    } else if (g_str_equal (name, "text-anchor")) {
872
        state->has_text_anchor = TRUE;
873
        if (g_str_equal (value, "inherit")) {
874 875 876
            state->text_anchor = TEXT_ANCHOR_START;
            state->has_text_anchor = FALSE;
        } else {
877
            if (strstr (value, "start"))
878
                state->text_anchor = TEXT_ANCHOR_START;
879
            else if (strstr (value, "middle"))
880
                state->text_anchor = TEXT_ANCHOR_MIDDLE;
881
            else if (strstr (value, "end"))
882 883
                state->text_anchor = TEXT_ANCHOR_END;
        }
884
    } else if (g_str_equal (name, "letter-spacing")) {
885
	state->has_letter_spacing = TRUE;
886
	state->letter_spacing = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
887
    } else if (g_str_equal (name, "stop-color")) {
888 889
        state->has_stop_color = TRUE;
        state->stop_color = rsvg_css_parse_color (value, ALLOW_INHERIT_YES, ALLOW_CURRENT_COLOR_YES);
890
    } else if (g_str_equal (name, "stop-opacity")) {
891
        state->stop_opacity = rsvg_css_parse_opacity (value);
892
        state->has_stop_opacity = TRUE;
893
    } else if (g_str_equal (name, "marker-start")) {
Benjamin Otte's avatar
Benjamin Otte committed
894
        g_free (state->startMarker);
895
        state->startMarker = rsvg_get_url_string (value, NULL);
896
        state->has_startMarker = TRUE;
897
    } else if (g_str_equal (name, "marker-mid")) {
Benjamin Otte's avatar
Benjamin Otte committed
898
        g_free (state->middleMarker);
899
        state->middleMarker = rsvg_get_url_string (value, NULL);
900
        state->has_middleMarker = TRUE;
901
    } else if (g_str_equal (name, "marker-end")) {
Benjamin Otte's avatar
Benjamin Otte committed
902
        g_free (state->endMarker);
903
        state->endMarker = rsvg_get_url_string (value, NULL);
904
        state->has_endMarker = TRUE;
905 906 907
    } else if (g_str_equal (name, "marker")) {
        if (!state->has_startMarker) {
            g_free (state->startMarker);
908
            state->startMarker = rsvg_get_url_string (value, NULL);
909 910 911 912 913
            state->has_startMarker = TRUE;
        }

        if (!state->has_middleMarker) {
            g_free (state->middleMarker);
914
            state->middleMarker = rsvg_get_url_string (value, NULL);
915 916 917 918 919
            state->has_middleMarker = TRUE;
        }

        if (!state->has_endMarker) {
            g_free (state->endMarker);
920
            state->endMarker = rsvg_get_url_string (value, NULL);
921 922
            state->has_endMarker = TRUE;
        }
923
    } else if (g_str_equal (name, "stroke-miterlimit")) {
924
        state->has_miter_limit = TRUE;
925 926
        state->miter_limit = g_ascii_strtod (value, NULL);
    } else if (g_str_equal (name, "stroke-dashoffset")) {
927
        state->has_dashoffset = TRUE;
928
        state->dash.offset = rsvg_length_parse (value, LENGTH_DIR_BOTH);
929 930
        if (state->dash.offset.length < 0.)
            state->dash.offset.length = 0.;
931 932
    } else if (g_str_equal (name, "shape-rendering")) {
        state->has_shape_rendering_type = TRUE;
933

934
        if (g_str_equal (value, "auto") || g_str_equal (value, "default"))
935
            state->shape_rendering_type = SHAPE_RENDERING_AUTO;
936
        else if (g_str_equal (value, "optimizeSpeed"))
937
            state->shape_rendering_type = SHAPE_RENDERING_OPTIMIZE_SPEED;
938
        else if (g_str_equal (value, "crispEdges"))
939
            state->shape_rendering_type = SHAPE_RENDERING_CRISP_EDGES;
940
        else if (g_str_equal (value, "geometricPrecision"))
941
            state->shape_rendering_type = SHAPE_RENDERING_GEOMETRIC_PRECISION;
942

943 944
    } else if (g_str_equal (name, "text-rendering")) {
        state->has_text_rendering_type = TRUE;
945

946
        if (g_str_equal (value, "auto") || g_str_equal (value, "default"))
947
            state->text_rendering_type = TEXT_RENDERING_AUTO;
948
        else if (g_str_equal (value, "optimizeSpeed"))
949
            state->text_rendering_type = TEXT_RENDERING_OPTIMIZE_SPEED;
950
        else if (g_str_equal (value, "optimizeLegibility"))
951
            state->text_rendering_type = TEXT_RENDERING_OPTIMIZE_LEGIBILITY;
952
        else if (g_str_equal (value, "geometricPrecision"))
953
            state->text_rendering_type = TEXT_RENDERING_GEOMETRIC_PRECISION;
954

955
    } else if (g_str_equal (name, "stroke-dasharray")) {
956
        state->has_dash = TRUE;
957
        if (g_str_equal (value, "none")) {
958 959 960
            if (state->dash.n_dash != 0) {
                /* free any cloned dash data */
                g_free (state->dash.dash);
961
                state->dash.dash = NULL;
962 963 964
                state->dash.n_dash = 0;
            }
        } else {
965
            gchar **dashes = g_strsplit (value, ",", -1);
966 967 968 969 970 971 972 973 974 975
            if (NULL != dashes) {
                gint n_dashes, i;
                gboolean is_even = FALSE;
                gdouble total = 0;

                /* count the #dashes */
                for (n_dashes = 0; dashes[n_dashes] != NULL; n_dashes++);

                is_even = (n_dashes % 2 == 0);
                state->dash.n_dash = (is_even ? n_dashes : n_dashes * 2);
976
                state->dash.dash = g_new0 (double, state->dash.n_dash);
977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994

                /* TODO: handle negative value == error case */

                /* the even and base case */
                for (i = 0; i < n_dashes; i++) {
                    state->dash.dash[i] = g_ascii_strtod (dashes[i], NULL);
                    total += state->dash.dash[i];
                }
                /* if an odd number of dashes is found, it gets repeated */
                if (!is_even)
                    for (; i < state->dash.n_dash; i++)
                        state->dash.dash[i] = state->dash.dash[i - n_dashes];

                g_strfreev (dashes);
                /* If the dashes add up to 0, then it should 
                   be ignored */
                if (total == 0) {
                    g_free (state->dash.dash);
995
                    state->dash.dash = NULL;
996 997 998 999 1000
                    state->dash.n_dash = 0;
                }
            }
        }
    }
1001 1002
}

1003
static void
1004
rsvg_lookup_parse_style_pair (RsvgState * state,
1005
                              const char *key, RsvgPropertyBag * atts)
1006
{
1007
    const char *value;
1008

1009
    if ((value = rsvg_property_bag_lookup (atts, key)) != NULL)
1010
        rsvg_parse_style_pair (state, key, value, FALSE);
1011 1012 1013 1014
}

/* take a pair of the form (fill="#ff00ff") and parse it as a style */
void
1015
rsvg_parse_style_pairs (RsvgState * state, RsvgPropertyBag * atts)
1016
{
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
    rsvg_lookup_parse_style_pair (state, "baseline-shift", atts);
    rsvg_lookup_parse_style_pair (state, "clip-path", atts);
    rsvg_lookup_parse_style_pair (state, "clip-rule", atts);
    rsvg_lookup_parse_style_pair (state, "color", atts);
    rsvg_lookup_parse_style_pair (state, "direction", atts);
    rsvg_lookup_parse_style_pair (state, "display", atts);
    rsvg_lookup_parse_style_pair (state, "enable-background", atts);
    rsvg_lookup_parse_style_pair (state, "comp-op", atts);
    rsvg_lookup_parse_style_pair (state, "fill", atts);
    rsvg_lookup_parse_style_pair (state, "fill-opacity", atts);
    rsvg_lookup_parse_style_pair (state, "fill-rule", atts);
    rsvg_lookup_parse_style_pair (state, "filter", atts);
    rsvg_lookup_parse_style_pair (state, "flood-color", atts);
    rsvg_lookup_parse_style_pair (state, "flood-opacity", atts);
    rsvg_lookup_parse_style_pair (state, "font-family", atts);
    rsvg_lookup_parse_style_pair (state, "font-size", atts);
    rsvg_lookup_parse_style_pair (state, "font-stretch", atts);
    rsvg_lookup_parse_style_pair (state, "font-style", atts);
    rsvg_lookup_parse_style_pair (state, "font-variant", atts);
    rsvg_lookup_parse_style_pair (state, "font-weight", atts);
    rsvg_lookup_parse_style_pair (state, "marker-end", atts);
    rsvg_lookup_parse_style_pair (state, "mask", atts);
    rsvg_lookup_parse_style_pair (state, "marker-mid", atts);
    rsvg_lookup_parse_style_pair (state, "marker-start", atts);
    rsvg_lookup_parse_style_pair (state, "opacity", atts);
    rsvg_lookup_parse_style_pair (state, "overflow", atts);
    rsvg_lookup_parse_style_pair (state, "shape-rendering", atts);
    rsvg_lookup_parse_style_pair (state, "stop-color", atts);
    rsvg_lookup_parse_style_pair (state, "stop-opacity", atts);
    rsvg_lookup_parse_style_pair (state, "stroke", atts);
    rsvg_lookup_parse_style_pair (state, "stroke-dasharray", atts);
    rsvg_lookup_parse_style_pair (state, "stroke-dashoffset", atts);
    rsvg_lookup_parse_style_pair (state, "stroke-linecap", atts);
    rsvg_lookup_parse_style_pair (state, "stroke-linejoin", atts);
    rsvg_lookup_parse_style_pair (state, "stroke-miterlimit", atts);
    rsvg_lookup_parse_style_pair (state, "stroke-opacity", atts);
    rsvg_lookup_parse_style_pair (state, "stroke-width", atts);
    rsvg_lookup_parse_style_pair (state, "text-anchor", atts);
    rsvg_lookup_parse_style_pair (state, "text-decoration", atts);
    rsvg_lookup_parse_style_pair (state, "unicode-bidi", atts);
    rsvg_lookup_parse_style_pair (state, "letter-spacing", atts);
    rsvg_lookup_parse_style_pair (state, "visibility", atts);
    rsvg_lookup_parse_style_pair (state, "writing-mode", atts);
    rsvg_lookup_parse_style_pair (state, "xml:lang", atts);
    rsvg_lookup_parse_style_pair (state, "xml:space", atts);
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073

    {
        /* TODO: this conditional behavior isn't quite correct, and i'm not sure it should reside here */
        gboolean cond_true, has_cond;

        cond_true = rsvg_eval_switch_attributes (atts, &has_cond);

        if (has_cond) {
            state->cond_true = cond_true;
            state->has_cond = TRUE;
        }
    }
1074 1075
}

1076
static gboolean
1077
parse_style_value (const gchar *string, gchar **value, gboolean *important)
1078 1079 1080
{
    gchar **strings;

1081
    strings = g_strsplit (string, "!", 2);
Kurosawa Takeshi's avatar
Kurosawa Takeshi committed
1082

Christian Persch's avatar
Christian Persch committed
1083 1084 1085 1086
    if (strings == NULL || strings[0] == NULL) {
        g_strfreev (strings);
        return FALSE;
    }
Kurosawa Takeshi's avatar
Kurosawa Takeshi committed
1087

Christian Persch's avatar
Christian Persch committed
1088
    if (strings[1] != NULL && strings[2] == NULL &&
1089
        g_str_equal (g_strstrip (strings[1]), "important")) {
1090 1091 1092
        *important = TRUE;
    } else {
        *important = FALSE;
1093
    }
Christian Persch's avatar
Christian Persch committed
1094 1095

    *value = g_strdup (g_strstrip (strings[0]));
1096 1097 1098

    g_strfreev (strings);

1099
    return TRUE;
1100 1101
}

1102 1103 1104 1105 1106 1107 1108
/* Split a CSS2 style into individual style arguments, setting attributes
   in the SVG context.
   
   It's known that this is _way_ out of spec. A more complete CSS2
   implementation will happen later.
*/
void
1109
rsvg_parse_style (RsvgHandle * ctx, RsvgState * state, const char *str)
1110
{
1111 1112 1113 1114 1115 1116 1117 1118 1119 1120
    gchar **styles;
    guint i;

    styles = g_strsplit (str, ";", -1);
    for (i = 0; i < g_strv_length (styles); i++) {
        gchar **values;
        values = g_strsplit (styles[i], ":", 2);
        if (!values)
            continue;

Kurosawa Takeshi's avatar
Kurosawa Takeshi committed
1121
        if (g_strv_length (values) == 2) {
1122 1123
            gboolean important;
            gchar *style_value = NULL;
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139
            gchar *first_value = values[0];
            gchar *second_value = values[1];
            gchar **split_list;

            /* Just remove single quotes in a trivial way.  No handling for any
             * special character inside the quotes is done.  This relates
             * especially to font-family names but cases with special characters
             * are rare.
             *
             * We need a real CSS parser, sigh.
             */
            split_list = g_strsplit (second_value, "'", -1);
            second_value = g_strjoinv(NULL, split_list);
            g_strfreev(split_list);

            if (parse_style_value (second_value, &style_value, &important))
1140
                rsvg_parse_style_pair (state,
1141
                                       g_strstrip (first_value),
Kurosawa Takeshi's avatar
Kurosawa Takeshi committed
1142 1143
                                       style_value,
                                       important);
1144
            g_free (style_value);
1145
            g_free (second_value);
1146 1147
        }
        g_strfreev (values);
1148
    }
1149
    g_strfreev (styles);
1150 1151
}

1152
static void
1153 1154 1155 1156 1157
rsvg_css_define_style (RsvgHandle * ctx,
                       const gchar * selector,
                       const gchar * style_name,
                       const gchar * style_value,
                       gboolean important)
1158
{
1159 1160
    GHashTable *styles;
    gboolean need_insert = FALSE;
1161 1162

    /* push name/style pair into HT */
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179
    styles = g_hash_table_lookup (ctx->priv->css_props, selector);
    if (styles == NULL) {
        styles = g_hash_table_new_full (g_str_hash, g_str_equal,
                                        g_free, (GDestroyNotify) style_value_data_free);
        g_hash_table_insert (ctx->priv->css_props, (gpointer) g_strdup (selector), styles);
        need_insert = TRUE;
    } else {
        StyleValueData *current_value;
        current_value = g_hash_table_lookup (styles, style_name);
        if (current_value == NULL || !current_value->important)
            need_insert = TRUE;
    }
    if (need_insert) {
        g_hash_table_insert (styles,
                             (gpointer) g_strdup (style_name),
                             (gpointer) style_value_data_new (style_value, important));
    }
1180 1181
}

1182 1183
typedef struct _CSSUserData {
    RsvgHandle *ctx;
1184
    CRSelector *selector;
1185 1186 1187
} CSSUserData;

static void
1188
css_user_data_init (CSSUserData * user_data, RsvgHandle * ctx)
1189
{
1190
    user_data->ctx = ctx;
1191
    user_data->selector = NULL;
1192 1193 1194
}

static void
1195
ccss_start_selector (CRDocHandler * a_handler, CRSelector * a_selector_list)
1196
{
1197
    CSSUserData *user_data;
1198

1199
    g_return_if_fail (a_handler);
1200

1201
    user_data = (CSSUserData *) a_handler->app_data;
1202 1203
    cr_selector_ref (a_selector_list);
    user_data->selector = a_selector_list;
1204 1205 1206
}

static void
1207
ccss_end_selector (CRDocHandler * a_handler, CRSelector * a_selector_list)
1208
{
1209 1210 1211 1212 1213 1214
    CSSUserData *user_data;

    g_return_if_fail (a_handler);

    user_data = (CSSUserData *) a_handler->app_data;

1215 1216
    cr_selector_unref (user_data->selector);
    user_data->selector = NULL;
1217 1218 1219
}

static void
1220
ccss_property (CRDocHandler * a_handler, CRString * a_name, CRTerm * a_expr, gboolean a_important)
1221
{
1222
    CSSUserData *user_data;
1223
    gchar *name = NULL;
1224
    size_t len = 0;
1225

1226
    g_return_if_fail (a_handler);
1227

1228
    user_data = (CSSUserData *) a_handler->app_data;
1229

1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
    if (a_name && a_expr && user_data->selector) {
        CRSelector *cur;
        for (cur = user_data->selector; cur; cur = cur->next) {
            if (cur->simple_sel) {
                gchar *selector = (gchar *) cr_simple_sel_to_string (cur->simple_sel);
                if (selector) {
                    gchar *style_name, *style_value;
                    name = (gchar *) cr_string_peek_raw_str (a_name);
                    len = cr_string_peek_raw_str_len (a_name);
                    style_name = g_strndup (name, len);
                    style_value = (gchar *)cr_term_to_string (a_expr);
                    rsvg_css_define_style (user_data->ctx,
                                           selector,
                                           style_name,
                                           style_value,
                                           a_important);
                    g_free (selector);
                    g_free (style_name);
                    g_free (style_value);
                }
            }
        }
1252
    }
1253 1254
}

1255
static void
1256