source: trunk/third/librsvg/rsvg-styles.c @ 18805

Revision 18805, 21.9 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18804, which included commits to RCS files with non-trunk default branches.
Line 
1/* vim: set sw=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2/*
3   rsvg-styles.c: Handle SVG styles
4
5   Copyright (C) 2000 Eazel, Inc.
6   Copyright (C) 2002 Dom Lachowicz <cinamod@hotmail.com>
7
8   This program is free software; you can redistribute it and/or
9   modify it under the terms of the GNU Library General Public License as
10   published by the Free Software Foundation; either version 2 of the
11   License, or (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16   Library General Public License for more details.
17
18   You should have received a copy of the GNU Library General Public
19   License along with this program; if not, write to the
20   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21   Boston, MA 02111-1307, USA.
22
23   Author: Raph Levien <raph@artofcode.com>
24*/
25#include <string.h>
26
27#include "rsvg.h"
28#include "rsvg-css.h"
29#include "rsvg-styles.h"
30#include "rsvg-private.h"
31
32#include <libart_lgpl/art_rgba.h>
33#include <libart_lgpl/art_affine.h>
34
35/* Our default font */
36#define DEFAULT_FONT "Times Roman"
37
38gdouble
39rsvg_viewport_percentage (gdouble width, gdouble height)
40{
41        return ((width * width) + (height * height)) / M_SQRT2;
42}
43
44void
45rsvg_state_init (RsvgState *state)
46{
47        memset (state, 0, sizeof (*state));
48       
49        art_affine_identity (state->affine);
50       
51        state->opacity = 0xff;
52        state->fill = rsvg_paint_server_parse (NULL, "#000");
53        state->fill_opacity = 0xff;
54        state->stroke_opacity = 0xff;
55        state->stroke_width = 1;
56        state->miter_limit = 4;
57        state->cap = ART_PATH_STROKE_CAP_BUTT;
58        state->join = ART_PATH_STROKE_JOIN_MITER;
59        state->stop_opacity = 0xff;
60       
61        state->font_family  = g_strdup (DEFAULT_FONT);
62        state->font_size    = 12.0;
63        state->font_style   = PANGO_STYLE_NORMAL;
64        state->font_variant = PANGO_VARIANT_NORMAL;
65        state->font_weight  = PANGO_WEIGHT_NORMAL;
66        state->font_stretch = PANGO_STRETCH_NORMAL;
67
68        state->visible = TRUE;
69}
70
71void
72rsvg_state_clone (RsvgState *dst, const RsvgState *src)
73{
74        gint i;
75       
76        *dst = *src;
77        dst->font_family = g_strdup (src->font_family);
78        rsvg_paint_server_ref (dst->fill);
79        rsvg_paint_server_ref (dst->stroke);
80        dst->save_pixbuf = NULL;
81       
82        if (src->dash.n_dash > 0)
83                {
84                        dst->dash.dash = g_new (gdouble, src->dash.n_dash);
85                        for (i = 0; i < src->dash.n_dash; i++)
86                                dst->dash.dash[i] = src->dash.dash[i];
87                }
88}
89
90void
91rsvg_state_finalize (RsvgState *state)
92{
93        g_free (state->font_family);
94        rsvg_paint_server_unref (state->fill);
95        rsvg_paint_server_unref (state->stroke);
96       
97        if (state->dash.n_dash != 0)
98                g_free (state->dash.dash);
99}
100
101/* Parse a CSS2 style argument, setting the SVG context attributes. */
102static void
103rsvg_parse_style_arg (RsvgHandle *ctx, RsvgState *state, const char *str)
104{
105        RsvgState * parent_state = state; /* TODO: temporary hack */
106        int arg_off;
107       
108        arg_off = rsvg_css_param_arg_offset (str);
109        if (rsvg_css_param_match (str, "opacity"))
110                {
111                        state->opacity = rsvg_css_parse_opacity (str + arg_off);
112                }
113        else if (rsvg_css_param_match (str, "display"))
114                {
115                        if (!strcmp (str + arg_off, "none"))
116                                state->visible = FALSE;
117                        else if (strcmp (str + arg_off, "inherit") != 0)
118                                state->visible = TRUE;
119                        /* else inherit */
120                }
121        else if (rsvg_css_param_match (str, "visibility"))
122                {
123                        if (!strcmp (str + arg_off, "visable"))
124                                state->visible = TRUE;
125                        else if (strcmp (str + arg_off, "inherit") != 0)
126                                state->visible = FALSE; /* collapse or hidden */
127                        /* else inherit */
128                }
129        else if (rsvg_css_param_match (str, "fill"))
130                {
131                        rsvg_paint_server_unref (state->fill);
132                        state->fill = rsvg_paint_server_parse (ctx->defs, str + arg_off);
133                }
134        else if (rsvg_css_param_match (str, "fill-opacity"))
135                {
136                        state->fill_opacity = rsvg_css_parse_opacity (str + arg_off);
137                }
138        else if (rsvg_css_param_match (str, "stroke"))
139                {
140                        rsvg_paint_server_unref (state->stroke);
141                        state->stroke = rsvg_paint_server_parse (ctx->defs, str + arg_off);
142                }
143        else if (rsvg_css_param_match (str, "stroke-width"))
144                {
145                        state->stroke_width = rsvg_css_parse_normalized_length (str + arg_off, ctx->dpi,
146                                                                                                                                        (gdouble)ctx->height, state->font_size);
147                }
148        else if (rsvg_css_param_match (str, "stroke-linecap"))
149                {
150                        if (!strcmp (str + arg_off, "butt"))
151                                state->cap = ART_PATH_STROKE_CAP_BUTT;
152                        else if (!strcmp (str + arg_off, "round"))
153                                state->cap = ART_PATH_STROKE_CAP_ROUND;
154                        else if (!strcmp (str + arg_off, "square"))
155                                state->cap = ART_PATH_STROKE_CAP_SQUARE;
156                        else
157                                g_warning ("unknown line cap style %s", str + arg_off);
158                }
159        else if (rsvg_css_param_match (str, "stroke-opacity"))
160                {
161                        state->stroke_opacity = rsvg_css_parse_opacity (str + arg_off);
162                }
163        else if (rsvg_css_param_match (str, "stroke-linejoin"))
164                {
165                        if (!strcmp (str + arg_off, "miter"))
166                                state->join = ART_PATH_STROKE_JOIN_MITER;
167                        else if (!strcmp (str + arg_off, "round"))
168                                state->join = ART_PATH_STROKE_JOIN_ROUND;
169                        else if (!strcmp (str + arg_off, "bevel"))
170                                state->join = ART_PATH_STROKE_JOIN_BEVEL;
171                        else
172                                g_warning ("unknown line join style %s", str + arg_off);
173                }
174        else if (rsvg_css_param_match (str, "font-size"))
175                {
176                        state->font_size = rsvg_css_parse_normalized_length (str + arg_off, ctx->dpi,
177                                                                                                                                 (gdouble)ctx->height, state->font_size);
178                }
179        else if (rsvg_css_param_match (str, "font-family"))
180                {
181                        char * save = g_strdup (rsvg_css_parse_font_family (str + arg_off, parent_state->font_family));
182                        g_free (state->font_family);
183                        state->font_family = save;
184                }
185        else if (rsvg_css_param_match (str, "font-style"))
186                {
187                        state->font_style = rsvg_css_parse_font_style (str + arg_off, parent_state->font_style);
188                }
189        else if (rsvg_css_param_match (str, "font-variant"))
190                {
191                        state->font_variant = rsvg_css_parse_font_variant (str + arg_off, parent_state->font_variant);
192                }
193        else if (rsvg_css_param_match (str, "font-weight"))
194                {
195                        state->font_weight = rsvg_css_parse_font_weight (str + arg_off, parent_state->font_weight);
196                }
197        else if (rsvg_css_param_match (str, "font-stretch"))
198                {
199                        state->font_stretch = rsvg_css_parse_font_stretch (str + arg_off, parent_state->font_stretch);
200                }
201        else if (rsvg_css_param_match (str, "text-decoration"))
202                {
203                        if (!strcmp (str, "inherit"))
204                                state->font_decor = parent_state->font_decor;
205                        else
206                                {
207                                        if (strstr (str, "underline"))
208                                                state->font_decor |= TEXT_UNDERLINE;
209                                        if (strstr (str, "overline"))
210                                                state->font_decor |= TEXT_OVERLINE;
211                                        if (strstr (str, "strike") || strstr (str, "line-through")) /* strike though or line-through */
212                                                state->font_decor |= TEXT_STRIKE;
213                                }
214                }
215        else if (rsvg_css_param_match (str, "stop-color"))
216                {
217                        state->stop_color = rsvg_css_parse_color (str + arg_off);
218                }
219        else if (rsvg_css_param_match (str, "stop-opacity"))
220                {
221                        state->stop_opacity = rsvg_css_parse_opacity (str + arg_off);
222                }
223        else if (rsvg_css_param_match (str, "stroke-miterlimit"))
224                {
225                        state->miter_limit = g_ascii_strtod (str + arg_off, NULL);
226                }
227        else if (rsvg_css_param_match (str, "stroke-dashoffset"))
228                {
229                        state->dash.offset = rsvg_css_parse_normalized_length (str + arg_off, ctx->dpi,
230                                                                                                                                   rsvg_viewport_percentage((gdouble)ctx->width, (gdouble)ctx->height), state->font_size);
231                        if (state->dash.offset < 0.)
232                                state->dash.offset = 0.;
233                }
234        else if (rsvg_css_param_match (str, "stroke-dasharray"))
235                {
236                        if(!strcmp(str + arg_off, "none"))
237                                {
238                                        if (state->dash.n_dash != 0)
239                                                {
240                                                        /* free any cloned dash data */
241                                                        g_free (state->dash.dash);
242                                                        state->dash.n_dash = 0;
243                                                }
244                                }
245                        else
246                                {
247                                        gchar ** dashes = g_strsplit (str + arg_off, ",", -1);
248                                        if (NULL != dashes)
249                                                {
250                                                        gint n_dashes, i;
251                                                        gboolean is_even = FALSE ;
252                                                       
253                                                        /* count the #dashes */
254                                                        for (n_dashes = 0; dashes[n_dashes] != NULL; n_dashes++)
255                                                                ;
256                                                       
257                                                        is_even = (n_dashes % 2 == 0);
258                                                        state->dash.n_dash = (is_even ? n_dashes : n_dashes * 2);
259                                                        state->dash.dash = g_new (double, state->dash.n_dash);
260                                                       
261                                                        /* TODO: handle negative value == error case */
262                                                       
263                                                        /* the even and base case */
264                                                        for (i = 0; i < n_dashes; i++)
265                                                                state->dash.dash[i] = g_ascii_strtod (dashes[i], NULL);
266                                                       
267                                                        /* if an odd number of dashes is found, it gets repeated */
268                                                        if (!is_even)
269                                                                for (; i < state->dash.n_dash; i++)
270                                                                        state->dash.dash[i] = g_ascii_strtod (dashes[i - n_dashes], NULL);
271                                                       
272                                                        g_strfreev (dashes) ;
273                                                }
274                                }
275                }
276}
277
278/* tell whether @str is a supported style argument
279   whenever something gets added to parse_arg, please
280   remember to add it here too
281*/
282gboolean
283rsvg_is_style_arg(const char *str)
284{
285        static GHashTable *styles = NULL;
286        if (!styles)
287                {
288                        styles = g_hash_table_new (g_str_hash, g_str_equal);
289                       
290                        g_hash_table_insert (styles, "display",           GINT_TO_POINTER (TRUE));
291                        g_hash_table_insert (styles, "fill",              GINT_TO_POINTER (TRUE));
292                        g_hash_table_insert (styles, "fill-opacity",      GINT_TO_POINTER (TRUE));
293                        g_hash_table_insert (styles, "font-family",       GINT_TO_POINTER (TRUE));
294                        g_hash_table_insert (styles, "font-size",         GINT_TO_POINTER (TRUE));
295                        g_hash_table_insert (styles, "font-stretch",      GINT_TO_POINTER (TRUE));
296                        g_hash_table_insert (styles, "font-style",        GINT_TO_POINTER (TRUE));
297                        g_hash_table_insert (styles, "font-variant",      GINT_TO_POINTER (TRUE));
298                        g_hash_table_insert (styles, "font-weight",       GINT_TO_POINTER (TRUE));
299                        g_hash_table_insert (styles, "opacity",           GINT_TO_POINTER (TRUE));
300                        g_hash_table_insert (styles, "stop-color",        GINT_TO_POINTER (TRUE));
301                        g_hash_table_insert (styles, "stop-opacity",      GINT_TO_POINTER (TRUE));
302                        g_hash_table_insert (styles, "stroke",            GINT_TO_POINTER (TRUE));
303                        g_hash_table_insert (styles, "stroke-dasharray",  GINT_TO_POINTER (TRUE));
304                        g_hash_table_insert (styles, "stroke-dashoffset", GINT_TO_POINTER (TRUE));
305                        g_hash_table_insert (styles, "stroke-linecap",    GINT_TO_POINTER (TRUE));
306                        g_hash_table_insert (styles, "stroke-linejoin",   GINT_TO_POINTER (TRUE));
307                        g_hash_table_insert (styles, "stroke-miterlimit", GINT_TO_POINTER (TRUE));
308                        g_hash_table_insert (styles, "stroke-opacity",    GINT_TO_POINTER (TRUE));
309                        g_hash_table_insert (styles, "stroke-width",      GINT_TO_POINTER (TRUE));
310                        g_hash_table_insert (styles, "text-decoration",   GINT_TO_POINTER (TRUE));
311                        g_hash_table_insert (styles, "visibility",        GINT_TO_POINTER (TRUE));
312                }
313       
314        /* this will default to 0 (FALSE) on a failed lookup */
315        return GPOINTER_TO_INT (g_hash_table_lookup (styles, str));
316}
317
318/* take a pair of the form (fill="#ff00ff") and parse it as a style */
319void
320rsvg_parse_style_pair (RsvgHandle *ctx, RsvgState *state,
321                                           const char *key, const char *val)
322{
323        gchar * str = g_strdup_printf ("%s:%s", key, val);
324        rsvg_parse_style_arg (ctx, state, str);
325        g_free (str);
326}
327
328/* Split a CSS2 style into individual style arguments, setting attributes
329   in the SVG context.
330   
331   It's known that this is _way_ out of spec. A more complete CSS2
332   implementation will happen later.
333*/
334void
335rsvg_parse_style (RsvgHandle *ctx, RsvgState *state, const char *str)
336{
337        int start, end;
338        char *arg;
339       
340        start = 0;
341        while (str[start] != '\0')
342                {
343                        for (end = start; str[end] != '\0' && str[end] != ';'; end++);
344                        arg = g_new (char, 1 + end - start);
345                        memcpy (arg, str + start, end - start);
346                        arg[end - start] = '\0';
347                        rsvg_parse_style_arg (ctx, state, arg);
348                        g_free (arg);
349                        start = end;
350                        if (str[start] == ';') start++;
351                        while (str[start] == ' ') start++;
352                }
353}
354
355/*
356 * Extremely poor man's CSS parser. Not robust. Not compliant.
357 * Should work well enough for our needs ;-)
358 * See also: http://www.w3.org/TR/REC-CSS2/syndata.html
359 * I should use that sometime in order to make a complaint parser
360 */
361void
362rsvg_parse_cssbuffer (RsvgHandle *ctx, const char * buff, size_t buflen)
363{
364        size_t loc = 0;
365       
366        while (loc < buflen)
367                {
368                        GString * style_name  = g_string_new (NULL);
369                        GString * style_props = g_string_new (NULL);
370                        const char * existing    = NULL;
371
372                        /* advance to the style's name */
373                        while (loc < buflen && g_ascii_isspace (buff[loc]))
374                                loc++;
375                       
376                        while (loc < buflen && !g_ascii_isspace (buff[loc]))
377                                g_string_append_c (style_name, buff[loc++]);
378                       
379                        /* advance to the first { that defines the style's properties */
380                        while (loc < buflen && buff[loc++] != '{' )
381                                ;
382                       
383                        while (loc < buflen && g_ascii_isspace (buff[loc]))
384                                loc++;
385                       
386                        while (loc < buflen && buff[loc] != '}')
387                                {
388                                        /* suck in and append our property */
389                                        while (loc < buflen && buff[loc] != ';' && buff[loc] != '}' )
390                                                g_string_append_c (style_props, buff[loc++]);
391
392                                        if (loc == buflen || buff[loc] == '}')
393                                                break;
394                                        else
395                                                {
396                                                        g_string_append_c (style_props, ';');
397                                                       
398                                                        /* advance to the next property */
399                                                        loc++;
400                                                        while (loc < buflen && g_ascii_isspace (buff[loc]))
401                                                                loc++;
402                                                }
403                                }
404
405                        /* push name/style pair into HT */
406                        existing = (const char *)g_hash_table_lookup (ctx->css_props, style_name->str);
407                        if (existing != NULL)
408                                g_string_append_len (style_props, existing, strlen (existing));
409
410                        /* will destroy the existing key and value for us */
411                        g_hash_table_insert (ctx->css_props, style_name->str, style_props->str);
412
413                        g_string_free (style_name, FALSE);
414                        g_string_free (style_props, FALSE);
415                       
416                        loc++;
417                        while (loc < buflen && g_ascii_isspace (buff[loc]))
418                                loc++;
419                }
420}
421
422/* Parse an SVG transform string into an affine matrix. Reference: SVG
423   working draft dated 1999-07-06, section 8.5. Return TRUE on
424   success. */
425gboolean
426rsvg_parse_transform (double dst[6], const char *src)
427{
428        int idx;
429        char keyword[32];
430        double args[6];
431        int n_args;
432        guint key_len;
433        double tmp_affine[6];
434       
435        art_affine_identity (dst);
436       
437        idx = 0;
438        while (src[idx])
439                {
440                        /* skip initial whitespace */
441                        while (g_ascii_isspace (src[idx]))
442                                idx++;
443                       
444                        /* parse keyword */
445                        for (key_len = 0; key_len < sizeof (keyword); key_len++)
446                                {
447                                        char c;
448                                       
449                                        c = src[idx];
450                                        if (g_ascii_isalpha (c) || c == '-')
451                                                keyword[key_len] = src[idx++];
452                                        else
453                                                break;
454                                }
455                        if (key_len >= sizeof (keyword))
456                                return FALSE;
457                        keyword[key_len] = '\0';
458                       
459                        /* skip whitespace */
460                        while (g_ascii_isspace (src[idx]))
461                                idx++;
462                       
463                        if (src[idx] != '(')
464                                return FALSE;
465                        idx++;
466                       
467                        for (n_args = 0; ; n_args++)
468                                {
469                                        char c;
470                                        char *end_ptr;
471                                       
472                                        /* skip whitespace */
473                                        while (g_ascii_isspace (src[idx]))
474                                                idx++;
475                                        c = src[idx];
476                                        if (g_ascii_isdigit (c) || c == '+' || c == '-' || c == '.')
477                                                {
478                                                        if (n_args == sizeof(args) / sizeof(args[0]))
479                                                                return FALSE; /* too many args */
480                                                        args[n_args] = g_ascii_strtod (src + idx, &end_ptr);
481                                                        idx = end_ptr - src;
482                                                       
483                                                        while (g_ascii_isspace (src[idx]))
484                                                                idx++;
485                                                       
486                                                        /* skip optional comma */
487                                                        if (src[idx] == ',')
488                                                                idx++;
489                                                }
490                                        else if (c == ')')
491                                                break;
492                                        else
493                                                return FALSE;
494                                }
495                        idx++;
496                       
497                        /* ok, have parsed keyword and args, now modify the transform */
498                        if (!strcmp (keyword, "matrix"))
499                                {
500                                        if (n_args != 6)
501                                                return FALSE;
502                                        art_affine_multiply (dst, args, dst);
503                                }
504                        else if (!strcmp (keyword, "translate"))
505                                {
506                                        if (n_args == 1)
507                                                args[1] = 0;
508                                        else if (n_args != 2)
509                                                return FALSE;
510                                        art_affine_translate (tmp_affine, args[0], args[1]);
511                                        art_affine_multiply (dst, tmp_affine, dst);
512                                }
513                        else if (!strcmp (keyword, "scale"))
514                                {
515                                        if (n_args == 1)
516                                                args[1] = args[0];
517                                        else if (n_args != 2)
518                                                return FALSE;
519                                        art_affine_scale (tmp_affine, args[0], args[1]);
520                                        art_affine_multiply (dst, tmp_affine, dst);
521                                }
522                        else if (!strcmp (keyword, "rotate"))
523                                {
524                                        if (n_args != 1)
525                                                return FALSE;
526                                        art_affine_rotate (tmp_affine, args[0]);
527                                        art_affine_multiply (dst, tmp_affine, dst);
528                                }
529                        else if (!strcmp (keyword, "skewX"))
530                                {
531                                        if (n_args != 1)
532                                                return FALSE;
533                                        art_affine_shear (tmp_affine, args[0]);
534                                        art_affine_multiply (dst, tmp_affine, dst);
535                                }
536                        else if (!strcmp (keyword, "skewY"))
537                                {
538                                        if (n_args != 1)
539                                                return FALSE;
540                                        art_affine_shear (tmp_affine, args[0]);
541                                        /* transpose the affine, given that we know [1] is zero */
542                                        tmp_affine[1] = tmp_affine[2];
543                                        tmp_affine[2] = 0;
544                                        art_affine_multiply (dst, tmp_affine, dst);
545                                }
546                        else
547                                return FALSE; /* unknown keyword */
548                }
549        return TRUE;
550}
551
552/**
553 * rsvg_parse_transform_attr: Parse transform attribute and apply to state.
554 * @ctx: Rsvg context.
555 * @state: State in which to apply the transform.
556 * @str: String containing transform.
557 *
558 * Parses the transform attribute in @str and applies it to @state.
559 **/
560static void
561rsvg_parse_transform_attr (RsvgHandle *ctx, RsvgState *state, const char *str)
562{
563        double affine[6];
564       
565        if (rsvg_parse_transform (affine, str))
566                {
567                        art_affine_multiply (state->affine, affine, state->affine);
568                }
569        else
570                {
571                        /* parse error for transform attribute. todo: report */
572                }
573}
574
575static gboolean
576rsvg_lookup_apply_css_style (RsvgHandle *ctx, const char * target)
577{
578        const char * value = (const char *)g_hash_table_lookup (ctx->css_props, target);
579       
580        if (value != NULL)
581                {
582                        rsvg_parse_style (ctx, &ctx->state[ctx->n_state - 1],
583                                                          value);
584                        return TRUE;
585                }
586        return FALSE;
587}
588
589/**
590 * rsvg_parse_style_attrs: Parse style attribute.
591 * @ctx: Rsvg context.
592 * @tag: The SVG tag we're processing (eg: circle, ellipse), optionally %NULL
593 * @klazz: The space delimited class list, optionally %NULL
594 * @atts: Attributes in SAX style.
595 *
596 * Parses style and transform attributes and modifies state at top of
597 * stack.
598 **/
599void
600rsvg_parse_style_attrs (RsvgHandle *ctx,
601                                                const char * tag,
602                                                const char * klazz,
603                                                const char * id,
604                                                const xmlChar **atts)
605{
606        int i = 0, j = 0;
607        char * target = NULL;
608        gboolean found = FALSE;
609        GString * klazz_list = NULL;
610       
611        /* handle the all-encompassing "star" entry first, ignoring found-ness */
612        rsvg_lookup_apply_css_style (ctx, "*");
613
614        if (id != NULL)
615                {
616                        target = g_strdup_printf ("#%s", id);
617                        rsvg_lookup_apply_css_style (ctx, target);
618                        g_free (target);
619                }
620
621        /* todo: see if klazz or tag should accumulate onto tag.klazz */
622
623        if (tag != NULL && klazz != NULL)
624                {
625                        target = g_strdup_printf ("%s.%s", tag, klazz);
626                        found = rsvg_lookup_apply_css_style (ctx, target);
627                        g_free (target);
628                }
629       
630        if (found == FALSE)
631                {
632                        if (tag != NULL)
633                                rsvg_lookup_apply_css_style (ctx, tag);
634                       
635                        if (klazz != NULL)
636                                {
637                                        i = strlen (klazz);
638                                        while (j < i)
639                                                {
640                                                        klazz_list = g_string_new (".");
641                                                       
642                                                        while (j < i && g_ascii_isspace(klazz[j]))
643                                                                j++;
644                                                       
645                                                        while (j < i && !g_ascii_isspace(klazz[j]))
646                                                                g_string_append_c (klazz_list, klazz[j++]);
647                                                       
648                                                        rsvg_lookup_apply_css_style (ctx, klazz_list->str);
649                                                        g_string_free (klazz_list, TRUE);
650                                                }
651                                }
652                }
653       
654        if (atts != NULL)
655                {
656                        for (i = 0; atts[i] != NULL; i += 2)
657                                {
658                                        if (!strcmp ((char *)atts[i], "style"))
659                                                rsvg_parse_style (ctx, &ctx->state[ctx->n_state - 1],
660                                                                                  (char *)atts[i + 1]);
661                                        else if (!strcmp ((char *)atts[i], "transform"))
662                                                rsvg_parse_transform_attr (ctx, &ctx->state[ctx->n_state - 1],
663                                                                                                   (char *)atts[i + 1]);
664                                        else if (rsvg_is_style_arg ((char *)atts[i]))
665                                                rsvg_parse_style_pair (ctx, &ctx->state[ctx->n_state - 1],
666                                                                                           (char *)atts[i], (char *)atts[i + 1]);
667                                }
668                }
669}
670
671static void
672rsvg_pixmap_destroy (guchar *pixels, gpointer data)
673{
674  g_free (pixels);
675}
676
677/**
678 * rsvg_push_opacity_group: Begin a new transparency group.
679 * @ctx: Context in which to push.
680 *
681 * Pushes a new transparency group onto the stack. The top of the stack
682 * is stored in the context, while the "saved" value is in the state
683 * stack.
684 **/
685void
686rsvg_push_opacity_group (RsvgHandle *ctx)
687{
688        RsvgState *state;
689        GdkPixbuf *pixbuf;
690        art_u8 *pixels;
691        int width, height, rowstride;
692       
693        state = &ctx->state[ctx->n_state - 1];
694        pixbuf = ctx->pixbuf;
695       
696        state->save_pixbuf = pixbuf;
697       
698        if (pixbuf == NULL)
699                {
700                        /* FIXME: What warning/GError here? */
701                        return;
702                }
703
704        if (!gdk_pixbuf_get_has_alpha (pixbuf))
705    {
706                g_warning ("push/pop transparency group on non-alpha buffer nyi");
707                return;
708    }
709       
710        width = gdk_pixbuf_get_width (pixbuf);
711        height = gdk_pixbuf_get_height (pixbuf);
712        rowstride = gdk_pixbuf_get_rowstride (pixbuf);
713        pixels = g_new (art_u8, rowstride * height);
714        memset (pixels, 0, rowstride * height);
715       
716        pixbuf = gdk_pixbuf_new_from_data (pixels,
717                                                                           GDK_COLORSPACE_RGB,
718                                                                           TRUE,
719                                                                           gdk_pixbuf_get_bits_per_sample (pixbuf),
720                                                                           width,
721                                                                           height,
722                                                                           rowstride,
723                                                                           rsvg_pixmap_destroy,
724                                                                           NULL);
725        ctx->pixbuf = pixbuf;
726}
727
728/**
729 * rsvg_pop_opacity_group: End a transparency group.
730 * @ctx: Context in which to push.
731 * @opacity: Opacity for blending (0..255).
732 *
733 * Pops a new transparency group from the stack, recompositing with the
734 * next on stack.
735 **/
736void
737rsvg_pop_opacity_group (RsvgHandle *ctx, int opacity)
738{
739        RsvgState *state = &ctx->state[ctx->n_state - 1];
740        GdkPixbuf *tos, *nos;
741        art_u8 *tos_pixels, *nos_pixels;
742        int width;
743        int height;
744        int rowstride;
745        int x, y;
746        int tmp;
747       
748        tos = ctx->pixbuf;
749        nos = state->save_pixbuf;
750       
751        if (tos == NULL || nos == NULL)
752                {
753                        /* FIXME: What warning/GError here? */
754                        return;
755                }
756       
757        if (!gdk_pixbuf_get_has_alpha (nos))
758                {
759                        g_warning ("push/pop transparency group on non-alpha buffer nyi");
760                        return;
761                }
762       
763        width = gdk_pixbuf_get_width (tos);
764        height = gdk_pixbuf_get_height (tos);
765        rowstride = gdk_pixbuf_get_rowstride (tos);
766       
767        tos_pixels = gdk_pixbuf_get_pixels (tos);
768        nos_pixels = gdk_pixbuf_get_pixels (nos);
769       
770        for (y = 0; y < height; y++)
771                {
772                        for (x = 0; x < width; x++)
773                                {
774                                        art_u8 r, g, b, a;
775                                        a = tos_pixels[4 * x + 3];
776                                        if (a)
777                                                {
778                                                        r = tos_pixels[4 * x];
779                                                        g = tos_pixels[4 * x + 1];
780                                                        b = tos_pixels[4 * x + 2];
781                                                        tmp = a * opacity + 0x80;
782                                                        a = (tmp + (tmp >> 8)) >> 8;
783                                                        art_rgba_run_alpha (nos_pixels + 4 * x, r, g, b, a, 1);
784                                                }
785                                }
786                        tos_pixels += rowstride;
787                        nos_pixels += rowstride;
788                }
789       
790        g_object_unref (tos);
791        ctx->pixbuf = nos;
792}
Note: See TracBrowser for help on using the repository browser.