Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpsglyphs.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
 */
15
16
17
/* XPS interpreter - text drawing support */
18
19
#include "ghostxps.h"
20
#include <stdlib.h>
21
22
66
#define XPS_TEXT_BUFFER_SIZE 300
23
24
typedef struct xps_text_buffer_s xps_text_buffer_t;
25
26
struct xps_text_buffer_s
27
{
28
    int count;
29
    float x[XPS_TEXT_BUFFER_SIZE + 1];
30
    float y[XPS_TEXT_BUFFER_SIZE + 1];
31
    gs_glyph g[XPS_TEXT_BUFFER_SIZE];
32
};
33
34
static inline int unhex(int c)
35
64
{
36
64
    if (c >= '0' && c <= '9')
37
34
        return c - '0';
38
30
    if (c >= 'A' && c <= 'Z')
39
30
        return c - 'A' + 10;
40
0
    return c - 'a' + 10;
41
30
}
42
43
void
44
xps_debug_path(xps_context_t *ctx)
45
0
{
46
0
    segment *seg;
47
0
    curve_segment *cseg;
48
49
0
    seg = (segment*)ctx->pgs->path->first_subpath;
50
0
    while (seg)
51
0
    {
52
0
        switch (seg->type)
53
0
        {
54
0
        case s_start:
55
0
            dmprintf2(ctx->memory, "%g %g moveto\n",
56
0
                      fixed2float(seg->pt.x) * 0.001,
57
0
                      fixed2float(seg->pt.y) * 0.001);
58
0
            break;
59
0
        case s_line:
60
0
            dmprintf2(ctx->memory, "%g %g lineto\n",
61
0
                      fixed2float(seg->pt.x) * 0.001,
62
0
                      fixed2float(seg->pt.y) * 0.001);
63
0
            break;
64
0
        case s_line_close:
65
0
            dmputs(ctx->memory, "closepath\n");
66
0
            break;
67
0
        case s_curve:
68
0
            cseg = (curve_segment*)seg;
69
0
            dmprintf6(ctx->memory, "%g %g %g %g %g %g curveto\n",
70
0
                      fixed2float(cseg->p1.x) * 0.001,
71
0
                      fixed2float(cseg->p1.y) * 0.001,
72
0
                      fixed2float(cseg->p2.x) * 0.001,
73
0
                      fixed2float(cseg->p2.y) * 0.001,
74
0
                      fixed2float(seg->pt.x) * 0.001,
75
0
                      fixed2float(seg->pt.y) * 0.001);
76
0
            break;
77
0
        }
78
0
        seg = seg->next;
79
0
    }
80
0
}
81
82
/*
83
 * Some fonts in XPS are obfuscated by XOR:ing the first 32 bytes of the
84
 * data with the GUID in the fontname.
85
 */
86
static void
87
xps_deobfuscate_font_resource(xps_context_t *ctx, xps_part_t *part)
88
2
{
89
2
    byte buf[33];
90
2
    byte key[16];
91
2
    char *p;
92
2
    int i;
93
94
    /* Ensure the part has at least 32 bytes we can write */
95
2
    if (part->size < 32)
96
0
    {
97
0
        gs_warn("obfuscated font part is too small");
98
0
        return;
99
0
    }
100
101
2
    p = strrchr(part->name, '/');
102
2
    if (!p)
103
0
        p = part->name;
104
105
76
    for (i = 0; i < 32 && *p; p++)
106
74
    {
107
74
        if (isxdigit(*p))
108
64
            buf[i++] = *p;
109
74
    }
110
2
    buf[i] = 0;
111
112
2
    if (i != 32)
113
0
    {
114
0
        gs_warn("cannot extract GUID from obfuscated font part name");
115
0
        return;
116
0
    }
117
118
34
    for (i = 0; i < 16; i++)
119
32
        key[i] = unhex(buf[i*2+0]) * 16 + unhex(buf[i*2+1]);
120
121
34
    for (i = 0; i < 16; i++)
122
32
    {
123
32
        part->data[i] ^= key[15-i];
124
32
        part->data[i+16] ^= key[15-i];
125
32
    }
126
2
}
127
128
static void
129
xps_select_best_font_encoding(xps_font_t *font)
130
2
{
131
2
    static struct { int pid, eid; } xps_cmap_list[] =
132
2
    {
133
2
        { 3, 10 },      /* Unicode with surrogates */
134
2
        { 3, 1 },       /* Unicode without surrogates */
135
2
        { 3, 5 },       /* Wansung */
136
2
        { 3, 4 },       /* Big5 */
137
2
        { 3, 3 },       /* Prc */
138
2
        { 3, 2 },       /* ShiftJis */
139
2
        { 3, 0 },       /* Symbol */
140
2
        { 1, 0 },
141
2
        { -1, -1 },
142
2
    };
143
144
2
    int i, k, n, pid, eid;
145
146
2
    n = xps_count_font_encodings(font);
147
14
    for (k = 0; xps_cmap_list[k].pid != -1; k++)
148
14
    {
149
40
        for (i = 0; i < n; i++)
150
28
        {
151
28
            xps_identify_font_encoding(font, i, &pid, &eid);
152
28
            if (pid == xps_cmap_list[k].pid && eid == xps_cmap_list[k].eid)
153
2
            {
154
2
                if (xps_select_font_encoding(font, i))
155
2
                    return;
156
2
            }
157
28
        }
158
14
    }
159
160
0
    gs_warn("could not find a suitable cmap");
161
0
}
162
163
/*
164
 * Call text drawing primitives.
165
 */
