Coverage Report

Created: 2025-01-28 06:17

/src/mupdf/source/xps/xps-glyphs.c
Line
Count
Source (jump to first uncovered line)
1
// Copyright (C) 2004-2021 Artifex Software, Inc.
2
//
3
// This file is part of MuPDF.
4
//
5
// MuPDF is free software: you can redistribute it and/or modify it under the
6
// terms of the GNU Affero General Public License as published by the Free
7
// Software Foundation, either version 3 of the License, or (at your option)
8
// any later version.
9
//
10
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
// details.
14
//
15
// You should have received a copy of the GNU Affero General Public License
16
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17
//
18
// Alternative licensing terms are available from the licensor.
19
// For commercial licensing, see <https://www.artifex.com/> or contact
20
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21
// CA 94129, USA, for further information.
22
23
#include "mupdf/fitz.h"
24
#include "xps-imp.h"
25
26
#include <ft2build.h>
27
#include FT_FREETYPE_H
28
#include FT_ADVANCES_H
29
30
static inline int ishex(int a)
31
0
{
32
0
  return (a >= 'A' && a <= 'F') ||
33
0
    (a >= 'a' && a <= 'f') ||
34
0
    (a >= '0' && a <= '9');
35
0
}
36
37
static inline int unhex(int a)
38
0
{
39
0
  if (a >= 'A' && a <= 'F') return a - 'A' + 0xA;
40
0
  if (a >= 'a' && a <= 'f') return a - 'a' + 0xA;
41
0
  if (a >= '0' && a <= '9') return a - '0';
42
0
  return 0;
43
0
}
44
45
int
46
xps_count_font_encodings(fz_context *ctx, fz_font *font)
47
0
{
48
0
  FT_Face face = fz_font_ft_face(ctx, font);
49
0
  return face->num_charmaps;
50
0
}
51
52
void
53
xps_identify_font_encoding(fz_context *ctx, fz_font *font, int idx, int *pid, int *eid)
54
0
{
55
0
  FT_Face face = fz_font_ft_face(ctx, font);
56
0
  *pid = face->charmaps[idx]->platform_id;
57
0
  *eid = face->charmaps[idx]->encoding_id;
58
0
}
59
60
void
61
xps_select_font_encoding(fz_context *ctx, fz_font *font, int idx)
62
0
{
63
0
  FT_Face face = fz_font_ft_face(ctx, font);
64
0
  fz_ft_lock(ctx);
65
0
  FT_Set_Charmap(face, face->charmaps[idx]);
66
0
  fz_ft_unlock(ctx);
67
0
}
68
69
int
70
xps_encode_font_char(fz_context *ctx, fz_font *font, int code)
71
0
{
72
0
  FT_Face face = fz_font_ft_face(ctx, font);
73
0
  int gid;
74
0
  fz_ft_lock(ctx);
75
0
  gid = FT_Get_Char_Index(face, code);
76
0
  if (gid == 0 && face->charmap && face->charmap->platform_id == 3 && face->charmap->encoding_id == 0)
77
0
    gid = FT_Get_Char_Index(face, 0xF000 | code);
78
0
  fz_ft_unlock(ctx);
79
0
  return gid;
80
0
}
81
82
void
83
xps_measure_font_glyph(fz_context *ctx, xps_document *doc, fz_font *font, int gid, xps_glyph_metrics *mtx)
84
0
{
85
0
  int mask = FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM;
86
0
  FT_Face face = fz_font_ft_face(ctx, font);
87
0
  FT_Fixed hadv = 0, vadv = 0;
88
89
0
  fz_ft_lock(ctx);
90
0
  FT_Get_Advance(face, gid, mask, &hadv);
91
0
  FT_Get_Advance(face, gid, mask | FT_LOAD_VERTICAL_LAYOUT, &vadv);
92
0
  fz_ft_unlock(ctx);
93
94
0
  mtx->hadv = (float) hadv / face->units_per_EM;
95
0
  mtx->vadv = (float) vadv / face->units_per_EM;
96
0
  mtx->vorg = (float) face->ascender / face->units_per_EM;
97
0
}
98
99
static fz_font *
100
xps_lookup_font_imp(fz_context *ctx, xps_document *doc, char *name)
101
0
{
102
0
  xps_font_cache *cache;
103
0
  for (cache = doc->font_table; cache; cache = cache->next)
104
0
    if (!xps_strcasecmp(cache->name, name))
105
0
      return fz_keep_font(ctx, cache->font);
106
0
  return NULL;
107
0
}
108
109
static void
110
xps_insert_font(fz_context *ctx, xps_document *doc, char *name, fz_font *font)
111
0
{
112
0
  xps_font_cache *cache = fz_malloc_struct(ctx, xps_font_cache);
113
0
  cache->font = NULL;
114
0
  cache->name = NULL;
115
116
0
  fz_try(ctx)
117
0
  {
118
0
    cache->font = fz_keep_font(ctx, font);
119
0
    cache->name = fz_strdup(ctx, name);
120
0
    cache->next = doc->font_table;
121
0
  }
122
0
  fz_catch(ctx)
123
0
  {
124
0
    fz_drop_font(ctx, cache->font);
125
0
    fz_free(ctx, cache->name);
126
0
    fz_free(ctx, cache);
127
0
    fz_rethrow(ctx);
128
0
  }
129
130
0
  doc->font_table = cache;
131
0
}
132
133
/*
134
 * Some fonts in XPS are obfuscated by XOR:ing the first 32 bytes of the
135
 * data with the GUID in the fontname.
136
 */
