Coverage Report

Created: 2025-04-22 06:20

/src/libspectre/ghostscript/base/gsfont.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2020 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.,  1305 Grant Avenue - Suite 200, Novato,
13
   CA 94945, U.S.A., +1(415)492-9861, for further information.
14
*/
15
16
17
/* Font operators for Ghostscript library */
18
#include "gx.h"
19
#include "memory_.h"
20
#include "gserrors.h"
21
#include "gsstruct.h"
22
#include "gsutil.h"
23
#include "gxfixed.h"
24
#include "gxmatrix.h"
25
#include "gzstate.h"    /* must precede gxdevice */
26
#include "gxdevice.h"   /* must precede gxfont */
27
#include "gxfont.h"
28
#include "gxfcache.h"
29
#include "gzpath.h"   /* for default implementation */
30
31
/* Define the sizes of the various aspects of the font/character cache. */
32
/*** Big memory machines ***/
33
218
#define smax_LARGE 50    /* smax - # of scaled fonts */
34
218
#define bmax_LARGE 1000000  /* bmax - space for cached chars */
35
218
#define mmax_LARGE 200    /* mmax - # of cached font/matrix pairs */
36
218
#define cmax_LARGE 5000    /* cmax - # of cached chars */
37
218
#define blimit_LARGE 32000  /* blimit/upper - max size of a single cached char */
38
/*** Small memory machines ***/
39
0
#define smax_SMALL 20    /* smax - # of scaled fonts */
40
0
#define bmax_SMALL 25000  /* bmax - space for cached chars */
41
0
#define mmax_SMALL 40    /* mmax - # of cached font/matrix pairs */
42
0
#define cmax_SMALL 500    /* cmax - # of cached chars */
43
0
#define blimit_SMALL 100  /* blimit/upper - max size of a single cached char */
44
45
/* Define a default procedure vector for fonts. */
46
const gs_font_procs gs_font_procs_default = {
47
    gs_no_define_font,    /* (actually a default) */
48
    gs_no_make_font,    /* (actually a default) */
49
    gs_default_font_info,
50
    gs_default_same_font,
51
    gs_no_encode_char,
52
    gs_no_decode_glyph,
53
    gs_no_enumerate_glyph,
54
    gs_default_glyph_info,
55
    gs_no_glyph_outline,
56
    gs_no_glyph_name,
57
    gs_default_init_fstack,
58
    gs_default_next_char_glyph,
59
    gs_no_build_char
60
};
61
62
private_st_font_dir();
63
static struct_proc_enum_ptrs(font_enum_ptrs);
64
static struct_proc_reloc_ptrs(font_reloc_ptrs);
65
66
public_st_gs_font_info();
67
public_st_gs_font();
68
public_st_gs_font_base();
69
private_st_gs_font_ptr();
70
public_st_gs_font_ptr_element();
71
72
/*
73
 * Garbage collection of fonts poses some special problems.  On the one
74
 * hand, we need to keep track of all existing base (not scaled) fonts,
75
 * using the next/prev list whose head is the orig_fonts member of the font
76
 * directory; on the other hand, we want these to be "weak" pointers that
77
 * don't keep fonts in existence if the fonts aren't referenced from
78
 * anywhere else.  We accomplish this as follows:
79
 *
80
 *     We don't trace through gs_font_dir.orig_fonts or gs_font.{next,prev}
81
 * during the mark phase of the GC.
82
 *
83
 *     When we finalize a base gs_font, we unlink it from the list.  (A
84
 * gs_font is a base font iff its base member points to itself.)
85
 *
86
 *     We *do* relocate the orig_fonts and next/prev pointers during the
87
 * relocation phase of the GC.  */
88
89
/* Font directory GC procedures */
90
static
91
7.81k
ENUM_PTRS_WITH(font_dir_enum_ptrs, gs_font_dir *dir)
92
868
{
93
    /* Enumerate pointers from cached characters to f/m pairs, */
94
    /* and mark the cached character glyphs. */
95
    /* See gxfcache.h for why we do this here. */
96
868
    uint cci = index - st_font_dir_max_ptrs;
97
868
    uint offset, count;
98
868
    uint tmask = dir->ccache.table_mask;
99
100
868
    if (cci == 0)
101
868
        offset = 0, count = 1;
102
0
    else if (cci == dir->enum_index + 1)
103
0
        offset = dir->enum_offset + 1, count = 1;
104
0
    else
105
0
        offset = 0, count = cci;
106
14.2M
    for (; offset <= tmask; ++offset) {
107
14.2M
        cached_char *cc = dir->ccache.table[offset];
108
109
14.2M
        if (cc != 0 && !--count) {
110
0
            (*dir->ccache.mark_glyph)
111
0
                (mem, cc->code, dir->ccache.mark_glyph_data);
112
            /****** HACK: break const.  We'll fix this someday. ******/
113
0
            ((gs_font_dir *)dir)->enum_index = cci;
114
0
            ((gs_font_dir *)dir)->enum_offset = offset;
115
0
            ENUM_RETURN(cc_pair(cc) - cc->pair_index);
116
0
        }
117
14.2M
    }
118
868
}
119
868
return 0;
120
6.94k
#define e1(i,elt) ENUM_PTR(i,gs_font_dir,elt);
121
7.81k
font_dir_do_ptrs(e1)
122
7.81k
#undef e1
123
7.81k
ENUM_PTRS_END
124
434
static RELOC_PTRS_WITH(font_dir_reloc_ptrs, gs_font_dir *dir);
125
    /* Relocate the pointers from cached characters to f/m pairs. */
126
    /* See gxfcache.h for why we do this here. */
127
434
{
128
434
    int chi;
129
130
7.11M
    for (chi = dir->ccache.table_mask; chi >= 0; --chi) {
131
7.11M
        cached_char *cc = dir->ccache.table[chi];
132
133
7.11M
        if (cc != 0)
134
0
            cc_set_pair_only(cc,
135
7.11M
                             (cached_fm_pair *)
136
7.11M
                             RELOC_OBJ(cc_pair(cc) - cc->pair_index) +
137
7.11M
                             cc->pair_index);
138
7.11M
    }
139
434
}
140
    /* We have to relocate the cached characters before we */
141
    /* relocate dir->ccache.table! */
