/* Copyright (C) 1989, 1995, 1996, 1997, 1998, 1999, 2000 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: zbfont.c,v 1.7.4.2.2.1 2003/01/17 00:49:05 giles Exp $ */ /* Font creation utilities */ #include "memory_.h" #include "string_.h" #include "ghost.h" #include "oper.h" #include "gxfixed.h" #include "gsmatrix.h" #include "gxdevice.h" #include "gxfont.h" #include "bfont.h" #include "ialloc.h" #include "idict.h" #include "idparam.h" #include "ilevel.h" #include "iname.h" #include "interp.h" /* for initial_enter_name */ #include "ipacked.h" #include "istruct.h" #include "store.h" /* Registered encodings. See ifont.h for documentation. */ ref registered_Encodings; private ref *const registered_Encodings_p = ®istered_Encodings; /* Structure descriptor */ public_st_font_data(); /* Initialize the font building operators */ private int zbfont_init(i_ctx_t *i_ctx_p) { /* Initialize the registered Encodings. */ int i; ialloc_ref_array(®istered_Encodings, a_all, registered_Encodings_countof, "registered_Encodings"); for (i = 0; i < registered_Encodings_countof; i++) make_empty_array(®istered_Encoding(i), 0); initial_enter_name("registeredencodings", ®istered_Encodings); return gs_register_ref_root(imemory, NULL, (void **)®istered_Encodings_p, "registered_Encodings"); } /* .buildfont3 */ /* Build a type 3 (user-defined) font. */ private int zbuildfont3(i_ctx_t *i_ctx_p) { os_ptr op = osp; int code; build_proc_refs build; gs_font_base *pfont; check_type(*op, t_dictionary); code = build_gs_font_procs(op, &build); if (code < 0) return code; code = build_gs_simple_font(i_ctx_p, op, &pfont, ft_user_defined, &st_gs_font_base, &build, bf_options_none); if (code < 0) return code; return define_gs_font((gs_font *) pfont); } /* Encode a character. */ gs_glyph zfont_encode_char(gs_font *pfont, gs_char chr, gs_glyph_space_t ignored) { const ref *pencoding = &pfont_data(pfont)->Encoding; ulong index = chr; /* work around VAX widening bug */ ref cname; int code = array_get(pencoding, (long)index, &cname); if (code < 0 || !r_has_type(&cname, t_name)) return gs_no_glyph; return (gs_glyph)name_index(&cname); } /* Encode a character in a known encoding. */ private gs_glyph zfont_known_encode(gs_char chr, int encoding_index) { ulong index = chr; /* work around VAX widening bug */ ref cname; int code; if (encoding_index < 0) return gs_no_glyph; code = array_get(®istered_Encoding(encoding_index), (long)index, &cname); if (code < 0 || !r_has_type(&cname, t_name)) return gs_no_glyph; return (gs_glyph) name_index(&cname); } /* Get the name of a glyph. */ /* The following typedef is needed to work around a bug in */ /* some AIX C compilers. */ typedef const char *const_chars; private const_chars zfont_glyph_name(gs_glyph index, uint * plen) { ref nref, sref; if (index >= gs_min_cid_glyph) { /* Fabricate a numeric name. */ char cid_name[sizeof(gs_glyph) * 3 + 1]; int code; sprintf(cid_name, "%lu", (ulong) index); code = name_ref((const byte *)cid_name, strlen(cid_name), &nref, 1); if (code < 0) return 0; /* What can we possibly do here? */ } else name_index_ref(index, &nref); name_string_ref(&nref, &sref); *plen = r_size(&sref); return (const char *)sref.value.const_bytes; } /* ------ Initialization procedure ------ */ const op_def zbfont_op_defs[] = { {"2.buildfont3", zbuildfont3}, op_def_end(zbfont_init) }; /* ------ Subroutines ------ */ /* Convert strings to executable names for build_proc_refs. */ int build_proc_name_refs(build_proc_refs * pbuild, const char *bcstr, const char *bgstr) { int code; if (!bcstr) make_null(&pbuild->BuildChar); else { if ((code = name_ref((const byte *)bcstr, strlen(bcstr), &pbuild->BuildChar, 0)) < 0) return code; r_set_attrs(&pbuild->BuildChar, a_executable); } if (!bgstr) make_null(&pbuild->BuildGlyph); else { if ((code = name_ref((const byte *)bgstr, strlen(bgstr), &pbuild->BuildGlyph, 0)) < 0) return code; r_set_attrs(&pbuild->BuildGlyph, a_executable); } return 0; } /* Get the BuildChar and/or BuildGlyph routines from a (base) font. */ int build_gs_font_procs(os_ptr op, build_proc_refs * pbuild) { int ccode, gcode; ref *pBuildChar; ref *pBuildGlyph; check_type(*op, t_dictionary); ccode = dict_find_string(op, "BuildChar", &pBuildChar); gcode = dict_find_string(op, "BuildGlyph", &pBuildGlyph); if (ccode <= 0) { if (gcode <= 0) return_error(e_invalidfont); make_null(&pbuild->BuildChar); } else { check_proc(*pBuildChar); pbuild->BuildChar = *pBuildChar; } if (gcode <= 0) make_null(&pbuild->BuildGlyph); else { check_proc(*pBuildGlyph); pbuild->BuildGlyph = *pBuildGlyph; } return 0; } /* Do the common work for building a primitive font -- one whose execution */ /* algorithm is implemented in C (Type 1, Type 2, Type 4, or Type 42). */ /* The caller guarantees that *op is a dictionary. */ int build_gs_primitive_font(i_ctx_t *i_ctx_p, os_ptr op, gs_font_base ** ppfont, font_type ftype, gs_memory_type_ptr_t pstype, const build_proc_refs * pbuild, build_font_options_t options) { ref *pcharstrings = 0; ref CharStrings; gs_font_base *pfont; font_data *pdata; int code; if (dict_find_string(op, "CharStrings", &pcharstrings) <= 0) { if (!(options & bf_CharStrings_optional)) return_error(e_invalidfont); } else { ref *ignore; if (!r_has_type(pcharstrings, t_dictionary)) return_error(e_invalidfont); if ((options & bf_notdef_required) != 0 && dict_find_string(pcharstrings, ".notdef", &ignore) <= 0 ) return_error(e_invalidfont); /* * Since build_gs_simple_font may resize the dictionary and cause * pointers to become invalid, save CharStrings. */ CharStrings = *pcharstrings; } code = build_gs_outline_font(i_ctx_p, op, ppfont, ftype, pstype, pbuild, options, build_gs_simple_font); if (code != 0) return code; pfont = *ppfont; pdata = pfont_data(pfont); if (pcharstrings) ref_assign(&pdata->CharStrings, &CharStrings); else make_null(&pdata->CharStrings); /* Check that the UniqueIDs match. This is part of the */ /* Adobe protection scheme, but we may as well emulate it. */ if (uid_is_valid(&pfont->UID) && !dict_check_uid_param(op, &pfont->UID) ) uid_set_invalid(&pfont->UID); return 0; } /* Build a FDArray entry for a CIDFontType 0 font. */ /* Note that as of Adobe PostScript version 3011, this may be either */ /* a Type 1 or Type 2 font. */ private int build_FDArray_sub_font(i_ctx_t *i_ctx_p, ref *op, gs_font_base **ppfont, font_type ftype, gs_memory_type_ptr_t pstype, const build_proc_refs * pbuild, build_font_options_t ignore_options) { gs_font *pfont; int code = build_gs_sub_font(i_ctx_p, op, &pfont, ftype, pstype, pbuild, NULL, op); if (code >= 0) *ppfont = (gs_font_base *)pfont; return code; } int build_gs_FDArray_font(i_ctx_t *i_ctx_p, ref *op, gs_font_base **ppfont, font_type ftype, gs_memory_type_ptr_t pstype, const build_proc_refs * pbuild) { gs_font_base *pfont; font_data *pdata; int code = build_gs_outline_font(i_ctx_p, op, &pfont, ftype, pstype, pbuild, bf_options_none, build_FDArray_sub_font); static const double bbox[4] = { 0, 0, 0, 0 }; gs_uid uid; if (code < 0) return code; pdata = pfont_data(pfont); /* Fill in members normally set by build_gs_primitive_font. */ make_null(&pdata->CharStrings); /* Fill in members normally set by build_gs_simple_font. */ uid_set_invalid(&uid); init_gs_simple_font(pfont, bbox, &uid); pfont->encoding_index = ENCODING_INDEX_UNKNOWN; pfont->nearest_encoding_index = ENCODING_INDEX_UNKNOWN; /* Fill in members normally set by build_gs_font. */ pfont->key_name = pfont->font_name; *ppfont = pfont; return 0; } /* Do the common work for building an outline font -- a non-composite font */ /* for which PaintType and StrokeWidth are meaningful. */ /* The caller guarantees that *op is a dictionary. */ int build_gs_outline_font(i_ctx_t *i_ctx_p, os_ptr op, gs_font_base ** ppfont, font_type ftype, gs_memory_type_ptr_t pstype, const build_proc_refs * pbuild, build_font_options_t options, build_base_font_proc_t build_base_font) { int painttype; float strokewidth; gs_font_base *pfont; int code = dict_int_param(op, "PaintType", 0, 3, 0, &painttype); if (code < 0) return code; code = dict_float_param(op, "StrokeWidth", 0.0, &strokewidth); if (code < 0) return code; code = build_base_font(i_ctx_p, op, ppfont, ftype, pstype, pbuild, options); if (code != 0) return code; pfont = *ppfont; pfont->PaintType = painttype; pfont->StrokeWidth = strokewidth; return 0; } /* Do the common work for building a font of any non-composite FontType. */ /* The caller guarantees that *op is a dictionary. */ int build_gs_simple_font(i_ctx_t *i_ctx_p, os_ptr op, gs_font_base ** ppfont, font_type ftype, gs_memory_type_ptr_t pstype, const build_proc_refs * pbuild, build_font_options_t options) { double bbox[4]; gs_uid uid; int code; gs_font_base *pfont; code = font_bbox_param(op, bbox); if (code < 0) return code; code = dict_uid_param(op, &uid, 0, imemory, i_ctx_p); if (code < 0) return code; if ((options & bf_UniqueID_ignored) && uid_is_UniqueID(&uid)) uid_set_invalid(&uid); code = build_gs_font(i_ctx_p, op, (gs_font **) ppfont, ftype, pstype, pbuild, options); if (code != 0) /* invalid or scaled font */ return code; pfont = *ppfont; pfont->procs.init_fstack = gs_default_init_fstack; pfont->procs.define_font = gs_no_define_font; pfont->procs.make_font = zbase_make_font; pfont->procs.next_char_glyph = gs_default_next_char_glyph; init_gs_simple_font(pfont, bbox, &uid); lookup_gs_simple_font_encoding(pfont); return 0; } /* Initialize the FontBBox and UID of a non-composite font. */ void init_gs_simple_font(gs_font_base *pfont, const double bbox[4], const gs_uid *puid) { pfont->FontBBox.p.x = bbox[0]; pfont->FontBBox.p.y = bbox[1]; pfont->FontBBox.q.x = bbox[2]; pfont->FontBBox.q.y = bbox[3]; pfont->UID = *puid; } /* Compare the encoding of a simple font with the registered encodings. */ void lookup_gs_simple_font_encoding(gs_font_base * pfont) { const ref *pfe = &pfont_data(pfont)->Encoding; int index; for (index = NUM_KNOWN_REAL_ENCODINGS; --index >= 0;) if (obj_eq(pfe, ®istered_Encoding(index))) break; pfont->encoding_index = index; if (index < 0) { /* Look for an encoding that's "close". */ int near_index = -1; uint esize = r_size(pfe); uint best = esize / 3; /* must match at least this many */ for (index = NUM_KNOWN_REAL_ENCODINGS; --index >= 0;) { const ref *pre = ®istered_Encoding(index); bool r_packed = r_has_type(pre, t_shortarray); bool f_packed = !r_has_type(pfe, t_array); uint match = esize; int i; ref fchar, rchar; const ref *pfchar = &fchar; if (r_size(pre) != esize) continue; for (i = esize; --i >= 0;) { uint rnidx; if (r_packed) rnidx = packed_name_index(pre->value.packed + i); else { array_get(pre, (long)i, &rchar); rnidx = name_index(&rchar); } if (f_packed) array_get(pfe, (long)i, &fchar); else pfchar = pfe->value.const_refs + i; if (!r_has_type(pfchar, t_name) || name_index(pfchar) != rnidx ) if (--match <= best) break; } if (match > best) best = match, near_index = index; } index = near_index; } pfont->nearest_encoding_index = index; } /* Get FontMatrix and FontName parameters. */ private void get_font_name(P2(ref *, const ref *)); private void copy_font_name(P2(gs_font_name *, const ref *)); private int sub_font_params(const ref *op, gs_matrix *pmat, ref *pfname) { ref *pmatrix; ref *pfontname; if (dict_find_string(op, "FontMatrix", &pmatrix) <= 0 || read_matrix(pmatrix, pmat) < 0 ) return_error(e_invalidfont); if (dict_find_string(op, "FontName", &pfontname) > 0) get_font_name(pfname, pfontname); else make_empty_string(pfname, a_readonly); return 0; } /* Do the common work for building a font of any FontType. */ /* The caller guarantees that *op is a dictionary. */ /* op[-1] must be the key under which the font is being registered */ /* in FontDirectory, normally a name or string. */ /* Return 0 for a new font, 1 for a font made by makefont or scalefont, */ /* or a negative error code. */ int build_gs_font(i_ctx_t *i_ctx_p, os_ptr op, gs_font ** ppfont, font_type ftype, gs_memory_type_ptr_t pstype, const build_proc_refs * pbuild, build_font_options_t options) { ref kname; /* t_string */ ref *pftype; ref *pencoding = 0; bool bitmapwidths; int exactsize, inbetweensize, transformedchar; int wmode; int code; gs_font *pfont; ref *pfid; ref *aop = dict_access_ref(op); get_font_name(&kname, op - 1); if (dict_find_string(op, "FontType", &pftype) <= 0 || !r_has_type(pftype, t_integer) || pftype->value.intval != (int)ftype ) return_error(e_invalidfont); if (dict_find_string(op, "Encoding", &pencoding) <= 0) { if (!(options & bf_Encoding_optional)) return_error(e_invalidfont); } else { if (!r_is_array(pencoding)) return_error(e_invalidfont); } if ((code = dict_int_param(op, "WMode", 0, 1, 0, &wmode)) < 0 || (code = dict_bool_param(op, "BitmapWidths", false, &bitmapwidths)) < 0 || (code = dict_int_param(op, "ExactSize", 0, 2, fbit_use_bitmaps, &exactsize)) < 0 || (code = dict_int_param(op, "InBetweenSize", 0, 2, fbit_use_outlines, &inbetweensize)) < 0 || (code = dict_int_param(op, "TransformedChar", 0, 2, fbit_use_outlines, &transformedchar)) < 0 ) return code; code = dict_find_string(op, "FID", &pfid); if (code > 0) { if (!r_has_type(pfid, t_fontID)) return_error(e_invalidfont); /* * If this font has a FID entry already, it might be * a scaled font made by makefont or scalefont; * in a Level 2 environment, it might be an existing font * being registered under a second name, or a re-encoded * font (which is questionable PostScript, but dvips * is known to do this). */ pfont = r_ptr(pfid, gs_font); if (pfont->base == pfont) { /* original font */ if (!level2_enabled) return_error(e_invalidfont); if (obj_eq(pfont_dict(pfont), op)) { *ppfont = pfont; return 1; } /* * This is a re-encoded font, or some other * questionable situation in which the FID * was preserved. Pretend the FID wasn't there. */ } else { /* This was made by makefont or scalefont. */ /* Just insert the new name. */ gs_matrix mat; ref fname; /* t_string */ code = sub_font_params(op, &mat, &fname); if (code < 0) return code; code = 1; copy_font_name(&pfont->font_name, &fname); goto set_name; } } /* This is a new font. */ if (!r_has_attr(aop, a_write)) return_error(e_invalidaccess); { ref encoding; /* * Since add_FID may resize the dictionary and cause * pencoding to become invalid, save the Encoding. */ if (pencoding) { encoding = *pencoding; pencoding = &encoding; } code = build_gs_sub_font(i_ctx_p, op, &pfont, ftype, pstype, pbuild, pencoding, op); if (code < 0) return code; } pfont->BitmapWidths = bitmapwidths; pfont->ExactSize = (fbit_type)exactsize; pfont->InBetweenSize = (fbit_type)inbetweensize; pfont->TransformedChar = (fbit_type)transformedchar; pfont->WMode = wmode; pfont->procs.font_info = zfont_info; code = 0; set_name: copy_font_name(&pfont->key_name, &kname); *ppfont = pfont; return code; } /* Create a sub-font -- a font or an entry in the FDArray of a CIDFontType 0 */ /* font. Default BitmapWidths, ExactSize, InBetweenSize, TransformedChar, */ /* and WMode; do not fill in key_name. */ /* The caller guarantees that *op is a dictionary. */ int build_gs_sub_font(i_ctx_t *i_ctx_p, const ref *op, gs_font **ppfont, font_type ftype, gs_memory_type_ptr_t pstype, const build_proc_refs * pbuild, const ref *pencoding, ref *fid_op) { gs_matrix mat; ref fname; /* t_string */ gs_font *pfont; font_data *pdata; /* * Make sure that we allocate the font data * in the same VM as the font dictionary. */ uint space = ialloc_space(idmemory); int code = sub_font_params(op, &mat, &fname); if (code < 0) return code; ialloc_set_space(idmemory, r_space(op)); pfont = gs_font_alloc(imemory, pstype, &gs_font_procs_default, NULL, "buildfont(font)"); pdata = ialloc_struct(font_data, &st_font_data, "buildfont(data)"); if (pfont == 0 || pdata == 0) code = gs_note_error(e_VMerror); else if (fid_op) code = add_FID(i_ctx_p, fid_op, pfont, iimemory); if (code < 0) { ifree_object(pdata, "buildfont(data)"); ifree_object(pfont, "buildfont(font)"); ialloc_set_space(idmemory, space); return code; } refset_null((ref *) pdata, sizeof(font_data) / sizeof(ref)); ref_assign_new(&pdata->dict, op); ref_assign_new(&pdata->BuildChar, &pbuild->BuildChar); ref_assign_new(&pdata->BuildGlyph, &pbuild->BuildGlyph); if (pencoding) ref_assign_new(&pdata->Encoding, pencoding); pfont->client_data = pdata; pfont->FontType = ftype; pfont->FontMatrix = mat; pfont->BitmapWidths = false; pfont->ExactSize = fbit_use_bitmaps; pfont->InBetweenSize = fbit_use_outlines; pfont->TransformedChar = fbit_use_outlines; pfont->WMode = 0; pfont->procs.encode_char = zfont_encode_char; pfont->procs.callbacks.glyph_name = zfont_glyph_name; pfont->procs.callbacks.known_encode = zfont_known_encode; ialloc_set_space(idmemory, space); copy_font_name(&pfont->font_name, &fname); *ppfont = pfont; return 0; } /* Get the string corresponding to a font name. */ /* If the font name isn't a name or a string, return an empty string. */ private void get_font_name(ref * pfname, const ref * op) { switch (r_type(op)) { case t_string: *pfname = *op; break; case t_name: name_string_ref(op, pfname); break; default: /* This is weird, but legal.... */ make_empty_string(pfname, a_readonly); } } /* Copy a font name into the gs_font structure. */ private void copy_font_name(gs_font_name * pfstr, const ref * pfname) { uint size = r_size(pfname); if (size > gs_font_name_max) size = gs_font_name_max; memcpy(&pfstr->chars[0], pfname->value.const_bytes, size); /* Following is only for debugging printout. */ pfstr->chars[size] = 0; pfstr->size = size; } /* Finish building a font, by calling gs_definefont if needed. */ int define_gs_font(gs_font * pfont) { return (pfont->base == pfont && pfont->dir == 0 ? /* i.e., unregistered original font */ gs_definefont(ifont_dir, pfont) : 0); }