1 /* Copyright (C) 2001-2006 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, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 /* $Id: zfont.c 9043 2008-08-28 22:48:19Z giles $ */
15 /* Generic font operators */
16 #include "ghost.h"
17 #include "oper.h"
18 #include "gsstruct.h"		/* for registering root */
19 #include "gzstate.h"		/* must precede gxdevice */
20 #include "gxdevice.h"		/* must precede gxfont */
21 #include "gxfont.h"
22 #include "gxfcache.h"
23 #include "bfont.h"
24 #include "ialloc.h"
25 #include "iddict.h"
26 #include "igstate.h"
27 #include "iname.h"		/* for name_mark_index */
28 #include "isave.h"
29 #include "store.h"
30 #include "ivmspace.h"
31 #include "gscencs.h"
32 
33 /* Forward references */
34 static int make_font(i_ctx_t *, const gs_matrix *);
35 static void make_uint_array(os_ptr, const uint *, int);
36 static int setup_unicode_decoder(i_ctx_t *i_ctx_p, ref *Decoding);
37 
38 /* The (global) font directory */
39 gs_font_dir *ifont_dir = 0;	/* needed for buildfont */
40 
41 /* Mark a glyph as a PostScript name (if it isn't a CID). */
42 bool
zfont_mark_glyph_name(const gs_memory_t * mem,gs_glyph glyph,void * ignore_data)43 zfont_mark_glyph_name(const gs_memory_t *mem, gs_glyph glyph, void *ignore_data)
44 {
45     return (glyph >= gs_c_min_std_encoding_glyph || glyph == gs_no_glyph ? false :
46 	    name_mark_index(mem, (uint) glyph));
47 }
48 
49 /* Get a global glyph code.  */
50 static int
zfont_global_glyph_code(const gs_memory_t * mem,gs_const_string * gstr,gs_glyph * pglyph)51 zfont_global_glyph_code(const gs_memory_t *mem, gs_const_string *gstr, gs_glyph *pglyph)
52 {
53     ref v;
54     int code = name_ref(mem, gstr->data, gstr->size, &v, 0);
55 
56     if (code < 0)
57 	return code;
58     *pglyph = (gs_glyph)name_index(mem, &v);
59     return 0;
60 }
61 
62 /* Initialize the font operators */
63 static int
zfont_init(i_ctx_t * i_ctx_p)64 zfont_init(i_ctx_t *i_ctx_p)
65 {
66     ifont_dir = gs_font_dir_alloc2(imemory, imemory->non_gc_memory);
67     ifont_dir->ccache.mark_glyph = zfont_mark_glyph_name;
68     ifont_dir->global_glyph_code = zfont_global_glyph_code;
69     return gs_register_struct_root(imemory, NULL, (void **)&ifont_dir,
70 				   "ifont_dir");
71 }
72 
73 /* <font> <scale> scalefont <new_font> */
74 static int
zscalefont(i_ctx_t * i_ctx_p)75 zscalefont(i_ctx_t *i_ctx_p)
76 {
77     os_ptr op = osp;
78     int code;
79     double scale;
80     gs_matrix mat;
81 
82     if ((code = real_param(op, &scale)) < 0)
83 	return code;
84     if ((code = gs_make_scaling(scale, scale, &mat)) < 0)
85 	return code;
86     return make_font(i_ctx_p, &mat);
87 }
88 
89 /* <font> <matrix> makefont <new_font> */
90 static int
zmakefont(i_ctx_t * i_ctx_p)91 zmakefont(i_ctx_t *i_ctx_p)
92 {
93     os_ptr op = osp;
94     int code;
95     gs_matrix mat;
96 
97     if ((code = read_matrix(imemory, op, &mat)) < 0)
98 	return code;
99     return make_font(i_ctx_p, &mat);
100 }
101 
102 /* <font> setfont - */
103 int
zsetfont(i_ctx_t * i_ctx_p)104 zsetfont(i_ctx_t *i_ctx_p)
105 {
106     os_ptr op = osp;
107     gs_font *pfont;
108     int code = font_param(op, &pfont);
109 
110     if (code < 0 || (code = gs_setfont(igs, pfont)) < 0)
111 	return code;
112     pop(1);
113     return code;
114 }
115 
116 /* - currentfont <font> */
117 static int
zcurrentfont(i_ctx_t * i_ctx_p)118 zcurrentfont(i_ctx_t *i_ctx_p)
119 {
120     os_ptr op = osp;
121 
122     push(1);
123     *op = *pfont_dict(gs_currentfont(igs));
124     return 0;
125 }
126 
127 /* - cachestatus <mark> <bsize> <bmax> <msize> <mmax> <csize> <cmax> <blimit> */
128 static int
zcachestatus(i_ctx_t * i_ctx_p)129 zcachestatus(i_ctx_t *i_ctx_p)
130 {
131     os_ptr op = osp;
132     uint status[7];
133 
134     gs_cachestatus(ifont_dir, status);
135     push(7);
136     make_uint_array(op - 6, status, 7);
137     return 0;
138 }
139 
140 /* <blimit> setcachelimit - */
141 static int
zsetcachelimit(i_ctx_t * i_ctx_p)142 zsetcachelimit(i_ctx_t *i_ctx_p)
143 {
144     os_ptr op = osp;
145 
146     check_int_leu(*op, max_uint);
147     gs_setcachelimit(ifont_dir, (uint) op->value.intval);
148     pop(1);
149     return 0;
150 }
151 
152 /* <mark> <size> <lower> <upper> setcacheparams - */
153 static int
zsetcacheparams(i_ctx_t * i_ctx_p)154 zsetcacheparams(i_ctx_t *i_ctx_p)
155 {
156     os_ptr op = osp;
157     uint params[3];
158     int i, code;
159     os_ptr opp = op;
160 
161     for (i = 0; i < 3 && !r_has_type(opp, t_mark); i++, opp--) {
162 	check_int_leu(*opp, max_uint);
163 	params[i] = opp->value.intval;
164     }
165     switch (i) {
166 	case 3:
167 	    if ((code = gs_setcachesize(ifont_dir, params[2])) < 0)
168 		return code;
169 	case 2:
170 	    if ((code = gs_setcachelower(ifont_dir, params[1])) < 0)
171 		return code;
172 	case 1:
173 	    if ((code = gs_setcacheupper(ifont_dir, params[0])) < 0)
174 		return code;
175 	case 0:;
176     }
177     return zcleartomark(i_ctx_p);
178 }
179 
180 /* - currentcacheparams <mark> <size> <lower> <upper> */
181 static int
zcurrentcacheparams(i_ctx_t * i_ctx_p)182 zcurrentcacheparams(i_ctx_t *i_ctx_p)
183 {
184     os_ptr op = osp;
185     uint params[3];
186 
187     params[0] = gs_currentcachesize(ifont_dir);
188     params[1] = gs_currentcachelower(ifont_dir);
189     params[2] = gs_currentcacheupper(ifont_dir);
190     push(4);
191     make_mark(op - 3);
192     make_uint_array(op - 2, params, 3);
193     return 0;
194 }
195 
196 /* <font> .registerfont - */
197 static int
zregisterfont(i_ctx_t * i_ctx_p)198 zregisterfont(i_ctx_t *i_ctx_p)
199 {
200     os_ptr op = osp;
201     gs_font *pfont;
202     int code = font_param(op, &pfont);
203 
204     if (code < 0)
205 	return code;
206     pfont->is_resource = true;
207     pop(1);
208     return 0;
209 }
210 
211 
212 /* <Decoding> .setupUnicodeDecoder - */
213 static int
zsetupUnicodeDecoder(i_ctx_t * i_ctx_p)214 zsetupUnicodeDecoder(i_ctx_t *i_ctx_p)
215 {   /* The allocation mode must be global. */
216     os_ptr op = osp;
217     int code;
218 
219     check_type(*op, t_dictionary);
220     code = setup_unicode_decoder(i_ctx_p, op);
221     if (code < 0)
222 	return code;
223     pop(1);
224     return 0;
225 }
226 
227 /* ------ Initialization procedure ------ */
228 
229 const op_def zfont_op_defs[] =
230 {
231     {"0currentfont", zcurrentfont},
232     {"2makefont", zmakefont},
233     {"2scalefont", zscalefont},
234     {"1setfont", zsetfont},
235     {"0cachestatus", zcachestatus},
236     {"1setcachelimit", zsetcachelimit},
237     {"1setcacheparams", zsetcacheparams},
238     {"0currentcacheparams", zcurrentcacheparams},
239     {"1.registerfont", zregisterfont},
240     {"1.setupUnicodeDecoder", zsetupUnicodeDecoder},
241     op_def_end(zfont_init)
242 };
243 
244 /* ------ Subroutines ------ */
245 
246 /* Validate a font parameter. */
247 int
font_param(const ref * pfdict,gs_font ** ppfont)248 font_param(const ref * pfdict, gs_font ** ppfont)
249 {	/*
250 	 * Check that pfdict is a read-only dictionary, that it has a FID
251 	 * entry whose value is a fontID, and that the fontID points to a
252 	 * gs_font structure whose associated PostScript dictionary is
253 	 * pfdict.
254 	 */
255     ref *pid;
256     gs_font *pfont;
257     const font_data *pdata;
258 
259     check_type(*pfdict, t_dictionary);
260     if (dict_find_string(pfdict, "FID", &pid) <= 0 ||
261 	!r_has_type(pid, t_fontID)
262 	)
263 	return_error(e_invalidfont);
264     pfont = r_ptr(pid, gs_font);
265     if (pfont == 0)
266 	return_error(e_invalidfont);	/* unregistered font */
267     pdata = pfont->client_data;
268     if (!obj_eq(pfont->memory, &pdata->dict, pfdict))
269 	return_error(e_invalidfont);
270     *ppfont = pfont;
271     return 0;
272 }
273 
274 /* Add the FID entry to a font dictionary. */
275 /* Note that i_ctx_p may be NULL. */
276 int
add_FID(i_ctx_t * i_ctx_p,ref * fp,gs_font * pfont,gs_ref_memory_t * imem)277 add_FID(i_ctx_t *i_ctx_p, ref * fp /* t_dictionary */ , gs_font * pfont,
278 	gs_ref_memory_t *imem)
279 {
280     ref fid;
281 
282     make_tav(&fid, t_fontID,
283 	     a_readonly | imemory_space(imem) | imemory_new_mask(imem),
284 	     pstruct, (void *)pfont);
285     return (i_ctx_p ? idict_put_string(fp, "FID", &fid) :
286 	    dict_put_string(fp, "FID", &fid, NULL));
287 }
288 
289 /* Make a transformed font (common code for makefont/scalefont). */
290 static int
make_font(i_ctx_t * i_ctx_p,const gs_matrix * pmat)291 make_font(i_ctx_t *i_ctx_p, const gs_matrix * pmat)
292 {
293     os_ptr op = osp;
294     os_ptr fp = op - 1;
295     gs_font *oldfont, *newfont;
296     int code;
297     ref *pencoding = 0;
298 
299     code = font_param(fp, &oldfont);
300     if (code < 0)
301 	return code;
302     {
303 	uint space = ialloc_space(idmemory);
304 
305 	ialloc_set_space(idmemory, r_space(fp));
306 	if (dict_find_string(fp, "Encoding", &pencoding) > 0 &&
307 	    !r_is_array(pencoding)
308 	    )
309 	    code = gs_note_error(e_invalidfont);
310 	else {
311 		/*
312 		 * Temporarily substitute the new dictionary
313 		 * for the old one, in case the Encoding changed.
314 		 */
315 	    ref olddict;
316 
317 	    olddict = *pfont_dict(oldfont);
318 	    *pfont_dict(oldfont) = *fp;
319 	    code = gs_makefont(ifont_dir, oldfont, pmat, &newfont);
320 	    *pfont_dict(oldfont) = olddict;
321 	}
322 	ialloc_set_space(idmemory, space);
323     }
324     if (code < 0)
325 	return code;
326     /*
327      * We have to allow for the possibility that the font's Encoding
328      * is different from that of the base font.  Note that the
329      * font_data of the new font was simply copied from the old one.
330      */
331     if (pencoding != 0 &&
332 	!obj_eq(imemory, pencoding, &pfont_data(newfont)->Encoding)
333 	) {
334 	if (newfont->FontType == ft_composite)
335 	    return_error(e_rangecheck);
336 	/* We should really do validity checking here.... */
337 	ref_assign(&pfont_data(newfont)->Encoding, pencoding);
338 	lookup_gs_simple_font_encoding((gs_font_base *) newfont);
339     }
340     *fp = *pfont_dict(newfont);
341     pop(1);
342     return 0;
343 }
344 /* Create the transformed font dictionary. */
345 /* This is the make_font completion procedure for all non-composite fonts */
346 /* created at the interpreter level (see build_gs_simple_font in zbfont.c.) */
347 int
zbase_make_font(gs_font_dir * pdir,const gs_font * oldfont,const gs_matrix * pmat,gs_font ** ppfont)348 zbase_make_font(gs_font_dir * pdir, const gs_font * oldfont,
349 		const gs_matrix * pmat, gs_font ** ppfont)
350 {
351     /*
352      * We must call gs_base_make_font so that the XUID gets copied
353      * if necessary.
354      */
355     int code = gs_base_make_font(pdir, oldfont, pmat, ppfont);
356 
357     if (code < 0)
358 	return code;
359     return zdefault_make_font(pdir, oldfont, pmat, ppfont);
360 }
361 int
zdefault_make_font(gs_font_dir * pdir,const gs_font * oldfont,const gs_matrix * pmat,gs_font ** ppfont)362 zdefault_make_font(gs_font_dir * pdir, const gs_font * oldfont,
363 		   const gs_matrix * pmat, gs_font ** ppfont)
364 {
365     gs_font *newfont = *ppfont;
366     gs_memory_t *mem = newfont->memory;
367     /* HACK: we know this font was allocated by the interpreter. */
368     gs_ref_memory_t *imem = (gs_ref_memory_t *)mem;
369     ref *fp = pfont_dict(oldfont);
370     font_data *pdata;
371     ref newdict, newmat, scalemat;
372     uint dlen = dict_maxlength(fp);
373     uint mlen = dict_length(fp) + 3;	/* FontID, OrigFont, ScaleMatrix */
374     int code;
375 
376     if (dlen < mlen)
377 	dlen = mlen;
378     if ((pdata = gs_alloc_struct(mem, font_data, &st_font_data,
379 				 "make_font(font_data)")) == 0
380 	)
381 	return_error(e_VMerror);
382     /*
383      * This dictionary is newly created: it's safe to pass NULL as the
384      * dstack pointer to dict_copy and dict_put_string.
385      */
386     if ((code = dict_alloc(imem, dlen, &newdict)) < 0 ||
387 	(code = dict_copy(fp, &newdict, NULL)) < 0 ||
388 	(code = gs_alloc_ref_array(imem, &newmat, a_all, 12,
389 				   "make_font(matrices)")) < 0
390 	)
391 	return code;
392     refset_null_new(newmat.value.refs, 12, imemory_new_mask(imem));
393     ref_assign(&scalemat, &newmat);
394     r_set_size(&scalemat, 6);
395     scalemat.value.refs += 6;
396     /*
397      * Create the scaling matrix.  We could do this several different
398      * ways: by "dividing" the new FontMatrix by the base FontMatrix, by
399      * multiplying the current scaling matrix by a ScaleMatrix kept in
400      * the gs_font, or by multiplying the current scaling matrix by the
401      * ScaleMatrix from the font dictionary.  We opt for the last of
402      * these.
403      */
404     {
405 	gs_matrix scale, prev_scale;
406 	ref *ppsm;
407 
408 	if (!(dict_find_string(fp, "ScaleMatrix", &ppsm) > 0 &&
409 	      read_matrix(mem, ppsm, &prev_scale) >= 0 &&
410 	      gs_matrix_multiply(pmat, &prev_scale, &scale) >= 0)
411 	    )
412 	    scale = *pmat;
413 	write_matrix_new(&scalemat, &scale, imem);
414     }
415     r_clear_attrs(&scalemat, a_write);
416     r_set_size(&newmat, 6);
417     write_matrix_new(&newmat, &newfont->FontMatrix, imem);
418     r_clear_attrs(&newmat, a_write);
419     if ((code = dict_put_string(&newdict, "FontMatrix", &newmat, NULL)) < 0 ||
420 	(code = dict_put_string(&newdict, "OrigFont", pfont_dict(oldfont->base), NULL)) < 0 ||
421 	(code = dict_put_string(&newdict, "ScaleMatrix", &scalemat, NULL)) < 0 ||
422 	(code = add_FID(NULL, &newdict, newfont, imem)) < 0
423 	)
424 	return code;
425     newfont->client_data = pdata;
426     *pdata = *pfont_data(oldfont);
427     pdata->dict = newdict;
428     r_clear_attrs(dict_access_ref(&newdict), a_write);
429     return 0;
430 }
431 
432 /* Convert an array of (unsigned) integers to stack form. */
433 static void
make_uint_array(register os_ptr op,const uint * intp,int count)434 make_uint_array(register os_ptr op, const uint * intp, int count)
435 {
436     int i;
437 
438     for (i = 0; i < count; i++, op++, intp++)
439 	make_int(op, *intp);
440 }
441 
442 /* Remove scaled font and character cache entries that would be */
443 /* invalidated by a restore. */
444 static bool
purge_if_name_removed(const gs_memory_t * mem,cached_char * cc,void * vsave)445 purge_if_name_removed(const gs_memory_t *mem, cached_char * cc, void *vsave)
446 {
447     return alloc_name_index_is_since_save(mem, cc->code, vsave);
448 }
449 
450 /* Remove entries from font and character caches. */
451 int
font_restore(const alloc_save_t * save)452 font_restore(const alloc_save_t * save)
453 {
454     gs_font_dir *pdir = ifont_dir;
455     const gs_memory_t *mem = 0;
456     int code;
457 
458     if (pdir == 0)		/* not initialized yet */
459 	return 0;
460 
461     /* Purge original (unscaled) fonts. */
462 
463     {
464 	gs_font *pfont;
465 
466 otop:
467 	for (pfont = pdir->orig_fonts; pfont != 0;
468 	     pfont = pfont->next
469 	    ) {
470 	    mem = pfont->memory;
471 	    if (alloc_is_since_save((char *)pfont, save)) {
472 		code = gs_purge_font(pfont);
473 		if (code < 0)
474 		    return code;
475 		goto otop;
476 	    }
477 	}
478     }
479 
480     /* Purge cached scaled fonts. */
481 
482     {
483 	gs_font *pfont;
484 
485 top:
486 	for (pfont = pdir->scaled_fonts; pfont != 0;
487 	     pfont = pfont->next
488 	    ) {
489 	    if (alloc_is_since_save((char *)pfont, save)) {
490 		code = gs_purge_font(pfont);
491 		if (code < 0)
492 		    return code;
493 		goto top;
494 	    }
495 	}
496     }
497 
498     /* Purge xfonts and uncached scaled fonts. */
499 
500     {
501 	cached_fm_pair *pair;
502 	uint n;
503 
504 	for (pair = pdir->fmcache.mdata, n = pdir->fmcache.mmax;
505 	     n > 0; pair++, n--
506 	    )
507 	    if (!fm_pair_is_free(pair)) {
508 #if 0
509 		/* We disabled this code portion because
510 		   gx_add_fm_pair now copied xvalues
511 		   into a stable memory.
512 		 */
513 		if ((uid_is_XUID(&pair->UID) &&
514 		     alloc_is_since_save((char *)pair->UID.xvalues,
515 					 save))
516 		    ) {
517 		    code = gs_purge_fm_pair(pdir, pair, 0);
518 		    if (code < 0)
519 			return code;
520 		    continue;
521 		}
522 #endif
523 		if (pair->font != 0 &&
524 		    alloc_is_since_save((char *)pair->font, save)
525 		    ) {
526 		    if (!uid_is_valid(&pair->UID))
527 			gs_clean_fm_pair(pdir, pair);
528 		    /* Don't discard pairs with a surviving UID. */
529 		    pair->font = 0;
530 		}
531 		if (pair->xfont != 0 &&
532 		    alloc_is_since_save((char *)pair->xfont, save)
533 		    ) {
534 		    code = gs_purge_fm_pair(pdir, pair, 1);
535 		    if (code < 0)
536 			return code;
537 		}
538 	    }
539     }
540 
541     /* Purge characters with names about to be removed. */
542     /* We only need to do this if any new names have been created */
543     /* since the save. */
544 
545     if (alloc_any_names_since_save(save))
546 	gx_purge_selected_cached_chars(pdir, purge_if_name_removed,
547 				       (void *)save);
548     return 0;
549 }
550 
551 /* ------ Font procedures for PostScript fonts ------ */
552 
553 /* font_info procedure */
554 static bool
zfont_info_has(const ref * pfidict,const char * key,gs_const_string * pmember)555 zfont_info_has(const ref *pfidict, const char *key, gs_const_string *pmember)
556 {
557     ref *pvalue;
558 
559     if (dict_find_string(pfidict, key, &pvalue) > 0 &&
560 	r_has_type(pvalue, t_string)
561 	) {
562 	pmember->data = pvalue->value.const_bytes;
563 	pmember->size = r_size(pvalue);
564 	return true;
565     }
566     return false;
567 }
568 int
zfont_info(gs_font * font,const gs_point * pscale,int members,gs_font_info_t * info)569 zfont_info(gs_font *font, const gs_point *pscale, int members,
570 	   gs_font_info_t *info)
571 {
572     int code = gs_default_font_info(font, pscale, members &
573 		    ~(FONT_INFO_COPYRIGHT | FONT_INFO_NOTICE |
574 		      FONT_INFO_FAMILY_NAME | FONT_INFO_FULL_NAME),
575 				    info);
576     const ref *pfdict;
577     ref *pfontinfo, *pvalue;
578 
579     if (code < 0)
580 	return code;
581     pfdict = &pfont_data(font)->dict;
582     if (dict_find_string(pfdict, "FontInfo", &pfontinfo) <= 0 ||
583 	!r_has_type(pfontinfo, t_dictionary))
584 	return 0;
585     if ((members & FONT_INFO_COPYRIGHT) &&
586 	zfont_info_has(pfontinfo, "Copyright", &info->Copyright))
587 	info->members |= FONT_INFO_COPYRIGHT;
588     if ((members & FONT_INFO_NOTICE) &&
589 	zfont_info_has(pfontinfo, "Notice", &info->Notice))
590 	info->members |= FONT_INFO_NOTICE;
591     if ((members & FONT_INFO_FAMILY_NAME) &&
592 	zfont_info_has(pfontinfo, "FamilyName", &info->FamilyName))
593 	info->members |= FONT_INFO_FAMILY_NAME;
594     if ((members & FONT_INFO_FULL_NAME) &&
595 	zfont_info_has(pfontinfo, "FullName", &info->FullName))
596 	info->members |= FONT_INFO_FULL_NAME;
597     if ((members & FONT_INFO_EMBEDDING_RIGHTS)
598 	&& (dict_find_string(pfontinfo, "FSType", &pvalue) > 0)) {
599 	info->EmbeddingRights = pvalue->value.intval;
600 	info->members |= FONT_INFO_EMBEDDING_RIGHTS;
601     }
602     return code;
603 }
604 
605 /* -------------------- Utilities --------------*/
606 
607 typedef struct gs_unicode_decoder_s {
608     ref data;
609 } gs_unicode_decoder;
610 
611 /* GC procedures */
612 static
CLEAR_MARKS_PROC(unicode_decoder_clear_marks)613 CLEAR_MARKS_PROC(unicode_decoder_clear_marks)
614 {   gs_unicode_decoder *const pptr = vptr;
615 
616     r_clear_attrs(&pptr->data, l_mark);
617 }
618 static
619 ENUM_PTRS_WITH(unicode_decoder_enum_ptrs, gs_unicode_decoder *pptr) return 0;
620 case 0:
621 ENUM_RETURN_REF(&pptr->data);
622 ENUM_PTRS_END
623 static RELOC_PTRS_WITH(unicode_decoder_reloc_ptrs, gs_unicode_decoder *pptr);
624 RELOC_REF_VAR(pptr->data);
625 r_clear_attrs(&pptr->data, l_mark);
626 RELOC_PTRS_END
627 
628 gs_private_st_complex_only(st_unicode_decoder, gs_unicode_decoder,\
629     "unicode_decoder", unicode_decoder_clear_marks, unicode_decoder_enum_ptrs,
630     unicode_decoder_reloc_ptrs, 0);
631 
632 /* Get the Unicode value for a glyph. */
633 const ref *
zfont_get_to_unicode_map(gs_font_dir * dir)634 zfont_get_to_unicode_map(gs_font_dir *dir)
635 {
636     const gs_unicode_decoder *pud = (gs_unicode_decoder *)dir->glyph_to_unicode_table;
637 
638     return (pud == NULL ? NULL : &pud->data);
639 }
640 
641 static int
setup_unicode_decoder(i_ctx_t * i_ctx_p,ref * Decoding)642 setup_unicode_decoder(i_ctx_t *i_ctx_p, ref *Decoding)
643 {
644     gs_unicode_decoder *pud = gs_alloc_struct(imemory, gs_unicode_decoder,
645                              &st_unicode_decoder, "setup_unicode_decoder");
646     if (pud == NULL)
647 	return_error(e_VMerror);
648     ref_assign_new(&pud->data, Decoding);
649     ifont_dir->glyph_to_unicode_table = pud;
650     return 0;
651 }
652