142
434
RELOC_PTR(gs_font_dir, orig_fonts);
143
3.47k
#define r1(i,elt) RELOC_PTR(gs_font_dir, elt);
144
3.47k
font_dir_do_ptrs(r1)
145
434
#undef r1
146
434
RELOC_PTRS_END
147
148
/* GC procedures for fonts */
149
/*
150
 * When we finalize a base font, we unlink it from the orig_fonts list;
151
 * when we finalize a scaled font, we unlink it from scaled_fonts.
152
 * See above for more information.
153
 */
154
void
155
gs_font_finalize(const gs_memory_t *cmem, void *vptr)
156
218
{
157
218
    gs_font *const pfont = vptr;
158
218
    gs_font **ppfirst;
159
218
    gs_font *next = pfont->next;
160
218
    gs_font *prev = pfont->prev;
161
218
    (void)cmem; /* unused */
162
163
218
    if_debug4m('u', cmem, "[u]unlinking font "PRI_INTPTR", base="PRI_INTPTR", prev="PRI_INTPTR", next="PRI_INTPTR"\n",
164
218
               (intptr_t)pfont, (intptr_t)pfont->base, (intptr_t)prev, (intptr_t)next);
165
    /* Notify clients that the font is being freed. */
166
218
    gs_notify_all(&pfont->notify_list, NULL);
167
218
    gs_purge_font_from_char_caches(pfont);
168
218
    if (pfont->dir == 0)
169
0
        ppfirst = 0;
170
218
    else if (pfont->base == pfont)
171
218
        ppfirst = &pfont->dir->orig_fonts;
172
0
    else {
173
        /*
174
         * Track the number of cached scaled fonts.  Only decrement the
175
         * count if we didn't do this already in gs_makefont.
176
         */
177
0
        if (next || prev || pfont->dir->scaled_fonts == pfont)
178
0
            pfont->dir->ssize--;
179
0
        ppfirst = &pfont->dir->scaled_fonts;
180
0
    }
181
    /*
182
     * gs_purge_font may have unlinked this font already:
183
     * don't unlink it twice.
184
     */
185
218
    if (next != 0 && next->prev == pfont)
186
0
        next->prev = prev;
187
218
    if (prev != 0) {
188
0
        if (prev->next == pfont)
189
0
            prev->next = next;
190
218
    } else if (ppfirst != 0 && *ppfirst == pfont)
191
1
        *ppfirst = next;
192
218
    gs_notify_release(&pfont->notify_list);
193
218
}
194
static
195
3.03k
ENUM_PTRS_WITH(font_enum_ptrs, gs_font *pfont) return ENUM_USING(st_gs_notify_list, &pfont->notify_list, sizeof(gs_notify_list_t), index - 5);
196
        /* We don't enumerate next or prev of base fonts. */
197
        /* See above for details. */
198
434
case 0: ENUM_RETURN((pfont->base == pfont ? 0 : pfont->next));
199
434
case 1: ENUM_RETURN((pfont->base == pfont ? 0 : pfont->prev));
200
3.03k
ENUM_PTR3(2, gs_font, dir, base, client_data);
201
3.03k
ENUM_PTRS_END
202
434
static RELOC_PTRS_WITH(font_reloc_ptrs, gs_font *pfont);
203
434
RELOC_USING(st_gs_notify_list, &pfont->notify_list, sizeof(gs_notify_list_t));
204
        /* We *do* always relocate next and prev. */
205
        /* Again, see above for details. */
206
434
RELOC_PTR(gs_font, next);
207
434
RELOC_PTR(gs_font, prev);
208
434
RELOC_PTR3(gs_font, dir, base, client_data);
209
434
RELOC_PTRS_END
210
211
/* Allocate a font directory */
212
static bool
213
cc_no_mark_glyph(const gs_memory_t *mem, gs_glyph glyph, void *ignore_data)
214
0
{
215
0
    return false;
216
0
}
217
gs_font_dir *
218
gs_font_dir_alloc2(gs_memory_t * struct_mem, gs_memory_t * bits_mem)
219
218
{
220
218
    gs_font_dir *pdir = 0;
221
222
218
#if !ARCH_SMALL_MEMORY
223
#  ifdef DEBUG
224
    if (!gs_debug_c('.'))
225
#  endif
226
218
    {       /* Try allocating a very large cache. */
227
        /* If this fails, allocate a small one. */
228
218
        pdir = gs_font_dir_alloc2_limits(struct_mem, bits_mem,
229
218
                                         smax_LARGE, bmax_LARGE, mmax_LARGE,
230
218
                                         cmax_LARGE, blimit_LARGE);
231
218
    }
232
218
    if (pdir == 0)
233
0
#endif
234
0
        pdir = gs_font_dir_alloc2_limits(struct_mem, bits_mem,
235
0
                                         smax_SMALL, bmax_SMALL, mmax_SMALL,
236
0
                                         cmax_SMALL, blimit_SMALL);
237
218
    if (pdir == 0)
238
0
        return 0;
239
218
    pdir->ccache.mark_glyph = cc_no_mark_glyph;
240
218
    pdir->ccache.mark_glyph_data = 0;
241
218
    return pdir;
242
218
}
243
gs_font_dir *
244
gs_font_dir_alloc2_limits(gs_memory_t * struct_mem, gs_memory_t * bits_mem,
245
                     uint smax, uint bmax, uint mmax, uint cmax, uint upper)
