Coverage Report

Created: 2026-10-01 07:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cairo/src/cairo-svg-glyph-render.c
Line
Count
Source
1
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2
/* cairo - a vector graphics library with display and print output
3
 *
4
 * Copyright © 2022 Adrian Johnson
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it either under the terms of the GNU Lesser General Public
8
 * License version 2.1 as published by the Free Software Foundation
9
 * (the "LGPL") or, at your option, under the terms of the Mozilla
10
 * Public License Version 1.1 (the "MPL"). If you do not alter this
11
 * notice, a recipient may use your version of this file under either
12
 * the MPL or the LGPL.
13
 *
14
 * You should have received a copy of the LGPL along with this library
15
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17
 * You should have received a copy of the MPL along with this library
18
 * in the file COPYING-MPL-1.1
19
 *
20
 * The contents of this file are subject to the Mozilla Public License
21
 * Version 1.1 (the "License"); you may not use this file except in
22
 * compliance with the License. You may obtain a copy of the License at
23
 * http://www.mozilla.org/MPL/
24
 *
25
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27
 * the specific language governing rights and limitations.
28
 *
29
 * The Original Code is the cairo graphics library.
30
 *
31
 * The Initial Developer of the Original Code is Adrian Johnson.
32
 *
33
 * Contributor(s):
34
 *  Adrian Johnson <ajohnson@redneon.com>
35
 */
36
37
#include "cairoint.h"
38
#include "cairo-array-private.h"
39
#include "cairo-ft-private.h"
40
#include "cairo-pattern-private.h"
41
#include "cairo-scaled-font-subsets-private.h"
42
43
#include <stdarg.h>
44
#include <stdio.h>
45
#include <string.h>
46
47
#if HAVE_FT_SVG_DOCUMENT
48
49
#include <ft2build.h>
50
#include FT_COLOR_H
51
52
/* #define SVG_RENDER_PRINT_FUNCTIONS 1 */
53
54
2.94M
#define WHITE_SPACE_CHARS " \n\r\t\v\f"
55
56
typedef struct {
57
    const char *name;
58
    int red;
59
    int green;
60
    int blue;
61
} color_name_t;
62
63
/* Must be sorted */
64
static color_name_t color_names[] = {
65
    { "aliceblue", 240, 248, 255 },
66
    { "antiquewhite", 250, 235, 215 },
67
    { "aqua",  0, 255, 255 },
68
    { "aquamarine", 127, 255, 212 },
69
    { "azure", 240, 255, 255 },
70
    { "beige", 245, 245, 220 },
71
    { "bisque", 255, 228, 196 },
72
    { "black",  0, 0, 0 },
73
    { "blanchedalmond", 255, 235, 205 },
74
    { "blue",  0, 0, 255 },
75
    { "blueviolet", 138, 43, 226 },
76
    { "brown", 165, 42, 42 },
77
    { "burlywood", 222, 184, 135 },
78
    { "cadetblue",  95, 158, 160 },
79
    { "chartreuse", 127, 255, 0 },
80
    { "chocolate", 210, 105, 30 },
81
    { "coral", 255, 127, 80 },
82
    { "cornflowerblue", 100, 149, 237 },
83
    { "cornsilk", 255, 248, 220 },
84
    { "crimson", 220, 20, 60 },
85
    { "cyan",  0, 255, 255 },
86
    { "darkblue",  0, 0, 139 },
87
    { "darkcyan",  0, 139, 139 },
88
    { "darkgoldenrod", 184, 134, 11 },
89
    { "darkgray", 169, 169, 169 },
90
    { "darkgreen",  0, 100, 0 },
91
    { "darkgrey", 169, 169, 169 },
92
    { "darkkhaki", 189, 183, 107 },
93
    { "darkmagenta", 139, 0, 139 },
94
    { "darkolivegreen",  85, 107, 47 },
95
    { "darkorange", 255, 140, 0 },
96
    { "darkorchid", 153, 50, 204 },
97
    { "darkred", 139, 0, 0 },
98
    { "darksalmon", 233, 150, 122 },
99
    { "darkseagreen", 143, 188, 143 },
100
    { "darkslateblue",  72, 61, 139 },
101
    { "darkslategray",  47, 79, 79 },
102
    { "darkslategrey",  47, 79, 79 },
103
    { "darkturquoise",  0, 206, 209 },
104
    { "darkviolet", 148, 0, 211 },
105
    { "deeppink", 255, 20, 147 },
106
    { "deepskyblue",  0, 191, 255 },
107
    { "dimgray", 105, 105, 105 },
108
    { "dimgrey", 105, 105, 105 },
109
    { "dodgerblue",  30, 144, 255 },
110
    { "firebrick", 178, 34, 34 },
111
    { "floralwhite", 255, 250, 240 },
112
    { "forestgreen",  34, 139, 34 },
113
    { "fuchsia", 255, 0, 255 },
114
    { "gainsboro", 220, 220, 220 },
115
    { "ghostwhite", 248, 248, 255 },
116
    { "gold", 255, 215, 0 },
117
    { "goldenrod", 218, 165, 32 },
118
    { "gray", 128, 128, 128 },
119
    { "green",  0, 128, 0 },
120
    { "greenyellow", 173, 255, 47 },
121
    { "grey", 128, 128, 128 },
122
    { "honeydew", 240, 255, 240 },
123
    { "hotpink", 255, 105, 180 },
124
    { "indianred", 205, 92, 92 },
125
    { "indigo",  75, 0, 130 },
126
    { "ivory", 255, 255, 240 },
127
    { "khaki", 240, 230, 140 },
128
    { "lavender", 230, 230, 250 },
129
    { "lavenderblush", 255, 240, 245 },
130
    { "lawngreen", 124, 252, 0 },
131
    { "lemonchiffon", 255, 250, 205 },
132
    { "lightblue", 173, 216, 230 },
133
    { "lightcoral", 240, 128, 128 },
134
    { "lightcyan", 224, 255, 255 },
135
    { "lightgoldenrodyellow", 250, 250, 210 },
136
    { "lightgray", 211, 211, 211 },
137
    { "lightgreen", 144, 238, 144 },
138
    { "lightgrey", 211, 211, 211 },
139
    { "lightpink", 255, 182, 193 },
140
    { "lightsalmon", 255, 160, 122 },
141
    { "lightseagreen",  32, 178, 170 },
142
    { "lightskyblue", 135, 206, 250 },
143
    { "lightslategray", 119, 136, 153 },
144
    { "lightslategrey", 119, 136, 153 },
145
    { "lightsteelblue", 176, 196, 222 },
146
    { "lightyellow", 255, 255, 224 },
147
    { "lime",  0, 255, 0 },
148
    { "limegreen",  50, 205, 50 },
149
    { "linen", 250, 240, 230 },
150
    { "magenta", 255, 0, 255 },
151
    { "maroon", 128, 0, 0 },
152
    { "mediumaquamarine", 102, 205, 170 },
153
    { "mediumblue",  0, 0, 205 },
154
    { "mediumorchid", 186, 85, 211 },
155
    { "mediumpurple", 147, 112, 219 },
156
    { "mediumseagreen",  60, 179, 113 },
157
    { "mediumslateblue", 123, 104, 238 },
158
    { "mediumspringgreen",  0, 250, 154 },
159
    { "mediumturquoise",  72, 209, 204 },
160
    { "mediumvioletred", 199, 21, 133 },
161
    { "midnightblue",  25, 25, 112 },
162
    { "mintcream", 245, 255, 250 },
163
    { "mistyrose", 255, 228, 225 },
164
    { "moccasin", 255, 228, 181 },
165
    { "navajowhite", 255, 222, 173 },
166
    { "navy",  0, 0, 128 },
167
    { "oldlace", 253, 245, 230 },
168
    { "olive", 128, 128, 0 },
169
    { "olivedrab", 107, 142, 35 },
170
    { "orange", 255, 165, 0 },
171
    { "orangered", 255, 69, 0 },
172
    { "orchid", 218, 112, 214 },
173
    { "palegoldenrod", 238, 232, 170 },
174
    { "palegreen", 152, 251, 152 },
175
    { "paleturquoise", 175, 238, 238 },
176
    { "palevioletred", 219, 112, 147 },
177
    { "papayawhip", 255, 239, 213 },
178
    { "peachpuff", 255, 218, 185 },
179
    { "peru", 205, 133, 63 },
180
    { "pink", 255, 192, 203 },
181
    { "plum", 221, 160, 221 },
182
    { "powderblue", 176, 224, 230 },
183
    { "purple", 128, 0, 128 },
184
    { "red", 255, 0, 0 },
185
    { "rosybrown", 188, 143, 143 },
186
    { "royalblue",  65, 105, 225 },
187
    { "saddlebrown", 139, 69, 19 },
188
    { "salmon", 250, 128, 114 },
189
    { "sandybrown", 244, 164, 96 },
190
    { "seagreen",  46, 139, 87 },
191
    { "seashell", 255, 245, 238 },
192
    { "sienna", 160, 82, 45 },
193
    { "silver", 192, 192, 192 },
194
    { "skyblue", 135, 206, 235 },
195
    { "slateblue", 106, 90, 205 },
196
    { "slategray", 112, 128, 144 },
197
    { "slategrey", 112, 128, 144 },
198
    { "snow", 255, 250, 250 },
199
    { "springgreen",  0, 255, 127 },
200
    { "steelblue",  70, 130, 180 },
201
    { "tan", 210, 180, 140 },
202
    { "teal",  0, 128, 128 },
203
    { "thistle", 216, 191, 216 },
204
    { "tomato", 255, 99, 71 },
205
    { "turquoise",  64, 224, 208 },
206
    { "violet", 238, 130, 238 },
207
    { "wheat", 245, 222, 179 },
208
    { "white", 255, 255, 255 },
209
    { "whitesmoke", 245, 245, 245 },
210
    { "yellow", 255, 255, 0 },
211
    { "yellowgreen", 154, 205, 50 }
212
};
213
214
typedef struct {
215
    char *name;
216
    char *value;
217
} svg_attribute_t;
218
219
typedef enum {
220
    CONTAINER_ELEMENT,
221
    EMPTY_ELEMENT,
222
    PROCESSING_INSTRUCTION,
223
    DOCTYPE,
224
    CDATA,
225
    COMMENT
226
} tag_type_t;
227
228
2.22k
#define TOP_ELEMENT_TAG "_top"
229
230
typedef struct _cairo_svg_element {
231
    cairo_hash_entry_t base;
232
    tag_type_t type;
233
    char *tag;
234
    char *id;
235
    cairo_array_t attributes; /* svg_attribute_t */
236
    cairo_array_t children; /* cairo_svg_element_t* */
237
    cairo_array_t  content; /* char */
238
    cairo_pattern_t *pattern; /* defined if a paint server */
239
    struct _cairo_svg_element *next; /* next on element stack */
240
} cairo_svg_element_t;
241
242
typedef struct _cairo_svg_color {
243
    enum { RGB, FOREGROUND } type;
244
    double red;
245
    double green;
246
    double blue;
247
} cairo_svg_color_t;
248
249
typedef struct _cairo_svg_paint {
250
    enum { PAINT_COLOR, PAINT_SERVER, PAINT_NONE } type;
251
    cairo_svg_color_t color;
252
    cairo_svg_element_t *paint_server;
253
} cairo_svg_paint_t;
254
255
typedef enum {
256
    GS_RENDER,
257
    GS_NO_RENDER,
258
    GS_COMPUTE_BBOX,
259
    GS_CLIP
260
} gs_mode_t;
261
262
typedef struct _cairo_svg_graphics_state {
263
    cairo_svg_paint_t fill;
264
    cairo_svg_paint_t stroke;
265
    cairo_svg_color_t color;
266
    double fill_opacity;
267
    double stroke_opacity;
268
    double opacity;
269
    cairo_fill_rule_t fill_rule;
270
    cairo_fill_rule_t clip_rule;
271
    cairo_path_t *clip_path;
272
    char *dash_array;
273
    double dash_offset;
274
    gs_mode_t mode;
275
    struct {
276
        double x;
277
        double y;
278
        double width;
279
        double height;
280
    } bbox;
281
    struct _cairo_svg_graphics_state *next;
282
} cairo_svg_graphics_state_t;
283
284
typedef enum {
285
    BUILD_PATTERN_NONE,
286
    BUILD_PATTERN_LINEAR,
287
    BUILD_PATTERN_RADIAL
288
} build_pattern_t;
289
290
typedef struct _cairo_svg_glyph_render {
291
    cairo_svg_element_t *tree;
292
    cairo_hash_table_t *ids;
293
    cairo_svg_graphics_state_t *graphics_state;
294
    cairo_t *cr;
295
    double units_per_em;
296
    struct {
297
        cairo_svg_element_t *paint_server;
298
        cairo_pattern_t *pattern;
299
        build_pattern_t type;
300
    } build_pattern;
301
    int render_element_tree_depth;
302
    int num_palette_entries;
303
    FT_Color* palette;
304
305
    /* Viewport */
306
    double width;
307
    double height;
308
    cairo_bool_t view_port_set;
309
310
    cairo_pattern_t *foreground_marker;
311
    cairo_pattern_t *foreground_source;
312
    cairo_bool_t foreground_source_used;
313
314
    int debug; /* 0 = quiet, 1 = errors, 2 = warnings, 3 = info */
315
} cairo_svg_glyph_render_t;
316
317
318
132
#define SVG_RENDER_ERROR 1
319
7.74M
#define SVG_RENDER_WARNING 2
320
8.90k
#define SVG_RENDER_INFO 3
321
322
72
#define print_error(render, ...) cairo_svg_glyph_render_printf(render, SVG_RENDER_ERROR, ##__VA_ARGS__)
323
7.74M
#define print_warning(render, ...) cairo_svg_glyph_render_printf(render, SVG_RENDER_WARNING, ##__VA_ARGS__)
324
8.90k
#define print_info(render, ...) cairo_svg_glyph_render_printf(render, SVG_RENDER_INFO, ##__VA_ARGS__)
325
326
static void
327
cairo_svg_glyph_render_printf (cairo_svg_glyph_render_t *svg_render,
328
                               int level,
329
                               const char *fmt, ...) CAIRO_PRINTF_FORMAT (3, 4);
330
331
static void
332
cairo_svg_glyph_render_printf (cairo_svg_glyph_render_t *svg_render,
333
                               int level,
334
                               const char *fmt, ...)
335
7.75M
{
336
7.75M
    va_list ap;
337
338
7.75M
    if (svg_render->debug >= level ) {
339
0
        switch (level) {
340
0
            case SVG_RENDER_ERROR:
341
0
                printf("ERROR: ");
342
0
                break;
343
0
            case SVG_RENDER_WARNING:
344
0
                printf("WARNING: ");
345
0
                break;
346
0
        }
347
0
  va_start (ap, fmt);
348
0
  vprintf (fmt, ap);
349
0
  va_end (ap);
350
0
  printf ("\n");
351
0
    }
352
7.75M
}
353
354
static cairo_bool_t
355
string_equal (const char *s1, const char *s2)
356
234M
{
357
234M
    if (s1 && s2)
358
210M
        return strcmp (s1, s2) == 0;
359
360
24.5M
    if (!s1 && !s2)
361
0
        return TRUE;
362
363
24.5M
    return FALSE;
364
24.5M
}
365
366
static cairo_bool_t
367
string_match (const char **p, const char *str)
368
9.08M
{
369
9.08M
    if (*p && strncmp (*p, str, strlen (str)) == 0) {
370
3.45M
        *p += strlen (str);
371
3.45M
        return TRUE;
372
3.45M
    }
373
5.63M
    return FALSE;
374
9.08M
}
375
376
static const char *
377
skip_space (const char *p)
378
26.5M
{
379
34.6M
    while (*p && _cairo_isspace (*p))
380
8.01M
        p++;
381
382
26.5M
    return p;
383
26.5M
}
384
385
/* Skip over character c and and whitespace before or after. Returns
386
 * NULL if c not found. */
387
static const char *
388
skip_char (const char *p, char c)
389
4.18M
{
390
12.6M
    while (_cairo_isspace (*p))
391
8.43M
        p++;
392
393
4.18M
    if (*p != c)
394
81.2k
        return NULL;
395
396
4.10M
    p++;
397
398
7.19M
    while (_cairo_isspace (*p))
399
3.09M
        p++;
400
401
4.10M
    return p;
402
4.18M
}
403
404
static int
405
_color_name_compare (const void *a, const void *b)
406
444k
{
407
444k
    const color_name_t *a_color = a;
408
444k
    const color_name_t *b_color = b;
409
410
444k
    return strcmp (a_color->name, b_color->name);
411
444k
}
412
413
static void
414
init_element_id_key (cairo_svg_element_t *element)
415
2.50M
{
416
2.50M
    element->base.hash = _cairo_hash_string (element->id);
417
2.50M
}
418
419
static cairo_bool_t
420
_element_id_equal (const void *key_a, const void *key_b)
421
4.69M
{
422
4.69M
    const cairo_svg_element_t *a = key_a;
423
4.69M
    const cairo_svg_element_t *b = key_b;
424
425
4.69M
    return string_equal (a->id, b->id);
426
4.69M
}
427
428
/* Find element with the "id" attribute matching id. id may have the
429
 * '#' prefix. It will be stripped before searching.
430
 */