137
static void
138
xps_deobfuscate_font_resource(fz_context *ctx, xps_document *doc, xps_part *part)
139
0
{
140
0
  unsigned char buf[33];
141
0
  unsigned char key[16];
142
0
  unsigned char *data;
143
0
  size_t size;
144
0
  char *p;
145
0
  int i;
146
147
0
  size = fz_buffer_storage(ctx, part->data, &data);
148
0
  if (size < 32)
149
0
  {
150
0
    fz_warn(ctx, "insufficient data for font deobfuscation");
151
0
    return;
152
0
  }
153
154
0
  p = strrchr(part->name, '/');
155
0
  if (!p)
156
0
    p = part->name;
157
158
0
  for (i = 0; i < 32 && *p; p++)
159
0
  {
160
0
    if (ishex(*p))
161
0
      buf[i++] = *p;
162
0
  }
163
0
  buf[i] = 0;
164
165
0
  if (i != 32)
166
0
  {
167
0
    fz_warn(ctx, "cannot extract GUID from obfuscated font part name");
168
0
    return;
169
0
  }
170
171
0
  for (i = 0; i < 16; i++)
172
0
    key[i] = unhex(buf[i*2+0]) * 16 + unhex(buf[i*2+1]);
173
174
0
  for (i = 0; i < 16; i++)
175
0
  {
176
0
    data[i] ^= key[15-i];
177
0
    data[i+16] ^= key[15-i];
178
0
  }
179
0
}
180
181
static void
182
xps_select_best_font_encoding(fz_context *ctx, xps_document *doc, fz_font *font)
183
0
{
184
0
  static struct { int pid, eid; } xps_cmap_list[] =
185
0
  {
186
0
    { 3, 10 },    /* Unicode with surrogates */
187
0
    { 3, 1 },   /* Unicode without surrogates */
188
0
    { 3, 5 },   /* Wansung */
189
0
    { 3, 4 },   /* Big5 */
190
0
    { 3, 3 },   /* Prc */
191
0
    { 3, 2 },   /* ShiftJis */
192
0
    { 3, 0 },   /* Symbol */
193
0
    { 1, 0 },
194
0
    { -1, -1 },
195
0
  };
196
197
0
  int i, k, n, pid, eid;
198
199
0
  n = xps_count_font_encodings(ctx, font);
200
0
  for (k = 0; xps_cmap_list[k].pid != -1; k++)
201
0
  {
202
0
    for (i = 0; i < n; i++)
203
0
    {
204
0
      xps_identify_font_encoding(ctx, font, i, &pid, &eid);
205
0
      if (pid == xps_cmap_list[k].pid && eid == xps_cmap_list[k].eid)
206
0
      {
207
0
        xps_select_font_encoding(ctx, font, i);
208
0
        return;
209
0
      }
210
0
    }
211
0
  }
212
213
0
  fz_warn(ctx, "cannot find a suitable cmap");
214
0
}
215
216
fz_font *
217
xps_lookup_font(fz_context *ctx, xps_document *doc, char *base_uri, char *font_uri, char *style_att)
218
0
{
219
0
  char partname[1024];
220
0
  char fakename[1024];
221
0
  char *subfont;
222
0
  int subfontid = 0;
223
0
  xps_part *part;
224
0
  fz_font *font;
225
226
0
  xps_resolve_url(ctx, doc, partname, base_uri, font_uri, sizeof partname);
227
0
  subfont = strrchr(partname, '#');
228
0
  if (subfont)
229
0
  {
230
0
    subfontid = atoi(subfont + 1);
231
0
    *subfont = 0;
232
0
  }
233
234
  /* Make a new part name for font with style simulation applied */
235
0
  fz_strlcpy(fakename, partname, sizeof fakename);
236
0
  if (style_att)
237
0
  {
238
0
    if (!strcmp(style_att, "BoldSimulation"))
239
0
      fz_strlcat(fakename, "#Bold", sizeof fakename);
240
0
    else if (!strcmp(style_att, "ItalicSimulation"))
241
0
      fz_strlcat(fakename, "#Italic", sizeof fakename);
242
0
    else if (!strcmp(style_att, "BoldItalicSimulation"))
243
0
      fz_strlcat(fakename, "#BoldItalic", sizeof fakename);
244
0
  }
245
246
0
  font = xps_lookup_font_imp(ctx, doc, fakename);
247
0
  if (!font)
248
0
  {
249
0
    fz_buffer *buf = NULL;
250
0
    fz_var(buf);
251
252
0
    fz_try(ctx)
253
0
    {
254
0
      part = xps_read_part(ctx, doc, partname);
255
0
    }
256
0
    fz_catch(ctx)
257
0
    {
258
0
      if (fz_caught(ctx) == FZ_ERROR_TRYLATER)
259
0
      {
260
0
        if (doc->cookie)
261
0
        {
262
0
          doc->cookie->incomplete = 1;
263
0
          fz_ignore_error(ctx);
264
0
        }
265
0
        else
266
0
          fz_rethrow(ctx);
267
0
      }
268
0
      else
269
0
      {
270
0
        fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
271
0
        fz_report_error(ctx);
272
0
        fz_warn(ctx, "cannot find font resource part '%s'", partname);
273
0
      }
274
0
      return NULL;
275
0
    }
276
277
    /* deobfuscate if necessary */
278
0
    if (strstr(part->name, ".odttf"))
279
0
      xps_deobfuscate_font_resource(ctx, doc, part);
280
0
    if (strstr(part->name, ".ODTTF"))
281
0
      xps_deobfuscate_font_resource(ctx, doc, part);
282
283
0
    fz_var(font);
284
0
    fz_try(ctx)
285
0
    {
286
0
      font = fz_new_font_from_buffer(ctx, NULL, part->data, subfontid, 1);
287
0
      xps_select_best_font_encoding(ctx, doc, font);
288
0
      xps_insert_font(ctx, doc, fakename, font);
289
0
    }
290
0
    fz_always(ctx)
291
0
    {
292
0
      xps_drop_part(ctx, doc, part);
293
0
    }
294
0
    fz_catch(ctx)
295
0
    {
296
0
      fz_drop_font(ctx, font);
297
0
      fz_warn(ctx, "cannot load font resource '%s'", partname);
298
0
      return NULL;
299
0
    }
300
301
0
    if (style_att)
302
0
    {
303
0
      fz_font_flags_t *flags = fz_font_flags(font);
304
0
      int bold = !!strstr(style_att, "Bold");
305
0
      int italic = !!strstr(style_att, "Italic");
306
0
      flags->fake_bold = bold;
307
0
      flags->is_bold = bold;
308
0
      flags->fake_italic = italic;
309
0
      flags->is_italic = italic;
310
0
    }
311
0
  }
312
0
  return font;
313
0
}
314
315
/*
316
 * Parse and draw an XPS <Glyphs> element.
317
 *
318
 * Indices syntax:
319
320
 GlyphIndices = GlyphMapping ( ";" GlyphMapping )
321
 GlyphMapping = ( [ClusterMapping] GlyphIndex ) [GlyphMetrics]
322
 ClusterMapping = "(" ClusterCodeUnitCount [":" ClusterGlyphCount] ")"
323
 ClusterCodeUnitCount = * DIGIT
324
 ClusterGlyphCount    = * DIGIT
325
 GlyphIndex   = * DIGIT
326
 GlyphMetrics = "," AdvanceWidth ["," uOffset ["," vOffset]]
327
 AdvanceWidth = ["+"] RealNum
328
 uOffset    = ["+" | "-"] RealNum
329
 vOffset    = ["+" | "-"] RealNum
330
 RealNum    = ((DIGIT ["." DIGIT]) | ("." DIGIT)) [Exponent]
331
 Exponent   = ( ("E"|"e") ("+"|"-") DIGIT )
332
333
 */
