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