431
static cairo_svg_element_t *
432
lookup_element (cairo_svg_glyph_render_t *svg_render, const char *id)
433
2.47M
{
434
2.47M
    cairo_svg_element_t key;
435
436
2.47M
    if (!id || strlen (id) < 1)
437
5.81k
        return NULL;
438
439
2.46M
    key.id = (char *)(id[0] == '#' ? id + 1 : id);
440
2.46M
    init_element_id_key (&key);
441
2.46M
    return _cairo_hash_table_lookup (svg_render->ids, &key.base);
442
2.47M
}
443
444
/* Find element with the "id" attribute matching url where url is of
445
 * the form "url(#id)".
446
 */
447
static cairo_svg_element_t *
448
lookup_url_element (cairo_svg_glyph_render_t *svg_render, const char *url)
449
2.48M
{
450
2.48M
    const char *p = url;
451
2.48M
    cairo_svg_element_t *element = NULL;
452
453
2.48M
    if (p && string_match (&p, "url")) {
454
2.48M
        p = skip_char (p, '(');
455
2.48M
        if (!p)
456
14.7k
            return NULL;
457
458
2.46M
        const char *end = strpbrk(p, WHITE_SPACE_CHARS ")");
459
2.46M
        if (end) {
460
2.46M
            char *id = _cairo_strndup (p, end - p);
461
2.46M
            element = lookup_element (svg_render, id);
462
2.46M
            free (id);
463
2.46M
        }
464
2.46M
    }
465
2.46M
    return element;
466
2.48M
}
467
468
static const char *
469
get_attribute (const cairo_svg_element_t *element, const char *name)
470
43.3M
{
471
43.3M
    svg_attribute_t attr;
472
43.3M
    int num_elems, i;
473
474
43.3M
    num_elems = _cairo_array_num_elements (&element->attributes);
475
233M
    for (i = 0; i < num_elems; i++) {
476
196M
  _cairo_array_copy_element (&element->attributes, i, &attr);
477
196M
        if (string_equal (attr.name, name))
478
6.36M
            return attr.value;
479
196M
    }
480
36.9M
    return NULL;
481
43.3M
}
482
483
static const char *
484
get_href_attribute (const cairo_svg_element_t *element)
485
18.5k
{
486
18.5k
    svg_attribute_t attr;
487
18.5k
    int num_elems, i, len;
488
489
    /* SVG2 requires the href attribute to be "href". Older versions
490
     * used "xlink:href". I have seen at least one font that used an
491
     * alternative name space eg "ns1:href". To keep things simple we
492
     * search for an attribute named "href" or ending in ":href".
493
     */
494
18.5k
    num_elems = _cairo_array_num_elements (&element->attributes);
495
56.5k
    for (i = 0; i < num_elems; i++) {
496
55.4k
  _cairo_array_copy_element (&element->attributes, i, &attr);
497
55.4k
        if (string_equal (attr.name, "href"))
498
9.82k
            return attr.value;
499
500
45.6k
        len = strlen (attr.name);
501
45.6k
        if (len > 4 && string_equal (attr.name + len - 5, ":href"))
502
7.64k
            return attr.value;
503
45.6k
    }
504
1.13k
    return NULL;
505
18.5k
}
506
507
/* Get a float attribute or float percentage. If attribute is a
508
 * percentage, the returned value is percentage * scale.  Does not
509
 * modify value if it returns FALSE. This allows value to be set to a
510
 * default before calling get_float_attribute(), then used without
511
 * checking the return value of this function.
512
 */
513
static cairo_bool_t
514
get_float_or_percent_attribute (const cairo_svg_element_t *element,
515
                                const char *name,
516
                                double scale,
517
                                double *value)
518
6.99M
{
519
6.99M
    const char *p;
520
6.99M
    char *end;
521
6.99M
    double v;
522
523
6.99M
    p = get_attribute (element, name);
524
6.99M
    if (p) {
525
284k
        v = _cairo_strtod (p, &end);
526
284k
        if (end != p) {
527
259k
            *value = v;
528
259k
            if (*end == '%')
529
159k
                *value *= scale / 100.0;
530
259k
            return TRUE;
531
259k
        }
532
284k
    }
533
6.73M
    return FALSE;
534
6.99M
}
535
536
/* Does not modify value if it returns FALSE. This allows value to be
537
 * set to a default before calling get_float_attribute(), then used
538
 * without checking the return value of this function.
539
 */
540
static cairo_bool_t
541
get_float_attribute (const cairo_svg_element_t *element, const char *name, double *value)
542
7.79M
{
543
7.79M
    const char *p;
544
7.79M
    char *end;
545
7.79M
    double v;
546
547
7.79M
    p = get_attribute (element, name);
548
7.79M
    if (p) {
549
205k
        v = _cairo_strtod (p, &end);
550
205k
        if (end != p) {
551
201k
            *value = v;
552
201k
            return TRUE;
553
201k
        }
554
205k
    }
555
7.58M
    return FALSE;
556
7.79M
}
557
558
static cairo_fill_rule_t
559
get_fill_rule_attribute (const cairo_svg_element_t *element, const char *name, cairo_fill_rule_t default_value)
560
5.12M
{
561
5.12M
    const char *p;
562
563
5.12M
    p = get_attribute (element, name);
564
5.12M
    if (string_equal (p, "nonzero"))
565
3.46k
        return CAIRO_FILL_RULE_WINDING;
566
5.12M
    else if (string_equal (p, "evenodd"))
567
863k
        return CAIRO_FILL_RULE_EVEN_ODD;
568
4.26M
    else
569
4.26M
        return default_value;
570
5.12M
}
571
572
static void
573
free_elements (cairo_svg_glyph_render_t *svg_render,
574
              cairo_svg_element_t      *element)
575
364k
{
576
364k
    int num_elems;
577
578
364k
    num_elems = _cairo_array_num_elements (&element->children);
579
689k
    for (int i = 0; i < num_elems; i++) {
580
324k
  cairo_svg_element_t *child;
581
324k
        _cairo_array_copy_element (&element->children, i, &child);
582
324k
  free_elements (svg_render, child);
583
324k
    }
584
364k
    _cairo_array_fini (&element->children);
585
586
364k
    num_elems = _cairo_array_num_elements (&element->attributes);
587
478k
    for (int i = 0; i < num_elems; i++) {
588
114k
  svg_attribute_t *attr = _cairo_array_index (&element->attributes, i);
589
114k
  free (attr->name);
590
114k
  free (attr->value);
591
114k
    }
592
364k
    _cairo_array_fini (&element->attributes);
593
364k
    _cairo_array_fini (&element->content);
594
595
364k
    free (element->tag);
596
597
364k
    if (element->id) {
598
38.2k
        _cairo_hash_table_remove (svg_render->ids, &element->base);
599
38.2k
        free (element->id);
600
38.2k
    }
601
602
364k
    if (element->pattern)
603
0
        cairo_pattern_destroy (element->pattern);
604
605
364k
    free (element);
606
364k
}
607
608
#if SVG_RENDER_PRINT_FUNCTIONS
609
610
static void indent(int level)
611
{
612
    for (int i = 1; i < level; i++)
613
        printf("  ");
614
}
615
616
static void
617
print_element (cairo_svg_element_t *element, cairo_bool_t recurse, int level)
618
{
619
    char *content = strndup (_cairo_array_index_const (&element->content, 0),
620
                             _cairo_array_num_elements (&element->content));
621
622
    indent(level);
623
    if (element->type == COMMENT) {
624
        printf("<!--%s-->\n", content);
625
    } else if (element->type == CDATA) {
626
        printf("<![CDATA[%s]]>\n", content);
627
    } else if (element->type == DOCTYPE) {
628
        printf("<!DOCTYPE%s>\n", content);
629
    } else if (element->type == PROCESSING_INSTRUCTION) {
630
        printf("<?%s?>\n", content);
631
    } else {
632
        cairo_bool_t top_element = string_equal (element->tag, TOP_ELEMENT_TAG);
633
634
        if (!top_element) {
635
            printf("<%s", element->tag);
636
            int num_elems = _cairo_array_num_elements (&element->attributes);
637
            for (int i = 0; i < num_elems; i++) {
638
                svg_attribute_t *attr = _cairo_array_index (&element->attributes, i);
639
                printf(" %s=\"%s\"", attr->name, attr->value);
640
            }
641
            if (num_elems > 0)
642
                printf(" ");
643
644
            if (element->type == EMPTY_ELEMENT)
645
                printf("/>\n");
646
            else
647
                printf(">\n");
648
        }
649
650
        if (element->type == CONTAINER_ELEMENT) {
651
            if (recurse) {
652
                int num_elems = _cairo_array_num_elements (&element->children);
653
                for (int i = 0; i < num_elems; i++) {
654
                    cairo_svg_element_t *child;
655
                    _cairo_array_copy_element (&element->children, i, &child);
656
                    print_element (child, TRUE, level + 1);
657
                }
658
            }
659
            if (!top_element)
660
                printf("</%s>\n", element->tag);
661
        }
662
    }
663
    free (content);
664
}
665
#endif
666
667
static const char *
668
parse_list_of_floats (const char *p,
669
                      int num_required,
670
                      int num_optional,
671
                      cairo_bool_t *have_optional,
672
                      va_list ap)
673
33.7M
{
674
33.7M
    double d;
675
33.7M
    double *dp;
676
33.7M
    char *end;
677
33.7M
    const char *q = NULL;
678
33.7M
    int num_found = 0;
679
680
68.4M
    for (int i = 0; i < num_required + num_optional; i++) {
681
56.6M
        while (p && (*p == ',' || _cairo_isspace (*p)))
682
13.7M
            p++;
683
684
42.8M
        if (!p)
685
959
            break;
686
687
42.8M
        d = _cairo_strtod (p, &end);
688
42.8M
        if (end == p) {
689
8.12M
            p = NULL;
690
8.12M
            break;
691
8.12M
        }
692
34.7M
        p = end;
693
34.7M
        dp = va_arg (ap, double *);
694
34.7M
        *dp = d;
695
34.7M
        num_found++;
696
34.7M
        if (num_found == num_required)
697
26.2M
            q = p;
698
34.7M
    }
699
700
33.7M
    if (num_optional > 0) {
701
806k
        if (num_found == num_required + num_optional) {
702
100k
            *have_optional = TRUE;
703
706k
        } else {
704
706k
            *have_optional = FALSE;
705
            /* restore pointer to end of required floats */
706
706k
            p = q;
707
706k
        }
708
806k
    }
709
710
33.7M
    return p;
711
33.7M
}
712
713
static const char *
714
get_floats (const char *p,
715
            int num_required,
716
            int num_optional,
717
            cairo_bool_t *have_optional,
718
            ...)
719
851k
{
720
851k
    va_list ap;
721
722
851k
    va_start (ap, have_optional);
723
851k
    p = parse_list_of_floats (p, num_required, num_optional, have_optional, ap);
724
851k
    va_end (ap);
725
851k
    return p;
726
851k
}
727
728
static const char *
729
get_path_params (const char *p, int num_params, ...)
730
32.8M
{
731
32.8M
    va_list ap;
732
733
32.8M
    va_start (ap, num_params);
734
32.8M
    p = parse_list_of_floats (p, num_params, 0, NULL, ap);
735
32.8M
    va_end (ap);
736
32.8M
    return p;
737
32.8M
}
738
739
static cairo_bool_t
740
get_color (cairo_svg_glyph_render_t *svg_render,
741
           const char               *s,
742
           cairo_svg_color_t        *color)
743
136k
{
744
136k
    int len, matched;
745
136k
    unsigned r = 0, g = 0, b = 0;
746
747
136k
    if (!s)
748
39.3k
        return FALSE;
749
750
96.7k
    len = strlen(s);
751
752
96.7k
    if (string_equal (s, "inherit")) {
753
18
  return FALSE;
754
96.7k
    } else if (string_equal (s, "currentColor") ||
755
96.2k
         string_equal (s, "context-fill") ||
756
91.2k
         string_equal (s, "context-stroke"))
757
5.86k
    {
758
5.86k
  *color = svg_render->graphics_state->color;
759
5.86k
        return TRUE;
760
90.8k
    } else if (len > 0 && s[0] == '#') {
761
15.0k
        if (len == 4) {
762
1.63k
            matched = sscanf (s + 1, "%1x%1x%1x", &r, &g, &b);
763
1.63k
            if (matched == 3) {
764
                /* Each digit is repeated to convert to 6 digits. eg 0x123 -> 0x112233 */
765
1.60k
                color->type = RGB;
766
1.60k
                color->red = 0x11*r/255.0;
767
1.60k
                color->green = 0x11*g/255.0;
768
1.60k
                color->blue = 0x11*b/255.0;
769
1.60k
                return TRUE;
770
1.60k
            }
771
13.4k
        } else if (len == 7) {
772
12.4k
            matched = sscanf (s + 1, "%2x%2x%2x", &r, &g, &b);
773
12.4k
            if (matched == 3) {
774
6.38k
                color->type = RGB;
775
6.38k
                color->red = r/255.0;
776
6.38k
                color->green = g/255.0;
777
6.38k
                color->blue = b/255.0;
778
6.38k
                return TRUE;
779
6.38k
            }
780
12.4k
        }
781
75.8k
    } else if (strncmp (s, "rgb", 3) == 0) {
782
1.03k
        matched = sscanf (s, "rgb ( %u , %u , %u )", &r, &g, &b);
783
1.03k
        if (matched == 3) {
784
198
            color->type = RGB;
785
198
            color->red = r/255.0;
786
198
            color->green = g/255.0;
787
198
            color->blue = b/255.0;
788
198
            return TRUE;
789
198
        }
790
74.7k
    } else if (strncmp (s, "var", 3) == 0) {
791
        /* CPAL palettes colors. eg "var(--color0, yellow)" */
792
919
        s += 3;
793
919
        s = skip_char (s, '(');
794
919
        if (!string_match (&s, "--color"))
795
60
            return FALSE;
796
797
859
        char *end;
798
859
        int entry = strtol (s, &end, 10);
799
859
        if (end == s)
800
220
            return FALSE;
801
802
639
        if (svg_render->palette && entry >= 0 && entry < svg_render->num_palette_entries) {
803
0
            FT_Color *palette_color = &svg_render->palette[entry];
804
0
            color->type = RGB;
805
0
            color->red = palette_color->red / 255.0;
806
0
            color->green = palette_color->green/ 255.0;
807
0
            color->blue = palette_color->blue / 255.0;
808
0
            return TRUE;
809
639
        } else {
810
            /* Fallback color */
811
639
            s = skip_char (end, ',');
812
639
            if (!s)
813
246
            return FALSE;
814
815
393
            end = strpbrk(s, WHITE_SPACE_CHARS ")");
816
393
            if (!end || end == s)
817
159
    return FALSE;
818
819
234
            char *fallback = _cairo_strndup (s, end - s);
820
234
            cairo_bool_t success = get_color (svg_render, fallback, color);
821
234
            free (fallback);
822
234
            return success;
823
393
        }
824
73.8k
    } else {
825
73.8k
        const color_name_t *color_name;
826
73.8k
        color_name_t color_name_key;
827
828
73.8k
        color_name_key.name = (char *) s;
829
73.8k
        color_name = bsearch (&color_name_key,
830
73.8k
                              color_names,
831
73.8k
                              ARRAY_LENGTH (color_names),
832
73.8k
                              sizeof (color_name_t),
833
73.8k
                             _color_name_compare);
834
73.8k
        if (color_name) {
835
58.7k
            color->type = RGB;
836
58.7k
            color->red = color_name->red/255.0;
837
58.7k
            color->green = color_name->green/255.0;
838
58.7k
            color->blue = color_name->blue/255.0;
839
58.7k
            return TRUE;
840
58.7k
        }
841
73.8k
    }
842
22.9k
    return FALSE;
843
96.7k
}
844
845
static void
846
get_paint (cairo_svg_glyph_render_t *svg_render,
847
           const char *p,
848
           cairo_svg_paint_t *paint)
849
1.17M
{
850
1.17M
    cairo_svg_element_t *element;
851
852
1.17M
    if (string_match (&p, "none")) {
853
3.86k
        paint->type = PAINT_NONE;
854
3.86k
        paint->paint_server = NULL;
855
1.16M
    } else if (p && strncmp (p, "url", 3) == 0) {
856
1.13M
        element = lookup_url_element (svg_render, p);
857
1.13M
        if (element) {
858
1.10M
            paint->type = PAINT_SERVER;
859
1.10M
            paint->paint_server = element;
860
1.10M
        }
861
1.13M
    } else {
862
33.8k
        if (get_color (svg_render, p, &paint->color)) {
863
17.4k
            paint->type = PAINT_COLOR;
864
17.4k
            paint->paint_server = NULL;
865
17.4k
        }
866
33.8k
    }
867
1.17M
}
868
869
#ifdef SVG_RENDER_PRINT_FUNCTIONS
870
871
static void
872
print_color (cairo_svg_color_t *color)
873
{
874
    switch (color->type) {
875
        case FOREGROUND_COLOR:
876
            printf("foreground");
877
            break;
878
        case RGB:
879
            printf("#%02x%02x%02x",
880
                   (int)(color->red*255),
881
                   (int)(color->red*255),
882
                   (int)(color->red*255));
883
            break;
884
    }
885
}
886
887
static void
888
print_paint (cairo_svg_paint_t *paint)
889
{
890
    printf("Paint: ");
891
    switch (paint->type) {
892
        case PAINT_COLOR:
893
            printf("color: ");
894
            print_color (&paint->color);
895
            break;
896
        case PAINT_SERVER:
897
            printf("server: %s", paint->paint_server->tag);
898
            break;
899
        case PAINT_NONE:
900
            printf("none");
901
            break;
902
    }
903
    printf("\n");
904
}
905
906
#endif
907
908
static void
909
parse_error (cairo_svg_glyph_render_t *svg_render,
910
             const char *string,
911
             const char *location,
912
             const char *fmt,
913
             ...) CAIRO_PRINTF_FORMAT (4, 5);