166
167
/* See if the device can handle Text Rendering Mode natively
168
 * returns < 0 for error, 0 for can't handle it, 1 for can handle it
169
 */
170
static int
171
PreserveTrMode(xps_context_t *ctx)
172
0
{
173
174
0
    gs_c_param_list list;
175
0
    dev_param_req_t request;
176
0
    gs_param_name ParamName = "PreserveTrMode";
177
0
    gs_param_typed_value Param;
178
0
    char *data;
179
0
    gs_gstate *pgs = ctx->pgs;
180
0
    int code = 0;
181
182
    /* Interrogate the device to see if it supports Text Rendering Mode */
183
0
    data = (char *)gs_alloc_bytes(ctx->memory, 15, "temporary special_op string");
184
0
    if (data == NULL)
185
0
        return_error(gs_error_VMerror);
186
187
0
    memset(data, 0x00, 15);
188
0
    memcpy(data, "PreserveTrMode", 15);
189
0
    gs_c_param_list_write(&list, ctx->memory);
190
    /* Make a null object so that the param list won't check for requests */
191
0
    Param.type = gs_param_type_null;
192
0
    list.procs->xmit_typed((gs_param_list *)&list, ParamName, &Param);
193
    /* Stuff the data into a structure for passing to the spec_op */
194
0
    request.Param = data;
195
0
    request.list = &list;
196
197
0
    code = dev_proc(gs_currentdevice(pgs), dev_spec_op)(gs_currentdevice(pgs), gxdso_get_dev_param,
198
0
                                                        &request, sizeof(dev_param_req_t));
199
200
0
    if (code != gs_error_undefined) {
201
        /* The parameter is present in the device, now we need to see its value */
202
0
        gs_c_param_list_read(&list);
203
0
        list.procs->xmit_typed((gs_param_list *)&list, ParamName, &Param);
204
205
0
        if (Param.type != gs_param_type_bool) {
206
            /* This really shoudn't happen, but its best to be sure */
207
0
            gs_free_object(ctx->memory, data,"temporary special_op string");
208
0
            gs_c_param_list_release(&list);
209
0
            return gs_error_typecheck;
210
0
        }
211
212
0
        if (Param.value.b) {
213
0
            code = 1;
214
0
        } else {
215
0
            code = 0;
216
0
        }
217
0
    } else {
218
0
        code = 0;
219
0
    }
220
0
    gs_free_object(ctx->memory, data,"temporary special_op string");
221
0
    gs_c_param_list_release(&list);
222
0
    return code;
223
0
}
224
225
static int
226
xps_flush_text_buffer(xps_context_t *ctx, xps_font_t *font,
227
        xps_text_buffer_t *buf, int is_charpath)