334
335
static char *
336
xps_parse_digits(char *s, int *digit)
337
0
{
338
0
  *digit = 0;
339
0
  while (*s >= '0' && *s <= '9')
340
0
  {
341
0
    *digit = *digit * 10 + (*s - '0');
342
0
    s ++;
343
0
  }
344
0
  return s;
345
0
}
346
347
static char *
348
xps_parse_real_num(char *s, float *number, int *override)
349
0
{
350
0
  char *tail;
351
0
  float v;
352
0
  v = fz_strtof(s, &tail);
353
0
  *override = tail != s;
354
0
  if (*override)
355
0
    *number = v;
356
0
  return tail;
357
0
}
358
359
static char *
360
xps_parse_cluster_mapping(char *s, int *code_count, int *glyph_count)
361
0
{
362
0
  if (*s == '(')
363
0
    s = xps_parse_digits(s + 1, code_count);
364
0
  if (*s == ':')
365
0
    s = xps_parse_digits(s + 1, glyph_count);
366
0
  if (*s == ')')
367
0
    s ++;
368
0
  return s;
369
0
}
370
371
static char *
372
xps_parse_glyph_index(char *s, int *glyph_index)
373
0
{
374
0
  if (*s >= '0' && *s <= '9')
375
0
    s = xps_parse_digits(s, glyph_index);
376
0
  return s;
377
0
}
378
379
static char *
380
xps_parse_glyph_metrics(char *s, float *advance, float *uofs, float *vofs, int bidi_level)
381
0
{
382
0
  int override;
383
0
  if (*s == ',')
384
0
  {
385
0
    s = xps_parse_real_num(s + 1, advance, &override);
386
0
    if (override && (bidi_level & 1))
387
0
      *advance = -*advance;
388
0
  }
389
0
  if (*s == ',')
390
0
    s = xps_parse_real_num(s + 1, uofs, &override);
391
0
  if (*s == ',')
392
0
    s = xps_parse_real_num(s + 1, vofs, &override);
393
0
  return s;
394
0
}
395
396
fz_text *
397
xps_parse_glyphs_imp(fz_context *ctx, xps_document *doc, fz_matrix ctm,
398
  fz_font *font, float size, float originx, float originy,
399
  int is_sideways, int bidi_level,
400
  char *indices, char *unicode)