246
218
{
247
218
    gs_font_dir *pdir =
248
218
        gs_alloc_struct(struct_mem, gs_font_dir, &st_font_dir,
249
218
                        "font_dir_alloc(dir)");
250
218
    int code;
251
252
218
    if (pdir == 0)
253
0
        return 0;
254
218
    memset(pdir, 0, sizeof(*pdir));
255
218
    code = gx_char_cache_alloc(struct_mem, bits_mem, pdir,
256
218
                               bmax, mmax, cmax, upper);
257
218
    if (code < 0) {
258
0
        gs_free_object(struct_mem, pdir->ccache.table, "font_dir_alloc(chars)");
259
0
        gs_free_object(struct_mem, pdir->fmcache.mdata, "font_dir_alloc(mdata)");
260
0
        gs_free_object(struct_mem, pdir, "font_dir_alloc(dir)");
261
0
        return 0;
262
0
    }
263
218
    pdir->orig_fonts = 0;
264
218
    pdir->scaled_fonts = 0;
265
218
    pdir->ssize = 0;
266
218
    pdir->smax = smax;
267
218
    pdir->align_to_pixels = false;
268
218
    pdir->glyph_to_unicode_table = NULL;
269
218
    pdir->grid_fit_tt = 1;
270
218
    pdir->memory = struct_mem;
271
218
    pdir->tti = 0;
272
218
    pdir->ttm = 0;
273
218
    pdir->san = 0;
274
218
    pdir->global_glyph_code = NULL;
275
218
    pdir->text_enum_id = 0;
276
218
    pdir->hash = 42;  /* initialize the hash to a randomly picked number */
277
218
    return pdir;
278
218
}
279
static void
280
gs_font_dir_finalize(const gs_memory_t *cmem, void *vptr)
281
218
{
282
218
    gs_font_dir *pdir = vptr;
283
218
    gx_bits_cache_chunk *chunk = pdir->ccache.chunks;
284
218
    gx_bits_cache_chunk *start_chunk = chunk;
285
218
    gx_bits_cache_chunk *prev_chunk;
286
287
218
    if (pdir == cmem->gs_lib_ctx->font_dir) {
288
218
        cmem->gs_lib_ctx->font_dir = NULL;
289
218
    }
290
291
    /* free character cache machinery */
292
218
    gs_free_object(pdir->memory, pdir->fmcache.mdata, "gs_font_dir_finalize");
293
218
    gs_free_object(pdir->memory, pdir->ccache.table, "gs_font_dir_finalize");
294
295
    /* free the circular list of memory chunks */
296
218
    while (chunk) {
297
218
        if (start_chunk == chunk->next) {
298
218
            gs_free_object(pdir->ccache.bits_memory, chunk->data, "gs_font_dir_finalize");
299
218
            gs_free_object(pdir->ccache.bits_memory, chunk, "gs_font_dir_finalize");
300
218
            break;
301
218
        }
302
303
0
        prev_chunk = chunk;
304
0
        chunk = chunk->next;
305
0
        gs_free_object(pdir->ccache.bits_memory, prev_chunk->data, "gs_font_dir_finalize");
306
0
        gs_free_object(pdir->ccache.bits_memory, prev_chunk, "gs_font_dir_finalize");
307
0
    }
308
218
    pdir->ccache.chunks = NULL;
309
218
}
310
311
/* Allocate and minimally initialize a font. */
312
gs_font *
313
gs_font_alloc(gs_memory_t *mem, gs_memory_type_ptr_t pstype,
314
              const gs_font_procs *procs, gs_font_dir *dir,
315
              client_name_t cname)
316
218
{
317
218
    gs_font *pfont = gs_alloc_struct(mem, gs_font, pstype, cname);
318
319
218
    if (pfont == 0)
320
0
        return 0;
321
218
#if 1 /* Clear entire structure to avoid unitialized pointers
322
         when the initialization exits prematurely by error. */
323
218
    memset(pfont, 0, pstype->ssize);
324
218
    pfont->memory = mem;
325
218
    pfont->dir = dir;
326
218
    gs_font_notify_init(pfont);
327
218
    pfont->id = gs_next_ids(mem, 1);
328
218
    pfont->base = pfont;
329
218
    pfont->ExactSize = pfont->InBetweenSize = pfont->TransformedChar =
330
218
        fbit_use_outlines;
331
218
    pfont->procs = *procs;
332
#else
333
    /* For clarity we leave old initializations here
334
       to know which fields needs to be initialized. */
335
    pfont->next = pfont->prev = 0;
336
    pfont->memory = mem;
337
    pfont->dir = dir;
338
    pfont->is_resource = false;
339
    gs_font_notify_init(pfont);
340
    pfont->id = gs_next_ids(mem, 1);
341
    pfont->base = pfont;
342
    pfont->client_data = 0;
343
    /* not FontMatrix, FontType */
344
    pfont->BitmapWidths = false;
345
    pfont->ExactSize = pfont->InBetweenSize = pfont->TransformedChar =
346
        fbit_use_outlines;
347
    pfont->WMode = 0;
348
    pfont->PaintType = 0;
349
    pfont->StrokeWidth = 0;
350
    pfont->is_cached = false;
351
    pfont->procs = *procs;
352
    memset(&pfont->orig_FontMatrix, 0, sizeof(pfont->orig_FontMatrix));
353
#endif
354
    /* not key_name, font_name */
355
218
    return pfont;
356
218
}
357
/* Allocate and minimally initialize a base font. */
358
gs_font_base *
359
gs_font_base_alloc(gs_memory_t *mem, gs_memory_type_ptr_t pstype,
360
                   const gs_font_procs *procs, gs_font_dir *dir,
361
                   client_name_t cname)
362
0
{
363
0
    gs_font_base *pfont =
364
0
        (gs_font_base *)gs_font_alloc(mem, pstype, procs, dir, cname);
365
366
0
    if (pfont == 0)
367
0
        return 0;
368
0
    pfont->FontBBox.p.x = pfont->FontBBox.p.y =
369
0
        pfont->FontBBox.q.x = pfont->FontBBox.q.y = 0;
370
0
    uid_set_invalid(&pfont->UID);
371
0
    pfont->encoding_index = pfont->nearest_encoding_index = -1;
372
0
    return pfont;
373
0
}
374
375
/* Initialize the notification list for a font. */
376
void
377
gs_font_notify_init(gs_font *font)
378
218
{
379
    /*
380
     * The notification list for a font must be allocated in the font's
381
     * stable memory, because of the following possible sequence of events:
382
     *
383
     *   - Allocate font X in local VM.
384
     *   - Client A registers for notification when X is freed.
385
     *   - 'save'
386
     *   - Client B registers for notification when X is freed.
387
     *   - 'restore'
388
     *
389
     * If the notification list element for client B is allocated in
390
     * restorable local VM (i.e., the same VM as the font), then when the
391
     * 'restore' occurs, either the list element will be deleted (not what
392
     * client B wants, because font X hasn't been freed yet), or there will
393
     * be a dangling pointer.
394
     */
395
218
    gs_notify_init(&font->notify_list, gs_memory_stable(font->memory));
396
218
}
397
398
/*
399
 * Register/unregister a client for notification by a font.  Currently
400
 * the clients are only notified when a font is freed.  Note that any
401
 * such client must unregister itself when *it* is freed.
402
 */