228
14
{
229
14
    gs_text_params_t params;
230
14
    gs_text_enum_t *textenum;
231
14
    float initial_x, x = buf->x[0];
232
14
    float initial_y, y = buf->y[0];
233
14
    int code;
234
14
    int i;
235
14
    gs_gstate_color saved;
236
237
    /* dmprintf1(ctx->memory, "flushing text buffer (%d glyphs)\n", buf->count); */
238
239
14
    initial_x = x;
240
14
    initial_y = y;
241
242
14
    params.operation = TEXT_FROM_GLYPHS | TEXT_REPLACE_WIDTHS;
243
14
    if (is_charpath)
244
0
        params.operation |= TEXT_DO_FALSE_CHARPATH;
245
14
    else
246
14
        params.operation |= TEXT_DO_DRAW;
247
14
    params.data.glyphs = buf->g;
248
14
    params.size = buf->count;
249
14
    params.x_widths = buf->x + 1;
250
14
    params.y_widths = buf->y + 1;
251
14
    params.widths_size = buf->count;
252
253
80
    for (i = 0; i < buf->count; i++)
254
66
    {
255
66
        buf->x[i] = buf->x[i] - x;
256
66
        buf->y[i] = buf->y[i] - y;
257
66
        x += buf->x[i];
258
66
        y += buf->y[i];
259
66
    }
260
14
    buf->x[buf->count] = 0;
261
14
    buf->y[buf->count] = 0;
262
263
14
    if (ctx->pgs->text_rendering_mode == 2 ) {
264
0
        gs_text_enum_t *Tr_textenum;
265
0
        gs_text_params_t Tr_params;
266
267
        /* Save the 'stroke' colour, which XPS doesn't normally use, or set.
268
         * This isn't used by rendering, but it is used by the pdfwrite
269
         * device family, and must be correct for stroking text rendering
270
         * modes.
271
         */
272
0
        saved = ctx->pgs->color[1];
273
        /* And now make the stroke color the same as the fill color */
274
0
        ctx->pgs->color[1] = ctx->pgs->color[0];
275
276
0
        if (PreserveTrMode(ctx) != 1) {
277
            /* The device doesn't want (or can't handle) Text Rendering Modes
278
             * So start by doing a 'charpath stroke' to embolden the text
279
             */
280
0
            gs_moveto(ctx->pgs, initial_x, initial_y);
281
0
            Tr_params.operation = TEXT_FROM_GLYPHS | TEXT_REPLACE_WIDTHS | TEXT_DO_TRUE_CHARPATH;
282
0
            Tr_params.data.glyphs = params.data.glyphs;
283
0
            Tr_params.size = params.size;
284
0
            Tr_params.x_widths = params.x_widths;
285
0
            Tr_params.y_widths = params.y_widths;
286
0
            Tr_params.widths_size = params.widths_size;
287
288
0
            code = gs_text_begin(ctx->pgs, &Tr_params, ctx->memory, &Tr_textenum);
289
0
            if (code != 0)
290
0
                return gs_throw1(-1, "cannot gs_text_begin() (%d)", code);
291
292
0
            code = gs_text_process(Tr_textenum);
293
294
0
            if (code != 0)
295
0
                return gs_throw1(-1, "cannot gs_text_process() (%d)", code);
296
297
0
            gs_text_release(ctx->pgs, Tr_textenum, "gslt font render");
298
299
0
            gs_stroke(ctx->pgs);
300
0
        }
301
0
    }
302
303
14
    gs_moveto(ctx->pgs, initial_x, initial_y);
304
14
    code = gs_text_begin(ctx->pgs, &params, ctx->memory, &textenum);
305
14
    if (code != 0)
306
0
        return gs_throw1(-1, "cannot gs_text_begin() (%d)", code);
307
308
14
    code = gs_text_process(textenum);
309
310
14
    if (code != 0)
311
0
        return gs_throw1(-1, "cannot gs_text_process() (%d)", code);
312
313
14
    gs_text_release(ctx->pgs, textenum, "gslt font render");
314
315
14
    buf->count = 0;
316
317
14
    if (ctx->pgs->text_rendering_mode == 2 ) {
318
        /* Restore the stroke colour which we overwrote above */
319
0
        ctx->pgs->color[1] = saved;
320
0
    }
321
14
    return 0;
322
14
}
323
324
/*
325
 * Parse and draw an XPS <Glyphs> element.
326
 *
327
 * Indices syntax:
328
329
 GlyphIndices   = GlyphMapping ( ";" GlyphMapping )
330
 GlyphMapping   = ( [ClusterMapping] GlyphIndex ) [GlyphMetrics]
331
 ClusterMapping = "(" ClusterCodeUnitCount [":" ClusterGlyphCount] ")"
332
 ClusterCodeUnitCount   = * DIGIT
333
 ClusterGlyphCount      = * DIGIT
334
 GlyphIndex     = * DIGIT
335
 GlyphMetrics   = "," AdvanceWidth ["," uOffset ["," vOffset]]
336
 AdvanceWidth   = ["+"] RealNum
337
 uOffset        = ["+" | "-"] RealNum
338
 vOffset        = ["+" | "-"] RealNum
339
 RealNum        = ((DIGIT ["." DIGIT]) | ("." DIGIT)) [Exponent]
340
 Exponent       = ( ("E"|"e") ("+"|"-") DIGIT )
341
342
 */