914
915
static void
916
parse_error (cairo_svg_glyph_render_t *svg_render,
917
             const char *string,
918
             const char *location,
919
             const char *fmt,
920
             ...)
921
60
{
922
60
    va_list ap;
923
60
    const int context = 40;
924
60
    const char *start;
925
60
    const char *end;
926
927
60
    if (svg_render->debug >= SVG_RENDER_ERROR) {
928
0
        printf("ERROR: ");
929
0
  va_start (ap, fmt);
930
0
  vprintf (fmt, ap);
931
0
  va_end (ap);
932
0
        putchar ('\n');
933
0
        start = location - context;
934
0
        if (start < string)
935
0
            start = string;
936
937
0
        end = location + strlen (location);
938
0
        if (end - location > context)
939
0
            end = location + context;
940
941
0
        for (const char *p = start; p < end; p++) {
942
0
            if (_cairo_isspace (*p))
943
0
                putchar (' ');
944
0
            else
945
0
                putchar (*p);
946
0
        }
947
0
        putchar ('\n');
948
949
0
        for (int i = 0; i < location - start; i++)
950
0
            putchar(' ');
951
0
        putchar ('^');
952
0
        putchar ('\n');
953
0
  printf (" at position %td\n", location - string);
954
0
    }
955
60
}
956
957
static cairo_bool_t
958
append_attribute (cairo_svg_element_t *element, svg_attribute_t *attribute)
959
105k
{
960
105k
    const char *p;
961
105k
    const char *end;
962
105k
    svg_attribute_t attr;
963
964
105k
    memset (&attr, 0, sizeof (attr));
965
105k
    if (string_equal (attribute->name, "style")) {
966
        /* split style into individual attributes */
967
1.81k
        p = attribute->value;
968
11.0k
        while (*p) {
969
10.3k
            end = strchr (p, ':');
970
10.3k
            if (!end || end == p)
971
1.18k
                break;
972
9.19k
            attr.name = _cairo_strndup (p, end - p);
973
9.19k
            p = end + 1;
974
9.19k
            p = skip_space(p);
975
9.19k
            end = strchr (p, ';');
976
9.19k
            if (!end)
977
2.50k
                end = strchr (p, 0);
978
9.19k
            if (end == p)
979
0
                goto split_style_fail;
980
981
9.19k
            attr.value = _cairo_strndup (p, end - p);
982
9.19k
            if (*end)
983
6.68k
                p = end + 1;
984
985
9.19k
            if (_cairo_array_append (&element->attributes, &attr))
986
0
                goto split_style_fail;
987
988
9.19k
            memset (&attr, 0, sizeof (attr));
989
9.19k
            p = skip_space (p);
990
9.19k
        }
991
1.81k
    }
992
993
105k
    if (_cairo_array_append (&element->attributes, attribute))
994
0
        return FALSE;
995
996
105k
    return TRUE;
997
998
0
  split_style_fail:
999
0
    free (attr.name);
1000
0
    free (attr.value);
1001
0
    return FALSE;
1002
105k
}
1003
1004
static cairo_bool_t
1005
add_child_element (cairo_svg_glyph_render_t *svg_render,
1006
                   cairo_svg_element_t *parent,
1007
                   cairo_svg_element_t *child)
1008
324k
{
1009
324k
    cairo_status_t status;
1010
324k
    const char* id;
1011
1012
324k
    id = get_attribute (child, "id");
1013
324k
    if (id) {
1014
38.2k
        child->id = strdup (id);
1015
38.2k
        init_element_id_key (child);
1016
38.2k
  status = _cairo_hash_table_insert (svg_render->ids, &child->base);
1017
38.2k
  if (unlikely (status))
1018
0
            return FALSE;
1019
38.2k
    }
1020
1021
324k
    status = _cairo_array_append (&parent->children, &child);
1022
324k
    return status == CAIRO_STATUS_SUCCESS;
1023
324k
}
1024
1025
static cairo_svg_element_t *
1026
create_element (tag_type_t type, char *tag)
1027
364k
{
1028
364k
    cairo_svg_element_t *elem;
1029
364k
    cairo_status_t status;
1030
1031
364k
    elem = _cairo_calloc (sizeof (cairo_svg_element_t));
1032
364k
    if (unlikely (elem == NULL)) {
1033
0
  status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1034
0
        return NULL;
1035
0
    }
1036
1037
364k
    elem->type = type;
1038
364k
    elem->tag = tag;
1039
364k
    elem->id = NULL;
1040
364k
    _cairo_array_init (&elem->attributes, sizeof(svg_attribute_t));
1041
364k
    _cairo_array_init (&elem->children, sizeof(cairo_svg_element_t *));
1042
364k
    _cairo_array_init (&elem->content, sizeof(char));
1043
364k
    elem->pattern = NULL;
1044
364k
    elem->next = NULL;
1045
1046
364k
    return elem;
1047
364k
}
1048
1049
static const char *
1050
parse_attributes (cairo_svg_glyph_render_t *svg_render,
1051
                  const char               *attributes,
1052
                  cairo_svg_element_t      *element)
1053
277k
{
1054
277k
    svg_attribute_t attr;
1055
277k
    char quote_char;
1056
277k
    const char *p;
1057
277k
    const char *end;
1058
1059
277k
    p = attributes;
1060
277k
    memset (&attr, 0, sizeof (svg_attribute_t));
1061
277k
    p = skip_space (p);
1062
382k
    while (*p && *p != '/' && *p != '>' && *p != '?') {
1063
105k
        end = strpbrk(p, WHITE_SPACE_CHARS "=");
1064
105k
        if (!end) {
1065
5
            parse_error (svg_render, attributes, p, "Could not find '='");
1066
5
            goto fail;
1067
5
        }
1068
1069
105k
        if (end == p) {
1070
0
            parse_error (svg_render, attributes, p, "Missing attribute name");
1071
0
            goto fail;
1072
0
        }
1073
1074
105k
        attr.name = _cairo_strndup (p, end - p);
1075
105k
        p = end;
1076
1077
105k
        p = skip_space (p);
1078
105k
        if (*p != '=') {
1079
9
            parse_error (svg_render, attributes, p, "Expected '='");
1080
9
            goto fail;
1081
9
        }
1082
1083
105k
        p++;
1084
105k
        p = skip_space (p);
1085
105k
        if (*p == '\"' || *p == '\'') {
1086
105k
            quote_char = *p;
1087
105k
        } else {
1088
4
            parse_error (svg_render, attributes, p, "Could not find '\"' or '''");
1089
4
            goto fail;
1090
4
        }
1091
1092
105k
        p++;
1093
105k
        end = strchr (p, quote_char);
1094
105k
        if (!end) {
1095
2
            parse_error (svg_render, attributes, p, "Could not find '%c'", quote_char);
1096
2
            goto fail;
1097
2
        }
1098
1099
105k
        attr.value = _cairo_strndup (p, end - p);
1100
105k
        p = end + 1;
1101
1102
105k
        if (!append_attribute (element, &attr))
1103
0
            goto fail;
1104
1105
105k
        memset (&attr, 0, sizeof (svg_attribute_t));
1106
1107
105k
        p = skip_space (p);
1108
105k
    }
1109
1110
277k
    return p;
1111
1112
20
  fail:
1113
20
    free (attr.name);
1114
20
    free (attr.value);
1115
20
    return NULL;
1116
277k
}
1117
1118
static cairo_bool_t
1119
parse_svg (cairo_svg_glyph_render_t *svg_render,
1120
           const char               *svg_document)
1121
2.22k
{
1122
2.22k
    const char *p = svg_document;
1123
2.22k
    const char *end;
1124
2.22k
    int nesting; /* when > 0 we parse content */
1125
2.22k
    cairo_svg_element_t *open_elem; /* Stack of open elements */
1126
2.22k
    cairo_svg_element_t *new_elem = NULL;
1127
2.22k
    char *name;
1128
2.22k
    cairo_status_t status;
1129
1130
    /* Create top level element to use as a container for all top
1131
     * level elements in the document and push it on the stack. */
1132
2.22k
    open_elem = create_element (CONTAINER_ELEMENT, strdup(TOP_ELEMENT_TAG));
1133
1134
    /* We don't want to add content to the top level container. There
1135
     * should only be whitesapce between tags. */
1136
2.22k
    nesting = 0;
1137
1138
461k
    while (*p) {
1139
460k
        if (nesting > 0) {
1140
            /* In an open element. Anything before the next '<' is content */
1141
280k
            end = strchr (p, '<');
1142
280k
            if (!end) {
1143
1
                parse_error (svg_render, svg_document, p, "Could not find '<'");
1144
1
                goto fail;
1145
1
            }
1146
280k
            status = _cairo_array_append_multiple (&open_elem->content, p, end - p);
1147
280k
            p = end;
1148
1149
280k
        } else {
1150
179k
            p = skip_space (p);
1151
179k
            if (*p == 0)
1152
792
                break; /* end of document */
1153
179k
        }
1154
1155
        /* We should now be at the start of a tag */
1156
459k
        if (*p != '<') {
1157
8
            parse_error (svg_render, svg_document, p, "Could not find '<'");
1158
8
            goto fail;
1159
8
        }
1160
1161
459k
        p++;
1162
459k
        if (*p == '!') {
1163
84.1k
            p++;
1164
84.1k
            if (string_match (&p, "[CDATA[")) {
1165
160
                new_elem = create_element (CDATA, NULL);
1166
160
                end = strstr (p, "]]>");
1167
160
                if (!end) {
1168
0
                    parse_error (svg_render, svg_document, p, "Could not find ']]>'");
1169
0
                    goto fail;
1170
0
                }
1171
1172
160
                status = _cairo_array_append_multiple (&new_elem->content, p, end - p);
1173
160
                p = end + 3;
1174
84.0k
            } else if (string_match (&p, "--")) {
1175
385
                new_elem = create_element (COMMENT, NULL);
1176
385
                end = strstr (p, "-->");
1177
385
                if (!end) {
1178
2
                    parse_error (svg_render, svg_document, p, "Could not find '-->'");
1179
2
                    goto fail;
1180
2
                }
1181
1182
383
                status = _cairo_array_append_multiple (&new_elem->content, p, end - p);
1183
383
                p = end + 3;
1184
83.6k
            } else if (string_match (&p, "DOCTYPE")) {
1185
83.6k
                new_elem = create_element (DOCTYPE, NULL);
1186
83.6k
                end = strchr (p, '>');
1187
83.6k
                if (!end) {
1188
0
                    parse_error (svg_render, svg_document, p, "Could not find '>'");
1189
0
                    goto fail;
1190
0
                }
1191
1192
83.6k
                status = _cairo_array_append_multiple (&new_elem->content, p, end - p);
1193
83.6k
                p = end + 1;
1194
83.6k
            } else {
1195
0
                parse_error (svg_render, svg_document, p, "Invalid");
1196
0
                goto fail;
1197
0
            }
1198
1199
84.1k
            if (!add_child_element (svg_render, open_elem, new_elem))
1200
0
                goto fail;
1201
1202
84.1k
            new_elem = NULL;
1203
84.1k
            continue;
1204
84.1k
        }
1205
1206
375k
        if (*p == '?') {
1207
439
            p++;
1208
439
            new_elem = create_element (PROCESSING_INSTRUCTION, NULL);
1209
439
            end = strstr (p, "?>");
1210
439
            if (!end) {
1211
0
                parse_error (svg_render, svg_document, p, "Could not find '?>'");
1212
0
                goto fail;
1213
0
            }
1214
1215
439
            status = _cairo_array_append_multiple (&new_elem->content, p, end - p);
1216
439
            p = end + 2;
1217
1218
439
            if (!add_child_element (svg_render, open_elem, new_elem))
1219
0
                goto fail;
1220
1221
439
            new_elem = NULL;
1222
439
            continue;
1223
439
        }
1224
1225
374k
        if (*p == '/') {
1226
            /* Closing tag */
1227
96.9k
            p++;
1228
1229
            /* find end of tag name */
1230
96.9k
            end = strpbrk(p, WHITE_SPACE_CHARS ">");
1231
96.9k
            if (!end) {
1232
2
                parse_error (svg_render, svg_document, p, "Could not find '>'");
1233
2
                goto fail;
1234
2
            }
1235
1236
96.9k
            name = _cairo_strndup (p, end - p);
1237
96.9k
            p = end;
1238
96.9k
            p = skip_space (p);
1239
96.9k
            if (*p != '>') {
1240
0
                parse_error (svg_render, svg_document, p, "Could not find '>'");
1241
0
                free (name);
1242
0
                goto fail;
1243
0
            }
1244
1245
96.9k
            p++;
1246
96.9k
            if (nesting == 0) {
1247
1
                parse_error (svg_render, svg_document, p, "parse_elements: parsed </%s> but no matching start tag", name);
1248
1
                free (name);
1249
1
                goto fail;
1250
1
            }
1251
96.9k
            if (!string_equal (name, open_elem->tag)) {
1252
17
                parse_error (svg_render, svg_document, p,
1253
17
                             "parse_elements: found </%s> but current open tag is <%s>",
1254
17
                             name, open_elem->tag);
1255
17
                free (name);
1256
17
                goto fail;
1257
17
            }
1258
1259
            /* pop top element on open elements stack into new_elem */
1260
96.9k
            new_elem = open_elem;
1261
96.9k
            open_elem = open_elem->next;
1262
96.9k
            new_elem->next = NULL;
1263
96.9k
            nesting--;
1264
1265
96.9k
            free (name);
1266
96.9k
            if (!add_child_element (svg_render, open_elem, new_elem))
1267
0
                goto fail;
1268
1269
96.9k
            new_elem = NULL;
1270
96.9k
            continue;
1271
96.9k
        }
1272
1273
        /* We should now be in a start or empty element tag */
1274
1275
        /* find end of tag name */
1276
277k
        end = strpbrk(p, WHITE_SPACE_CHARS "/>");
1277
277k
        if (!end) {
1278
3
            parse_error (svg_render, svg_document, p, "Could not find '>'");
1279
3
            goto fail;
1280
3
        }
1281
1282
277k
        name = _cairo_strndup (p, end - p);
1283
277k
        p = end;
1284
1285
277k
        new_elem = create_element (CONTAINER_ELEMENT, name);
1286
277k
        p = parse_attributes (svg_render, p, new_elem);
1287
277k
        if (!p)
1288
20
            goto fail;
1289
1290
277k
        p = skip_space (p);
1291
277k
        if (*p == '/') {
1292
142k
            new_elem->type = EMPTY_ELEMENT;
1293
142k
            p++;
1294
142k
        }
1295
1296
277k
        if (!p || *p != '>') {
1297
6
            print_error (svg_render, "Could not find '>'");
1298
6
            goto fail;
1299
6
        }
1300
1301
277k
        p++;
1302
277k
        if (new_elem->type == EMPTY_ELEMENT) {
1303
142k
            if (!add_child_element (svg_render, open_elem, new_elem))
1304
0
                goto fail;
1305
1306
142k
            new_elem = NULL;
1307
142k
        } else {
1308
            /* push new elem onto open elements stack */
1309
134k
            new_elem->next = open_elem;
1310
134k
            open_elem = new_elem;
1311
134k
            new_elem = NULL;
1312
134k
            nesting++;
1313
134k
        }
1314
277k
    }
1315
1316
2.16k
    if (nesting != 0) {
1317
6
        parse_error (svg_render, svg_document, p, "Missing closing tag for <%s>", open_elem->tag);
1318
6
        goto fail;
1319
6
    }
1320
1321
2.16k
    svg_render->tree = open_elem;
1322
2.16k
    return TRUE;
1323
1324
66
  fail:
1325
66
    if (new_elem)
1326
28
        free_elements (svg_render, new_elem);
1327
1328
38.0k
    while (open_elem) {
1329
38.0k
        cairo_svg_element_t *elem = open_elem;
1330
38.0k
        open_elem = open_elem->next;
1331
38.0k
        free_elements (svg_render, elem);
1332
38.0k
    }
1333
1334
66
    return FALSE;
1335
2.16k
}
1336
1337
static cairo_bool_t
1338
parse_transform (const char *p, cairo_matrix_t *matrix)
1339
926k
{
1340
926k
    cairo_matrix_t m;
1341
926k
    double x, y, a;
1342
926k
    cairo_bool_t have_optional;
1343
1344
926k
    cairo_matrix_init_identity (matrix);
1345
1.71M
    while (p) {
1346
2.33M
        while (p && (*p == ',' || _cairo_isspace (*p)))
1347
640k
            p++;
1348
1349
1.69M
        if (!p || *p == 0)
1350
417k
            break;
1351
1352
1.27M
        if (string_match (&p, "matrix")) {
1353
17.2k
            p = skip_char (p, '(');
1354
17.2k
            if (!p)
1355
10.2k
                break;
1356
1357
6.97k
            p = get_floats (p, 6, 0, NULL, &m.xx, &m.yx, &m.xy, &m.yy, &m.x0, &m.y0);
1358
6.97k
            if (!p)
1359
2.88k
                break;
1360
1361
4.08k
            p = skip_char (p, ')');
1362
4.08k
            if (!p)
1363
225
                break;
1364
1365
3.85k
            cairo_matrix_multiply (matrix, &m, matrix);
1366
1367
1.26M
        } else if (string_match (&p, "translate")) {
1368
12.7k
            p = skip_char (p, '(');
1369
12.7k
            if (!p)
1370
1.40k
                break;
1371
1372
11.3k
            p = get_floats (p, 1, 1, &have_optional, &x, &y);
1373
11.3k
            if (!p)
1374
10.3k
                break;
1375
1376
1.05k
            p = skip_char (p, ')');
1377
1.05k
            if (!p)
1378
312
                break;
1379
1380
744
            if (!have_optional)
1381
271
                y = 0;
1382
1383
744
            cairo_matrix_translate (matrix, x, y);
1384
1385
1.24M
        } else if (string_match (&p, "scale")) {
1386
713k
            p = skip_char (p, '(');
1387
713k
            if (!p)
1388
15.7k
                break;
1389
1390
697k
            p = get_floats (p, 1, 1, &have_optional, &x, &y);
1391
697k
            if (!p)
1392
10.6k
                break;
1393
1394
687k
            p = skip_char (p, ')');
1395
687k
            if (!p)
1396
15.6k
                break;
1397
1398
671k
            if (!have_optional)
1399
627k
                y = x;
1400
1401
671k
            cairo_matrix_scale (matrix, x, y);
1402
1403
671k
        } else if (string_match (&p, "rotate")) {
1404
99.3k
            p = skip_char (p, '(');
1405
99.3k
            if (!p)
1406
1.90k
                break;
1407
1408
97.4k
            p = get_floats (p, 1, 2, &have_optional, &a, &x, &y);
1409
97.4k
            if (!p)
1410
241
                break;
1411
1412
97.2k
            p = skip_char (p, ')');
1413
97.2k
            if (!p)
1414
5.28k
                break;
1415
1416
91.9k
            if (!have_optional) {
1417
35.7k
                x = 0;
1418
35.7k
                y = 0;
1419
35.7k
            }
1420
1421
91.9k
            a *= M_PI/180.0;
1422
91.9k
            cairo_matrix_translate (matrix, x, y);
1423
91.9k
            cairo_matrix_rotate (matrix, a);
1424
91.9k
            cairo_matrix_translate (matrix, -x, -y);
1425
1426
434k
        } else if (string_match (&p, "skewX")) {
1427
11.6k
            p = skip_char (p, '(');
1428
11.6k
            if (!p)
1429
238
                break;
1430
1431
11.4k
            p = get_floats (p, 1, 0, NULL, &a);
1432
11.4k
            if (!p)
1433
1.01k
                break;
1434
1435
10.3k
            p = skip_char (p, ')');
1436
10.3k
            if (!p)
1437
31
                break;
1438
1439
10.3k
            a *= M_PI/180.0;
1440
10.3k
            cairo_matrix_init_identity (&m);
1441
10.3k
            m.xy = tan (a);
1442
10.3k
            cairo_matrix_multiply (matrix, &m, matrix);
1443
1444
422k
        } else if (string_match (&p, "skewY")) {
1445
26.6k
            p = skip_char (p, '(');
1446
26.6k
            if (!p)
1447
17
                break;
1448
1449
26.5k
            p = get_floats (p, 1, 0, NULL, &a);
1450
26.5k
            if (!p)
1451
33
                break;
1452
1453
26.5k
            p = skip_char (p, ')');
1454
26.5k
            if (!p)
1455
15.1k
                break;
1456
1457
11.4k
            a *= M_PI/180.0;
1458
11.4k
            cairo_matrix_init_identity (&m);
1459
11.4k
            m.yx = tan (a);
1460
11.4k
            cairo_matrix_multiply (matrix, &m, matrix);
1461
1462
396k
        } else {
1463
396k
            break;
1464
396k
        }
1465
1.27M
    }
1466
926k
    return p != NULL;
1467
926k
}
1468
1469
static void
1470
render_element_tree (cairo_svg_glyph_render_t *svg_render,
1471
                     cairo_svg_element_t      *element,
1472
                     cairo_svg_element_t      *display_element,
1473
                     cairo_bool_t              children_only);
