xps-glyphs.c 16 KB

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