401
0
{
402
0
  xps_glyph_metrics mtx;
403
0
  fz_text *text;
404
0
  fz_matrix tm;
405
0
  float x = originx;
406
0
  float y = originy;
407
0
  char *us = unicode;
408
0
  char *is = indices;
409
0
  size_t un = 0;
410
411
0
  if (!unicode && !indices)
412
0
    fz_warn(ctx, "glyphs element with neither characters nor indices");
413
414
0
  if (us)
415
0
  {
416
0
    if (us[0] == '{' && us[1] == '}')
417
0
      us = us + 2;
418
0
    un = strlen(us);
419
0
  }
420
421
0
  if (is_sideways)
422
0
    tm = fz_pre_scale(fz_rotate(90), -size, size);
423
0
  else
424
0
    tm = fz_scale(size, -size);
425
426
0
  text = fz_new_text(ctx);
427
428
0
  fz_try(ctx)
429
0
  {
430
0
    while ((us && un > 0) || (is && *is))
431
0
    {
432
0
      int char_code = FZ_REPLACEMENT_CHARACTER;
433
0
      int code_count = 1;
434
0
      int glyph_count = 1;
435
436
0
      if (is && *is)
437
0
      {
438
0
        is = xps_parse_cluster_mapping(is, &code_count, &glyph_count);
439
0
      }
440
441
0
      if (code_count < 1)
442
0
        code_count = 1;
443
0
      if (glyph_count < 1)
444
0
        glyph_count = 1;
445
446
      /* TODO: add code chars with cluster mappings for text extraction */
447
448
0
      while (code_count--)
449
0
      {
450
0
        if (us && un > 0)
451
0
        {
452
0
          int t = fz_chartorune(&char_code, us);
453
0
          us += t; un -= t;
454
0
        }
455
0
      }
456
457
0
      while (glyph_count--)
458
0
      {
459
0
        int glyph_index = -1;
460
0
        float u_offset = 0;
461
0
        float v_offset = 0;
462
0
        float advance;
463
0
        int dir;
464
465
0
        if (is && *is)
466
0
          is = xps_parse_glyph_index(is, &glyph_index);
467
468
0
        if (glyph_index == -1)
469
0
          glyph_index = xps_encode_font_char(ctx, font, char_code);
470
471
0
        xps_measure_font_glyph(ctx, doc, font, glyph_index, &mtx);
472
0
        if (is_sideways)
473
0
          advance = mtx.vadv * 100;
474
0
        else if (bidi_level & 1)
475
0
          advance = -mtx.hadv * 100;
476
0
        else
477
0
          advance = mtx.hadv * 100;
478
479
0
        if (fz_font_flags(font)->fake_bold)
480
0
          advance *= 1.02f;
481
482
0
        if (is && *is)
483
0
        {
484
0
          is = xps_parse_glyph_metrics(is, &advance, &u_offset, &v_offset, bidi_level);
485
0
          if (*is == ';')
486
0
            is ++;
487
0
        }
488
489
0
        if (bidi_level & 1)
490
0
          u_offset = -mtx.hadv * 100 - u_offset;
491
492
0
        u_offset = u_offset * 0.01f * size;
493
0
        v_offset = v_offset * 0.01f * size;
494
495
0
        if (is_sideways)
496
0
        {
497
0
          tm.e = x + u_offset + (mtx.vorg * size);
498
0
          tm.f = y - v_offset + (mtx.hadv * 0.5f * size);
499
0
        }
500
0
        else
501
0
        {
502
0
          tm.e = x + u_offset;
503
0
          tm.f = y - v_offset;
504
0
        }
505
506
0
        dir = bidi_level & 1 ? FZ_BIDI_RTL : FZ_BIDI_LTR;
507
0
        fz_show_glyph(ctx, text, font, tm, glyph_index, char_code, is_sideways, bidi_level, dir, FZ_LANG_UNSET);
508
509
0
        x += advance * 0.01f * size;
510
0
      }
511
0
    }
512
0
  }
513
0
  fz_catch(ctx)
514
0
  {
515
0
    fz_drop_text(ctx, text);
516
0
    fz_rethrow(ctx);
517
0
  }
518
519
0
  return text;
520
0
}
521
522
void
523
xps_parse_glyphs(fz_context *ctx, xps_document *doc, fz_matrix ctm,
524
    char *base_uri, xps_resource *dict, fz_xml *root)