1474
1475
static cairo_pattern_t *
1476
create_pattern (cairo_svg_glyph_render_t *svg_render,
1477
                cairo_svg_element_t      *paint_server)
1478
170k
{
1479
170k
    cairo_pattern_t *pattern = NULL;
1480
1481
170k
    if (paint_server) {
1482
170k
        svg_render->build_pattern.paint_server = paint_server;
1483
170k
        render_element_tree (svg_render, paint_server, NULL, FALSE);
1484
170k
        pattern = svg_render->build_pattern.pattern;
1485
170k
        svg_render->build_pattern.pattern = NULL;
1486
170k
        svg_render->build_pattern.paint_server = NULL;
1487
170k
        svg_render->build_pattern.type = BUILD_PATTERN_NONE;
1488
170k
    }
1489
1490
170k
    if (!pattern)
1491
139k
        pattern = cairo_pattern_create_rgb (0, 0, 0);
1492
1493
170k
    return pattern;
1494
170k
}
1495
1496
static cairo_bool_t
1497
render_element_svg (cairo_svg_glyph_render_t *svg_render,
1498
                    cairo_svg_element_t      *element,
1499
                    cairo_bool_t              end_tag)
1500
2.46k
{
1501
2.46k
    double width, height;
1502
2.46k
    double vb_x, vb_y, vb_height, vb_width;
1503
2.46k
    const char *p;
1504
2.46k
    const char *end;
1505
1506
2.46k
    if (end_tag)
1507
10
        return FALSE;
1508
1509
    /* Default viewport width, height is EM square */
1510
2.45k
    if (!get_float_or_percent_attribute (element, "width", svg_render->units_per_em, &width))
1511
2.40k
        width = svg_render->units_per_em;
1512
1513
2.45k
    if (!get_float_or_percent_attribute (element, "height", svg_render->units_per_em, &height))
1514
2.39k
        height = svg_render->units_per_em;
1515
1516
    /* Transform viewport to unit square, centering it if width != height. */
1517
2.45k
    if (width > height) {
1518
61
        cairo_scale (svg_render->cr, 1.0/width, 1.0/width);
1519
61
        cairo_translate (svg_render->cr, 0, (width - height)/2.0);
1520
2.39k
    } else {
1521
2.39k
        cairo_scale (svg_render->cr, 1.0/height, 1.0/height);
1522
2.39k
        cairo_translate (svg_render->cr, (height - width)/2.0, 0);
1523
2.39k
    }
1524
1525
2.45k
    svg_render->width = width;
1526
2.45k
    svg_render->height = height;
1527
1528
2.45k
    p = get_attribute (element, "viewBox");
1529
2.45k
    if (p) {
1530
        /* Transform viewport to viewbox */
1531
271
        end = get_path_params (p, 4, &vb_x, &vb_y, &vb_width, &vb_height);
1532
271
        if (!end) {
1533
10
            print_warning (svg_render, "viewBox expected 4 numbers: %s", p);
1534
10
            return FALSE;
1535
10
        }
1536
261
        cairo_translate (svg_render->cr, -vb_x * width/vb_width, -vb_y * width/vb_width);
1537
261
        cairo_scale (svg_render->cr, width/vb_width, height/vb_height);
1538
261
        svg_render->width = vb_width;
1539
261
        svg_render->height = vb_height;
1540
261
    }
1541
1542
2.44k
    svg_render->view_port_set = TRUE;
1543
2.44k
    return TRUE;
1544
2.45k
}
1545
1546
static cairo_bool_t
1547
render_element_clip_path (cairo_svg_glyph_render_t *svg_render,
1548
                          cairo_svg_element_t      *element,
1549
                          cairo_bool_t              end_tag)
1550
1.31M
{
1551
1.31M
    cairo_svg_graphics_state_t *gs = svg_render->graphics_state;
1552
1.31M
    const char *p;
1553
1554
1.31M
    if (end_tag || gs->mode != GS_CLIP || svg_render->build_pattern.type != BUILD_PATTERN_NONE) {
1555
660k
        return FALSE;
1556
660k
    }
1557
1558
657k
    p = get_attribute (element, "clipPathUnits");
1559
657k
    if (string_equal (p, "objectBoundingBox")) {
1560
132k
        cairo_translate (svg_render->cr,
1561
132k
                                svg_render->graphics_state->bbox.x,
1562
132k
                                svg_render->graphics_state->bbox.y);
1563
132k
        cairo_scale (svg_render->cr,
1564
132k
                     svg_render->graphics_state->bbox.width,
1565
132k
                     svg_render->graphics_state->bbox.height);
1566
132k
    }
1567
1568
657k
    return TRUE;
1569
1.31M
}
1570
1571
static void
1572
apply_gradient_attributes (cairo_svg_glyph_render_t *svg_render,
1573
                           cairo_svg_element_t      *element)
1574
31.6k
{
1575
31.6k
    cairo_pattern_t *pattern = svg_render->build_pattern.pattern;
1576
31.6k
    cairo_bool_t object_bbox = TRUE;
1577
31.6k
    cairo_matrix_t transform;
1578
31.6k
    cairo_matrix_t mat;
1579
31.6k
    const char *p;
1580
1581
31.6k
    if (!pattern)
1582
0
        return;
1583
1584
31.6k
    p = get_attribute (element, "gradientUnits");
1585
31.6k
    if (string_equal (p, "userSpaceOnUse"))
1586
302
        object_bbox = FALSE;
1587
1588
31.6k
    cairo_matrix_init_identity (&mat);
1589
31.6k
    if (object_bbox) {
1590
31.3k
        cairo_matrix_translate (&mat,
1591
31.3k
                                svg_render->graphics_state->bbox.x,
1592
31.3k
                                svg_render->graphics_state->bbox.y);
1593
31.3k
        cairo_matrix_scale (&mat,
1594
31.3k
                            svg_render->graphics_state->bbox.width,
1595
31.3k
                            svg_render->graphics_state->bbox.height);
1596
31.3k
    }
1597
1598
31.6k
    p = get_attribute (element, "gradientTransform");
1599
31.6k
     if (parse_transform (p, &transform))
1600
9.21k
         cairo_matrix_multiply (&mat, &transform, &mat);
1601
1602
31.6k
    if (cairo_matrix_invert (&mat) == CAIRO_STATUS_SUCCESS)
1603
21.2k
        cairo_pattern_set_matrix (pattern, &mat);
1604
1605
31.6k
    p = get_attribute (element, "spreadMethod");
1606
31.6k
    if (string_equal (p, "reflect"))
1607
673
        cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT);
1608
30.9k
    else if (string_equal (p, "repeat"))
1609
185
        cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
1610
31.6k
}
1611
1612
static cairo_bool_t
1613
render_element_linear_gradient (cairo_svg_glyph_render_t *svg_render,
1614
                                cairo_svg_element_t      *element,
1615
                                cairo_bool_t              end_tag)
