/src/ghostpdl/xps/xpsttf.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2025 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 - truetype font support */ |
18 | | |
19 | | #include "ghostxps.h" |
20 | | |
21 | | #include <gxfont.h> |
22 | | #include "xpsfapi.h" |
23 | | |
24 | | /* |
25 | | * Some extra TTF parsing magic that isn't covered by the graphics library. |
26 | | */ |
27 | | |
28 | | static inline int u16(const byte *p) |
29 | 0 | { |
30 | 0 | return (p[0] << 8) | p[1]; |
31 | 0 | } |
32 | | |
33 | | static inline int u32(const byte *p) |
34 | 0 | { |
35 | 0 | return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; |
36 | 0 | } |
37 | | |
38 | | static const char *pl_mac_names[258] = { |
39 | | ".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl", |
40 | | "numbersign", "dollar", "percent", "ampersand", "quotesingle", "parenleft", |
41 | | "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", |
42 | | "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", |
43 | | "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", |
44 | | "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", |
45 | | "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", |
46 | | "backslash", "bracketright", "asciicircum", "underscore", "grave", "a", |
47 | | "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", |
48 | | "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", |
49 | | "braceright", "asciitilde", "Adieresis", "Aring", "Ccedilla", "Eacute", |
50 | | "Ntilde", "Odieresis", "Udieresis", "aacute", "agrave", "acircumflex", |
51 | | "adieresis", "atilde", "aring", "ccedilla", "eacute", "egrave", |
52 | | "ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis", |
53 | | "ntilde", "oacute", "ograve", "ocircumflex", "odieresis", "otilde", |
54 | | "uacute", "ugrave", "ucircumflex", "udieresis", "dagger", "degree", "cent", |
55 | | "sterling", "section", "bullet", "paragraph", "germandbls", "registered", |
56 | | "copyright", "trademark", "acute", "dieresis", "notequal", "AE", "Oslash", |
57 | | "infinity", "plusminus", "lessequal", "greaterequal", "yen", "mu", |
58 | | "partialdiff", "summation", "product", "pi", "integral", "ordfeminine", |
59 | | "ordmasculine", "Omega", "ae", "oslash", "questiondown", "exclamdown", |
60 | | "logicalnot", "radical", "florin", "approxequal", "Delta", "guillemotleft", |
61 | | "guillemotright", "ellipsis", "nonbreakingspace", "Agrave", "Atilde", |
62 | | "Otilde", "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright", |
63 | | "quoteleft", "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis", |
64 | | "fraction", "currency", "guilsinglleft", "guilsinglright", "fi", "fl", |
65 | | "daggerdbl", "periodcentered", "quotesinglbase", "quotedblbase", |
66 | | "perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", |
67 | | "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute", |
68 | | "Ocircumflex", "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", |
69 | | "dotlessi", "circumflex", "tilde", "macron", "breve", "dotaccent", "ring", |
70 | | "cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash", "Scaron", |
71 | | "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth", "Yacute", |
72 | | "yacute", "Thorn", "thorn", "minus", "multiply", "onesuperior", |
73 | | "twosuperior", "threesuperior", "onehalf", "onequarter", "threequarters", |
74 | | "franc", "Gbreve", "gbreve", "Idotaccent", "Scedilla", "scedilla", |
75 | | "Cacute", "cacute", "Ccaron", "ccaron", "dcroat" |
76 | | }; |
77 | | |
78 | | /* |
79 | | * A bunch of callback functions that the ghostscript |
80 | | * font machinery will call. The most important one |
81 | | * is the build_char function. These are specific to |
82 | | * truetype (loca/glyf) flavored opentypes. |
83 | | */ |
84 | | |
85 | | static unsigned int |
86 | | xps_true_get_glyph_index(gs_font_type42 *pfont42, gs_glyph glyph) |
87 | 0 | { |
88 | | /* identity */ |
89 | 0 | return glyph; |
90 | 0 | } |
91 | | |
92 | | static int |
93 | | xps_true_callback_string_proc(gs_font_type42 *p42, ulong offset, uint length, const byte **pdata) |
94 | 459 | { |
95 | 459 | xps_font_t *font = p42->client_data; |
96 | 459 | if (offset + length > font->length) |
97 | 0 | { |
98 | 0 | *pdata = NULL; |
99 | 0 | return gs_throw2(-1, "font data access out of bounds (offset=%lu size=%u)", offset, length); |
100 | 0 | } |
101 | 459 | *pdata = font->data + offset; |
102 | 459 | return 0; |
103 | 459 | } |
104 | | |
105 | | static gs_glyph |
106 | | xps_true_callback_encode_char(gs_font *pfont, gs_char chr, gs_glyph_space_t spc) |
107 | 0 | { |
108 | 0 | xps_font_t *font = pfont->client_data; |
109 | 0 | int value; |
110 | |
|
111 | 0 | value = xps_encode_font_char(font, chr); |
112 | 0 | if (value == 0) |
113 | 0 | return GS_NO_GLYPH; |
114 | 0 | return value; |
115 | 0 | } |
116 | | |
117 | | static int |
118 | | xps_true_callback_decode_glyph(gs_font *pfont, gs_glyph glyph, int ch, ushort *unicode_return, unsigned int length) |
119 | 0 | { |
120 | 0 | xps_font_t *font = pfont->client_data; |
121 | 0 | char *ur = (char *)unicode_return; |
122 | 0 | int u; |
123 | |
|
124 | 0 | if (length == 0) |
125 | 0 | return 2; |
126 | 0 | u = xps_decode_font_char(font, glyph); |
127 | | /* Unfortunate assumptions in the pdfwrite code mea that we have to return the |
128 | | * value as a big-endian short, no matter what platform we are on |
129 | | */ |
130 | 0 | ur[1] = u & 0xff; |
131 | 0 | ur[0] = u >> 8; |
132 | 0 | return 2; |
133 | 0 | } |
134 | | |
135 | | static int |
136 | | xps_true_callback_glyph_name(gs_font *pfont, gs_glyph glyph, gs_const_string *pstr) |
137 | 0 | { |
138 | | /* This function is copied verbatim from plfont.c */ |
139 | |
|
140 | 0 | int table_length; |
141 | 0 | int table_offset; |
142 | |
|
143 | 0 | ulong format; |
144 | 0 | int numGlyphs; |
145 | 0 | uint glyph_name_index; |
146 | 0 | const byte *postp, *indexp; /* post table pointer */ |
147 | 0 | xps_font_t *font = pfont->client_data; |
148 | |
|
149 | 0 | if (glyph >= GS_MIN_GLYPH_INDEX) { |
150 | 0 | glyph -= GS_MIN_GLYPH_INDEX; |
151 | 0 | } |
152 | | |
153 | | /* guess if the font type is not truetype */ |
154 | 0 | if ( pfont->FontType != ft_TrueType ) |
155 | 0 | { |
156 | 0 | glyph -= 29; |
157 | 0 | if (glyph < 258 ) |
158 | 0 | { |
159 | 0 | pstr->data = (byte*) pl_mac_names[glyph]; |
160 | 0 | pstr->size = strlen((char*)pstr->data); |
161 | 0 | return 0; |
162 | 0 | } |
163 | 0 | else |
164 | 0 | { |
165 | 0 | return gs_throw1(-1, "glyph index %lu out of range", (ulong)glyph); |
166 | 0 | } |
167 | 0 | } |
168 | | |
169 | 0 | table_offset = xps_find_sfnt_table((xps_font_t*)pfont->client_data, "post", &table_length); |
170 | | |
171 | | /* no post table */ |
172 | 0 | if (table_offset < 0) |
173 | 0 | return gs_throw(-1, "no post table"); |
174 | | |
175 | | /* this shoudn't happen but... */ |
176 | 0 | if ( table_length == 0 ) |
177 | 0 | return gs_throw(-1, "zero-size post table"); |
178 | | |
179 | 0 | ((gs_font_type42 *)pfont)->data.string_proc((gs_font_type42 *)pfont, |
180 | 0 | table_offset, table_length, &postp); |
181 | 0 | format = u32(postp); |
182 | | |
183 | | /* Format 1.0 (mac encoding) is a simple table see the TT spec. |
184 | | * We don't implement this because we don't see it in practice. |
185 | | * Format 2.5 is deprecated. |
186 | | * Format 3.0 means that there is no post data in the font file. |
187 | | * We see this a lot but can't do much about it. |
188 | | * The only format we support is 2.0. |
189 | | */ |
190 | 0 | if ( format != 0x20000 ) |
191 | 0 | { |
192 | | /* Invent a name if we don't know the table format. */ |
193 | 0 | char buf[32]; |
194 | 0 | gs_snprintf(buf, sizeof(buf), "glyph%d", (int)glyph); |
195 | | |
196 | | /* Ugly hackery. see comment below, after 'not mac' this ends up as a memory leak. |
197 | | * The PostScript interpreter adds the strings it creates to the PostScript name table |
198 | | * which is cleared and freed at EOJ. Presumably because these functions were |
199 | | * written with PostScript in mind, there is no provision for there not to be a |
200 | | * persistent copy of the name data, so we have to make one, which means it leaks. |
201 | | */ |
202 | 0 | pstr->size = strlen(buf); |
203 | 0 | pstr->data = gs_alloc_bytes(pfont->memory, (size_t)pstr->size + 1, "glyph to name"); |
204 | 0 | if ( pstr->data == 0 ) |
205 | 0 | return -1; |
206 | | |
207 | 0 | memset((byte *)pstr->data, 0x00, pstr->size + 1); |
208 | 0 | memcpy((byte *)pstr->data, buf, pstr->size); |
209 | 0 | return 0; |
210 | 0 | } |
211 | | |
212 | | /* skip over the post header */ |
213 | 0 | numGlyphs = (int)u16(postp + 32); |
214 | 0 | if ((int)glyph > numGlyphs - 1) |
215 | 0 | { |
216 | 0 | return gs_throw1(-1, "glyph index %lu out of range", (ulong)glyph); |
217 | 0 | } |
218 | | |
219 | | /* glyph name index starts at post + 34 each entry is 2 bytes */ |
220 | 0 | indexp = postp + 34 + (glyph * 2); |
221 | 0 | if (indexp > (postp + table_length - 2)) |
222 | 0 | return gs_throw(-1, "post table format error"); |
223 | | |
224 | 0 | glyph_name_index = u16(indexp); |
225 | | |
226 | | /* this shouldn't happen */ |
227 | 0 | if ( glyph_name_index > 0x7fff ) |
228 | 0 | return gs_throw(-1, "post table format error"); |
229 | | |
230 | | /* mac easy */ |
231 | 0 | if ( glyph_name_index < 258 ) |
232 | 0 | { |
233 | | /* dmprintf2(pfont->memory, "glyph name (mac) %d = %s\n", glyph, pl_mac_names[glyph_name_index]); */ |
234 | 0 | pstr->data = (byte*) pl_mac_names[glyph_name_index]; |
235 | 0 | pstr->size = strlen((char*)pstr->data); |
236 | 0 | return 0; |
237 | 0 | } |
238 | | |
239 | | /* not mac */ |
240 | 0 | else |
241 | 0 | { |
242 | 0 | byte *mydata; |
243 | | |
244 | | /* and here's the tricky part */ |
245 | 0 | const byte *pascal_stringp = postp + 34 + (numGlyphs * 2); |
246 | | |
247 | | /* 0 - 257 lives in the mac table above */ |
248 | 0 | glyph_name_index -= 258; |
249 | | |
250 | | /* The string we want is the index'th pascal string, |
251 | | * so we "hop" to each length byte "index" times. */ |
252 | 0 | while (glyph_name_index > 0) |
253 | 0 | { |
254 | 0 | pascal_stringp += ((int)(*pascal_stringp)+1); |
255 | 0 | glyph_name_index--; |
256 | 0 | if (pascal_stringp >= postp + table_length) |
257 | 0 | return gs_throw(-1, "data out of range"); |
258 | 0 | } |
259 | | |
260 | | /* length byte */ |
261 | 0 | pstr->size = (int)(*pascal_stringp); |
262 | | |
263 | | /* + 1 is for the length byte */ |
264 | 0 | pstr->data = pascal_stringp + 1; |
265 | | |
266 | | /* sanity check */ |
267 | 0 | if ( pstr->data + pstr->size > postp + table_length || pstr->data - 1 < postp) |
268 | 0 | return gs_throw(-1, "data out of range"); |
269 | | |
270 | | /* sigh - we have to allocate a copy of the data - by the |
271 | | time a high level device makes use of it the font data |
272 | | may be freed. Track the allocated memory in our |
273 | | font 'wrapper' so we can free it when we free tha font wrapper. |
274 | | */ |
275 | 0 | mydata = gs_alloc_bytes(pfont->memory, (size_t)pstr->size + 1, "glyph to name"); |
276 | 0 | if ( mydata == 0 ) |
277 | 0 | return -1; |
278 | 0 | memcpy(mydata, pascal_stringp + 1, pstr->size); |
279 | 0 | pstr->data = mydata; |
280 | |
|
281 | 0 | mydata[pstr->size] = 0; |
282 | |
|
283 | 0 | if (font->names == NULL) { |
284 | 0 | font->names = (char **)gs_alloc_bytes(pfont->memory, (size_t)256 * sizeof (char *), "names storage"); |
285 | 0 | if (font->names == NULL) { |
286 | 0 | gs_free_object(pfont->memory, (byte *)pstr->data, "free string on error"); |
287 | 0 | pstr->data = NULL; |
288 | 0 | pstr->size = 0; |
289 | 0 | return -1; |
290 | 0 | } |
291 | 0 | font->max_name_index = 255; |
292 | 0 | font->next_name_index = 0; |
293 | 0 | memset(font->names, 0x00, 256 * sizeof (char *)); |
294 | 0 | } |
295 | 0 | if (font->next_name_index > font->max_name_index) { |
296 | 0 | char **temp = NULL; |
297 | 0 | temp = (char **)gs_alloc_bytes(pfont->memory, ((size_t)font->max_name_index + 256) * sizeof (char *), "names storage"); |
298 | 0 | if (temp == NULL) { |
299 | 0 | gs_free_object(pfont->memory, (byte *)pstr->data, "free string on error"); |
300 | 0 | pstr->data = NULL; |
301 | 0 | pstr->size = 0; |
302 | 0 | return -1; |
303 | 0 | } |
304 | 0 | memset(temp, 0x00, (font->max_name_index + 256) * sizeof (char *)); |
305 | 0 | memcpy(temp, font->names, font->max_name_index * sizeof(char *)); |
306 | 0 | gs_free_object(pfont->memory, (void *)font->names, "realloc names storage"); |
307 | 0 | font->names = temp; |
308 | 0 | font->max_name_index += 256; |
309 | 0 | } |
310 | 0 | font->names[font->next_name_index++] = (char *)pstr->data; |
311 | 0 | return 0; |
312 | 0 | } |
313 | 0 | } |
314 | | |
315 | | static int |
316 | | xps_true_callback_build_char(gs_show_enum *penum, gs_gstate *pgs, gs_font *pfont, |
317 | | gs_char chr, gs_glyph glyph) |
318 | 0 | { |
319 | 0 | gs_font_type42 *p42 = (gs_font_type42*)pfont; |
320 | 0 | const gs_rect *pbbox; |
321 | 0 | float sbw[4], w2[6]; |
322 | 0 | gs_fixed_point saved_adjust; |
323 | 0 | int code; |
324 | | |
325 | | /* dmprintf1(pfont->memory, "build char ttf %d\n", glyph); */ |
326 | |
|
327 | 0 | code = gs_type42_get_metrics(p42, glyph, sbw); |
328 | 0 | if (code < 0) |
329 | 0 | return code; |
330 | | |
331 | 0 | w2[0] = sbw[2]; |
332 | 0 | w2[1] = sbw[3]; |
333 | |
|
334 | 0 | pbbox = &p42->FontBBox; |
335 | 0 | w2[2] = pbbox->p.x; |
336 | 0 | w2[3] = pbbox->p.y; |
337 | 0 | w2[4] = pbbox->q.x; |
338 | 0 | w2[5] = pbbox->q.y; |
339 | | |
340 | | /* Expand the bbox when stroking */ |
341 | 0 | if ( pfont->PaintType ) |
342 | 0 | { |
343 | 0 | float expand = max(1.415, gs_currentmiterlimit(pgs)) * gs_currentlinewidth(pgs) / 2; |
344 | 0 | w2[2] -= expand, w2[3] -= expand; |
345 | 0 | w2[4] += expand, w2[5] += expand; |
346 | 0 | } |
347 | |
|
348 | 0 | if ( (code = gs_moveto(pgs, 0.0, 0.0)) < 0 ) |
349 | 0 | return code; |
350 | | |
351 | 0 | if ( (code = gs_setcachedevice(penum, pgs, w2)) < 0 ) |
352 | 0 | return code; |
353 | | |
354 | 0 | code = gs_type42_append(glyph, pgs, |
355 | 0 | gx_current_path(pgs), |
356 | 0 | (gs_text_enum_t*)penum, (gs_font*)p42, |
357 | 0 | gs_show_in_charpath(penum) != cpm_show); |
358 | 0 | if (code < 0) |
359 | 0 | return code; |
360 | | |
361 | | /* Indicate that dropout prevention should be enabled by setting |
362 | | fill_adjust to the special value -1. */ |
363 | 0 | saved_adjust = pgs->fill_adjust; |
364 | 0 | pgs->fill_adjust.x = -1; |
365 | 0 | pgs->fill_adjust.y = -1; |
366 | |
|
367 | 0 | code = (pfont->PaintType ? gs_stroke(pgs) : gs_fill(pgs)); |
368 | 0 | if (code < 0) |
369 | 0 | return code; |
370 | | |
371 | 0 | pgs->fill_adjust = saved_adjust; |
372 | |
|
373 | 0 | return 0; |
374 | 0 | } |
375 | | |
376 | | /* |
377 | | * Initialize the ghostscript font machinery for a truetype |
378 | | * (type42 in postscript terminology) font. |
379 | | */ |
380 | | |
381 | | int |
382 | | xps_init_truetype_font(xps_context_t *ctx, xps_font_t *font) |
383 | 2 | { |
384 | 2 | int code = 0; |
385 | | |
386 | 2 | font->font = (void*) gs_alloc_struct(ctx->memory, gs_font_type42, &st_gs_font_type42, "xps_font type42"); |
387 | 2 | if (!font->font) |
388 | 0 | return gs_throw(gs_error_VMerror, "out of memory"); |
389 | | |
390 | | /* no shortage of things to initialize */ |
391 | 2 | { |
392 | 2 | gs_font_type42 *p42 = (gs_font_type42*) font->font; |
393 | | |
394 | | /* Common to all fonts: */ |
395 | | |
396 | 2 | p42->next = 0; |
397 | 2 | p42->prev = 0; |
398 | 2 | p42->memory = ctx->memory; |
399 | | |
400 | 2 | p42->dir = ctx->fontdir; /* NB also set by gs_definefont later */ |
401 | 2 | p42->base = font->font; /* NB also set by gs_definefont later */ |
402 | 2 | p42->is_resource = false; |
403 | 2 | gs_notify_init(&p42->notify_list, gs_memory_stable(ctx->memory)); |
404 | 2 | p42->id = gs_next_ids(ctx->memory, 1); |
405 | | |
406 | 2 | p42->client_data = font; /* that's us */ |
407 | | |
408 | | /* this is overwritten in grid_fit() */ |
409 | 2 | gs_make_identity(&p42->FontMatrix); |
410 | 2 | gs_make_identity(&p42->orig_FontMatrix); /* NB ... original or zeroes? */ |
411 | | |
412 | 2 | p42->FontType = ft_TrueType; |
413 | 2 | p42->BitmapWidths = false; |
414 | 2 | p42->ExactSize = fbit_use_outlines; |
415 | 2 | p42->InBetweenSize = fbit_use_outlines; |
416 | 2 | p42->TransformedChar = fbit_use_outlines; |
417 | 2 | p42->WMode = 0; |
418 | 2 | p42->PaintType = 0; |
419 | 2 | p42->StrokeWidth = 0; |
420 | 2 | p42->is_cached = 0; |
421 | | |
422 | 2 | p42->procs.define_font = gs_no_define_font; |
423 | 2 | p42->procs.make_font = gs_no_make_font; |
424 | 2 | p42->procs.font_info = gs_type42_font_info; |
425 | 2 | p42->procs.same_font = gs_default_same_font; |
426 | 2 | p42->procs.encode_char = xps_true_callback_encode_char; |
427 | 2 | p42->procs.decode_glyph = xps_true_callback_decode_glyph; |
428 | 2 | p42->procs.enumerate_glyph = gs_type42_enumerate_glyph; |
429 | 2 | p42->procs.glyph_info = gs_type42_glyph_info; |
430 | 2 | p42->procs.glyph_outline = gs_type42_glyph_outline; |
431 | 2 | p42->procs.glyph_name = xps_true_callback_glyph_name; |
432 | 2 | p42->procs.init_fstack = gs_default_init_fstack; |
433 | 2 | p42->procs.next_char_glyph = gs_default_next_char_glyph; |
434 | 2 | p42->procs.build_char = xps_true_callback_build_char; |
435 | | |
436 | 2 | memset(p42->font_name.chars, 0, sizeof(p42->font_name.chars)); |
437 | 2 | xps_load_sfnt_name(font, (char*)p42->font_name.chars, sizeof(p42->font_name.chars)); |
438 | 2 | p42->font_name.size = strlen((char*)p42->font_name.chars); |
439 | | |
440 | 2 | memset(p42->key_name.chars, 0, sizeof(p42->key_name.chars)); |
441 | 2 | strcpy((char*)p42->key_name.chars, (char*)p42->font_name.chars); |
442 | 2 | p42->key_name.size = strlen((char*)p42->key_name.chars); |
443 | | |
444 | | /* Base font specific: */ |
445 | | |
446 | 2 | p42->FontBBox.p.x = 0; |
447 | 2 | p42->FontBBox.p.y = 0; |
448 | 2 | p42->FontBBox.q.x = 0; |
449 | 2 | p42->FontBBox.q.y = 0; |
450 | | |
451 | 2 | uid_set_UniqueID(&p42->UID, p42->id); |
452 | | |
453 | 2 | p42->encoding_index = ENCODING_INDEX_UNKNOWN; |
454 | 2 | p42->nearest_encoding_index = ENCODING_INDEX_ISOLATIN1; |
455 | | |
456 | 2 | p42->FAPI = 0; |
457 | 2 | p42->FAPI_font_data = 0; |
458 | | |
459 | | /* Type 42 specific: */ |
460 | | |
461 | 2 | p42->data.string_proc = xps_true_callback_string_proc; |
462 | 2 | p42->data.proc_data = font; |
463 | | |
464 | 2 | code = gs_type42_font_init(p42, font->subfontid); |
465 | 2 | if (code < 0) |
466 | 0 | return code; |
467 | 2 | p42->data.get_glyph_index = xps_true_get_glyph_index; |
468 | 2 | } |
469 | | |
470 | 2 | if ((code = gs_definefont(ctx->fontdir, font->font)) < 0) { |
471 | 0 | return(code); |
472 | 0 | } |
473 | | |
474 | 2 | code = xps_fapi_passfont (font->font, NULL, NULL, font->data, font->length); |
475 | 2 | return code; |
476 | 2 | } |