403
int
404
gs_font_notify_register(gs_font *font, gs_notify_proc_t proc, void *proc_data)
405
0
{
406
0
    return gs_notify_register(&font->notify_list, proc, proc_data);
407
0
}
408
int
409
gs_font_notify_unregister(gs_font *font, gs_notify_proc_t proc, void *proc_data)
410
0
{
411
0
    return gs_notify_unregister(&font->notify_list, proc, proc_data);
412
0
}
413
414
/* Link an element at the head of a chain. */
415
static void
416
font_link_first(gs_font **pfirst, gs_font *elt)
417
218
{
418
218
    gs_font *first = elt->next = *pfirst;
419
420
218
    if (first)
421
0
        first->prev = elt;
422
218
    elt->prev = 0;
423
218
    *pfirst = elt;
424
218
}
425
426
/* definefont */
427
/* Use this only for original (unscaled) fonts! */
428
/* Note that it expects pfont->procs.define_font to be set already. */
429
int
430
gs_definefont(gs_font_dir * pdir, gs_font * pfont)
431
218
{
432
218
    int code;
433
434
218
    pfont->dir = pdir;
435
218
    pfont->base = pfont;
436
218
    code = (*pfont->procs.define_font) (pdir, pfont);
437
218
    if (code < 0) {   /* Make sure we don't try to finalize this font. */
438
0
        pfont->base = 0;
439
0
        return code;
440
0
    }
441
218
    font_link_first(&pdir->orig_fonts, pfont);
442
218
    if_debug2m('m', pfont->memory, "[m]defining font "PRI_INTPTR", next="PRI_INTPTR"\n",
443
218
               (intptr_t)pfont, (intptr_t)pfont->next);
444
218
    return 0;
445
218
}
446
447
/* Find a sililar registered font of same type. */
448
int
449
gs_font_find_similar(const gs_font_dir * pdir, const gs_font **ppfont,
450
                       int (*similar)(const gs_font *, const gs_font *))
451
0
{
452
0
    const gs_font *pfont0 = *ppfont;
453
0
    const gs_font *pfont1 = pdir->orig_fonts;
454
455
0
    for (; pfont1 != NULL; pfont1 = pfont1->next) {
456
0
        if (pfont1 != pfont0 && pfont1->FontType == pfont0->FontType) {
457
0
            int code = similar(pfont0, pfont1);
458
0
            if (code != 0) {
459
0
                *ppfont = pfont1;
460
0
                return code;
461
0
            }
462
0
        }
463
0
    }
464
0
    return 0;
465
0
}
466
467
/* scalefont */
468
int
469
gs_scalefont(gs_font_dir * pdir, const gs_font * pfont, double scale,
470
             gs_font ** ppfont)
471
0
{
472
0
    gs_matrix mat;
473
474
0
    gs_make_scaling(scale, scale, &mat);
475
0
    return gs_makefont(pdir, pfont, &mat, ppfont);
476
0
}
477
478
/* makefont */
479
int
480
gs_makefont(gs_font_dir * pdir, const gs_font * pfont,
481
            const gs_matrix * pmat, gs_font ** ppfont)