1616
51.5k
{
1617
51.5k
    double x1, y1, x2, y2;
1618
1619
51.5k
    if (svg_render->build_pattern.paint_server != element ||
1620
49.2k
        end_tag ||
1621
25.7k
        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
1622
25.7k
        return FALSE;
1623
1624
    /* FIXME default value for userSpaceOnUse? */
1625
25.7k
    double width = 1.0;
1626
25.7k
    double height = 1.0;
1627
1628
25.7k
    if (!get_float_or_percent_attribute (element, "x1", width, &x1))
1629
24.5k
        x1 = 0.0;
1630
1631
25.7k
    if (!get_float_or_percent_attribute (element, "y1", height, &y1))
1632
24.7k
        y1 = 0.0;
1633
1634
25.7k
    if (!get_float_or_percent_attribute (element, "x2", width, &x2))
1635
21.4k
        x2 = width;
1636
1637
25.7k
    if (!get_float_or_percent_attribute (element, "y2", height, &y2))
1638
19.7k
        y2 = 0.0;
1639
1640
25.7k
    if (svg_render->build_pattern.pattern)
1641
0
        abort();
1642
1643
25.7k
    svg_render->build_pattern.pattern = cairo_pattern_create_linear (x1, y1, x2, y2);
1644
25.7k
    svg_render->build_pattern.type = BUILD_PATTERN_LINEAR;
1645
25.7k
    apply_gradient_attributes (svg_render, element);
1646
25.7k
    return TRUE;
1647
25.7k
}
1648
1649
static cairo_bool_t
1650
render_element_radial_gradient (cairo_svg_glyph_render_t *svg_render,
1651
                                cairo_svg_element_t      *element,
1652
                                cairo_bool_t              end_tag)
1653
12.7k
{
1654
12.7k
    double cx, cy, r, fx, fy;
1655
1656
12.7k
    if (svg_render->build_pattern.paint_server != element ||
1657
11.7k
        end_tag ||
1658
5.91k
        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
1659
6.86k
        return FALSE;
1660
1661
    /* FIXME default value for userSpaceOnUse? */
1662
5.87k
    double width = 1.0;
1663
5.87k
    double height = 1.0;
1664
1665
5.87k
    if (!get_float_or_percent_attribute (element, "cx", width, &cx))
1666
5.57k
        cx = 0.5 * width;
1667
1668
5.87k
    if (!get_float_or_percent_attribute (element, "cy", height, &cy))
1669
4.14k
        cy = 0.5 * height;
1670
1671
5.87k
    if (!get_float_or_percent_attribute (element, "r", width, &r))
1672
4.34k
        r = 0.5 * width;
1673
1674
5.87k
    if (!get_float_or_percent_attribute (element, "fx", width, &fx))
1675
3.86k
        fx = cx;
1676
1677
5.87k
    if (!get_float_or_percent_attribute (element, "fy", height, &fy))
1678
4.80k
        fy = cy;
1679
1680
5.87k
    svg_render->build_pattern.pattern = cairo_pattern_create_radial (fx, fy, 0, cx, cy, r);
1681
5.87k
    svg_render->build_pattern.type = BUILD_PATTERN_RADIAL;
1682
5.87k
    apply_gradient_attributes (svg_render, element);
1683
5.87k
    return TRUE;
1684
12.7k
}
1685
1686
static cairo_bool_t
1687
render_element_stop (cairo_svg_glyph_render_t *svg_render,
1688
                     cairo_svg_element_t      *element,
1689
                     cairo_bool_t              end_tag)
1690
56.7k
{
1691
56.7k
    double offset, opacity;
1692
56.7k
    cairo_pattern_t *pattern = svg_render->build_pattern.pattern;
1693
1694
56.7k
    if (!pattern)
1695
263
        return FALSE;
1696
1697
56.5k
    if (cairo_pattern_get_type (pattern) != CAIRO_PATTERN_TYPE_LINEAR &&
1698
6.52k
        cairo_pattern_get_type (pattern) != CAIRO_PATTERN_TYPE_RADIAL)
1699
0
        return FALSE;
1700
1701
56.5k
    if (!get_float_or_percent_attribute (element, "offset", 1.0, &offset))
1702
9.76k
        return FALSE;
1703
1704
46.7k
    if (!get_float_attribute (element, "stop-opacity", &opacity))
1705
37.8k
        opacity = 1.0;
1706
1707
46.7k
    cairo_svg_color_t color;
1708
46.7k
    get_color (svg_render, "black", &color);
1709
46.7k
    get_color (svg_render, get_attribute(element, "stop-color"), &color);
1710
46.7k
    if (color.type == RGB) {
1711
46.7k
        cairo_pattern_add_color_stop_rgba (pattern,
1712
46.7k
                                           offset,
1713
46.7k
                                           color.red,
1714
46.7k
                                           color.green,
1715
46.7k
                                           color.blue,
1716
46.7k
                                           opacity);
1717
46.7k
    } else { /* color.type == FOREGROUND */
1718
1
        double red, green, blue, alpha;
1719
1
        if (cairo_pattern_get_rgba (svg_render->foreground_source, &red, &green, &blue, &alpha) == CAIRO_STATUS_SUCCESS) {
1720
1
      svg_render->foreground_source_used = TRUE;
1721
1
  } else {
1722
0
            red = green = blue = 0;
1723
0
            alpha = 1;
1724
0
        }
1725
1
        cairo_pattern_add_color_stop_rgba (pattern, offset, red, green, blue, alpha);
1726
1
    }
1727
46.7k
    return TRUE;
1728
56.5k
}
1729
1730
static cairo_bool_t
1731
render_element_g (cairo_svg_glyph_render_t *svg_render,
1732
                  cairo_svg_element_t      *element,
1733
                  cairo_bool_t              end_tag)
1734
62.0k
{
1735
62.0k
    if (svg_render->graphics_state->mode == GS_NO_RENDER ||
1736
62.0k
        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
1737
2.96k
        return FALSE;
1738
1739
59.0k
    if (!end_tag) {
1740
29.7k
        cairo_push_group (svg_render->cr);
1741
29.7k
    } else {
1742
29.2k
        cairo_pop_group_to_source (svg_render->cr);
1743
29.2k
        cairo_paint_with_alpha (svg_render->cr, svg_render->graphics_state->opacity);
1744
29.2k
    }
1745
59.0k
    return TRUE;
1746
62.0k
}
1747
1748
typedef struct {
1749
    const char *data; /* current position in base64 data */
1750
    char buf[3]; /* decode buffer */
1751
    int buf_pos; /* current position in buf_pos. */
1752
} base64_decode_t;
1753
1754
static cairo_status_t
1755
_read_png_from_base64 (void *closure, unsigned char *data, unsigned int length)
1756
68.7k
{
1757
68.7k
    base64_decode_t *decode = closure;
1758
68.7k
    int n, c;
1759
68.7k
    unsigned val;
1760
1761
621k
    while (length) {
1762
556k
        if (decode->buf_pos >= 0) {
1763
543k
            *data++ = decode->buf[decode->buf_pos++];
1764
543k
            length--;
1765
543k
            if (decode->buf_pos == 3)
1766
179k
                decode->buf_pos = -1;
1767
543k
        }
1768
556k
        if (length > 0 && decode->buf_pos < 0) {
1769
184k
            n = 0;
1770
184k
            val = 0;
1771
1.02M
            while (*decode->data && n < 4) {
1772
842k
                c = *decode->data++;
1773
842k
                if (c >='A' && c <='Z') {
1774
399k
                    val = (val << 6) | (c -'A');
1775
399k
                    n++;
1776
442k
                } else if (c >='a' && c <='z') {
1777
200k
                    val = (val << 6) | (c -'a' + 26);
1778
200k
                    n++;
1779
242k
                } else if (c >='0' && c <='9') {
1780
87.1k
                    val = (val << 6) | (c -'0' + 52);
1781
87.1k
                    n++;
1782
155k
                } else if (c =='+') {
1783
5.10k
                    val = (val << 6) | 62;
1784
5.10k
                    n++;
1785
150k
                } else if (c =='/') {
1786
38.1k
                    val = (val << 6) | 63;
1787
38.1k
                    n++;
1788
111k
                } else if (c == '=') {
1789
1.12k
                    val = (val << 6);
1790
1.12k
                    n++;
1791
1.12k
                }
1792
842k
            }
1793
184k
            if (n < 4)
1794
2.93k
                return CAIRO_STATUS_READ_ERROR;
1795
1796
181k
            decode->buf[0] = val >> 16;
1797
181k
            decode->buf[1] = val >> 8;
1798
181k
            decode->buf[2] = val >> 0;
1799
181k
            decode->buf_pos = 0;
1800
181k
        }
1801
556k
    }
1802
1803
65.8k
    return CAIRO_STATUS_SUCCESS;
1804
68.7k
}
1805
1806
static cairo_bool_t
1807
render_element_image (cairo_svg_glyph_render_t *svg_render,
1808
                      cairo_svg_element_t      *element,
1809
                      cairo_bool_t              end_tag)
1810
7.93k
{
1811
7.93k
    double x, y, width, height;
1812
7.93k
    int w, h;
1813
7.93k
    const char *data;
1814
7.93k
    cairo_surface_t *surface;
1815
7.93k
    base64_decode_t decode;
1816
1817
7.93k
    if (svg_render->graphics_state->mode == GS_NO_RENDER ||
1818
7.93k
        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
1819
225
        return FALSE;
1820
1821
7.71k
    if (!get_float_attribute (element, "x", &x))
1822
4.90k
        x = 0;
1823
1824
7.71k
    if (!get_float_attribute (element, "y", &y))
1825
3.02k
        y = 0;
1826
1827
7.71k
    if (!get_float_attribute (element, "width", &width))
1828
226
        return FALSE;
1829
1830
7.48k
    if (!get_float_attribute (element, "height", &height))
1831
264
        return FALSE;
1832
1833
7.22k
    data = get_href_attribute (element);
1834
7.22k
    if (!data)
1835
222
        return FALSE;
1836
1837
6.99k
    if (!string_match (&data, "data:image/png;base64,"))
1838
1.01k
        return FALSE;
1839
1840
5.98k
    decode.data = data;
1841
5.98k
    decode.buf_pos = -1;
1842
5.98k
    surface = cairo_image_surface_create_from_png_stream (_read_png_from_base64, &decode);
1843
5.98k
    if (cairo_surface_status (surface)) {
1844
5.93k
        print_warning (svg_render, "Unable to decode PNG");
1845
5.93k
        cairo_surface_destroy (surface);
1846
5.93k
        return FALSE;
1847
5.93k
    }
1848
1849
55
    w = cairo_image_surface_get_width (surface);
1850
55
    h = cairo_image_surface_get_height (surface);
1851
1852
55
    if (w > 0 && h > 0) {
1853
55
        cairo_translate (svg_render->cr, x, y);
1854
55
        cairo_scale (svg_render->cr, width/w, height/h);
1855
55
        cairo_set_source_surface (svg_render->cr, surface, 0, 0);
1856
55
        cairo_paint (svg_render->cr);
1857
55
    }
1858
1859
55
    cairo_surface_destroy (surface);
1860
1861
55
    return FALSE;
1862
5.98k
}
1863
1864
static cairo_bool_t
1865
render_element_use (cairo_svg_glyph_render_t *svg_render,
1866
                    cairo_svg_element_t      *element,
1867
                    cairo_bool_t              end_tag)
1868
16.0k
{
1869
16.0k
    double x = 0;
1870
16.0k
    double y = 0;
1871
16.0k
    const char *id;
1872
1873
16.0k
    if (end_tag || svg_render->graphics_state->mode == GS_NO_RENDER ||
1874
15.8k
        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
1875
4.64k
        return FALSE;
1876
1877
11.3k
    get_float_attribute (element, "x", &x);
1878
11.3k
    get_float_attribute (element, "y", &y);
1879
1880
11.3k
    id = get_href_attribute (element);
1881
11.3k
    if (!id)
1882
910
        return FALSE;
1883
1884
10.4k
    cairo_svg_element_t *use_element = lookup_element (svg_render, id);
1885
10.4k
    cairo_translate (svg_render->cr, x, y);
1886
10.4k
    render_element_tree (svg_render, use_element, NULL, FALSE);
1887
10.4k
    return TRUE;
1888
11.3k
}
1889
1890
static cairo_bool_t
1891
draw_path (cairo_svg_glyph_render_t *svg_render)
1892
1.50M
{
1893
1.50M
    cairo_svg_graphics_state_t *gs = svg_render->graphics_state;
1894
1.50M
    cairo_pattern_t *pattern;
1895
1.50M
    cairo_bool_t opacity_group = FALSE;
1896
1897
1.50M
    if (gs->mode == GS_COMPUTE_BBOX) {
1898
61.7k
        cairo_set_source_rgb (svg_render->cr, 0, 0, 0);
1899
61.7k
        cairo_set_fill_rule (svg_render->cr, gs->fill_rule);
1900
61.7k
        cairo_fill (svg_render->cr);
1901
61.7k
        return FALSE;
1902
1.44M
    } else if (gs->mode == GS_CLIP) {
1903
1.28M
        return FALSE;
1904
1.28M
    }
1905
1906
159k
    if (gs->opacity < 1.0) {
1907
37.2k
        cairo_push_group (svg_render->cr);
1908
37.2k
        opacity_group = TRUE;
1909
37.2k
    }
1910
1911
159k
    cairo_path_t *path = cairo_copy_path (svg_render->cr);
1912
159k
    cairo_new_path (svg_render->cr);
1913
1914
159k
    if (gs->fill.type != PAINT_NONE) {
1915
159k
        cairo_bool_t group = FALSE;
1916
159k
        if (gs->fill.type == PAINT_COLOR) {
1917
127k
            if (gs->fill.color.type == RGB) {
1918
126k
                cairo_set_source_rgba (svg_render->cr,
1919
126k
                                       gs->fill.color.red,
1920
126k
                                       gs->fill.color.green,
1921
126k
                                       gs->fill.color.blue,
1922
126k
                                       gs->fill_opacity);
1923
126k
            } else if (gs->fill.color.type == FOREGROUND) {
1924
1.47k
    cairo_set_source (svg_render->cr, svg_render->foreground_marker);
1925
1.47k
    if (gs->fill_opacity < 1.0)
1926
389
        group = TRUE;
1927
1.47k
            }
1928
127k
        } else if (gs->fill.type == PAINT_SERVER) {
1929
31.9k
            pattern = create_pattern (svg_render, gs->fill.paint_server);
1930
31.9k
            cairo_set_source (svg_render->cr, pattern);
1931
31.9k
            cairo_pattern_destroy (pattern);
1932
31.9k
            if (gs->fill_opacity < 1.0)
1933
785
                group = TRUE;
1934
31.9k
        }
1935
1936
159k
        if (group)
1937
1.17k
            cairo_push_group (svg_render->cr);
1938
1939
159k
        cairo_append_path (svg_render->cr, path);
1940
159k
        cairo_set_fill_rule (svg_render->cr, gs->fill_rule);
1941
159k
        cairo_fill (svg_render->cr);
1942
159k
        if (group) {
1943
1.17k
            cairo_pop_group_to_source (svg_render->cr);
1944
1.17k
            cairo_paint_with_alpha (svg_render->cr, gs->fill_opacity);
1945
1.17k
        }
1946
159k
    }
1947
1948
159k
    if (gs->stroke.type != PAINT_NONE) {
1949
140k
        cairo_bool_t group = FALSE;
1950
140k
        if (gs->stroke.type == PAINT_COLOR) {
1951
1.86k
            if (gs->stroke.color.type == RGB) {
1952
1.69k
                cairo_set_source_rgba (svg_render->cr,
1953
1.69k
                                       gs->stroke.color.red,
1954
1.69k
                                       gs->stroke.color.green,
1955
1.69k
                                       gs->stroke.color.blue,
1956
1.69k
                                       gs->stroke_opacity);
1957
1.69k
            } else if (gs->fill.color.type == FOREGROUND) {
1958
126
    cairo_set_source (svg_render->cr, svg_render->foreground_marker);
1959
126
    if (gs->fill_opacity < 1.0)
1960
88
        group = TRUE;
1961
126
            }
1962
138k
        } else if (gs->stroke.type == PAINT_SERVER) {
1963
138k
            pattern = create_pattern (svg_render, gs->stroke.paint_server);
1964
138k
            cairo_set_source (svg_render->cr, pattern);
1965
138k
            cairo_pattern_destroy (pattern);
1966
138k
            if (gs->stroke_opacity < 1.0)
1967
0
                group = TRUE;
1968
138k
        }
1969
1970
140k
        if (group)
1971
88
            cairo_push_group (svg_render->cr);
1972
1973
140k
        cairo_append_path (svg_render->cr, path);
1974
140k
        cairo_stroke (svg_render->cr);
1975
1976
140k
        if (group) {
1977
88
            cairo_pop_group_to_source (svg_render->cr);
1978
88
            cairo_paint_with_alpha (svg_render->cr, gs->stroke_opacity);
1979
88
        }
1980
140k
    }
1981
1982
159k
    cairo_path_destroy (path);
1983
1984
159k
    if (opacity_group) {
1985
37.2k
        cairo_pop_group_to_source (svg_render->cr);
1986
37.2k
        cairo_paint_with_alpha (svg_render->cr, gs->opacity);
1987
37.2k
    }
1988
159k
    return TRUE;
1989
1.50M
}
1990
1991
static void
1992
elliptical_arc (cairo_svg_glyph_render_t *svg_render,
1993
                double                    cx,
1994
                double                    cy,
1995
                double                    rx,
1996
                double                    ry,
1997
                double                    angle1,
1998
                double                    angle2)
1999
776
{
2000
776
    cairo_save (svg_render->cr);
2001
776
    cairo_translate (svg_render->cr, cx, cy);
2002
776
    cairo_scale (svg_render->cr, rx, ry);
2003
776
    cairo_arc (svg_render->cr, 0, 0, 1, angle1, angle2);
2004
776
    cairo_restore (svg_render->cr);
2005
776
}
2006
2007
static cairo_bool_t
2008
render_element_rect (cairo_svg_glyph_render_t *svg_render,
2009
                     cairo_svg_element_t      *element,
2010
                     cairo_bool_t              end_tag)
2011
281k
{
2012
281k
    double x = 0;
2013
281k
    double y = 0;
2014
281k
    double width = svg_render->width;
2015
281k
    double height = svg_render->height;
2016
281k
    double rx = 0;
2017
281k
    double ry = 0;
2018
2019
281k
    if (end_tag ||
2020
281k
        svg_render->graphics_state->mode == GS_NO_RENDER ||
2021
281k
        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
2022
3.55k
        return FALSE;
2023
2024
277k
    get_float_or_percent_attribute (element, "x", svg_render->width, &x);
2025
277k
    get_float_or_percent_attribute (element, "y", svg_render->height, &y);
2026
277k
    get_float_or_percent_attribute (element, "width", svg_render->width, &width);
2027
277k
    get_float_or_percent_attribute (element, "height", svg_render->height, &height);
2028
277k
    get_float_or_percent_attribute (element, "rx", svg_render->width, &rx);
2029
277k
    get_float_or_percent_attribute (element, "ry", svg_render->height, &ry);
2030
2031
277k
    if (rx == 0 && ry == 0) {
2032
277k
        cairo_rectangle (svg_render->cr, x, y, width, height);
2033
277k
    } else {
2034
46
        cairo_move_to (svg_render->cr, x + rx, y);
2035
46
        cairo_line_to (svg_render->cr, x + width - rx, y);
2036
46
        elliptical_arc (svg_render,    x + width - rx, y + ry, rx, ry, -M_PI/2, 0);
2037
46
        cairo_line_to (svg_render->cr, x + width, y + height - ry);
2038
46
        elliptical_arc (svg_render,    x + width - rx, y + height - ry, rx, ry, 0, M_PI/2);
2039
46
        cairo_line_to (svg_render->cr, x + rx, y + height);
2040
46
        elliptical_arc (svg_render,    x + rx, y + height - ry, rx, ry, M_PI/2, M_PI);
2041
46
        cairo_line_to (svg_render->cr, x, y + ry);
2042
46
        elliptical_arc (svg_render,    x + rx, y + ry, rx, ry, M_PI, -M_PI/2);
2043
46
    }
2044
2045
277k
    draw_path (svg_render);
2046
277k
    return TRUE;
2047
281k
}
2048
2049
static cairo_bool_t
2050
render_element_circle (cairo_svg_glyph_render_t *svg_render,
2051
                       cairo_svg_element_t      *element,
2052
                       cairo_bool_t              end_tag)
2053
1.55k
{
2054
1.55k
    double cx = 0;
2055
1.55k
    double cy = 0;
2056
1.55k
    double r = 0;
2057
2058
1.55k
    if (end_tag ||
2059
1.55k
        svg_render->graphics_state->mode == GS_NO_RENDER ||
2060
1.55k
        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
2061
752
        return FALSE;
2062
2063
802
    get_float_or_percent_attribute (element, "cx", svg_render->width, &cx);
2064
802
    get_float_or_percent_attribute (element, "cy", svg_render->height, &cy);
2065
802
    get_float_or_percent_attribute (element, "r", svg_render->width, &r);
2066
2067
802
    cairo_arc (svg_render->cr, cx, cy, r, 0, 2*M_PI);
2068
2069
802
    draw_path (svg_render);
2070
802
    return TRUE;
2071
1.55k
}
2072
2073
static cairo_bool_t
2074
render_element_ellipse (cairo_svg_glyph_render_t *svg_render,
2075
                        cairo_svg_element_t      *element,
2076
                        cairo_bool_t              end_tag)
2077
1.40k
{
2078
1.40k
    double cx = 0;
2079
1.40k
    double cy = 0;
2080
1.40k
    double rx = 0;
2081
1.40k
    double ry = 0;
2082
2083
1.40k
    if (end_tag ||
2084
1.40k
        svg_render->graphics_state->mode == GS_NO_RENDER ||
2085
1.40k
        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
2086
811
        return FALSE;
2087
2088
592
    get_float_or_percent_attribute (element, "cx", svg_render->width, &cx);
2089
592
    get_float_or_percent_attribute (element, "cy", svg_render->height, &cy);
2090
592
    get_float_or_percent_attribute (element, "rx", svg_render->width, &rx);
2091
592
    get_float_or_percent_attribute (element, "ry", svg_render->height, &ry);
2092
2093
592
    elliptical_arc (svg_render, cx, cy, rx, ry, 0, 2*M_PI);
2094
592
    draw_path (svg_render);
2095
592
    return TRUE;
2096
1.40k
}
2097
2098
static cairo_bool_t
2099
render_element_line (cairo_svg_glyph_render_t *svg_render,
2100
                     cairo_svg_element_t      *element,
2101
                     cairo_bool_t              end_tag)
2102
1.87k
{
2103
1.87k
    double x1 = 0;
2104
1.87k
    double y1 = 0;
2105
1.87k
    double x2 = 0;
2106
1.87k
    double y2 = 0;
2107
2108
1.87k
    if (end_tag ||
2109
1.87k
        svg_render->graphics_state->mode == GS_NO_RENDER ||
2110
1.87k
        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
2111
1.72k
        return FALSE;
2112
2113
148
    get_float_or_percent_attribute (element, "x1", svg_render->width, &x1);
2114
148
    get_float_or_percent_attribute (element, "y1", svg_render->height, &y1);
2115
148
    get_float_or_percent_attribute (element, "x2", svg_render->width, &x2);
2116
148
    get_float_or_percent_attribute (element, "y2", svg_render->height, &y2);
2117
2118
148
    cairo_move_to (svg_render->cr, x1, y1);
2119
148
    cairo_line_to (svg_render->cr, x2, y2);
2120
2121
148
    draw_path (svg_render);
2122
148
    return TRUE;
2123
1.87k
}
2124
2125
static cairo_bool_t
2126
render_element_polyline (cairo_svg_glyph_render_t *svg_render,
2127
                         cairo_svg_element_t      *element,
2128
                         cairo_bool_t              end_tag)
2129
6.99k
{
2130
6.99k
    const char *p;
2131
6.99k
    const char *end;
2132
6.99k
    double x, y;
2133
6.99k
    cairo_bool_t have_move = FALSE;
2134
2135
6.99k
    if (end_tag ||
2136
6.99k
        svg_render->graphics_state->mode == GS_NO_RENDER ||
2137
6.99k
        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
2138
556
        return FALSE;
2139
2140
6.44k
    p = get_attribute (element, "points");
2141
6.96k
    do {
2142
6.96k
        end = get_path_params (p, 2, &x, &y);
2143
6.96k
        if (!end) {
2144
6.14k
            print_warning (svg_render, "points expected 2 numbers: %s", p);
2145
6.14k
            break;
2146
6.14k
        }
2147
817
        p = end;
2148
817
        if (!have_move) {
2149
433
            cairo_move_to (svg_render->cr, x, y);
2150
433
            have_move = TRUE;
2151
433
        } else {
2152
384
            cairo_line_to (svg_render->cr, x, y);
2153
384
        }
2154
817
        p = skip_space (p);
2155
817
    } while (p && *p);
2156
2157
6.44k
    if (string_equal (element->tag, "polygon"))
2158
5.51k
        cairo_close_path (svg_render->cr);
2159
2160
6.44k
    draw_path (svg_render);
2161
6.44k
    return TRUE;
2162
6.99k
}
2163
2164
static double
2165
angle_between_vectors (double ux,
2166
                       double uy,
2167
                       double vx,
2168
                       double vy)
2169
110k
{
2170
110k
    double dot = ux*vx + uy*vy;
2171
110k
    double umag = sqrt (ux*ux + uy*uy);
2172
110k
    double vmag = sqrt (vx*vx + vy*vy);
2173
110k
    double c = dot/(umag*vmag);
2174
110k
    if (c > 1.0)
2175
199
        c = 1.0;
2176
2177
110k
    if (c < -1.0)
2178
1.33k
        c = -1.0;
2179
2180
110k
    double a = acos (c);
2181
110k
    if (ux * vy - uy * vx < 0.0)
2182
37.0k
        a = -a;
2183
2184
110k
    return a;
2185
110k
}
2186
2187
static void
2188
arc_path (cairo_t *cr,
2189
          double x1, double y1,
2190
          double x2, double y2,
2191
          double rx, double ry,
2192
          double rotate,
2193
          cairo_bool_t large_flag,
2194
          cairo_bool_t sweep_flag)
2195
73.7k
{
2196
73.7k
    double x1_, y1_, cx_, cy_;
2197
73.7k
    double xm, ym, cx, cy;
2198
73.7k
    double a, b, d;
2199
73.7k
    double ux, uy, vx, vy;
2200
73.7k
    double theta, delta_theta;
2201
73.7k
    double epsilon;
2202
73.7k
    cairo_matrix_t ctm;
2203
2204
73.7k
    cairo_get_matrix (cr, &ctm);
2205
73.7k
    epsilon = _cairo_matrix_transformed_circle_major_axis (&ctm, cairo_get_tolerance (cr));
2206
2207
73.7k
    rotate *= M_PI/180.0;
2208
2209
    /* Convert endpoint to center parameterization.
2210
     * See SVG 1.1 Appendix F.6. Step numbers are the steps in the appendix.
2211
     */
2212
2213
73.7k
    rx = fabs (rx);
2214
73.7k
    ry = fabs (ry);
2215
73.7k
    if (rx < epsilon || ry < epsilon) {
2216
18.0k
        cairo_line_to (cr, x2, y2);
2217
18.0k
        return;
2218
18.0k
    }
2219
2220
55.6k
    if (fabs(x1 - x2) < epsilon && fabs(y1 - y2) < epsilon) {
2221
500
        cairo_line_to (cr, x2, y2);
2222
500
        return;
2223
500
    }
2224
2225
    /* Step 1 */
2226
55.1k
    xm = (x1 - x2)/2;
2227
55.1k
    ym = (y1 - y2)/2;
2228
55.1k
    x1_ = xm * cos (rotate) + ym * sin (rotate);
2229
55.1k
    y1_ = xm * -sin (rotate) + ym * cos (rotate);
2230
2231
55.1k
    d = (x1_*x1_)/(rx*rx) + (y1_*y1_)/(ry*ry);
2232
55.1k
    if (d > 1.0) {
2233
24.2k
        d = sqrt (d);
2234
24.2k
        rx *= d;
2235
24.2k
        ry *= d;
2236
24.2k
    }
2237
2238
    /* Step 2 */
2239
55.1k
    a = (rx*rx * y1_*y1_) + (ry*ry * x1_*x1_);
2240
55.1k
    if (a == 0.0)
2241
0
        return;
2242
2243
55.1k
    b = (rx*rx * ry*ry) / a - 1.0;
2244
55.1k
    if (b < 0)
2245
1.50k
        b = 0.0;
2246
2247
55.1k
    d = sqrt(b);
2248
55.1k
    if (large_flag == sweep_flag)
2249
35.8k
        d = -d;
2250
2251
55.1k
    cx_ = d * rx*y1_/ry;
2252
55.1k
    cy_ = d * -ry*x1_/rx;
2253
2254
    /* Step 3 */
2255
55.1k
    cx = cx_ * cos (rotate) - cy_ * sin (rotate) + (x1 + x2)/2;
2256
55.1k
    cy = cx_ * sin (rotate) + cy_ * cos (rotate) + (y1 + y2)/2;
2257
2258
    /* Step 4 */
2259
55.1k
    ux = (x1_ - cx_)/rx;
2260
55.1k
    uy = (y1_ - cy_)/ry;
2261
55.1k
    vx = (-x1_ - cx_)/rx;
2262
55.1k
    vy = (-y1_ - cy_)/ry;
2263
55.1k
    theta = angle_between_vectors (1.0, 0, ux, uy);
2264
55.1k
    delta_theta = angle_between_vectors (ux, uy, vx, vy);
2265
2266
55.1k
    if (!sweep_flag && delta_theta > 0)
2267
10.5k
        delta_theta -= 2 * M_PI;
2268
44.6k
    else if (sweep_flag && delta_theta < 0)
2269
12.9k
        delta_theta += 2 * M_PI;
2270
2271
    /* Now we can call cairo_arc() */
2272
55.1k
    cairo_save (cr);
2273
55.1k
    cairo_translate (cr, cx, cy);
2274
55.1k
    cairo_scale (cr, rx, ry);
2275
55.1k
    cairo_rotate (cr, theta);
2276
55.1k
    if (delta_theta >= 0.0)
2277
39.3k
        cairo_arc (cr, 0, 0, 1, 0, delta_theta);
2278
15.8k
    else
2279
15.8k
        cairo_arc_negative (cr, 0, 0, 1, 0, delta_theta);
2280
55.1k
    cairo_restore (cr);
2281
55.1k
}
2282
2283
static void
2284
get_current_point (cairo_svg_glyph_render_t *svg_render, double *x, double *y)
2285
25.3M
{
2286
25.3M
    if (cairo_has_current_point (svg_render->cr)) {
2287
24.2M
        cairo_get_current_point (svg_render->cr, x, y);
2288
24.2M
    } else {
2289
1.11M
        *x = 0;
2290
1.11M
        *y = 0;
2291
1.11M
    }
2292
25.3M
}
2293
2294
static void
2295
reflect_point (double origin_x, double origin_y, double *x, double *y)
2296
7.25M
{
2297
7.25M
    *x = 2*origin_x - *x;
2298
7.25M
    *y = 2*origin_y - *y;
2299
7.25M
}
2300
2301
static cairo_bool_t
2302
render_element_path (cairo_svg_glyph_render_t *svg_render,
2303
                     cairo_svg_element_t      *element,
2304
                     cairo_bool_t              end_tag)
2305
1.26M
{
2306
1.26M
    double cur_x, cur_y;
2307
1.26M
    double last_cp_x, last_cp_y;
2308
1.26M
    double x, y, x1, y1, x2, y2;
2309
1.26M
    double qx1, qy1, qx2, qy2;
2310
1.26M
    double rx, ry, rotate, large_flag, sweep_flag;
2311
1.26M
    cairo_bool_t rel, have_move;
2312
1.26M
    enum { CUBIC, QUADRATIC, OTHER } last_op;
2313
2314
1.26M
    if (end_tag ||
2315
1.24M
        svg_render->graphics_state->mode == GS_NO_RENDER ||
2316
1.24M
        svg_render->build_pattern.type != BUILD_PATTERN_NONE)
2317
50.7k
        return FALSE;
2318
2319
1.21M
    last_op = OTHER;
2320
1.21M
    const char *p = get_attribute (element, "d");
2321
1.21M
    const char *end;
2322
1.21M
    int op;
2323
2324
26.8M
    while (p) {
2325
26.7M
        while (p && _cairo_isspace (*p))
2326
1.06M
            p++;
2327
2328
25.6M
        if (!p || *p == 0)
2329
29.2k
            break;
2330
2331
25.6M
        op = *p;
2332
25.6M
        switch (op) {
2333
298k
            case 'M':
2334
323k
            case 'm':
2335
323k
                rel = op == 'm';
2336
323k
                p++;
2337
323k
                have_move = FALSE;
2338
337k
                do {
2339
337k
                    end = get_path_params (p, 2, &x, &y);
2340
337k
                    if (!end) {
2341
297k
                        print_warning (svg_render, "path %c expected 2 numbers: %s", op, p);
2342
297k
                        break;
2343
297k
                    }
2344
40.0k
                    p = end;
2345
40.0k
                    if (rel) {
2346
5.01k
                        get_current_point (svg_render, &cur_x, &cur_y);
2347
5.01k
                        x += cur_x;
2348
5.01k
                        y += cur_y;
2349
5.01k
                    }
2350
40.0k
                    if (!have_move) {
2351
37.2k
                        cairo_move_to (svg_render->cr, x, y);
2352
37.2k
                        have_move = TRUE;
2353
37.2k
                    } else {
2354
2.78k
                        cairo_line_to (svg_render->cr, x, y);
2355
2.78k
                    }
2356
40.0k
                    p = skip_space (p);
2357
40.0k
                } while (p && *p && !_cairo_isalpha(*p));
2358
323k
                last_op = OTHER;
2359
323k
                break;
2360
147k
            case 'Z':
2361
466k
            case 'z':
2362
466k
                p++;
2363
466k
                cairo_close_path (svg_render->cr);
2364
466k
                last_op = OTHER;
2365
466k
                break;
2366
84.3k
            case 'L':
2367
101k
            case 'l':
2368
101k
                rel = op == 'l';
2369
101k
                p++;
2370
113k
                do {
2371
113k
                    end = get_path_params (p, 2, &x, &y);
2372
113k
                    if (!end) {
2373
86.1k
                        print_warning (svg_render, "path %c expected 2 numbers: %s", op, p);
2374
86.1k
                        break;
2375
86.1k
                    }
2376
26.9k
                    p = end;
2377
26.9k
                    if (rel) {
2378
10.4k
                        get_current_point (svg_render, &cur_x, &cur_y);
2379
10.4k
                        x += cur_x;
2380
10.4k
                        y += cur_y;
2381
10.4k
                    }
2382
26.9k
                    cairo_line_to (svg_render->cr, x, y);
2383
26.9k
                    p = skip_space (p);
2384
26.9k
                } while (p && *p && !_cairo_isalpha(*p));
2385
101k
                last_op = OTHER;
2386
101k
                break;
2387
7.59M
            case 'H':
2388
11.8M
            case 'h':
2389
11.8M
                rel = op == 'h';
2390
11.8M
                p++;
2391
12.6M
                do {
2392
12.6M
                    end = get_path_params (p, 1, &x1);
2393
12.6M
                    if (!end) {
2394
2.52M
                        print_warning (svg_render, "path %c expected a number: %s", op, p);
2395
2.52M
                        break;
2396
2.52M
                    }
2397
10.1M
                    p = end;
2398
10.1M
                    get_current_point (svg_render, &cur_x, &cur_y);
2399
10.1M
                    if (rel) {
2400
2.62M
                        x1 += cur_x;
2401
2.62M
                    }
2402
10.1M
                    cairo_line_to (svg_render->cr, x1, cur_y);
2403
10.1M
                    p = skip_space (p);
2404
10.1M
                } while (p && *p && !_cairo_isalpha(*p));
2405
11.8M
                last_op = OTHER;
2406
11.8M
                break;
2407
6.22M
            case 'V':
2408
7.72M
            case 'v':
2409
7.72M
                rel = op == 'v';
2410
7.72M
                p++;
2411
8.57M
                do {
2412
8.57M
                    end = get_path_params (p, 1, &y1);
2413
8.57M
                    if (!end) {
2414
777k
                        print_warning (svg_render, "path %c expected a number: %s", op, p);
2415
777k
                        break;
2416
777k
                    }
2417
7.80M
                    p = end;
2418
7.80M
                    get_current_point (svg_render, &cur_x, &cur_y);
2419
7.80M
                    if (rel) {
2420
1.47M
                        y1 += cur_y;
2421
1.47M
                    }
2422
7.80M
                    cairo_line_to (svg_render->cr, cur_x, y1);
2423
7.80M
                    p = skip_space (p);
2424
7.80M
                } while (p && *p && !_cairo_isalpha(*p));
2425
7.72M
                last_op = OTHER;
2426
7.72M
                break;
2427
122k
            case 'C':
2428
249k
            case 'c':
2429
249k
                rel = op == 'c';
2430
249k
                p++;
2431
265k
                do {
2432
265k
                    end = get_path_params (p, 6, &x1, &y1, &x2, &y2, &x, &y);
2433
265k
                    if (!end) {
2434
246k
                        print_warning (svg_render, "path %c expected 6 numbers: %s", op, p);
2435
246k
                        break;
2436
246k
                    }
2437
18.3k
                    p = end;
2438
18.3k
                    if (rel) {
2439
679
                        get_current_point (svg_render, &cur_x, &cur_y);
2440
679
                        x1 += cur_x;
2441
679
                        y1 += cur_y;
2442
679
                        x2 += cur_x;
2443
679
                        y2 += cur_y;
2444
679
                        x += cur_x;
2445
679
                        y += cur_y;
2446
679
                    }
2447
18.3k
                    cairo_curve_to (svg_render->cr, x1, y1, x2, y2, x, y);
2448
18.3k
                    p = skip_space (p);
2449
18.3k
                } while (p && *p && !_cairo_isalpha(*p));
2450
249k
                last_op = CUBIC;
2451
249k
                last_cp_x = x2;
2452
249k
                last_cp_y = y2;
2453
249k
                break;
2454
12.2k
            case 'S':
2455
34.9k
            case 's':
2456
34.9k
                rel = op == 's';
2457
34.9k
                p++;
2458
48.5k
                do {
2459
48.5k
                    end = get_path_params (p, 4, &x2, &y2, &x, &y);
2460
48.5k
                    if (!end) {
2461
23.3k
                        print_warning (svg_render, "path %c expected 4 numbers: %s", op, p);
2462
23.3k
                        break;
2463
23.3k
                    }
2464
25.1k
                    p = end;
2465
25.1k
                    get_current_point (svg_render, &cur_x, &cur_y);
2466
25.1k
                    if (rel) {
2467
11.0k
                        x2 += cur_x;
2468
11.0k
                        y2 += cur_y;
2469
11.0k
                        x += cur_x;
2470
11.0k
                        y += cur_y;
2471
11.0k
                    }
2472
25.1k
                    if (last_op == CUBIC) {
2473
4.20k
                        x1 = last_cp_x;
2474
4.20k
                        y1 = last_cp_y;
2475
4.20k
                        reflect_point (cur_x, cur_y, &x1, &y1);
2476
20.9k
                    } else {
2477
20.9k
                        x1 = cur_x;
2478
20.9k
                        y1 = cur_y;
2479
20.9k
                    }
2480
25.1k
                    cairo_curve_to (svg_render->cr, x1, y1, x2, y2, x, y);
2481
25.1k
                    last_op = CUBIC;
2482
25.1k
                    last_cp_x = x2;
2483
25.1k
                    last_cp_y = y2;
2484
25.1k
                    p = skip_space (p);
2485
25.1k
                } while (p && *p && !_cairo_isalpha(*p));
2486
34.9k
                break;
2487
430k
            case 'Q':
2488
609k
            case 'q':
2489
609k
                rel = op == 'q';
2490
609k
                p++;
2491
611k
                do {
2492
611k
                    end = get_path_params (p, 4, &x1, &y1, &x, &y);
2493
611k
                    if (!end) {
2494
608k
                        print_warning (svg_render, "path %c expected 4 numbers: %s", op, p);
2495
608k
                        break;
2496
608k
                    }
2497
2.58k
                    p = end;
2498
2.58k
                    get_current_point (svg_render, &cur_x, &cur_y);
2499
2.58k
                    if (rel) {
2500
2.10k
                        x1 += cur_x;
2501
2.10k
                        y1 += cur_y;
2502
2.10k
                        x += cur_x;
2503
2.10k
                        y += cur_y;
2504
2.10k
                    }
2505
2.58k
                    qx1 = cur_x + (x1 - cur_x)*2/3;
2506
2.58k
                    qy1 = cur_y + (y1 - cur_y)*2/3;
2507
2.58k
                    qx2 = x + (x1 - x)*2/3;
2508
2.58k
                    qy2 = y + (y1 - y)*2/3;
2509
2.58k
                    cairo_curve_to (svg_render->cr, qx1, qy1, qx2, qy2, x, y);
2510
2.58k
                    p = skip_space (p);
2511
2.58k
                } while (p && *p && !_cairo_isalpha(*p));
2512
609k
                last_op = QUADRATIC;
2513
609k
                last_cp_x = x1;
2514
609k
                last_cp_y = y1;
2515
609k
                break;
2516
905k
            case 'T':
2517
2.71M
            case 't':
2518
2.71M
                rel = op == 't';
2519
2.71M
                p++;
2520
9.74M
                do {
2521
9.74M
                    end = get_path_params (p, 2, &x, &y);
2522
9.74M
                    if (!end) {
2523
2.47M
                        print_warning (svg_render, "path %c expected 2 numbers: %s", op, p);
2524
2.47M
                        break;
2525
2.47M
                    }
2526
7.27M
                    p = end;
2527
7.27M
                    get_current_point (svg_render, &cur_x, &cur_y);
2528
7.27M
                    if (rel) {
2529
50.5k
                        x += cur_x;
2530
50.5k
                        y += cur_y;
2531
50.5k
                    }
2532
7.27M
                    if (last_op == QUADRATIC) {
2533
7.24M
                        x1 = last_cp_x;
2534
7.24M
                        y1 = last_cp_y;
2535
7.24M
                        reflect_point (cur_x, cur_y, &x1, &y1);
2536
7.24M
                    } else {
2537
30.0k
                        x1 = cur_x;
2538
30.0k
                        y1 = cur_y;
2539
30.0k
                    }
2540
7.27M
                    qx1 = cur_x + (x1 - cur_x)*2/3;
2541
7.27M
                    qy1 = cur_y + (y1 - cur_y)*2/3;
2542
7.27M
                    qx2 = x + (x1 - x)*2/3;
2543
7.27M
                    qy2 = y + (y1 - y)*2/3;
2544
7.27M
                    cairo_curve_to (svg_render->cr, qx1, qy1, qx2, qy2, x, y);
2545
7.27M
                    last_op = QUADRATIC;
2546
7.27M
                    last_cp_x = x1;
2547
7.27M
                    last_cp_y = y1;
2548
7.27M
                    p = skip_space (p);
2549
7.27M
                } while (p && *p && *p && !_cairo_isalpha(*p));
2550
2.71M
                break;
2551
70.3k
            case 'A':
2552
400k
            case 'a':
2553
400k
                rel = op == 'a';
2554
400k
                p++;
2555
453k
                do {
2556
453k
                    end = get_path_params (p, 7, &rx, &ry, &rotate, &large_flag, &sweep_flag, &x, &y);
2557
453k
                    if (!end) {
2558
379k
                        print_warning (svg_render, "path %c expected 7 numbers: %s", op, p);
2559
379k
                        break;
2560
379k
                    }
2561
73.7k
                    p = end;
2562
73.7k
                    get_current_point (svg_render, &cur_x, &cur_y);
2563
73.7k
                    if (rel) {
2564
30.8k
                        x += cur_x;
2565
30.8k
                        y += cur_y;
2566
30.8k
                    }
2567
73.7k
                    arc_path (svg_render->cr,
2568
73.7k
                              cur_x, cur_y,
2569
73.7k
                              x, y,
2570
73.7k
                              rx, ry,
2571
73.7k
                              rotate,
2572
73.7k
                              large_flag > 0.5,
2573
73.7k
                              sweep_flag > 0.5);
2574
73.7k
                    p = skip_space (p);
2575
73.7k
                } while (p && *p && !_cairo_isalpha(*p));
2576
400k
                last_op = OTHER;
2577
400k
                break;
2578
1.14M
            default:
2579
1.14M
                p = NULL;
2580
1.14M
                break;
2581
25.6M
        }
2582
25.6M
    }
2583
2584
1.21M
    draw_path (svg_render);
2585
1.21M
    return TRUE;
2586
1.21M
}
2587
2588
static void
2589
init_graphics_state (cairo_svg_glyph_render_t *svg_render)
2590
2.22k
{
2591
2.22k
    cairo_svg_graphics_state_t *gs;
2592
2593
2.22k
    gs = _cairo_calloc (sizeof (cairo_svg_graphics_state_t));
2594
2.22k
    get_paint (svg_render, "black", &gs->fill);
2595
2.22k
    get_paint (svg_render, "none", &gs->stroke);
2596
2.22k
    gs->color.type = FOREGROUND;
2597
2.22k
    gs->fill_opacity = 1.0;
2598
2.22k
    gs->stroke_opacity = 1.0;
2599
2.22k
    gs->opacity = 1.0;
2600
2.22k
    gs->fill_rule = CAIRO_FILL_RULE_WINDING;
2601
2.22k
    gs->clip_rule = CAIRO_FILL_RULE_WINDING;
2602
2.22k
    gs->clip_path = NULL;
2603
2.22k
    gs->dash_array = NULL;
2604
2.22k
    gs->dash_offset = 0.0;
2605
2.22k
    gs->mode = GS_RENDER;
2606
2.22k
    gs->bbox.x = 0;
2607
2.22k
    gs->bbox.y = 0;
2608
2.22k
    gs->bbox.width = 0;
2609
2.22k
    gs->bbox.height = 0;
2610
2.22k
    gs->next = NULL;
2611
2612
2.22k
    svg_render->graphics_state = gs;
2613
2614
2.22k
    cairo_save (svg_render->cr);
2615
2.22k
    cairo_set_source_rgb (svg_render->cr, 0, 0, 0);
2616
2.22k
    cairo_set_line_width (svg_render->cr, 1.0);
2617
2.22k
    cairo_set_line_cap (svg_render->cr, CAIRO_LINE_CAP_BUTT);
2618
2.22k
    cairo_set_line_join (svg_render->cr, CAIRO_LINE_JOIN_MITER);
2619
2.22k
    cairo_set_miter_limit (svg_render->cr, 4.0);
2620
2.22k
}
2621
2622
263k
#define MAX_DASHES 100
2623
static void update_dash (cairo_svg_glyph_render_t *svg_render,
2624
                         cairo_svg_element_t      *element)
2625
2.56M
{
2626
2.56M
    cairo_svg_graphics_state_t *gs = svg_render->graphics_state;
2627
2.56M
    const char *p;
2628
2.56M
    char *end;
2629
2.56M
    double value;
2630
2.56M
    double dash_array[MAX_DASHES];
2631
2.56M
    int num_dashes = 0;
2632
2.56M
    cairo_bool_t not_zero = FALSE;
2633
    
2634
2.56M
    if (gs->dash_array == NULL || string_equal (gs->dash_array, "none")) {
2635
2.43M
        cairo_set_dash (svg_render->cr, NULL, 0, 0);
2636
2.43M
        return;
2637
2.43M
    }
2638
2639
126k
    p = gs->dash_array;
2640
313k
    while (*p && num_dashes < MAX_DASHES) {
2641
337k
        while (*p && (*p == ',' || _cairo_isspace (*p)))
2642
74.0k
            p++;
2643
2644
263k
        if (*p == 0)
2645
213
            break;
2646
2647
263k
        value = _cairo_strtod (p, &end);
2648
263k
        if (end == p)
2649
76.5k
            break;
2650
2651
186k
        p = end;
2652
186k
        if (*p == '%') {
2653
0
            value *= svg_render->width / 100.0;
2654
0
            p++;
2655
0
        }
2656
2657
186k
        if (value < 0.0)
2658
0
            return;
2659
2660
186k
        if (value > 0.0)
2661
140k
            not_zero = TRUE;
2662
2663
186k
        dash_array[num_dashes++] = value;
2664
186k
    }
2665
2666
126k
    if (not_zero)
2667
126k
        cairo_set_dash (svg_render->cr, dash_array, num_dashes, gs->dash_offset);
2668
126k
}
2669
2670
static cairo_bool_t
2671
pattern_requires_bbox (cairo_svg_glyph_render_t *svg_render,
2672
                       cairo_svg_element_t      *paint_server)
2673
420k
{
2674
420k
    const char *p;
2675
2676
420k
    if (string_equal (paint_server->tag, "linearGradient") ||
2677
284k
        string_equal (paint_server->tag, "radialGradient"))
2678
162k
    {
2679
162k
        p = get_attribute (paint_server, "gradientUnits");
2680
162k
        if (string_equal (p, "userSpaceOnUse"))
2681
642
            return FALSE;
2682
2683
161k
        return TRUE;
2684
162k
    }
2685
258k
    return FALSE;
2686
420k
}
2687
2688
static cairo_bool_t
2689
clip_requires_bbox (cairo_svg_glyph_render_t *svg_render,
2690
                    const char               *clip_path)
2691
337k
{
2692
337k
    cairo_svg_element_t *element;
2693
337k
    const char *p;
2694
2695
337k
    if (clip_path && strncmp (clip_path, "url", 3) == 0) {
2696
29.8k
        element = lookup_url_element (svg_render, clip_path);
2697
29.8k
        if (element) {
2698
27.3k
            p = get_attribute (element, "clipPathUnits");
2699
27.3k
            if (string_equal (p, "objectBoundingBox"))
2700
600
                return TRUE;
2701
27.3k
        }
2702
29.8k
    }
2703
336k
    return FALSE;
2704
337k
}
2705
2706
static cairo_bool_t
2707
need_bbox (cairo_svg_glyph_render_t *svg_render,
2708
           cairo_svg_element_t      *element)
2709
337k
{
2710
337k
    cairo_svg_graphics_state_t *gs = svg_render->graphics_state;
2711
337k
    cairo_bool_t fill_needs_bbox = FALSE;
2712
337k
    cairo_bool_t stroke_needs_bbox = FALSE;
2713
337k
    cairo_bool_t clip_needs_bbox = FALSE;
2714
    
2715
337k
    if (gs->mode != GS_RENDER)
2716
0
        return FALSE;
2717
2718
337k
    if (gs->fill.type == PAINT_SERVER && pattern_requires_bbox (svg_render, gs->fill.paint_server))
2719
148k
        fill_needs_bbox = TRUE;
2720
2721
337k
    if (gs->stroke.type == PAINT_SERVER && pattern_requires_bbox (svg_render, gs->stroke.paint_server))
2722
12.9k
        stroke_needs_bbox = TRUE;
2723
2724
337k
    if (clip_requires_bbox (svg_render, get_attribute (element, "clip-path")))
2725
600
        clip_needs_bbox = TRUE;
2726
2727
337k
    if (string_equal (element->tag, "circle") ||
2728
336k
        string_equal (element->tag, "ellipse") ||
2729
336k
        string_equal (element->tag, "path") ||
2730
210k
        string_equal (element->tag, "polygon") ||
2731
210k
        string_equal (element->tag, "rect"))
2732
174k
    {
2733
174k
        return fill_needs_bbox || stroke_needs_bbox || clip_needs_bbox;
2734
174k
    }
2735
2736
163k
    if (string_equal (element->tag, "line") ||
2737
162k
        string_equal (element->tag, "polyline"))
2738
1.86k
    {
2739
1.86k
        return stroke_needs_bbox || clip_needs_bbox;
2740
1.86k
    }
2741
2742
161k
    if (string_equal (element->tag, "g") ||
2743
144k
        string_equal (element->tag, "image") ||
2744
143k
        string_equal (element->tag, "use"))
2745
32.4k
    {
2746
32.4k
        return clip_needs_bbox;
2747
32.4k
    }
2748
    
2749
128k
    return FALSE;
2750
161k
}
2751
2752
static cairo_bool_t
2753
call_element (cairo_svg_glyph_render_t *svg_render,
2754
              cairo_svg_element_t      *element,
2755
              cairo_bool_t              end_tag);
2756
2757
static void
2758
update_graphics_state (cairo_svg_glyph_render_t *svg_render,
2759
                       cairo_svg_element_t      *element)
2760
2.61M
{
2761
2.61M
    double value;
2762
2.61M
    const char *p;
2763
2.61M
    cairo_svg_graphics_state_t *gs = svg_render->graphics_state;
2764
2765
2.61M
    p = get_attribute (element, "transform");
2766
2.61M
    if (p) {
2767
894k
        cairo_matrix_t m;
2768
894k
        if (parse_transform (p, &m))
2769
804k
            cairo_transform (svg_render->cr, &m);
2770
894k
    }
2771
2772
    /* The transform is all we need for bbox computation. The SVG spec
2773
     * excludes clipping and stroke-width from the bbox. */
2774
2.61M
    if (gs->mode == GS_COMPUTE_BBOX)
2775
51.1k
        return;
2776
    
2777
2.56M
    p = get_attribute (element, "color");
2778
2.56M
    if (p)
2779
8.43k
        get_color (svg_render, p, &gs->color);
2780
2781
2.56M
    if (!get_float_attribute (element, "opacity", &gs->opacity))
2782
2.39M
        gs->opacity = 1.0;
2783
2784
2.56M
    p = get_attribute (element, "fill");
2785
2.56M
    if (p) {
2786
72.6k
        get_paint (svg_render, p, &gs->fill);
2787
72.6k
    }
2788
2789
2.56M
    get_float_attribute (element, "fill-opacity", &gs->fill_opacity);
2790
2791
2.56M
    gs->fill_rule = get_fill_rule_attribute (element, "fill-rule", gs->fill_rule);
2792
2793
2.56M
    gs->clip_rule = get_fill_rule_attribute (element, "fill-rule", gs->clip_rule);
2794
2795
2.56M
    p = get_attribute (element, "stroke");
2796
2.56M
    if (p)
2797
1.09M
        get_paint (svg_render, p, &gs->stroke);
2798
2799
2.56M
    if (get_float_or_percent_attribute (element, "stroke-width", svg_render->width, &value))
2800
16.9k
        cairo_set_line_width (svg_render->cr, value);
2801
2802
2.56M
    p = get_attribute (element, "stroke-linecap");
2803
2.56M
    if (string_equal (p, "butt"))
2804
18
        cairo_set_line_cap (svg_render->cr, CAIRO_LINE_CAP_BUTT);
2805
2.56M
    else if (string_equal (p, "round"))
2806
1.76k
        cairo_set_line_cap (svg_render->cr, CAIRO_LINE_CAP_ROUND);
2807
2.56M
    else if (string_equal (p, "square"))
2808
13.0k
        cairo_set_line_cap (svg_render->cr, CAIRO_LINE_CAP_SQUARE);
2809
2810
2.56M
    p = get_attribute (element, "stroke-linejoin");
2811
2.56M
    if (string_equal (p, "miter"))
2812
24
        cairo_set_line_join (svg_render->cr, CAIRO_LINE_JOIN_MITER);
2813
2.56M
    else if (string_equal (p, "round"))
2814
3.31k
        cairo_set_line_join (svg_render->cr, CAIRO_LINE_JOIN_ROUND);
2815
2.56M
    else if (string_equal (p, "bevel"))
2816
11
        cairo_set_line_join (svg_render->cr, CAIRO_LINE_JOIN_BEVEL);
2817
2818
2.56M
    if (get_float_attribute (element, "stroke-miterlimit", &value))
2819
723
        cairo_set_miter_limit (svg_render->cr, value);
2820
2821
2.56M
    p = get_attribute (element, "stroke-dasharray");
2822
2.56M
    if (p) {
2823
121k
        free (gs->dash_array);
2824
121k
        gs->dash_array = strdup (p);
2825
121k
    }
2826
2827
2.56M
    get_float_or_percent_attribute (element, "stroke-dashoffset", svg_render->width, &gs->dash_offset);
2828
2.56M
    update_dash (svg_render, element);
2829
2830
    /* Some elements may need the bounding box of the element thay are
2831
     * applied to.  As this recursively calls render_element on the
2832
     * same element while we are in render_element and setting up the
2833
     * graphics state, we check gs->mode to avoid re-entering the
2834
     * compute bbox code. The GS_COMPUTE_MODE flag is also used by
2835
     * render functions to ignore patterns and strokes (SVG spec
2836
     * ignores stroke with in bbox calculations) and just use a solid
2837
     * color.
2838
     */
2839
2.56M
    if (gs->mode == GS_RENDER && need_bbox (svg_render, element)) {
2840
48.5k
        cairo_surface_t *recording = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
2841
48.5k
        cairo_t *old_cr = svg_render->cr;
2842
48.5k
        svg_render->cr = cairo_create (recording);
2843
48.5k
        gs_mode_t old_mode = gs->mode;
2844
48.5k
        gs->mode = GS_COMPUTE_BBOX;
2845
        /* To avoid recursing back into this function, we call the
2846
         * element directory then use render_element_tree to render
2847
         * the children */
2848
48.5k
        call_element (svg_render, element, FALSE);
2849
48.5k
        render_element_tree (svg_render, element, NULL, TRUE);
2850
48.5k
        if (element->type == CONTAINER_ELEMENT)
2851
3.34k
            call_element (svg_render, element, TRUE);
2852
48.5k
        gs->mode = old_mode;
2853
48.5k
        cairo_destroy (svg_render->cr);
2854
48.5k
        svg_render->cr = old_cr;
2855
48.5k
        cairo_recording_surface_ink_extents (recording,
2856
48.5k
                                             &gs->bbox.x,
2857
48.5k
                                             &gs->bbox.y,
2858
48.5k
                                             &gs->bbox.width,
2859
48.5k
                                             &gs->bbox.height);
2860
48.5k
        cairo_surface_destroy (recording);
2861
48.5k
    }
2862
2863
    /* clip-path may require bbox */
2864
2.56M
    p = get_attribute (element, "clip-path");
2865
2.56M
    if (p && strncmp (p, "url", 3) == 0) {
2866
1.31M
        element = lookup_url_element (svg_render, p);
2867
1.31M
        if (element) {
2868
1.31M
            gs_mode_t old_mode = gs->mode;
2869
1.31M
            gs->mode = GS_CLIP;
2870
1.31M
            render_element_tree (svg_render, element, NULL, FALSE);
2871
1.31M
            cairo_set_fill_rule (svg_render->cr, gs->clip_rule);
2872
1.31M
            cairo_clip (svg_render->cr);
2873
1.31M
            gs->mode = old_mode;
2874
1.31M
        }
2875
1.31M
    }
2876
2.56M
}
2877
2878
static void
2879
save_graphics_state (cairo_svg_glyph_render_t *svg_render)
2880
2.61M
{
2881
2.61M
    cairo_svg_graphics_state_t *gs;
2882
2883
2.61M
    cairo_save (svg_render->cr);
2884
2885
2.61M
    gs = _cairo_calloc (sizeof (cairo_svg_graphics_state_t));
2886
2.61M
    gs->fill           = svg_render->graphics_state->fill;
2887
2.61M
    gs->stroke         = svg_render->graphics_state->stroke;
2888
2.61M
    gs->color          = svg_render->graphics_state->color;
2889
2.61M
    gs->fill_opacity   = svg_render->graphics_state->fill_opacity;
2890
2.61M
    gs->stroke_opacity = svg_render->graphics_state->stroke_opacity;
2891
2.61M
    gs->opacity        = svg_render->graphics_state->opacity;
2892
2.61M
    gs->fill_rule      = svg_render->graphics_state->fill_rule;
2893
2.61M
    gs->clip_rule      = svg_render->graphics_state->clip_rule;
2894
2.61M
    gs->clip_path      = NULL;
2895
2.61M
    gs->dash_array     = NULL;
2896
2.61M
    if (svg_render->graphics_state->dash_array)
2897
127k
        gs->dash_array = strdup (svg_render->graphics_state->dash_array);
2898
2.61M
    gs->dash_offset    = svg_render->graphics_state->dash_offset;
2899
2.61M
    gs->mode           = svg_render->graphics_state->mode;
2900
2.61M
    gs->bbox           = svg_render->graphics_state->bbox;
2901
2.61M
    gs->next           = svg_render->graphics_state;
2902
2.61M
    svg_render->graphics_state = gs;
2903
2.61M
}
2904
2905
static void
2906
restore_graphics_state (cairo_svg_glyph_render_t *svg_render)
2907
2.61M
{
2908
2.61M
    cairo_svg_graphics_state_t *gs;
2909
2910
2.61M
    gs = svg_render->graphics_state;
2911
2.61M
    svg_render->graphics_state = gs->next;
2912
2.61M
    if (gs->clip_path)
2913
0
        cairo_path_destroy (gs->clip_path);
2914
2.61M
    free (gs->dash_array);
2915
2.61M
    free (gs);
2916
2917
2.61M
    cairo_restore (svg_render->cr);
2918
2.61M
}
2919
2920
/* render function returns TRUE if render_element_tree() is to render
2921
 * the child nodes, FALSE if render_element_tree() is to skip the
2922
 * child nodes.
2923
 */
2924
struct render_func {
2925
    const char *tag;
2926
    cairo_bool_t (*render) (cairo_svg_glyph_render_t *, cairo_svg_element_t *, cairo_bool_t);
2927
};
2928
2929
/* Must be sorted */
2930
static const struct render_func render_funcs[] = {
2931
    { "circle", render_element_circle },
2932
    { "clipPath", render_element_clip_path },
2933
    { "defs", NULL },
2934
    { "desc", NULL },
2935
    { "ellipse", render_element_ellipse },
2936
    { "g", render_element_g },
2937
    { "image", render_element_image },
2938
    { "line", render_element_line },
2939
    { "linearGradient", render_element_linear_gradient },
2940
    { "metadata", NULL },
2941
    { "path", render_element_path },
2942
    { "polygon", render_element_polyline },
2943
    { "polyline", render_element_polyline },
2944
    { "radialGradient", render_element_radial_gradient },
2945
    { "rect", render_element_rect },
2946
    { "stop", render_element_stop },
2947
    { "svg", render_element_svg },
2948
    { "title", NULL },
2949
    { "use", render_element_use },
2950
};
2951
2952
static int
2953
_render_func_compare (const void *a, const void *b)
2954
14.6M
{
2955
14.6M
    const struct render_func *render_func_a = a;
2956
14.6M
    const struct render_func *render_func_b = b;
2957
2958
14.6M
    return strcmp (render_func_a->tag, render_func_b->tag);
2959
14.6M
}
2960
2961
static cairo_bool_t
2962
call_element (cairo_svg_glyph_render_t *svg_render,
2963
              cairo_svg_element_t      *element,
2964
              cairo_bool_t              end_tag)
2965
3.40M
{
2966
3.40M
    const struct render_func *func;
2967
3.40M
    struct render_func key;
2968
3.40M
    cairo_bool_t recurse = FALSE;
2969
2970
3.40M
    key.tag = element->tag;
2971
3.40M
    key.render = NULL;
2972
3.40M
    func = bsearch (&key,
2973
3.40M
                    render_funcs,
2974
3.40M
                    ARRAY_LENGTH (render_funcs),
2975
3.40M
                    sizeof (struct render_func),
2976
3.40M
                    _render_func_compare);
2977
3.40M
    if (func) {
2978
3.08M
        if (func->render) {
2979
3.08M
            recurse = func->render (svg_render, element, end_tag);
2980
3.08M
        }
2981
3.08M
    } else {
2982
319k
        print_warning (svg_render, "Unsupported element: %s", element->tag);
2983
319k
    }
2984
2985
3.40M
    return recurse;
2986
3.40M
}
2987
2988
static cairo_bool_t
2989
render_element (cairo_svg_glyph_render_t *svg_render,
2990
                cairo_svg_element_t      *element,
2991
                cairo_bool_t              end_tag,
2992
                cairo_svg_element_t      *display_element)
2993
3.41M
{
2994
3.41M
    cairo_bool_t recurse = FALSE;
2995
3.41M
    cairo_svg_graphics_state_t *gs;
2996
2997
    /* Ignore elements if we have not seen "<svg>". Ignore
2998
      * "<svg>" if we have seen it */
2999
3.41M
    if (svg_render->view_port_set) {
3000
3.40M
        if (string_equal (element->tag, "svg"))
3001
52.1k
            return FALSE;
3002
3.40M
    } else {
3003
4.51k
        if (!string_equal (element->tag, "svg"))
3004
2.05k
            return FALSE;
3005
4.51k
    }
3006
3007
3.35M
    if (element->type == EMPTY_ELEMENT ||
3008
1.48M
        (element->type == CONTAINER_ELEMENT && !end_tag))
3009
2.61M
    {
3010
2.61M
        save_graphics_state (svg_render);
3011
2.61M
        update_graphics_state (svg_render, element);
3012
2.61M
    }
3013
3014
3.35M
    gs = svg_render->graphics_state;
3015
3.35M
    if (gs->mode == GS_NO_RENDER && element == display_element)
3016
0
        gs->mode = GS_RENDER;
3017
3018
3.35M
    recurse = call_element (svg_render, element, end_tag);
3019
3020
3.35M
    if (element->type == EMPTY_ELEMENT ||
3021
1.48M
        (element->type == CONTAINER_ELEMENT && end_tag))
3022
2.61M
    {
3023
2.61M
        restore_graphics_state (svg_render);
3024
2.61M
    }
3025
3026
3.35M
    return recurse;
3027
3.41M
}
3028
3029
2.95M
#define MAX_DEPTH 100
3030
3031
static void
3032
render_element_tree (cairo_svg_glyph_render_t *svg_render,
3033
                     cairo_svg_element_t      *element,
3034
                     cairo_svg_element_t      *display_element,
3035
                     cairo_bool_t              children_only)
3036
2.96M
{
3037
2.96M
    if (!element)
3038
6.51k
        return;
3039
3040
    /* Avoid circular references by limiting the number of recursive
3041
     * calls to this function. */
3042
2.95M
    if (svg_render->render_element_tree_depth > MAX_DEPTH)
3043
158k
        return;
3044
3045
2.80M
    svg_render->render_element_tree_depth++;
3046
2.80M
    if (element->type == EMPTY_ELEMENT && !children_only) {
3047
1.92M
        render_element (svg_render, element, FALSE, display_element);
3048
3049
1.92M
    } else if (element->type == CONTAINER_ELEMENT) {
3050
749k
        int num_elems;
3051
749k
        cairo_bool_t recurse = TRUE;;
3052
3053
749k
        if (!children_only)
3054
743k
            recurse = render_element (svg_render, element, FALSE, display_element);
3055
3056
        /* We only render the children if the parent returned
3057
         * success. This is how we avoid rendering non display
3058
         * elements like gradients, <defs>, and anything not
3059
         * implemented. */
3060
749k
        if (recurse) {
3061
740k
            num_elems = _cairo_array_num_elements (&element->children);
3062
2.16M
            for (int i = 0; i < num_elems; i++) {
3063
1.41M
                cairo_svg_element_t *child;
3064
1.41M
                _cairo_array_copy_element (&element->children, i, &child);
3065
1.41M
                render_element_tree (svg_render, child, display_element, FALSE);
3066
1.41M
            }
3067
740k
        }
3068
3069
749k
        if (!children_only)
3070
743k
            render_element (svg_render, element, TRUE, display_element);
3071
749k
    }
3072
2.80M
    svg_render->render_element_tree_depth--;
3073
2.80M
}
3074
3075
static void
3076
render_element_tree_id (cairo_svg_glyph_render_t *svg_render,
3077
                        const char               *element_id)
3078
2.16k
{
3079
2.16k
    cairo_svg_element_t *glyph_element = NULL;
3080
3081
2.16k
    if (element_id)
3082
0
        glyph_element = lookup_element (svg_render, element_id);
3083
3084
2.16k
    if (glyph_element)
3085
0
        svg_render->graphics_state->mode = GS_NO_RENDER;
3086
2.16k
    else
3087
2.16k
        svg_render->graphics_state->mode = GS_RENDER;
3088
3089
2.16k
    render_element_tree (svg_render, svg_render->tree, glyph_element, TRUE);
3090
2.16k
}
3091
3092
cairo_status_t
3093
_cairo_render_svg_glyph (const char           *svg_document,
3094
                         unsigned long         first_glyph,
3095
                         unsigned long         last_glyph,
3096
                         unsigned long         glyph,
3097
                         double                units_per_em,
3098
                         FT_Color             *palette,
3099
                         int                   num_palette_entries,
3100
                         cairo_t              *cr,
3101
                         cairo_pattern_t      *foreground_source,
3102
       cairo_bool_t         *foreground_source_used)
3103
2.22k
{
3104
2.22k
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
3105
3106
2.22k
    cairo_svg_glyph_render_t *svg_render = _cairo_calloc (sizeof (cairo_svg_glyph_render_t));
3107
2.22k
    if (unlikely (svg_render == NULL))
3108
0
  return _cairo_error (CAIRO_STATUS_NO_MEMORY);
3109
3110
2.22k
    svg_render->tree = NULL;
3111
2.22k
    svg_render->ids = _cairo_hash_table_create (_element_id_equal);
3112
2.22k
    if (unlikely (svg_render->ids == NULL)) {
3113
0
        free (svg_render);
3114
0
  return _cairo_error (CAIRO_STATUS_NO_MEMORY);
3115
0
    }
3116
3117
2.22k
    svg_render->debug = 0;
3118
2.22k
    const char *s = getenv ("CAIRO_DEBUG_SVG_RENDER");
3119
2.22k
    if (s) {
3120
0
        if (strlen (s) > 0)
3121
0
            svg_render->debug = atoi (s);
3122
0
        else
3123
0
            svg_render->debug = SVG_RENDER_ERROR;
3124
0
    }
3125
3126
2.22k
    svg_render->cr = cr;
3127
2.22k
    svg_render->units_per_em = units_per_em;
3128
2.22k
    svg_render->build_pattern.paint_server = NULL;
3129
2.22k
    svg_render->build_pattern.pattern = NULL;
3130
2.22k
    svg_render->build_pattern.type = BUILD_PATTERN_NONE;
3131
2.22k
    svg_render->render_element_tree_depth = 0;
3132
2.22k
    svg_render->view_port_set = FALSE;
3133
2.22k
    svg_render->num_palette_entries = num_palette_entries;
3134
2.22k
    svg_render->palette = palette;
3135
3136
2.22k
    svg_render->foreground_marker = _cairo_pattern_create_foreground_marker ();
3137
2.22k
    svg_render->foreground_source = cairo_pattern_reference (foreground_source);;
3138
2.22k
    svg_render->foreground_source_used = FALSE;
3139
3140
2.22k
    init_graphics_state (svg_render);
3141
3142
2.22k
    print_info (svg_render, "Glyph ID: %ld", glyph);
3143
2.22k
    print_info (svg_render, "Palette Entries: %d", num_palette_entries);
3144
2.22k
    print_info (svg_render, "Units per EM: %f", units_per_em);
3145
2.22k
    print_info (svg_render, "SVG Document:\n%s\n", svg_document);
3146
3147
    /* First parse elements into a tree and populate ids hash table */
3148
2.22k
    if (!parse_svg (svg_render, svg_document)) {
3149
66
        print_error (svg_render, "Parse SVG document failed");
3150
66
        status = CAIRO_STATUS_SVG_FONT_ERROR;
3151
66
        goto cleanup;
3152
66
    }
3153
3154
#if SVG_RENDER_PRINT_FUNCTIONS
3155
    printf("\nTREE\n");
3156
    if (svg_render->tree) {
3157
        print_element (svg_render->tree, TRUE, 0);
3158
        printf("\n");
3159
    }
3160
#endif
3161
3162
    /* Next, render glyph */
3163
2.16k
    if (first_glyph == last_glyph) {
3164
        /* Render whole document */
3165
2.16k
        render_element_tree_id (svg_render, NULL);
3166
2.16k
    } else {
3167
        /* Render element with id "glyphID" where ID is glyph number. */
3168
3169
0
        char glyph_id[30];
3170
0
        snprintf(glyph_id, sizeof(glyph_id), "#glyph%ld", glyph);
3171
0
        render_element_tree_id (svg_render, glyph_id);
3172
0
    }
3173
3174
2.22k
  cleanup:
3175
2.22k
    if (svg_render->build_pattern.pattern)
3176
0
        cairo_pattern_destroy (svg_render->build_pattern.pattern);
3177
3178
2.22k
    if (svg_render->tree)
3179
2.16k
        free_elements (svg_render, svg_render->tree);
3180
3181
6.14k
    while (svg_render->graphics_state)
3182
3.91k
        restore_graphics_state (svg_render);
3183
3184
2.22k
    cairo_pattern_destroy (svg_render->foreground_marker);
3185
2.22k
    cairo_pattern_destroy (svg_render->foreground_source);
3186
2.22k
    *foreground_source_used = svg_render->foreground_source_used;
3187
3188
    /* The hash entry for each element with an id is removed by
3189
     * free_elements() */
3190
2.22k
    _cairo_hash_table_destroy (svg_render->ids);
3191
3192
2.22k
    free (svg_render);
3193
3194
2.22k
    return status;
3195
2.16k
}
3196
3197
#ifdef DEBUG_SVG_RENDER
3198
3199
/**
3200
 * _cairo_debug_svg_render:
3201
 *
3202
 * Debug function for cairo-svg-glyph-render.c. Allows invoking the renderer from outside
3203
 * cairo to test with SVG documents, and to facilitate comparison with librsvg rendering.
3204
 * The viewport is .
3205
 *
3206
 * @cr: render target
3207
 * @svg_document: SVG Document
3208
 * @element: element within svg_document to render (eg "#glyph8"), or NULL to render entire document.
3209
 * @debug_level: 0 - quiet, 1 - print errors, 2 - print warnings, 3 - info
3210
 * @return TRUE on success, ie no errors, FALSE if error
3211
 **/
3212
cairo_public cairo_bool_t
3213
_cairo_debug_svg_render (cairo_t       *cr,
3214
                         const char    *svg_document,
3215
                         const char    *element,
3216
                         double         units_per_em,
3217
                         int            debug_level);
3218
3219
cairo_bool_t
3220
_cairo_debug_svg_render (cairo_t       *cr,
3221
                         const char    *svg_document,
3222
                         const char    *element,
3223
                         double         units_per_em,
3224
                         int            debug_level)
3225
2.22k
{
3226
2.22k
    cairo_status_t status;
3227
2.22k
    cairo_bool_t foreground_source_used;
3228
2.22k
    cairo_pattern_t *foreground = _cairo_pattern_create_foreground_marker ();
3229
3230
2.22k
    status = _cairo_render_svg_glyph (svg_document,
3231
2.22k
              1, 1, 1,
3232
2.22k
              units_per_em,
3233
2.22k
              NULL, 0,
3234
2.22k
              cr,
3235
2.22k
              foreground,
3236
2.22k
              &foreground_source_used);
3237
2.22k
    cairo_pattern_destroy (foreground);
3238
3239
2.22k
    return status == CAIRO_STATUS_SUCCESS;
3240
2.22k
}
3241
3242
#endif /* DEBUG_SVG_RENDER */
3243
3244
#endif /* HAVE_FT_SVG_DOCUMENT */