343
344
static char *
345
xps_parse_digits(char *s, int *digit)
346
66
{
347
66
    *digit = 0;
348
198
    while (*s >= '0' && *s <= '9')
349
132
    {
350
132
        *digit = *digit * 10 + (*s - '0');
351
132
        s ++;
352
132
    }
353
66
    return s;
354
66
}
355
356
static char *
357
xps_parse_real_num(char *s, float *number, bool *number_parsed)
358
18
{
359
18
    char *tail;
360
18
    float v;
361
18
    v = (float)strtod(s, &tail);
362
18
    *number_parsed = tail != s;
363
18
    if (*number_parsed)
364
18
        *number = v;
365
18
    return tail;
366
18
}
367
368
static char *
369
xps_parse_cluster_mapping(char *s, int *code_count, int *glyph_count)
370
66
{
371
66
    if (*s == '(')
372
0
        s = xps_parse_digits(s + 1, code_count);
373
66
    if (*s == ':')
374
0
        s = xps_parse_digits(s + 1, glyph_count);
375
66
    if (*s == ')')
376
0
        s ++;
377
66
    return s;
378
66
}
379
380
static char *
381
xps_parse_glyph_index(char *s, int *glyph_index)
382
66
{
383
66
    if (*s >= '0' && *s <= '9')
384
66
        s = xps_parse_digits(s, glyph_index);
385
66
    return s;
386
66
}
387
388
static char *
389
xps_parse_glyph_advance(char *s, float *advance, int bidi_level)
390
52
{
391
52
    bool advance_overridden = false;
392
393
52
    if (*s == ',') {
394
18
        s = xps_parse_real_num(s + 1, advance, &advance_overridden);
395
396
        /*
397
         * If the advance has been derived from the font and not
398
         * overridden by the Indices Attribute the sign has already
399
         * been direction adjusted.
400
         */
401
402
18
        if (advance_overridden && (bidi_level & 1))
403
0
            *advance *= -1;
404
18
    }
405
52
    return s;
406
52
}
407
408
static char *
409
xps_parse_glyph_offsets(char *s, float *uofs, float *vofs)
410
52
{
411
52
    bool offsets_overridden; /* not used */
412
413
52
    if (*s == ',')
414
0
        s = xps_parse_real_num(s + 1, uofs, &offsets_overridden);
415
52
    if (*s == ',')
416
0
        s = xps_parse_real_num(s + 1, vofs, &offsets_overridden);
417
52
    return s;
418
52
}
419
420
static char *
421
xps_parse_glyph_metrics(char *s, float *advance, float *uofs, float *vofs, int bidi_level)
422
52
{
423
52
    s = xps_parse_glyph_advance(s, advance, bidi_level);
424
52
    s = xps_parse_glyph_offsets(s, uofs, vofs);
425
52
    return s;
426
52
}
427
428
/*
429
 * Parse unicode and indices strings and encode glyphs.
430
 * Calculate metrics for positioning.
431
 */
432
static int
433
xps_parse_glyphs_imp(xps_context_t *ctx, xps_font_t *font, float size,
434
        float originx, float originy, int is_sideways, int bidi_level,
435
        char *indices, char *unicode, int is_charpath, int sim_bold)