482
0
{
483
0
    int code;
484
0
    gs_font *prev = 0;
485
0
    gs_font *pf_out = pdir->scaled_fonts;
486
0
    gs_memory_t *mem = pfont->memory;
487
0
    gs_matrix newmat;
488
0
    bool can_cache;
489
490
0
    if ((code = gs_matrix_multiply(&pfont->FontMatrix, pmat, &newmat)) < 0)
491
0
        return code;
492
    /*
493
     * Check for the font already being in the scaled font cache.
494
     * Until version 5.97, we only cached scaled fonts if the base
495
     * (unscaled) font had a valid UniqueID or XUID; now, we will cache
496
     * scaled versions of any non-composite font.
497
     */
498
#ifdef DEBUG
499
    if (gs_debug_c('m')) {
500
        const gs_font_base *const pbfont = (const gs_font_base *)pfont;
501
502
        if (pfont->FontType == ft_composite)
503
            dmlprintf(mem, "[m]composite");
504
        else if (uid_is_UniqueID(&pbfont->UID))
505
            dmlprintf1(mem, "[m]UniqueID=%ld", pbfont->UID.id);
506
        else if (uid_is_XUID(&pbfont->UID))
507
            dmlprintf1(mem, "[m]XUID(%u)", (uint) (-pbfont->UID.id));
508
        else
509
            dmlprintf(mem, "[m]no UID");
510
        dmprintf8(mem, ", FontType=%d, base="PRI_INTPTR",\n[m]  new FontMatrix=[%g %g %g %g %g %g]\n",
511
                 pfont->FontType, (intptr_t)pfont->base,
512
                 pmat->xx, pmat->xy, pmat->yx, pmat->yy,
513
                 pmat->tx, pmat->ty);
514
    }
515
#endif
516
    /*
517
     * Don't try to cache scaled composite fonts, because of the side
518
     * effects on FDepVector and descendant fonts that occur in makefont.
519
     */
520
0
    if (pfont->FontType != ft_composite) {
521
0
        for (; pf_out != 0; prev = pf_out, pf_out = pf_out->next)
522
0
            if (pf_out->FontType == pfont->FontType &&
523
0
                pf_out->base == pfont->base &&
524
0
                pf_out->FontMatrix.xx == newmat.xx &&
525
0
                pf_out->FontMatrix.xy == newmat.xy &&
526
0
                pf_out->FontMatrix.yx == newmat.yx &&
527
0
                pf_out->FontMatrix.yy == newmat.yy &&
528
0
                pf_out->FontMatrix.tx == newmat.tx &&
529
0
                pf_out->FontMatrix.ty == newmat.ty
530
0
                ) {
531
0
                *ppfont = pf_out;
532
0
                if_debug1m('m', pfont->memory, "[m]found font="PRI_INTPTR"\n", (intptr_t)pf_out);
533
0
                return 0;
534
0
            }
535
0
        can_cache = true;
536
0
    } else
537
0
        can_cache = false;
538
0
    pf_out = gs_alloc_struct(mem, gs_font, gs_object_type(mem, pfont),
539
0
                             "gs_makefont");
540
0
    if (!pf_out)
541
0
        return_error(gs_error_VMerror);
542
0
    memcpy(pf_out, pfont, gs_object_size(mem, pfont));
543
0
    gs_font_notify_init(pf_out);
544
0
    pf_out->FontMatrix = newmat;
545
0
    pf_out->client_data = 0;
546
0
    pf_out->dir = pdir;
547
0
    pf_out->base = pfont->base;
548
0
    *ppfont = pf_out;
549
0
    code = (*pf_out->procs.make_font) (pdir, pfont, pmat, ppfont);
550
0
    if (code < 0)
551
0
        return code;
552
0
    if (can_cache) {
553
0
        if (pdir->ssize >= pdir->smax && prev != 0) {
554
            /*
555
             * We must discard a cached scaled font.
556
             * prev points to the last (oldest) font.
557
             * (We can't free it, because there might be
558
             * other references to it.)
559
             */
560
0
            if_debug1m('m', pfont->memory, "[m]discarding font "PRI_INTPTR"\n",
561
0
                      (intptr_t)prev);
562
0
            if (prev->prev != 0)
563
0
                prev->prev->next = 0;
564
0
            else
565
0
                pdir->scaled_fonts = 0;
566
0
            pdir->ssize--;
567
0
            prev->prev = 0;
568
            /* This comment is a relatively new reconstruction of old assumptions,
569
               which were done 5+ years ago (see gsfont.c revision 1.1).
570
               Here the font is only removed from the pdir->scaled_fonts list
571
               to prevent the latter to grow huge. Thus the list is used only to
572
               merge scaled font duplicates by the 'for' loop in the beginning
573
               of this function. We do not discard related character rasters
574
               from character cache due to 3 reasons :
575
               1. At this point a cached_char instance may be referred
576
                  by one or more gs_show_enum instances, which may exist on the
577
                  PS estack while execution of a Type 3 BuildChar or BuildGlyph.
578
                  Such event really happens while rendering a re-distilled tpc2.ps .
579
                  We must not remove those isntances, but currently there is no
580
                  mechanizm for distinguishing them from othgers.
581
               2. If the font has an UID, another scaled font may use same fm_pair
582
                  instance due to different CTMs. Therefore same character rasters
583
                  may be useful for another scaled font.
584
               3. We don't know whether the font will be used again in nearest
585
                  future. Maybe it will be used again in the next 'show' operation.
586
                  Therefore we delay the decision about discarding character
587
                  rasters untill we need to release memory from them.
588
               4. Also note that the last created font, rather than the last used one,
589
                  is being discarded. An useful improvement would be
590
                  to move a font t the beginning of the list whenever it
591
                  appears in a show-like operation.
592
             */
593
#if 0     /* We disabled this code portion due to Bug 688392.
594
               The problem was dangling pointers, which appear in fm_pair instances
595
               after uid_free is applied to a font's UID,
596
               because they share same xvalues array. We're unable to guess
597
               for which reason uid_free was applied to the font's UID here
598
               5+ years ago (see gsfont.c revision 1.1).
599
               We do not remove this code portion until we get
600
               a complete understanding.
601
             */
602
            if (prev->FontType != ft_composite) {
603
                if_debug1m('m', pfont->memory, "[m]discarding UID 0x%lx\n",
604
                           (ulong) ((gs_font_base *) prev)->
605
                           UID.xvalues);
606
                uid_free(&((gs_font_base *) prev)->UID,
607
                         prev->memory,
608
                         "gs_makefont(discarding)");
609
                uid_set_invalid(&((gs_font_base *) prev)->UID);
610
            }
611
#endif
612
0
        }
613
0
        pdir->ssize++;
614
0
        font_link_first(&pdir->scaled_fonts, pf_out);
615
0
    } else {     /* Prevent garbage pointers. */
616
0
        pf_out->next = pf_out->prev = 0;
617
0
    }
618
0
    if_debug2m('m', pfont->memory, "[m]new font="PRI_INTPTR" can_cache=%s\n",
619
0
               (intptr_t)*ppfont, (can_cache ? "true" : "false"));
620
0
    return 1;
621
0
}
622
623
/* Set the current font.  This is provided only for the benefit of cshow, */
624
/* which must reset the current font without disturbing the root font. */
625
void
626
gs_set_currentfont(gs_gstate * pgs, gs_font * pfont)
627
0
{
628
0
    pgs->font = pfont;
629
0
    pgs->char_tm_valid = false;
630
0
}
631
632
/* setfont */
633
int
634
gs_setfont(gs_gstate * pgs, gs_font * pfont)
635
218
{
636
218
    pgs->font = pgs->root_font = pfont;
637
218
    pgs->char_tm_valid = false;
638
218
    return 0;
639
218
}
640
641
/* currentfont */
642
gs_font *
643
gs_currentfont(const gs_gstate * pgs)
644
218
{
645
218
    return pgs->font;
646
218
}
647
648
/* rootfont */
649
gs_font *
650
gs_rootfont(const gs_gstate * pgs)
651
0
{
652
0
    return pgs->root_font;
653
0
}
654
655
/* cachestatus */
656
void
657
gs_cachestatus(register const gs_font_dir * pdir, register uint pstat[7])
658
1.73k
{
659
1.73k
    pstat[0] = pdir->ccache.bsize;
660
1.73k
    pstat[1] = pdir->ccache.bmax;
661
1.73k
    pstat[2] = pdir->fmcache.msize;
662
1.73k
    pstat[3] = pdir->fmcache.mmax;
663
1.73k
    pstat[4] = pdir->ccache.csize;
664
1.73k
    pstat[5] = pdir->ccache.cmax;
665
1.73k
    pstat[6] = pdir->ccache.upper;
666
1.73k
}
667
668
/* setcacheparams */
669
int
670
gs_setcachesize(gs_gstate * pgs, gs_font_dir * pdir, uint size)
671
0
{
672
0
    gs_memory_t *stable_mem = pdir->memory->stable_memory;
673
0
    if (size < 100000)             /* limits derived from CPSI emulation (CET 27-07) */
674
0
        size = 100000;
675
0
    else if (size > 100000000)
676
0
        size = 100000000;
677
678
    /* Changing the cache size precipitates rebuilding the cache data
679
       structures.  Start with freeing cached chars and fm pairs. */
680
0
    {
681
0
        gs_font *pfont;
682
0
        int code;
683
0
        for (pfont = pdir->scaled_fonts; pfont != 0; pfont = pfont->next) {
684
0
            code = gs_purge_font_from_char_caches_completely(pfont);
685
0
            if (code != 0)
686
0
                gs_rethrow_code(code);
687
688
0
        }
689
0
    }
690
691
    /* now free the cache structures and rebuild everything with the
692
       new cache size */
693
0
    gs_free_object(stable_mem, pdir->fmcache.mdata, "gs_setcachesize(mdata)");
694
0
    gs_free_object(stable_mem, pdir->ccache.table, "gs_setcachesize(table)");
695
0
    pdir->ccache.bmax = size;
696
0
    return gx_char_cache_alloc(stable_mem, stable_mem->non_gc_memory, pdir,
697
0
                               pdir->ccache.bmax, pdir->fmcache.mmax,
698
0
                               pdir->ccache.cmax, pdir->ccache.upper);
699
0
}
700
701
int
702
gs_setcachelower(gs_font_dir * pdir, uint size)
703
1.08k
{
704
1.08k
    pdir->ccache.lower = ((int)size < 0) ? 0 : size; /* ?: for CET 27-07 */
705
1.08k
    return 0;
706
1.08k
}
707
int
708
gs_setcacheupper(gs_font_dir * pdir, uint size)
709
1.08k
{
710
1.08k
    pdir->ccache.upper = ((int)size < 0) ? 0 : size; /* ?: for CET 27-06 */
711
1.08k
    return 0;
712
1.08k
}
713
int
714
gs_setaligntopixels(gs_font_dir * pdir, uint v)
715
434
{
716
434
    pdir->align_to_pixels = v;
717
434
    return 0;
718
434
}
719
int
720
gs_setgridfittt(gs_font_dir * pdir, uint v)
721
434
{
722
434
    pdir->grid_fit_tt = v;
723
434
    return 0;
724
434
}
725
726
/* currentcacheparams */
727
uint
728
gs_currentcachesize(const gs_font_dir * pdir)
729
1.73k
{
730
1.73k
    return pdir->ccache.bmax;
731
1.73k
}
732
uint
733
gs_currentcachelower(const gs_font_dir * pdir)
734
1.30k
{
735
1.30k
    return pdir->ccache.lower;
736
1.30k
}
737
uint
738
gs_currentcacheupper(const gs_font_dir * pdir)
739
1.30k
{
740
1.30k
    return pdir->ccache.upper;
741
1.30k
}
742
uint
743
gs_currentaligntopixels(const gs_font_dir * pdir)
744
870
{
745
870
    return pdir->align_to_pixels;
746
870
}
747
uint
748
gs_currentgridfittt(const gs_font_dir * pdir)
749
870
{
750
870
    return pdir->grid_fit_tt;
751
870
}
752
753
/* Purge a font from all font- and character-related tables. */
754
/* This is only used by restore (and, someday, the GC). */
755
int
756
gs_purge_font(gs_font * pfont)
757
217
{
758
217
    gs_font_dir *pdir = pfont->dir;
759
217
    gs_font *pf;
760
761
    /* Remove the font from its list (orig_fonts or scaled_fonts). */
762
217
    gs_font *prev = pfont->prev;
763
217
    gs_font *next = pfont->next;
764
765
217
    if (next != 0)
766
0
        next->prev = prev, pfont->next = 0;
767
217
    if (prev != 0)
768
0
        prev->next = next, pfont->prev = 0;
769
217
    else if (pdir->orig_fonts == pfont)
770
217
        pdir->orig_fonts = next;
771
0
    else if (pdir->scaled_fonts == pfont)
772
0
        pdir->scaled_fonts = next;
773
0
    else {     /* Shouldn't happen! */
774
0
        lprintf1("purged font "PRI_INTPTR" not found\n", (intptr_t)pfont);
775
0
    }
776
777
    /* Purge the font from the scaled font cache. */
778
217
    for (pf = pdir->scaled_fonts; pf != 0;) {
779
0
        if (pf->base == pfont) {
780
0
            int code = gs_purge_font(pf);
781
782
0
            if (code < 0)
783
0
                return code;
784
0
            pf = pdir->scaled_fonts;  /* start over */
785
0
        } else
786
0
            pf = pf->next;
787
0
    }
788
789
    /* Purge the font from the font/matrix pair cache, */
790
    /* including all cached characters rendered with that font. */
791
217
    return gs_purge_font_from_char_caches(pfont);
792
217
}
793
794
/* Locate a gs_font by gs_id. */
795
gs_font *
796
gs_find_font_by_id(gs_font_dir *pdir, gs_id id, gs_matrix *FontMatrix)
797
0
 {
798
0
    gs_font *pfont = pdir->orig_fonts;
799
800
0
    for(; pfont != NULL; pfont = pfont->next)
801
0
        if(pfont->id == id &&
802
0
            !gs_matrix_compare(&pfont->FontMatrix, FontMatrix))
803
0
            return pfont;
804
0
    return NULL;
805
0
 }