525
0
{
526
0
  fz_device *dev = doc->dev;
527
528
0
  fz_xml *node;
529
530
0
  char *fill_uri;
531
0
  char *opacity_mask_uri;
532
533
0
  char *bidi_level_att;
534
0
  char *fill_att;
535
0
  char *font_size_att;
536
0
  char *font_uri_att;
537
0
  char *origin_x_att;
538
0
  char *origin_y_att;
539
0
  char *is_sideways_att;
540
0
  char *indices_att;
541
0
  char *unicode_att;
542
0
  char *style_att;
543
0
  char *transform_att;
544
0
  char *clip_att;
545
0
  char *opacity_att;
546
0
  char *opacity_mask_att;
547
548
0
  fz_xml *transform_tag = NULL;
549
0
  fz_xml *clip_tag = NULL;
550
0
  fz_xml *fill_tag = NULL;
551
0
  fz_xml *opacity_mask_tag = NULL;
552
553
0
  char *fill_opacity_att = NULL;
554
555
0
  fz_font *font;
556
557
0
  float font_size = 10;
558
0
  int is_sideways = 0;
559
0
  int bidi_level = 0;
560
561
0
  fz_text *text = NULL;
562
0
  fz_rect area;
563
564
  /*
565
   * Extract attributes and extended attributes.
566
   */
567
568
0
  bidi_level_att = fz_xml_att(root, "BidiLevel");
569
0
  fill_att = fz_xml_att(root, "Fill");
570
0
  font_size_att = fz_xml_att(root, "FontRenderingEmSize");
571
0
  font_uri_att = fz_xml_att(root, "FontUri");
572
0
  origin_x_att = fz_xml_att(root, "OriginX");
573
0
  origin_y_att = fz_xml_att(root, "OriginY");
574
0
  is_sideways_att = fz_xml_att(root, "IsSideways");
575
0
  indices_att = fz_xml_att(root, "Indices");
576
0
  unicode_att = fz_xml_att(root, "UnicodeString");
577
0
  style_att = fz_xml_att(root, "StyleSimulations");
578
0
  transform_att = fz_xml_att(root, "RenderTransform");
579
0
  clip_att = fz_xml_att(root, "Clip");
580
0
  opacity_att = fz_xml_att(root, "Opacity");
581
0
  opacity_mask_att = fz_xml_att(root, "OpacityMask");
582
583
0
  for (node = fz_xml_down(root); node; node = fz_xml_next(node))
584
0
  {
585
0
    if (fz_xml_is_tag(node, "Glyphs.RenderTransform"))
586
0
      transform_tag = fz_xml_down(node);
587
0
    if (fz_xml_is_tag(node, "Glyphs.OpacityMask"))
588
0
      opacity_mask_tag = fz_xml_down(node);
589
0
    if (fz_xml_is_tag(node, "Glyphs.Clip"))
590
0
      clip_tag = fz_xml_down(node);
591
0
    if (fz_xml_is_tag(node, "Glyphs.Fill"))
592
0
      fill_tag = fz_xml_down(node);
593
0
  }
594
595
0
  fill_uri = base_uri;
596
0
  opacity_mask_uri = base_uri;
597
598
0
  xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL);