436
14
{
437
14
    xps_text_buffer_t buf;
438
14
    xps_glyph_metrics_t mtx;
439
14
    float x = originx;
440
14
    float y = originy;
441
14
    char *us = unicode;
442
14
    char *is = indices;
443
14
    int un = 0;
444
14
    int code;
445
446
14
    buf.count = 0;
447
448
14
    if (!unicode && !indices)
449
0
        return gs_throw(-1, "no text in glyphs element");
450
451
14
    if (us)
452
14
    {
453
14
        if (us[0] == '{' && us[1] == '}')
454
0
            us = us + 2;
455
14
        un = strlen(us);
456
14
    }
457
458
80
    while ((us && un > 0) || (is && *is))
459
66
    {
460
66
        int char_code = '?';
461
66
        int code_count = 1;
462
66
        int glyph_count = 1;
463
464
66
        if (is && *is)
465
66
        {
466
66
            is = xps_parse_cluster_mapping(is, &code_count, &glyph_count);
467
66
        }
468
469
66
        if (code_count < 1)
470
0
            code_count = 1;
471
66
        if (glyph_count < 1)
472
0
            glyph_count = 1;
473
        /* HACK. Impose an upper limit on code_count and glyph_count as a
474
         * workaround for Bug 709192 Issue 558. An Indices value of
475
         * (1:50000000) will cause us to generate 50000000 chars to
476
         * render. */
477
66
        if (code_count > 9)
478
0
            code_count = 9;
479
66
        if (glyph_count > 9)
480
0
            glyph_count = 9;
481
482
132
        while (code_count--)
483
66
        {
484
66
            if (us && un > 0)
485
66
            {
486
66
                int t = xps_utf8_to_ucs(&char_code, us, un);
487
66
                if (t < 0)
488
0
                    return gs_rethrow(-1, "error decoding UTF-8 string");
489
66
                us += t; un -= t;
490
66
            }
491
66
        }
492
493
132
        while (glyph_count--)
494
66
        {
495
66
            int glyph_index = -1;
496
66
            float u_offset = 0.0;
497
66
            float v_offset = 0.0;
498
66
            float advance;
499
500
66
            if (is && *is)
501
66
                is = xps_parse_glyph_index(is, &glyph_index);
502
503
66
            if (glyph_index == -1)
504
0
                glyph_index = xps_encode_font_char(font, char_code);
505
506
66
            xps_measure_font_glyph(ctx, font, glyph_index, &mtx);
507
66
            if (is_sideways)
508
0
                advance = mtx.vadv * 100.0;
509
66
            else if (bidi_level & 1)
510
0
                advance = -mtx.hadv * 100.0;
511
66
            else
512
66
                advance = mtx.hadv * 100.0;
513
514
66
            if (is && *is)
515
52
            {
516
52
                is = xps_parse_glyph_metrics(is, &advance, &u_offset, &v_offset, bidi_level);
517
52
                if (*is == ';')
518
52
                    is ++;
519
52
            }
520
521
66
            if (bidi_level & 1)
522
0
                u_offset = -mtx.hadv * 100 - u_offset;
523
524
66
            u_offset = u_offset * 0.01 * size;
525
66
            v_offset = v_offset * 0.01 * size;
526
527
            /* Adjust glyph offset and advance width for emboldening */
528
66
            if (sim_bold)
529
0
            {
530
0
                advance *= 1.02f;
531
0
                u_offset += 0.01 * size;
532
0
                v_offset += 0.01 * size;
533
0
            }
534
535
66
            if (buf.count == XPS_TEXT_BUFFER_SIZE)
536
0
            {
537
0
                code = xps_flush_text_buffer(ctx, font, &buf, is_charpath);
538
0
                if (code)
539
0
                    return gs_rethrow(code, "cannot flush buffered text");
540
0
            }
541
542
66
            if (is_sideways)
543
0
            {
544
0
                buf.x[buf.count] = x + u_offset + (mtx.vorg * size);
545
0
                buf.y[buf.count] = y - v_offset + (mtx.hadv * 0.5 * size);
546
0
            }
547
66
            else
548
66
            {
549
66
                buf.x[buf.count] = x + u_offset;
550
66
                buf.y[buf.count] = y - v_offset;
551
66
            }
552
66
            buf.g[buf.count] = glyph_index;
553
66
            buf.count ++;
554
555
66
            x += advance * 0.01 * size;
556
66
        }
557
66
    }
558
559
14
    if (buf.count > 0)
560
14
    {
561
14
        code = xps_flush_text_buffer(ctx, font, &buf, is_charpath);
562
14
        if (code)
563
0
            return gs_rethrow(code, "cannot flush buffered text");
564
14
    }
565
566
14
    return 0;
567
14
}
568
569
int
570
xps_parse_glyphs(xps_context_t *ctx,
571
        char *base_uri, xps_resource_t *dict, xps_item_t *root)