806
807
/* ---------------- Default font procedures ---------------- */
808
809
/* ------ Font-level procedures ------ */
810
811
/* Default (vacuous) definefont handler. */
812
int
813
gs_no_define_font(gs_font_dir * pdir, gs_font * pfont)
814
218
{
815
218
    return 0;
816
218
}
817
818
/* Default (vacuous) makefont handler. */
819
int
820
gs_no_make_font(gs_font_dir * pdir, const gs_font * pfont,
821
                const gs_matrix * pmat, gs_font ** ppfont)
822
0
{
823
0
    return 0;
824
0
}
825
/* Makefont handler for base fonts, which must copy the XUID. */
826
int
827
gs_base_make_font(gs_font_dir * pdir, const gs_font * pfont,
828
                  const gs_matrix * pmat, gs_font ** ppfont)
829
0
{
830
0
    return uid_copy(&((gs_font_base *)*ppfont)->UID, (*ppfont)->memory,
831
0
                    "gs_base_make_font(XUID)");
832
0
}
833
834
/* Default font info procedure */
835
int
836
gs_default_font_info(gs_font *font, const gs_point *pscale, int members,
837
                     gs_font_info_t *info)
838
0
{
839
0
    int wmode = font->WMode;
840
0
    gs_font_base *bfont = (gs_font_base *)font;
841
0
    gs_point scale;
842
0
    gs_matrix smat;
843
0
    const gs_matrix *pmat;
844
845
0
    if (pscale == 0) {
846
0
        scale.x = scale.y = 0;
847
0
        pmat = 0;
848
0
    } else {
849
0
        scale = *pscale;
850
0
        gs_make_scaling(scale.x, scale.y, &smat);
851
0
        pmat = &smat;
852
0
    }
853
0
    info->members = 0;
854
0
    if (members & FONT_INFO_FLAGS)
855
0
        info->Flags_returned = 0;
856
0
    if (font->FontType == ft_composite)
857
0
        return 0;   /* nothing available */
858
0
    if (members & FONT_INFO_BBOX) {
859
0
        info->BBox.p.x = (int)bfont->FontBBox.p.x;
860
0
        info->BBox.p.y = (int)bfont->FontBBox.p.y;
861
0
        info->BBox.q.x = (int)bfont->FontBBox.q.x;
862
0
        info->BBox.q.y = (int)bfont->FontBBox.q.y;
863
0
        info->Flags_returned |= FONT_INFO_BBOX;
864
0
    }
865
0
    if ((members & FONT_INFO_FLAGS) &&
866
0
        (info->Flags_requested & FONT_IS_FIXED_WIDTH)
867
0
        ) {
868
        /*
869
         * Scan the glyph space to compute the fixed width if any.
870
         */
871
0
        gs_glyph notdef = GS_NO_GLYPH;
872
0
        gs_glyph glyph;
873
0
        int fixed_width = 0;
874
0
        int index;
875
0
        int code = 0; /* Quiet compiler. */
876
0
        int ecode = 0;
877
0
        bool has_glyphs = false;
878
879
0
        for (index = 0;
880
0
             fixed_width >= 0 &&
881
0
             (code = font->procs.enumerate_glyph(font, &index, GLYPH_SPACE_NAME, &glyph)) >= 0 &&
882
0
                 index != 0;
883
0
             ) {
884
0
            gs_glyph_info_t glyph_info;
885
886
0
            memset(&glyph_info, 0x00, sizeof(gs_glyph_info_t));
887
0
            code = font->procs.glyph_info(font, glyph, pmat,
888
0
                                          (GLYPH_INFO_WIDTH0 << wmode),
889
0
                                          &glyph_info);
890
0
            if (code < 0) {
891
0
                ecode = code;
892
0
                continue;
893
0
            }
894
0
            if (notdef == GS_NO_GLYPH && gs_font_glyph_is_notdef(bfont, glyph)) {
895
0
                notdef = glyph;
896
0
                info->MissingWidth = (int)glyph_info.width[wmode].x;
897
0
                info->members |= FONT_INFO_MISSING_WIDTH;
898
0
            }
899
0
            if (glyph_info.width[wmode].y != 0)
900
0
                fixed_width = min_int;
901
0
            else if (fixed_width == 0)
902
0
                fixed_width = (int)glyph_info.width[wmode].x;
903
0
            else if (glyph_info.width[wmode].x != fixed_width)
904
0
                fixed_width = min_int;
905
0
            has_glyphs = true;
906
0
        }
907
0
        if (ecode < 0 && !has_glyphs)
908
0
            return ecode;
909
0
        if (fixed_width > 0) {
910
0
            info->Flags |= FONT_IS_FIXED_WIDTH;
911
0
            info->members |= FONT_INFO_AVG_WIDTH | FONT_INFO_MAX_WIDTH |
912
0
                FONT_INFO_MISSING_WIDTH;
913
0
            info->AvgWidth = info->MaxWidth = info->MissingWidth = fixed_width;
914
0
        }
915
0
        info->Flags_returned |= FONT_IS_FIXED_WIDTH;
916
0
    } else if (members & FONT_INFO_MISSING_WIDTH) {
917
0
        gs_glyph glyph;
918
0
        int index;
919
920
0
        for (index = 0;
921
0
             font->procs.enumerate_glyph(font, &index, GLYPH_SPACE_NAME, &glyph) >= 0 &&
922
0
                 index != 0;
923
0
             ) {
924
            /*
925
             * If this is a CIDFont or TrueType font that uses integers as
926
             * glyph names, check for glyph 0; otherwise, check for .notdef.
927
             */
928
0
            if (!gs_font_glyph_is_notdef(bfont, glyph))
929
0
                continue;
930
0
            {
931
0
                gs_glyph_info_t glyph_info;
932
0
                int code = font->procs.glyph_info(font, glyph, pmat,
933
0
                                                  (GLYPH_INFO_WIDTH0 << wmode),
934
0
                                                  &glyph_info);
935
936
0
                if (code < 0)
937
0
                    return code;
938
0
                info->MissingWidth = (int)glyph_info.width[wmode].x;
939
0
                info->members |= FONT_INFO_MISSING_WIDTH;
940
0
                break;
941
0
            }
942
0
        }
943
0
    }
944
0
    return 0;
945
0
}
946
947
/* Default font similarity testing procedure */
948
int
949
gs_default_same_font(const gs_font *font, const gs_font *ofont, int mask)
950
0
{
951
0
    while (font->base != font)
952
0
        font = font->base;
953
0
    while (ofont->base != ofont)
954
0
        ofont = ofont->base;
955
0
    if (ofont == font)
956
0
        return mask;
957
    /* In general, we can't determine similarity. */
958
0
    return 0;
959
0
}
960
int
961
gs_base_same_font(const gs_font *font, const gs_font *ofont, int mask)
962
0
{
963
0
    int same = gs_default_same_font(font, ofont, mask);
964
965
0
    if (!same) {
966
0
        const gs_font_base *const bfont = (const gs_font_base *)font;
967
0
        const gs_font_base *const obfont = (const gs_font_base *)ofont;
968
969
0
        if (mask & FONT_SAME_ENCODING) {
970
0
            if (bfont->encoding_index != ENCODING_INDEX_UNKNOWN ||
971
0
                obfont->encoding_index != ENCODING_INDEX_UNKNOWN
972
0
                ) {
973
0
                if (bfont->encoding_index == obfont->encoding_index)
974
0
                    same |= FONT_SAME_ENCODING;
975
0
            }
976
0
        }
977
0
    }
978
0
    return same;
979
0
}
980
981
/* ------ Glyph-level procedures ------ */
982
983
/*
984
 * Test whether a glyph is the notdef glyph for a base font.
985
 * The test is somewhat adhoc: perhaps this should be a virtual procedure.
986
 */