599
0
  xps_resolve_resource_reference(ctx, doc, dict, &clip_att, &clip_tag, NULL);
600
0
  xps_resolve_resource_reference(ctx, doc, dict, &fill_att, &fill_tag, &fill_uri);
601
0
  xps_resolve_resource_reference(ctx, doc, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);
602
603
  /*
604
   * Check that we have all the necessary information.
605
   */
606
607
0
  if (!font_size_att || !font_uri_att || !origin_x_att || !origin_y_att) {
608
0
    fz_warn(ctx, "missing attributes in glyphs element");
609
0
    return;
610
0
  }
611
612
0
  if (!indices_att && !unicode_att)
613
0
    return; /* nothing to draw */
614
615
0
  if (is_sideways_att)
616
0
    is_sideways = !strcmp(is_sideways_att, "true");
617
618
0
  if (bidi_level_att)
619
0
    bidi_level = atoi(bidi_level_att);
620
621
  /*
622
   * Find and load the font resource.
623
   */
624
625
0
  font = xps_lookup_font(ctx, doc, base_uri, font_uri_att, style_att);
626
0
  if (!font)
627
0
    font = fz_new_base14_font(ctx, "Times-Roman");
628
629
0
  fz_var(text);
630
631
0
  fz_try(ctx)
632
0
  {
633
    /*
634
     * Set up graphics state.
635
     */
636
637
0
    ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm);