572
14
{
573
14
    xps_item_t *node;
574
14
    int code;
575
576
14
    char *fill_uri;
577
14
    char *opacity_mask_uri;
578
579
14
    char *bidi_level_att;
580
    /*char *caret_stops_att;*/
581
14
    char *fill_att;
582
14
    char *font_size_att;
583
14
    char *font_uri_att;
584
14
    char *origin_x_att;
585
14
    char *origin_y_att;
586
14
    char *is_sideways_att;
587
14
    char *indices_att;
588
14
    char *unicode_att;
589
14
    char *style_att;
590
14
    char *transform_att;
591
14
    char *clip_att;
592
14
    char *opacity_att;
593
14
    char *opacity_mask_att;
594
595
14
    xps_item_t *transform_tag = NULL;
596
14
    xps_item_t *clip_tag = NULL;
597
14
    xps_item_t *fill_tag = NULL;
598
14
    xps_item_t *opacity_mask_tag = NULL;
599
600
14
    char *fill_opacity_att = NULL;
601
602
14
    xps_part_t *part;
603
14
    xps_font_t *font;
604
605
14
    char partname[1024];
606
14
    char *subfont;
607
608
14
    gs_matrix matrix;
609
14
    float font_size = 10.0;
610
14
    int subfontid = 0;
611
14
    int is_sideways = 0;
612
14
    int bidi_level = 0;
613
614
14
    int sim_bold = 0;
615
14
    int sim_italic = 0;
616
617
14
    gs_matrix shear = { 1, 0, 0.36397f, 1, 0, 0 }; /* shear by 20 degrees */
618
619
    /*
620
     * Extract attributes and extended attributes.
621
     */
622
623
14
    bidi_level_att = xps_att(root, "BidiLevel");
624
    /*caret_stops_att = xps_att(root, "CaretStops");*/
625
14
    fill_att = xps_att(root, "Fill");
626
14
    font_size_att = xps_att(root, "FontRenderingEmSize");
627
14
    font_uri_att = xps_att(root, "FontUri");
628
14
    origin_x_att = xps_att(root, "OriginX");
629
14
    origin_y_att = xps_att(root, "OriginY");
630
14
    is_sideways_att = xps_att(root, "IsSideways");
631
14
    indices_att = xps_att(root, "Indices");
632
14
    unicode_att = xps_att(root, "UnicodeString");
633
14
    style_att = xps_att(root, "StyleSimulations");
634
14
    transform_att = xps_att(root, "RenderTransform");
635
14
    clip_att = xps_att(root, "Clip");
636
14
    opacity_att = xps_att(root, "Opacity");
637
14
    opacity_mask_att = xps_att(root, "OpacityMask");
638
639
14
    for (node = xps_down(root); node; node = xps_next(node))
640
0
    {
641
0
        if (!strcmp(xps_tag(node), "Glyphs.RenderTransform"))
642
0
            transform_tag = xps_down(node);
643
644
0
        if (!strcmp(xps_tag(node), "Glyphs.OpacityMask"))
645
0
            opacity_mask_tag = xps_down(node);
646
647
0
        if (!strcmp(xps_tag(node), "Glyphs.Clip"))
648
0
            clip_tag = xps_down(node);
649
650
0
        if (!strcmp(xps_tag(node), "Glyphs.Fill"))
651
0
            fill_tag = xps_down(node);
652
0
    }
653
654
14
    fill_uri = base_uri;
655
14
    opacity_mask_uri = base_uri;
656
657
14
    xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL);