987
bool
988
gs_font_glyph_is_notdef(gs_font_base *bfont, gs_glyph glyph)
989
0
{
990
0
    gs_const_string gnstr;
991
992
0
    if (glyph == GS_NO_GLYPH)
993
0
        return false;
994
0
    if (glyph >= GS_MIN_CID_GLYPH)
995
0
        return (glyph == GS_MIN_CID_GLYPH);
996
0
    return (bfont->procs.glyph_name((gs_font *)bfont, glyph, &gnstr) >= 0 &&
997
0
            gnstr.size == 7 && !memcmp(gnstr.data, ".notdef", 7));
998
0
}
999
1000
/* Dummy character encoding procedure */
1001
gs_glyph
1002
gs_no_encode_char(gs_font *pfont, gs_char chr, gs_glyph_space_t glyph_space)
1003
0
{
1004
0
    return GS_NO_GLYPH;
1005
0
}
1006
1007
/* Dummy glyph decoding procedure */
1008
int
1009
gs_no_decode_glyph(gs_font *pfont, gs_glyph glyph, int ch, ushort *unicode_return, unsigned int length)
1010
0
{
1011
0
    return (int)GS_NO_CHAR;
1012
0
}
1013
1014
/* Dummy glyph enumeration procedure */
1015
int
1016
gs_no_enumerate_glyph(gs_font *font, int *pindex, gs_glyph_space_t glyph_space,
1017
                      gs_glyph *pglyph)