638
639
0
    if (clip_att || clip_tag)
640
0
      xps_clip(ctx, doc, ctm, dict, clip_att, clip_tag);
641
642
0
    font_size = fz_atof(font_size_att);
643
644
0
    text = xps_parse_glyphs_imp(ctx, doc, ctm, font, font_size,
645
0
        fz_atof(origin_x_att), fz_atof(origin_y_att),
646
0
        is_sideways, bidi_level, indices_att, unicode_att);
647
648
0
    area = fz_bound_text(ctx, text, NULL, ctm);
649
650
0
    xps_begin_opacity(ctx, doc, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
651
652
    /* If it's a solid color brush fill/stroke do a simple fill */
653
654
0
    if (fz_xml_is_tag(fill_tag, "SolidColorBrush"))
655
0
    {
656
0
      fill_opacity_att = fz_xml_att(fill_tag, "Opacity");
657
0
      fill_att = fz_xml_att(fill_tag, "Color");
658
0
      fill_tag = NULL;
659
0
    }
660
661
0
    if (fill_att)
662
0
    {
663
0
      float samples[FZ_MAX_COLORS];
664
0
      fz_colorspace *colorspace;
665
666
0
      xps_parse_color(ctx, doc, base_uri, fill_att, &colorspace, samples);
667
0
      if (fill_opacity_att)
668
0
        samples[0] *= fz_atof(fill_opacity_att);
669
0
      xps_set_color(ctx, doc, colorspace, samples);
670
671
0
      fz_fill_text(ctx, dev, text, ctm, doc->colorspace, doc->color, doc->alpha, fz_default_color_params);
672
0
    }
673
674
    /* If it's a complex brush, use the charpath as a clip mask */
675
676
0
    if (fill_tag)
677
0
    {
678
0
      fz_clip_text(ctx, dev, text, ctm, area);
679
0
      xps_parse_brush(ctx, doc, ctm, area, fill_uri, dict, fill_tag);
680
0
      fz_pop_clip(ctx, dev);
681
0
    }
682
683
0
    xps_end_opacity(ctx, doc, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
684
685
0
    if (clip_att || clip_tag)
686
0
      fz_pop_clip(ctx, dev);
687
0
  }
688
0
  fz_always(ctx)
689
0
  {
690
0
    fz_drop_text(ctx, text);
691
0
    fz_drop_font(ctx, font);
692
0
  }
693
0
  fz_catch(ctx)
694
0
    fz_rethrow(ctx);
695
0
}