658
14
    xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL);
659
14
    xps_resolve_resource_reference(ctx, dict, &fill_att, &fill_tag, &fill_uri);
660
14
    xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);
661
662
    /*
663
     * Check that we have all the necessary information.
664
     */
665
666
14
    if (!font_size_att || !font_uri_att || !origin_x_att || !origin_y_att)
667
0
        return gs_throw(-1, "missing attributes in glyphs element");
668
669
14
    if (!indices_att && !unicode_att)
670
0
        return 0; /* nothing to draw */
671
672
14
    if (is_sideways_att)
673
0
        is_sideways = !strcmp(is_sideways_att, "true");
674
675
14
    if (bidi_level_att)
676
0
        bidi_level = atoi(bidi_level_att);
677
678
    /*
679
     * Find and load the font resource
680
     */
681
682
14
    xps_absolute_path(partname, base_uri, font_uri_att, sizeof partname);
683
14
    subfont = strrchr(partname, '#');
684
14
    if (subfont)
685
0
    {
686
0
        subfontid = atoi(subfont + 1);
687
0
        *subfont = 0;
688
0
    }
689
690
14
    font = xps_hash_lookup(ctx->font_table, partname);
691
14
    if (!font)
692
2
    {
693
2
        part = xps_read_part(ctx, partname);
694
2
        if (!part)
695
0
            return gs_throw1(-1, "cannot find font resource part '%s'", partname);
696
697
        /* deobfuscate if necessary */
698
2
        if (strstr(part->name, ".odttf"))
699
2
            xps_deobfuscate_font_resource(ctx, part);
700
2
        if (strstr(part->name, ".ODTTF"))
701
0
            xps_deobfuscate_font_resource(ctx, part);
702
703
2
        font = xps_new_font(ctx, part->data, part->size, subfontid);
704
2
        if (!font)
705
0
            return gs_rethrow1(-1, "cannot load font resource '%s'", partname);
706
707
2
        xps_select_best_font_encoding(font);
708
709
2
        xps_hash_insert(ctx, ctx->font_table, part->name, font);
710
711
        /* NOTE: we kept part->name in the hashtable and part->data in the font */
712
2
        xps_free(ctx, part);
713
2
    }
714
715
14
    if (style_att)
716
14
    {
717
14
        if (!strcmp(style_att, "BoldSimulation"))
718
0
            sim_bold = 1;
719
14
        else if (!strcmp(style_att, "ItalicSimulation"))
720
0
            sim_italic = 1;
721
14
        else if (!strcmp(style_att, "BoldItalicSimulation"))
722
0
            sim_bold = sim_italic = 1;
723
14
    }
724
725
    /*
726
     * Set up graphics state.
727
     */
728
729
14
    gs_gsave(ctx->pgs);
730
731
14
    if (transform_att || transform_tag)
732
0
    {
733
0
        gs_matrix transform;
734
735
0
        if (transform_att)
736
0
            xps_parse_render_transform(ctx, transform_att, &transform);
737
0
        if (transform_tag)
738
0
            xps_parse_matrix_transform(ctx, transform_tag, &transform);
739
740
0
        gs_concat(ctx->pgs, &transform);
741
0
    }
742
743
14
    if (clip_att || clip_tag)
744
0
    {
745
0
        if (clip_att)
746
0
            xps_parse_abbreviated_geometry(ctx, clip_att);
747
0
        if (clip_tag)
748
0
            xps_parse_path_geometry(ctx, dict, clip_tag, 0);
749
0
        xps_clip(ctx);
750
0
    }
751
752
14
    font_size = atof(font_size_att);
753
754
14
    gs_setfont(ctx->pgs, font->font);
755
14
    gs_make_scaling(font_size, -font_size, &matrix);
756
14
    if (is_sideways)
757
0
        gs_matrix_rotate(&matrix, 90.0, &matrix);
758
759
14
    if (sim_italic)
760
0
        gs_matrix_multiply(&shear, &matrix, &matrix);
761
762
14
    gs_setcharmatrix(ctx->pgs, &matrix);