1018
0
{
1019
0
    return_error(gs_error_undefined);
1020
0
}
1021
1022
/* Default glyph info procedure */
1023
int
1024
gs_default_glyph_info(gs_font *font, gs_glyph glyph, const gs_matrix *pmat,
1025
                      int members, gs_glyph_info_t *info)
1026
0
{   /* WMode may be inherited from an upper font. */
1027
0
    gx_path path;
1028
0
    int returned = 0;
1029
0
    int code;
1030
0
    int wmode = ((members & GLYPH_INFO_WIDTH1) != 0);
1031
0
    double sbw[4] = {0, 0, 0, 0};
1032
    /* Currently glyph_outline retrieves sbw only with type 1,2,9 fonts. */
1033
0
    bool charstrings_font = (font->FontType == ft_encrypted ||
1034
0
                             font->FontType == ft_encrypted2 ||
1035
0
                             font->FontType == ft_CID_encrypted);
1036
1037
0
    gx_path_init_bbox_accumulator(&path);
1038
0
    code = gx_path_add_point(&path, fixed_0, fixed_0);
1039
0
    if (code < 0)
1040
0
        goto out;
1041
0
    code = font->procs.glyph_outline(font, wmode, glyph, pmat, &path, sbw);
1042
0
    if (code < 0)
1043
0
        goto out;
1044
0
    if (members & GLYPH_INFO_WIDTHS) {
1045
0
        int wmode = font->WMode;
1046
0
        int wmask = GLYPH_INFO_WIDTH0 << wmode;
1047
1048
0
        if (members & wmask) {
1049
0
            gs_fixed_point pt;
1050
1051
0
            code = gx_path_current_point(&path, &pt);
1052
0
            if (code < 0)
1053
0
                goto out;
1054
0
            info->width[wmode].x = fixed2float(pt.x);
1055
0
            info->width[wmode].y = fixed2float(pt.y);
1056
0
            returned |= wmask;
1057
0
        }
1058
0
    }
1059
0
    if (members & GLYPH_INFO_BBOX) {
1060
0
        gs_fixed_rect bbox;
1061
1062
0
        code = gx_path_bbox(&path, &bbox);
1063
0
        if (code < 0)
1064
0
            goto out;
1065
0
        info->bbox.p.x = fixed2float(bbox.p.x);
1066
0
        info->bbox.p.y = fixed2float(bbox.p.y);
1067
0
        info->bbox.q.x = fixed2float(bbox.q.x);
1068
0
        info->bbox.q.y = fixed2float(bbox.q.y);
1069
0
        returned |= GLYPH_INFO_BBOX;
1070
0
    }
1071
0
    if (members & (GLYPH_INFO_WIDTH0 << wmode) && charstrings_font) {
1072
0
        if (pmat == 0) {
1073
0
            info->width[wmode].x = sbw[2];
1074
0
            info->width[wmode].y = sbw[3];
1075
0
        } else {
1076
0
            code = gs_distance_transform(sbw[2], sbw[3], pmat, &info->width[wmode]);
1077
0
            if (code < 0)
1078
0
                return code;
1079
0
        }
1080
0
        returned |= GLYPH_INFO_WIDTH0 << wmode;
1081
0
    }
1082
0
    if (members & (GLYPH_INFO_VVECTOR0 << wmode) && charstrings_font) {
1083
0
        if (pmat == 0) {
1084
0
            info->v.x = sbw[0];
1085
0
            info->v.y = sbw[1];
1086
0
        } else {
1087
0
            code = gs_distance_transform(sbw[0], sbw[1], pmat, &info->v);
1088
0
            if (code < 0)
1089
0
                return code;
1090
0
        }
1091
0
        returned |= GLYPH_INFO_VVECTOR0 << wmode;
1092
0
    }
1093
0
    if (members & GLYPH_INFO_NUM_PIECES) {
1094
0
        info->num_pieces = 0;
1095
0
        returned |= GLYPH_INFO_NUM_PIECES;
1096
0
    }
1097
0
    returned |= members & GLYPH_INFO_PIECES; /* no pieces stored */
1098
0
 out:
1099
0
    info->members = returned;
1100
0
    return code;
1101
0
}
1102
1103
/* Dummy glyph outline procedure */
1104
int
1105
gs_no_glyph_outline(gs_font *font, int WMode, gs_glyph glyph, const gs_matrix *pmat,
1106
                    gx_path *ppath, double sbw[4])
1107
0
{
1108
0
    return_error(gs_error_undefined);
1109
0
}
1110
1111
/* Dummy glyph name procedure */
1112
int
1113
gs_no_glyph_name(gs_font *font, gs_glyph glyph, gs_const_string *pstr)
1114
0
{
1115
0
    return_error(gs_error_undefined);
1116
0
}
1117
1118
#ifdef DEBUG
1119
/* Reserve a text enumerator instance id. */
1120
ulong gs_next_text_enum_id(const gs_font *font)
1121
{
1122
    return ++font->dir->text_enum_id;
1123
}
1124
#endif