/* Copyright (C) 1989, 1995, 1996, 1997, 1998, 1999 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gsfont.c,v 1.4.4.1.2.1 2003/01/17 00:49:02 giles Exp $ */ /* Font operators for Ghostscript library */ #include "gx.h" #include "memory_.h" #include "gserrors.h" #include "gsstruct.h" #include "gsutil.h" #include "gxfixed.h" #include "gxmatrix.h" #include "gzstate.h" /* must precede gxdevice */ #include "gxdevice.h" /* must precede gxfont */ #include "gxfont.h" #include "gxfcache.h" #include "gxpath.h" /* for default implementation */ /* Imported procedures */ void gs_purge_font_from_char_caches(P2(gs_font_dir *, const gs_font *)); /* Define the sizes of the various aspects of the font/character cache. */ /*** Big memory machines ***/ #define smax_LARGE 50 /* smax - # of scaled fonts */ #define bmax_LARGE 500000 /* bmax - space for cached chars */ #define mmax_LARGE 200 /* mmax - # of cached font/matrix pairs */ #define cmax_LARGE 5000 /* cmax - # of cached chars */ #define blimit_LARGE 2500 /* blimit/upper - max size of a single cached char */ /*** Small memory machines ***/ #define smax_SMALL 20 /* smax - # of scaled fonts */ #define bmax_SMALL 25000 /* bmax - space for cached chars */ #define mmax_SMALL 40 /* mmax - # of cached font/matrix pairs */ #define cmax_SMALL 500 /* cmax - # of cached chars */ #define blimit_SMALL 100 /* blimit/upper - max size of a single cached char */ /* Define a default procedure vector for fonts. */ const gs_font_procs gs_font_procs_default = { gs_no_define_font, /* (actually a default) */ gs_no_make_font, /* (actually a default) */ gs_default_font_info, gs_default_same_font, gs_no_encode_char, gs_no_enumerate_glyph, gs_default_glyph_info, gs_no_glyph_outline, gs_default_init_fstack, gs_default_next_char_glyph, gs_no_build_char, { 0, /* glyph_name */ 0 /* known_encode */ } /* callbacks */ }; private_st_font_dir(); private struct_proc_enum_ptrs(font_enum_ptrs); private struct_proc_reloc_ptrs(font_reloc_ptrs); public_st_gs_font(); public_st_gs_font_base(); private_st_gs_font_ptr(); public_st_gs_font_ptr_element(); /* * Garbage collection of fonts poses some special problems. On the one * hand, we need to keep track of all existing base (not scaled) fonts, * using the next/prev list whose head is the orig_fonts member of the font * directory; on the other hand, we want these to be "weak" pointers that * don't keep fonts in existence if the fonts aren't referenced from * anywhere else. We accomplish this as follows: * * We don't trace through gs_font_dir.orig_fonts or gs_font.{next,prev} * during the mark phase of the GC. * * When we finalize a base gs_font, we unlink it from the list. (A * gs_font is a base font iff its base member points to itself.) * * We *do* relocate the orig_fonts and next/prev pointers during the * relocation phase of the GC. */ /* Font directory GC procedures */ private ENUM_PTRS_WITH(font_dir_enum_ptrs, gs_font_dir *dir) { /* Enumerate pointers from cached characters to f/m pairs, */ /* and mark the cached character glyphs. */ /* See gxfcache.h for why we do this here. */ uint cci = index - st_font_dir_max_ptrs; uint offset, count; uint tmask = dir->ccache.table_mask; if (cci == 0) offset = 0, count = 1; else if (cci == dir->enum_index + 1) offset = dir->enum_offset + 1, count = 1; else offset = 0, count = cci; for (; offset <= tmask; ++offset) { cached_char *cc = dir->ccache.table[offset]; if (cc != 0 && !--count) { (*dir->ccache.mark_glyph) (cc->code, dir->ccache.mark_glyph_data); /****** HACK: break const. We'll fix this someday. ******/ ((gs_font_dir *)dir)->enum_index = cci; ((gs_font_dir *)dir)->enum_offset = offset; ENUM_RETURN(cc_pair(cc) - cc->pair_index); } } } return 0; #define e1(i,elt) ENUM_PTR(i,gs_font_dir,elt); font_dir_do_ptrs(e1) #undef e1 ENUM_PTRS_END private RELOC_PTRS_WITH(font_dir_reloc_ptrs, gs_font_dir *dir); /* Relocate the pointers from cached characters to f/m pairs. */ /* See gxfcache.h for why we do this here. */ { int chi; for (chi = dir->ccache.table_mask; chi >= 0; --chi) { cached_char *cc = dir->ccache.table[chi]; if (cc != 0) cc_set_pair_only(cc, (cached_fm_pair *) RELOC_OBJ(cc_pair(cc) - cc->pair_index) + cc->pair_index); } } /* We have to relocate the cached characters before we */ /* relocate dir->ccache.table! */ RELOC_PTR(gs_font_dir, orig_fonts); #define r1(i,elt) RELOC_PTR(gs_font_dir, elt); font_dir_do_ptrs(r1) #undef r1 RELOC_PTRS_END /* GC procedures for fonts */ /* * When we finalize a base font, we unlink it from the orig_fonts list; * when we finalize a scaled font, we unlink it from scaled_fonts. * See above for more information. */ void gs_font_finalize(void *vptr) { gs_font *const pfont = vptr; gs_font **ppfirst; gs_font *next = pfont->next; gs_font *prev = pfont->prev; if_debug4('u', "[u]unlinking font 0x%lx, base=0x%lx, prev=0x%lx, next=0x%lx\n", (ulong) pfont, (ulong) pfont->base, (ulong) prev, (ulong) next); /* Notify clients that the font is being freed. */ gs_notify_all(&pfont->notify_list, NULL); if (pfont->dir == 0) ppfirst = 0; else if (pfont->base == pfont) ppfirst = &pfont->dir->orig_fonts; else { /* * Track the number of cached scaled fonts. Only decrement the * count if we didn't do this already in gs_makefont. */ if (next || prev || pfont->dir->scaled_fonts == pfont) pfont->dir->ssize--; ppfirst = &pfont->dir->scaled_fonts; } /* * gs_purge_font may have unlinked this font already: * don't unlink it twice. */ if (next != 0 && next->prev == pfont) next->prev = prev; if (prev != 0) { if (prev->next == pfont) prev->next = next; } else if (ppfirst != 0 && *ppfirst == pfont) *ppfirst = next; gs_notify_release(&pfont->notify_list); } private 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); /* We don't enumerate next or prev of base fonts. */ /* See above for details. */ case 0: ENUM_RETURN((pfont->base == pfont ? 0 : pfont->next)); case 1: ENUM_RETURN((pfont->base == pfont ? 0 : pfont->prev)); ENUM_PTR3(2, gs_font, dir, base, client_data); ENUM_PTRS_END private RELOC_PTRS_WITH(font_reloc_ptrs, gs_font *pfont); RELOC_USING(st_gs_notify_list, &pfont->notify_list, sizeof(gs_notify_list_t)); /* We *do* always relocate next and prev. */ /* Again, see above for details. */ RELOC_PTR(gs_font, next); RELOC_PTR(gs_font, prev); RELOC_PTR3(gs_font, dir, base, client_data); RELOC_PTRS_END /* Allocate a font directory */ private bool cc_no_mark_glyph(gs_glyph glyph, void *ignore_data) { return false; } gs_font_dir * gs_font_dir_alloc2(gs_memory_t * struct_mem, gs_memory_t * bits_mem) { gs_font_dir *pdir = 0; #if !arch_small_memory # ifdef DEBUG if (!gs_debug_c('.')) # endif { /* Try allocating a very large cache. */ /* If this fails, allocate a small one. */ pdir = gs_font_dir_alloc2_limits(struct_mem, bits_mem, smax_LARGE, bmax_LARGE, mmax_LARGE, cmax_LARGE, blimit_LARGE); } if (pdir == 0) #endif pdir = gs_font_dir_alloc2_limits(struct_mem, bits_mem, smax_SMALL, bmax_SMALL, mmax_SMALL, cmax_SMALL, blimit_SMALL); if (pdir == 0) return 0; pdir->ccache.mark_glyph = cc_no_mark_glyph; pdir->ccache.mark_glyph_data = 0; return pdir; } gs_font_dir * gs_font_dir_alloc2_limits(gs_memory_t * struct_mem, gs_memory_t * bits_mem, uint smax, uint bmax, uint mmax, uint cmax, uint upper) { gs_font_dir *pdir = gs_alloc_struct(struct_mem, gs_font_dir, &st_font_dir, "font_dir_alloc(dir)"); int code; if (pdir == 0) return 0; code = gx_char_cache_alloc(struct_mem, bits_mem, pdir, bmax, mmax, cmax, upper); if (code < 0) { gs_free_object(struct_mem, pdir, "font_dir_alloc(dir)"); return 0; } pdir->orig_fonts = 0; pdir->scaled_fonts = 0; pdir->ssize = 0; pdir->smax = smax; return pdir; } /* Allocate and minimally initialize a font. */ gs_font * gs_font_alloc(gs_memory_t *mem, gs_memory_type_ptr_t pstype, const gs_font_procs *procs, gs_font_dir *dir, client_name_t cname) { gs_font *pfont = gs_alloc_struct(mem, gs_font, pstype, cname); if (pfont == 0) return 0; pfont->next = pfont->prev = 0; pfont->memory = mem; pfont->dir = dir; pfont->is_resource = false; gs_notify_init(&pfont->notify_list, gs_memory_stable(mem)); pfont->id = gs_next_ids(1); pfont->base = pfont; pfont->client_data = 0; /* not FontMatrix, FontType */ pfont->BitmapWidths = false; pfont->ExactSize = pfont->InBetweenSize = pfont->TransformedChar = fbit_use_outlines; pfont->WMode = 0; pfont->PaintType = 0; pfont->StrokeWidth = 0; pfont->procs = *procs; /* not key_name, font_name */ return pfont; } /* Allocate and minimally initialize a base font. */ gs_font_base * gs_font_base_alloc(gs_memory_t *mem, gs_memory_type_ptr_t pstype, const gs_font_procs *procs, gs_font_dir *dir, client_name_t cname) { gs_font_base *pfont = (gs_font_base *)gs_font_alloc(mem, pstype, procs, dir, cname); if (pfont == 0) return 0; pfont->FontBBox.p.x = pfont->FontBBox.p.y = pfont->FontBBox.q.x = pfont->FontBBox.q.y = 0; uid_set_invalid(&pfont->UID); pfont->encoding_index = pfont->nearest_encoding_index = -1; return pfont; } /* * Register/unregister a client for notification by a font. Currently * the clients are only notified when a font is freed. Note that any * such client must unregister itself when *it* is freed. */ int gs_font_notify_register(gs_font *font, gs_notify_proc_t proc, void *proc_data) { return gs_notify_register(&font->notify_list, proc, proc_data); } int gs_font_notify_unregister(gs_font *font, gs_notify_proc_t proc, void *proc_data) { return gs_notify_unregister(&font->notify_list, proc, proc_data); } /* Link an element at the head of a chain. */ private void font_link_first(gs_font **pfirst, gs_font *elt) { gs_font *first = elt->next = *pfirst; if (first) first->prev = elt; elt->prev = 0; *pfirst = elt; } /* definefont */ /* Use this only for original (unscaled) fonts! */ /* Note that it expects pfont->procs.define_font to be set already. */ int gs_definefont(gs_font_dir * pdir, gs_font * pfont) { int code; pfont->dir = pdir; pfont->base = pfont; code = (*pfont->procs.define_font) (pdir, pfont); if (code < 0) { /* Make sure we don't try to finalize this font. */ pfont->base = 0; return code; } font_link_first(&pdir->orig_fonts, pfont); if_debug2('m', "[m]defining font 0x%lx, next=0x%lx\n", (ulong) pfont, (ulong) pfont->next); return 0; } /* scalefont */ int gs_scalefont(gs_font_dir * pdir, const gs_font * pfont, floatp scale, gs_font ** ppfont) { gs_matrix mat; gs_make_scaling(scale, scale, &mat); return gs_makefont(pdir, pfont, &mat, ppfont); } /* makefont */ int gs_makefont(gs_font_dir * pdir, const gs_font * pfont, const gs_matrix * pmat, gs_font ** ppfont) { int code; gs_font *prev = 0; gs_font *pf_out = pdir->scaled_fonts; gs_memory_t *mem = pfont->memory; gs_matrix newmat; bool can_cache; if ((code = gs_matrix_multiply(&pfont->FontMatrix, pmat, &newmat)) < 0) return code; /* * Check for the font already being in the scaled font cache. * Until version 5.97, we only cached scaled fonts if the base * (unscaled) font had a valid UniqueID or XUID; now, we will cache * scaled versions of any non-composite font. */ #ifdef DEBUG if (gs_debug_c('m')) { const gs_font_base *const pbfont = (const gs_font_base *)pfont; if (pfont->FontType == ft_composite) dlprintf("[m]composite"); else if (uid_is_UniqueID(&pbfont->UID)) dlprintf1("[m]UniqueID=%ld", pbfont->UID.id); else if (uid_is_XUID(&pbfont->UID)) dlprintf1("[m]XUID(%u)", (uint) (-pbfont->UID.id)); else dlprintf("[m]no UID"); dprintf7(", FontType=%d,\n[m] new FontMatrix=[%g %g %g %g %g %g]\n", pfont->FontType, pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty); } #endif /* * Don't try to cache scaled composite fonts, because of the side * effects on FDepVector and descendant fonts that occur in makefont. */ if (pfont->FontType != ft_composite) { for (; pf_out != 0; prev = pf_out, pf_out = pf_out->next) if (pf_out->FontType == pfont->FontType && pf_out->base == pfont->base && pf_out->FontMatrix.xx == newmat.xx && pf_out->FontMatrix.xy == newmat.xy && pf_out->FontMatrix.yx == newmat.yx && pf_out->FontMatrix.yy == newmat.yy && pf_out->FontMatrix.tx == newmat.tx && pf_out->FontMatrix.ty == newmat.ty ) { *ppfont = pf_out; if_debug1('m', "[m]found font=0x%lx\n", (ulong) pf_out); return 0; } can_cache = true; } else can_cache = false; pf_out = gs_alloc_struct(mem, gs_font, gs_object_type(mem, pfont), "gs_makefont"); if (!pf_out) return_error(gs_error_VMerror); memcpy(pf_out, pfont, gs_object_size(mem, pfont)); gs_notify_init(&pf_out->notify_list, gs_memory_stable(mem)); pf_out->FontMatrix = newmat; pf_out->client_data = 0; pf_out->dir = pdir; pf_out->base = pfont->base; *ppfont = pf_out; code = (*pf_out->procs.make_font) (pdir, pfont, pmat, ppfont); if (code < 0) return code; if (can_cache) { if (pdir->ssize >= pdir->smax && prev != 0) { /* * We must discard a cached scaled font. * prev points to the last (oldest) font. * (We can't free it, because there might be * other references to it.) */ if_debug1('m', "[m]discarding font 0x%lx\n", (ulong) prev); if (prev->prev != 0) prev->prev->next = 0; else pdir->scaled_fonts = 0; pdir->ssize--; prev->prev = 0; if (prev->FontType != ft_composite) { if_debug1('m', "[m]discarding UID 0x%lx\n", (ulong) ((gs_font_base *) prev)-> UID.xvalues); uid_free(&((gs_font_base *) prev)->UID, prev->memory, "gs_makefont(discarding)"); uid_set_invalid(&((gs_font_base *) prev)->UID); } } pdir->ssize++; font_link_first(&pdir->scaled_fonts, pf_out); } else { /* Prevent garbage pointers. */ pf_out->next = pf_out->prev = 0; } if_debug2('m', "[m]new font=0x%lx can_cache=%s\n", (ulong) * ppfont, (can_cache ? "true" : "false")); return 1; } /* Set the current font. This is provided only for the benefit of cshow, */ /* which must reset the current font without disturbing the root font. */ void gs_set_currentfont(gs_state * pgs, gs_font * pfont) { pgs->font = pfont; pgs->char_tm_valid = false; } /* setfont */ int gs_setfont(gs_state * pgs, gs_font * pfont) { pgs->font = pgs->root_font = pfont; pgs->char_tm_valid = false; return 0; } /* currentfont */ gs_font * gs_currentfont(const gs_state * pgs) { return pgs->font; } /* rootfont */ gs_font * gs_rootfont(const gs_state * pgs) { return pgs->root_font; } /* cachestatus */ void gs_cachestatus(register const gs_font_dir * pdir, register uint pstat[7]) { pstat[0] = pdir->ccache.bsize; pstat[1] = pdir->ccache.bmax; pstat[2] = pdir->fmcache.msize; pstat[3] = pdir->fmcache.mmax; pstat[4] = pdir->ccache.csize; pstat[5] = pdir->ccache.cmax; pstat[6] = pdir->ccache.upper; } /* setcacheparams */ int gs_setcachesize(gs_font_dir * pdir, uint size) { /* This doesn't delete anything from the cache yet. */ pdir->ccache.bmax = size; return 0; } int gs_setcachelower(gs_font_dir * pdir, uint size) { pdir->ccache.lower = size; return 0; } int gs_setcacheupper(gs_font_dir * pdir, uint size) { pdir->ccache.upper = size; return 0; } /* currentcacheparams */ uint gs_currentcachesize(const gs_font_dir * pdir) { return pdir->ccache.bmax; } uint gs_currentcachelower(const gs_font_dir * pdir) { return pdir->ccache.lower; } uint gs_currentcacheupper(const gs_font_dir * pdir) { return pdir->ccache.upper; } /* Purge a font from all font- and character-related tables. */ /* This is only used by restore (and, someday, the GC). */ void gs_purge_font(gs_font * pfont) { gs_font_dir *pdir = pfont->dir; gs_font *pf; /* Remove the font from its list (orig_fonts or scaled_fonts). */ gs_font *prev = pfont->prev; gs_font *next = pfont->next; if (next != 0) next->prev = prev, pfont->next = 0; if (prev != 0) prev->next = next, pfont->prev = 0; else if (pdir->orig_fonts == pfont) pdir->orig_fonts = next; else if (pdir->scaled_fonts == pfont) pdir->scaled_fonts = next; else { /* Shouldn't happen! */ lprintf1("purged font 0x%lx not found\n", (ulong) pfont); } /* Purge the font from the scaled font cache. */ for (pf = pdir->scaled_fonts; pf != 0;) { if (pf->base == pfont) { gs_purge_font(pf); pf = pdir->scaled_fonts; /* start over */ } else pf = pf->next; } /* Purge the font from the font/matrix pair cache, */ /* including all cached characters rendered with that font. */ gs_purge_font_from_char_caches(pdir, pfont); } /* ---------------- Default font procedures ---------------- */ /* ------ Font-level procedures ------ */ /* Default (vacuous) definefont handler. */ int gs_no_define_font(gs_font_dir * pdir, gs_font * pfont) { return 0; } /* Default (vacuous) makefont handler. */ int gs_no_make_font(gs_font_dir * pdir, const gs_font * pfont, const gs_matrix * pmat, gs_font ** ppfont) { return 0; } /* Makefont handler for base fonts, which must copy the XUID. */ int gs_base_make_font(gs_font_dir * pdir, const gs_font * pfont, const gs_matrix * pmat, gs_font ** ppfont) { gs_font_base *const pbfont = (gs_font_base *)*ppfont; if (uid_is_XUID(&pbfont->UID)) { uint xsize = uid_XUID_size(&pbfont->UID); long *xvalues = (long *) gs_alloc_byte_array(pbfont->memory, xsize, sizeof(long), "gs_base_make_font(XUID)"); if (xvalues == 0) return_error(gs_error_VMerror); memcpy(xvalues, uid_XUID_values(&pbfont->UID), xsize * sizeof(long)); pbfont->UID.xvalues = xvalues; } return 0; } /* Default font info procedure */ int gs_default_font_info(gs_font *font, const gs_point *pscale, int members, gs_font_info_t *info) { int wmode = font->WMode; gs_font_base *bfont = (gs_font_base *)font; gs_point scale; gs_matrix smat; const gs_matrix *pmat; if (pscale == 0) { scale.x = scale.y = 0; pmat = 0; } else { scale = *pscale; gs_make_scaling(scale.x, scale.y, &smat); pmat = &smat; } info->members = 0; if (members & FONT_INFO_FLAGS) info->Flags_returned = 0; if (font->FontType == ft_composite) return 0; /* nothing available */ if ((members & FONT_INFO_FLAGS) && (info->Flags_requested & FONT_IS_FIXED_WIDTH) ) { /* * Scan the glyph space to compute the fixed width if any. */ gs_glyph notdef = gs_no_glyph; gs_glyph glyph; int fixed_width = 0; int index, code; for (index = 0; fixed_width >= 0 && (code = font->procs.enumerate_glyph(font, &index, GLYPH_SPACE_NAME, &glyph)) >= 0 && index != 0; ) { gs_glyph_info_t glyph_info; code = font->procs.glyph_info(font, glyph, pmat, (GLYPH_INFO_WIDTH0 << wmode), &glyph_info); if (code < 0) return code; if (notdef == gs_no_glyph && gs_font_glyph_is_notdef(bfont, glyph)) { notdef = glyph; info->MissingWidth = glyph_info.width[wmode].x; info->members |= FONT_INFO_MISSING_WIDTH; } if (glyph_info.width[wmode].y != 0) fixed_width = min_int; else if (fixed_width == 0) fixed_width = glyph_info.width[wmode].x; else if (glyph_info.width[wmode].x != fixed_width) fixed_width = min_int; } if (code < 0) return code; if (fixed_width > 0) { info->Flags |= FONT_IS_FIXED_WIDTH; info->members |= FONT_INFO_AVG_WIDTH | FONT_INFO_MAX_WIDTH | FONT_INFO_MISSING_WIDTH; info->AvgWidth = info->MaxWidth = info->MissingWidth = fixed_width; } info->Flags_returned |= FONT_IS_FIXED_WIDTH; } else if (members & FONT_INFO_MISSING_WIDTH) { gs_glyph glyph; int index; for (index = 0; font->procs.enumerate_glyph(font, &index, GLYPH_SPACE_NAME, &glyph) >= 0 && index != 0; ) { /* * If this is a CIDFont or TrueType font that uses integers as * glyph names, check for glyph 0; otherwise, check for .notdef. */ if (!gs_font_glyph_is_notdef(bfont, glyph)) continue; { gs_glyph_info_t glyph_info; int code = font->procs.glyph_info(font, glyph, pmat, (GLYPH_INFO_WIDTH0 << wmode), &glyph_info); if (code < 0) return code; info->MissingWidth = glyph_info.width[wmode].x; info->members |= FONT_INFO_MISSING_WIDTH; break; } } } return 0; } /* Default font similarity testing procedure */ int gs_default_same_font(const gs_font *font, const gs_font *ofont, int mask) { while (font->base != font) font = font->base; while (ofont->base != ofont) ofont = ofont->base; if (ofont == font) return mask; /* In general, we can't determine similarity. */ return 0; } int gs_base_same_font(const gs_font *font, const gs_font *ofont, int mask) { int same = gs_default_same_font(font, ofont, mask); if (!same) { const gs_font_base *const bfont = (const gs_font_base *)font; const gs_font_base *const obfont = (const gs_font_base *)ofont; if (mask & FONT_SAME_ENCODING) { if (bfont->encoding_index != ENCODING_INDEX_UNKNOWN || obfont->encoding_index != ENCODING_INDEX_UNKNOWN ) { if (bfont->encoding_index == obfont->encoding_index) same |= FONT_SAME_ENCODING; } } } return same; } /* ------ Glyph-level procedures ------ */ /* * Test whether a glyph is the notdef glyph for a base font. * The test is somewhat adhoc: perhaps this should be a virtual procedure. */ bool gs_font_glyph_is_notdef(gs_font_base *bfont, gs_glyph glyph) { gs_const_string gnstr; if (glyph == gs_no_glyph) return false; if (glyph >= gs_min_cid_glyph) return (glyph == gs_min_cid_glyph); gnstr.data = (const byte *) bfont->procs.callbacks.glyph_name(glyph, &gnstr.size); return (gnstr.size == 7 && !memcmp(gnstr.data, ".notdef", 7)); } /* Dummy character encoding procedure */ gs_glyph gs_no_encode_char(gs_font *pfont, gs_char chr, gs_glyph_space_t glyph_space) { return gs_no_glyph; } /* Dummy glyph enumeration procedure */ int gs_no_enumerate_glyph(gs_font *font, int *pindex, gs_glyph_space_t glyph_space, gs_glyph *pglyph) { return_error(gs_error_undefined); } /* Default glyph info procedure */ int gs_default_glyph_info(gs_font *font, gs_glyph glyph, const gs_matrix *pmat, int members, gs_glyph_info_t *info) { gx_path *ppath = gx_path_alloc(font->memory, "glyph_path"); int returned = 0; int code; if (ppath == 0) return_error(gs_error_VMerror); code = gx_path_add_point(ppath, fixed_0, fixed_0); if (code < 0) goto out; code = font->procs.glyph_outline(font, glyph, pmat, ppath); if (code < 0) goto out; if (members & GLYPH_INFO_WIDTHS) { int wmode = font->WMode; int wmask = GLYPH_INFO_WIDTH0 << wmode; if (members & wmask) { gs_fixed_point pt; code = gx_path_current_point(ppath, &pt); if (code < 0) goto out; info->width[wmode].x = fixed2float(pt.x); info->width[wmode].y = fixed2float(pt.y); returned |= wmask; } } if (members & GLYPH_INFO_BBOX) { gs_fixed_rect bbox; code = gx_path_bbox(ppath, &bbox); if (code < 0) goto out; info->bbox.p.x = fixed2float(bbox.p.x); info->bbox.p.y = fixed2float(bbox.p.y); info->bbox.q.x = fixed2float(bbox.q.x); info->bbox.q.y = fixed2float(bbox.q.y); returned |= GLYPH_INFO_BBOX; } if (members & GLYPH_INFO_NUM_PIECES) { info->num_pieces = 0; returned |= GLYPH_INFO_NUM_PIECES; } returned |= members & GLYPH_INFO_PIECES; /* no pieces stored */ out: gx_path_free(ppath, "gs_default_glyph_bbox"); info->members = returned; return code; } /* Dummy glyph outline procedure */ int gs_no_glyph_outline(gs_font *font, gs_glyph glyph, const gs_matrix *pmat, gx_path *ppath) { return_error(gs_error_undefined); }