763
764
14
    gs_matrix_multiply(&matrix, &font->font->orig_FontMatrix, &font->font->FontMatrix);
765
766
14
    code = xps_begin_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag, false, false);
767
14
    if (code)
768
0
    {
769
0
        gs_grestore(ctx->pgs);
770
0
        return gs_rethrow(code, "cannot create transparency group");
771
0
    }
772
773
    /*
774
     * If it's a solid color brush fill/stroke do a simple fill
775
     */
776
777
14
    if (fill_tag && !strcmp(xps_tag(fill_tag), "SolidColorBrush"))
778
0
    {
779
0
        fill_opacity_att = xps_att(fill_tag, "Opacity");
780
0
        fill_att = xps_att(fill_tag, "Color");
781
0
        fill_tag = NULL;
782
0
    }
783
784
14
    if (fill_att)
785
14
    {
786
14
        float samples[XPS_MAX_COLORS];
787
14
        gs_color_space *colorspace;
788
789
14
        xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples);
790
14
        if (fill_opacity_att)
791
0
            samples[0] *= atof(fill_opacity_att);
792
14
        xps_set_color(ctx, colorspace, samples);
793
14
        rc_decrement(colorspace, "xps_parse_glyphs");
794
795
14
        if (sim_bold)
796
0
        {
797
0
            if (!ctx->preserve_tr_mode)
798
                /* widening strokes by 1% of em size */
799
0
                gs_setlinewidth(ctx->pgs, font_size * 0.02);
800
0
            else
801
                /* Undo CTM scaling */
802
0
                gs_setlinewidth(ctx->pgs, font_size * 0.02 * fabs(ctx->pgs->ctm.xx) / (ctx->pgs->device->HWResolution[0] / 72.0));
803
0
            gs_settextrenderingmode(ctx->pgs, 2);
804
0
        }
805
806
14
        code = xps_parse_glyphs_imp(ctx, font, font_size,
807
14
                atof(origin_x_att), atof(origin_y_att),
808
14
                is_sideways, bidi_level,
809
14
                indices_att, unicode_att, sim_bold && !ctx->preserve_tr_mode, sim_bold);
810
14
        if (code)
811
0
        {
812
0
            xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
813
0
            gs_grestore(ctx->pgs);
814
0
            return gs_rethrow(code, "cannot parse glyphs data");
815
0
        }
816
817
14
        if (sim_bold && !ctx->preserve_tr_mode)
818
0
        {
819
0
            gs_gsave(ctx->pgs);
820
0
            gs_fill(ctx->pgs);
821
0
            gs_grestore(ctx->pgs);
822
0
            gs_stroke(ctx->pgs);
823
0
        }
824
825
14
        gs_settextrenderingmode(ctx->pgs, 0);
826
14
    }
827
828
    /*
829
     * If it's a visual brush or image, use the charpath as a clip mask to paint brush
830
     */
831
832
14
    if (fill_tag)
833
0
    {
834
0
        if (ctx->in_high_level_pattern)
835
0
        {
836
0
            float value[2];  /* alpha and gray */
837
838
0
            value[0] = gs_getfillconstantalpha(ctx->pgs);
839
0
            value[1] = 0;
840
0
            xps_set_color(ctx, ctx->gray, value);
841
0
        }
842
843
0
        ctx->fill_rule = 1; /* always use non-zero winding rule for char paths */
844
0
        code = xps_parse_glyphs_imp(ctx, font, font_size,
845
0
                atof(origin_x_att), atof(origin_y_att),
846
0
                is_sideways, bidi_level, indices_att, unicode_att, 1, sim_bold);
847
0
        if (code)
848
0
        {
849
0
            xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
850
0
            gs_grestore(ctx->pgs);
851
0
            return gs_rethrow(code, "cannot parse glyphs data");
852
0
        }
853
854
0
        code = xps_parse_brush(ctx, fill_uri, dict, fill_tag);
855
0
        if (code)
856
0
        {
857
0
            xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
858
0
            gs_grestore(ctx->pgs);
859
0
            return gs_rethrow(code, "cannot parse fill brush");
860
0
        }
861
0
    }
862
863
14
    xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
864
865
14
    gs_grestore(ctx->pgs);
866
867
14
    return 0;
868
14
}