summaryrefslogtreecommitdiff
path: root/pxl/pxffont.c
diff options
context:
space:
mode:
Diffstat (limited to 'pxl/pxffont.c')
-rw-r--r--pxl/pxffont.c603
1 files changed, 603 insertions, 0 deletions
diff --git a/pxl/pxffont.c b/pxl/pxffont.c
new file mode 100644
index 000000000..ab91fa6af
--- /dev/null
+++ b/pxl/pxffont.c
@@ -0,0 +1,603 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxffont.c */
+/* PCL XL font-finding procedures */
+
+#include "string_.h"
+#include "gx.h"
+#include "gschar.h"
+#include "gsmatrix.h" /* for gsfont.h */
+#include "gxfont.h"
+#include "gxfont42.h"
+#include "pxoper.h"
+#include "pxstate.h"
+#include "pxfont.h"
+
+/*
+ * Define the structure and parameters for doing font substitution.
+ * Ideally, we should build in a few hundred well-known font names and
+ * use PANOSE numbers; this is a refinement for the future.
+ */
+typedef enum {
+ pxff_fixed = 1,
+ pxff_variable = 2,
+ pxff_serif = 4,
+ pxff_sans = 8,
+ pxff_upright = 0x10,
+ pxff_oblique = 0x20,
+ pxff_italic = 0x40,
+ pxff_plain = 0x80,
+ pxff_bold = 0x100,
+ pxff_narrow = 0x200,
+ pxff_symbolic = 0x400,
+ pxff_typeface = 0x0010000,
+ pxff_typeface_mask = 0xfff0000,
+ /* This is a pretty random selection of typefaces. */
+ /* It just happens to be the ones in the standard PostScript set */
+ /* plus the ones in the Windows set. */
+ pxffArial = pxff_typeface * 218,
+ pxffAvantGarde = pxff_typeface * 31,
+ pxffBookman = pxff_typeface * 402,
+ pxffCourier = pxff_typeface * 3,
+ pxffHelvetica = pxff_typeface * 4,
+ pxffNewCenturySchlbk = pxff_typeface * 23,
+ pxffPalatino = pxff_typeface * 15,
+ pxffSymbol = pxff_typeface * 302,
+ pxffTimes = pxff_typeface * 5,
+ pxffWingdings = pxff_typeface * 2730,
+ /* These are the remaining typefaces in the PCL XL base set. */
+ pxffAlbertus = pxff_typeface * 266,
+ pxffAntiqueOlive = pxff_typeface * 72,
+ pxffClarendon = pxff_typeface * 44,
+ pxffCoronet = pxff_typeface * 20,
+ pxffGaramond = pxff_typeface * 18,
+ pxffLetterGothic = pxff_typeface * 6,
+ pxffLinePrinter = pxff_typeface * 0,
+ pxffMarigold = pxff_typeface * 201,
+ pxffUnivers = pxff_typeface * 52,
+ /* These two are not in the H-P catalog. */
+ pxffCGOmega = pxffGaramond,
+ pxffCGTimes = pxffTimes
+} px_font_flags_t;
+#define pxff_spacing (pxff_fixed | pxff_variable)
+#define pxff_serifs (pxff_serif | pxff_sans)
+#define pxff_posture (pxff_upright | pxff_oblique)
+#define pxff_weight (pxff_plain | pxff_bold)
+#define pxff_width (pxff_narrow)
+#define pxff_all_properties ((px_font_flags_t)0xffff)
+#define pxff_all (pxff_all_properties | pxff_typeface_mask)
+typedef struct px_font_desc_s {
+ /* All font names are in Unicode. See pxfont.h. */
+ char16 fname[17];
+ px_font_flags_t flags;
+} px_font_desc_t;
+
+/*
+ * Here we list the 45 built-in LaserJet 5 and 6 fonts. For host-based
+ * testing, we also provide the file names of the corresponding Windows
+ * TrueType fonts if available.
+ */
+typedef struct px_stored_font_s {
+ px_font_desc_t descriptor;
+ const char *file_name;
+} px_stored_font_t;
+private const char *known_font_file_prefixes[] = {
+ "/windows/system/", "/windows/fonts/", "/win95/fonts/", 0
+};
+private const px_stored_font_t known_fonts[] = {
+ {{{'A','l','b','e','r','t','u','s',' ',' ',' ',' ',' ',' ','X','b'},
+ pxffAlbertus | pxff_variable | pxff_sans | pxff_upright | pxff_bold},
+ 0},
+ {{{'A','l','b','e','r','t','u','s',' ',' ',' ',' ',' ',' ','M','d'},
+ pxffAlbertus | pxff_variable | pxff_sans | pxff_upright | pxff_plain},
+ 0},
+ {{{'A','n','t','i','q','O','l','i','v','e',' ',' ',' ',' ',' ',' '},
+ pxffAntiqueOlive | pxff_variable | pxff_sans | pxff_upright | pxff_plain},
+ 0},
+ {{{'A','n','t','i','q','O','l','i','v','e',' ',' ',' ',' ','B','d'},
+ pxffAntiqueOlive | pxff_variable | pxff_sans | pxff_upright | pxff_bold},
+ 0},
+ {{{'A','n','t','i','q','O','l','i','v','e',' ',' ',' ',' ','I','t'},
+ pxffAntiqueOlive | pxff_variable | pxff_sans | pxff_oblique | pxff_plain},
+ 0},
+ {{{'A','r','i','a','l',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+ pxffArial | pxff_variable | pxff_sans | pxff_upright | pxff_plain},
+ "arial.ttf"},
+ {{{'A','r','i','a','l',' ',' ',' ',' ',' ',' ',' ',' ',' ','B','d'},
+ pxffArial | pxff_variable | pxff_sans | pxff_upright | pxff_bold},
+ "arialbd.ttf"},
+ {{{'A','r','i','a','l',' ',' ',' ',' ',' ',' ',' ','B','d','I','t'},
+ pxffArial | pxff_variable | pxff_sans | pxff_oblique | pxff_bold},
+ "arialbi.ttf"},
+ {{{'A','r','i','a','l',' ',' ',' ',' ',' ',' ',' ',' ',' ','I','t'},
+ pxffArial | pxff_variable | pxff_sans | pxff_oblique | pxff_plain},
+ "ariali.ttf"},
+ {{{'C','G',' ','O','m','e','g','a',' ',' ',' ',' ',' ',' ',' ',' '},
+ pxffCGOmega | pxff_variable | pxff_sans | pxff_upright | pxff_plain},
+ 0},
+ {{{'C','G',' ','O','m','e','g','a',' ',' ',' ',' ',' ',' ','B','d'},
+ pxffCGOmega | pxff_variable | pxff_sans | pxff_upright | pxff_bold},
+ 0},
+ {{{'C','G',' ','O','m','e','g','a',' ',' ',' ',' ','B','d','I','t'},
+ pxffCGOmega | pxff_variable | pxff_sans | pxff_oblique | pxff_bold},
+ 0},
+ {{{'C','G',' ','O','m','e','g','a',' ',' ',' ',' ',' ',' ','I','t'},
+ pxffCGOmega | pxff_variable | pxff_sans | pxff_oblique | pxff_plain},
+ 0},
+ {{{'C','G',' ','T','i','m','e','s',' ',' ',' ',' ',' ',' ',' ',' '},
+ pxffCGTimes | pxff_variable | pxff_serif | pxff_upright | pxff_plain},
+ 0},
+ {{{'C','G',' ','T','i','m','e','s',' ',' ',' ',' ',' ',' ','B','d'},
+ pxffCGTimes | pxff_variable | pxff_serif | pxff_upright | pxff_bold},
+ 0},
+ {{{'C','G',' ','T','i','m','e','s',' ',' ',' ',' ','B','d','I','t'},
+ pxffCGTimes | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_bold},
+ 0},
+ {{{'C','G',' ','T','i','m','e','s',' ',' ',' ',' ',' ',' ','I','t'},
+ pxffCGTimes | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_plain},
+ 0},
+ {{{'C','l','a','r','e','n','d','o','n',' ',' ',' ','C','d','B','d'},
+ pxffClarendon | pxff_variable | pxff_serif | pxff_upright | pxff_bold},
+ 0},
+ {{{'C','o','r','o','n','e','t',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+ pxffCoronet | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_plain},
+ 0},
+ {{{'C','o','u','r','i','e','r',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+ pxffCourier | pxff_fixed | pxff_serif | pxff_upright | pxff_plain},
+ "cour.ttf"},
+ {{{'C','o','u','r','i','e','r',' ',' ',' ',' ',' ',' ',' ','B','d'},
+ pxffCourier | pxff_fixed | pxff_serif | pxff_upright | pxff_bold},
+ "courbd.ttf"},
+ {{{'C','o','u','r','i','e','r',' ',' ',' ',' ',' ',' ',' ','I','t'},
+ pxffCourier | pxff_fixed | pxff_serif | pxff_oblique | pxff_plain},
+ "couri.ttf"},
+ {{{'C','o','u','r','i','e','r',' ',' ',' ',' ',' ','B','d','I','t'},
+ pxffCourier | pxff_fixed | pxff_serif | pxff_oblique | pxff_bold},
+ "courbi.ttf"},
+ {{{'G','a','r','a','m','o','n','d',' ','A','n','t','i','q','u','a'},
+ pxffGaramond | pxff_variable | pxff_serif | pxff_upright | pxff_plain},
+ 0},
+ {{{'G','a','r','a','m','o','n','d',' ',' ',' ',' ',' ','H','l','b'},
+ pxffGaramond | pxff_variable | pxff_serif | pxff_upright | pxff_bold},
+ 0},
+ {{{'G','a','r','a','m','o','n','d',' ',' ',' ',' ','K','r','s','v'},
+ pxffGaramond | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_plain},
+ 0},
+ {{{'G','a','r','a','m','o','n','d',' ','K','r','s','v','H','l','b'},
+ pxffGaramond | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_bold},
+ 0},
+ {{{'L','e','t','t','e','r','G','o','t','h','i','c',' ',' ',' ',' '},
+ pxffLetterGothic | pxff_fixed | pxff_sans | pxff_upright | pxff_plain},
+ 0},
+ {{{'L','e','t','t','e','r','G','o','t','h','i','c',' ',' ','B','d'},
+ pxffLetterGothic | pxff_fixed | pxff_sans | pxff_upright | pxff_bold},
+ 0},
+ {{{'L','e','t','t','e','r','G','o','t','h','i','c',' ',' ','I','t'},
+ pxffLetterGothic | pxff_fixed | pxff_sans | pxff_oblique | pxff_plain},
+ 0},
+ /* The line printer fonts are identical except for symbol set. */
+#define LinePrinter(ss1,ss2,ss3)\
+ {{{'L','i','n','e',' ','P','r','i','n','t','e','r',' ',ss1,ss2,ss3},\
+ pxffLinePrinter | pxff_fixed | pxff_serif | pxff_upright | pxff_plain},\
+ 0}
+ LinePrinter(' ', '0', 'N'),
+ LinePrinter('1', '0', 'U'),
+ LinePrinter('1', '1', 'U'),
+ LinePrinter('1', '2', 'U'),
+ LinePrinter(' ', '1', 'U'),
+ LinePrinter(' ', '2', 'N'),
+ LinePrinter(' ', '5', 'N'),
+ LinePrinter(' ', '8', 'U'),
+#undef LinePrinter
+ {{{'M','a','r','i','g','o','l','d',' ',' ',' ',' ',' ',' ',' ',' '},
+ pxffMarigold | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_plain},
+ 0},
+ {{{'S','y','m','b','o','l',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+ pxffSymbol | pxff_variable | pxff_upright | pxff_symbolic},
+ "symbol.ttf"},
+ {{{'T','i','m','e','s','N','e','w','R','m','n',' ',' ',' ',' ',' '},
+ pxffTimes | pxff_variable | pxff_serif | pxff_upright | pxff_plain},
+ "times.ttf"},
+ {{{'T','i','m','e','s','N','e','w','R','m','n',' ',' ',' ','B','d'},
+ pxffTimes | pxff_variable | pxff_serif | pxff_upright | pxff_bold},
+ "timesbd.ttf"},
+ {{{'T','i','m','e','s','N','e','w','R','m','n',' ','B','d','I','t'},
+ pxffTimes | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_bold},
+ "timesbi.ttf"},
+ {{{'T','i','m','e','s','N','e','w','R','m','n',' ',' ',' ','I','t'},
+ pxffTimes | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_plain},
+ "timesi.ttf"},
+ {{{'U','n','i','v','e','r','s',' ',' ',' ',' ',' ',' ',' ','B','d'},
+ pxffUnivers | pxff_variable | pxff_sans | pxff_upright | pxff_bold},
+ 0},
+ {{{'U','n','i','v','e','r','s',' ',' ',' ','C','d','B','d','I','t'},
+ pxffUnivers | pxff_variable | pxff_sans | pxff_oblique | pxff_bold | pxff_narrow},
+ 0},
+ {{{'U','n','i','v','e','r','s',' ',' ',' ',' ',' ','B','d','I','t'},
+ pxffUnivers | pxff_variable | pxff_sans | pxff_oblique | pxff_bold},
+ 0},
+ {{{'U','n','i','v','e','r','s',' ',' ',' ',' ',' ',' ',' ','M','d'},
+ pxffUnivers | pxff_variable | pxff_sans | pxff_upright | pxff_plain},
+ 0},
+ {{{'U','n','i','v','e','r','s',' ',' ',' ',' ',' ','C','d','M','d'},
+ pxffUnivers | pxff_variable | pxff_sans | pxff_upright | pxff_plain | pxff_narrow},
+ 0},
+ {{{'U','n','i','v','e','r','s',' ',' ',' ','C','d','M','d','I','t'},
+ pxffUnivers | pxff_variable | pxff_sans | pxff_oblique | pxff_plain | pxff_narrow},
+ 0},
+ {{{'U','n','i','v','e','r','s',' ',' ',' ',' ',' ','M','d','I','t'},
+ pxffUnivers | pxff_variable | pxff_sans | pxff_oblique | pxff_plain},
+ 0},
+ {{{'W','i','n','g','d','i','n','g','s',' ',' ',' ',' ',' ',' ',' '},
+ pxffWingdings | pxff_variable | pxff_upright | pxff_symbolic},
+ "wingding.ttf"}
+};
+#define num_known_fonts countof(known_fonts)
+const uint px_num_known_fonts = num_known_fonts;
+
+/*
+ * Define some font name substrings that give clues about appropriate
+ * substitutions.
+ */
+#define ffAvantGarde (pxffAvantGarde | pxff_serif)
+#define ffBookman (pxffBookman | pxff_serif)
+#define ffCourier (pxffCourier | pxff_fixed)
+#define ffHelvetica (pxffHelvetica | pxff_sans)
+#define ffNewCenturySchlbk (pxffNewCenturySchlbk | pxff_serif)
+#define ffPalatino (pxffPalatino | pxff_serif)
+#define ffTimes (pxffTimes | pxff_serif)
+private const px_font_desc_t subst_font_families[] = {
+ /* Guess at suitable substitutions for random unknown fonts. */
+ {{'G','o','t','h'}, ffTimes},
+ {{'G','r','o','t'}, ffTimes},
+ {{'R','o','m','a','n'}, ffTimes},
+ {{'B','o','o','k'}, ffNewCenturySchlbk},
+ /* If the family name appears in the font name, */
+ /* use a font from that family. */
+ {{'A','l','b','e','r','t','u','s'}, ffHelvetica},
+ {{'A','r','i','a','l'}, ffHelvetica},
+ {{'A','v','a','n','t'}, ffAvantGarde},
+ {{'B','o','d','o','n','i'}, ffBookman},
+ {{'B','o','o','k','m','a','n'}, ffBookman},
+ {{'C','e','n','t','u','r','y'}, ffNewCenturySchlbk},
+ {{'C','l','a','r','e','n','d','o','n'}, ffTimes},
+ {{'C','o','u','r'}, ffCourier},
+ {{'G','e','n','e','v','a'}, ffHelvetica},
+ {{'G','o','u','d','y'}, ffNewCenturySchlbk},
+ {{'G','r','a','p','h','o','s'}, ffCourier},
+ {{'H','e','l','v'}, ffHelvetica},
+ {{'M','e','t','r','o','s','t','y','l','e'}, ffHelvetica},
+ {{'N','e','w','Y','o','r','k'}, ffTimes},
+ {{'P','a','l'}, ffPalatino},
+ {{'R','o','m'}, ffTimes},
+ {{'S','a','n'}, ffHelvetica},
+ {{'S','c','h','l','b','k'}, ffNewCenturySchlbk},
+ {{'S','e','r','i','f'}, ffTimes},
+ {{'S','o','r','t','s'}, pxff_symbolic},
+ {{'S','w','i','s','s'}, ffHelvetica},
+ {{'T','i','m','e','s'}, ffTimes},
+ {{'U','n','i','v','e','r','s'}, ffHelvetica},
+ /* Substitute for Adobe Multiple Master fonts. */
+ {{'M','y','r','i','a','d'}, ffTimes},
+ {{'M','i','n','i','o','n'}, ffHelvetica},
+};
+private const px_font_desc_t subst_font_attributes[] = {
+ {{'M','o','n'}, pxff_fixed},
+ {{'T','y','p','e','w','r','i','t','e','r'}, pxff_fixed},
+ {{'I','t'}, pxff_italic},
+ {{'O','b','l','i','q','u','e'}, pxff_oblique},
+ {{'B','d'}, pxff_bold},
+ {{'B','o','l','d'}, pxff_bold},
+ {{'b','o','l','d'}, pxff_bold},
+ {{'D','e','m','i'}, pxff_bold},
+ {{'H','e','a','v','y'}, pxff_bold},
+ {{'S','b'}, pxff_bold},
+ {{'X','b'}, pxff_bold},
+ {{'C','d'}, pxff_narrow},
+ {{'C','o','n','d'}, pxff_narrow},
+ {{'N','a','r','r','o','w'}, pxff_narrow},
+};
+
+/* All the font-related structures are big-endian, so: */
+#define u16(ptr) uint16at(ptr, true)
+#define s16(ptr) sint16at(ptr, true)
+#define u32(ptr) uint32at(ptr, true)
+
+#if arch_is_big_endian
+# define pxd_native_byte_order pxd_big_endian
+#else
+# define pxd_native_byte_order 0
+#endif
+
+/* ---------------- Operator utilities ---------------- */
+
+/* Compute the length of a Unicode string. */
+private uint
+ustrlen(const char16 *ustr)
+{ uint i;
+ for ( i = 0; ustr[i]; ++i ) ;
+ return i;
+}
+
+/* Search for a Unicode string in another Unicode string. */
+private const char16 *
+ustrstr(const char16 *str, uint strlen, const char16 *pattern, uint patlen)
+{ uint i;
+
+ for ( i = 0; i + patlen <= strlen; ++i )
+ if ( !memcmp(str + i, pattern, patlen * sizeof(char16)) )
+ return (const char16 *)(str + i);
+ return 0;
+}
+
+/* Widen and/or byte-swap a font name to Unicode if needed. */
+/* The argument is a ubyte or uint16 array; the result is a uint16 array */
+/* with the elements in native byte order. */
+/* We don't deal with mappings: we just widen 8-bit to 16-bit characters */
+/* and hope for the best.... */
+private int
+px_widen_font_name(px_value_t *pfnv, px_state_t *pxs)
+{ uint type = pfnv->type;
+
+ if ( (type & (pxd_uint16 | pxd_big_endian)) ==
+ (pxd_uint16 | pxd_native_byte_order)
+ )
+ return 0; /* already in correct format */
+ { byte *old_data = (byte *)pfnv->value.array.data; /* break const */
+ uint size = pfnv->value.array.size;
+ char16 *new_data;
+ uint i;
+
+ if ( type & pxd_on_heap )
+ old_data = (byte *)
+ (new_data =
+ (char16 *)gs_resize_object(pxs->memory, old_data,
+ size * 2, "px_widen_font_name"));
+ else
+ new_data =
+ (char16 *)gs_alloc_byte_array(pxs->memory, size, sizeof(char16),
+ "px_widen_font_name");
+ if ( new_data == 0 )
+ return_error(errorInsufficientMemory);
+ for ( i = size; i; )
+ { --i;
+ new_data[i] =
+ (type & pxd_ubyte ? old_data[i] :
+ uint16at(old_data + i * 2, type & pxd_big_endian));
+ }
+ pfnv->value.array.data = (byte *)new_data;
+ }
+ pfnv->type = (type & ~(pxd_ubyte | pxd_big_endian)) |
+ (pxd_uint16 | pxd_native_byte_order | pxd_on_heap);
+ return 0;
+}
+
+/* ---------------- Operator implementations ---------------- */
+
+/* Open the file for a known font. */
+private FILE *
+px_open_font_file(const char *fname)
+{ const char **pprefix = known_font_file_prefixes;
+
+ for ( ; *pprefix; ++pprefix )
+ { char file_name[80];
+ FILE *in;
+
+ strcpy(file_name, *pprefix);
+ strcat(file_name, fname);
+ in = fopen(file_name, "rb");
+ if ( in )
+ return in;
+ in = fopen(file_name, "r");
+ if ( in )
+ return in;
+ }
+ return 0;
+}
+
+/* Look up a font name and return its description, if known. */
+/* The font name must already have been widened to Unicode. */
+private const px_stored_font_t *
+px_find_known_font(px_value_t *pfnv, px_state_t *pxs)
+{ uint i;
+
+ if ( pfnv->value.array.size != 16 )
+ return 0;
+ for ( i = 0; i < num_known_fonts; ++i )
+ if ( !memcmp(known_fonts[i].descriptor.fname,
+ pfnv->value.array.data, 16 * sizeof(char16))
+ )
+ return &known_fonts[i];
+ return 0;
+}
+
+/* Look up a font name and return an existing font. */
+/* This procedure may widen and/or byte-swap the font name. */
+/* If this font is supposed to be built in but no .TTF file is available, */
+/* or if loading the font fails, return >= 0 and store 0 in *ppxfont. */
+int
+px_find_existing_font(px_value_t *pfnv, px_font_t **ppxfont,
+ px_state_t *pxs)
+{ int code;
+
+ /* Normalize the font name to Unicode. */
+ code = px_widen_font_name(pfnv, pxs);
+ if ( code < 0 )
+ return code;
+
+ /* Check if we know the font already. */
+ { void *pxfont;
+
+ if ( px_dict_find(&pxs->font_dict, pfnv, &pxfont) )
+ { /* Make sure this isn't a partially defined font */
+ /* in the process of being downloaded. */
+ if ( ((px_font_t *)pxfont)->pfont )
+ { *ppxfont = pxfont;
+ return 0;
+ }
+ }
+ }
+
+ /* Check for a built-in font. */
+ { const px_stored_font_t *psf = px_find_known_font(pfnv, pxs);
+
+ if ( psf == 0 )
+ return_error(errorFontUndefined);
+ *ppxfont = 0; /* in case of later failure */
+ if ( psf->file_name == 0 )
+ return 0;
+
+ { /* Load the TrueType font data. */
+ FILE *in = px_open_font_file(psf->file_name);
+ px_font_t *pxfont;
+
+ if ( in == 0 )
+ return 0; /* TrueType font not available */
+ code =
+ pl_load_tt_font(in, pxs->font_dir, pxs->memory,
+ (psf - known_fonts + pxs->known_fonts_base_id),
+ &pxfont);
+ if ( code >= 0 )
+ { pxfont->storage = pxfsInternal;
+ pxfont->params.symbol_set = 0; /* not known */
+ /* The character complements are bogus.... */
+ memcpy(pxfont->character_complement,
+ (psf->descriptor.flags & pxff_symbolic ?
+ "\377\377\377\377\377\377\377\376" :
+ "\377\377\377\377\000\000\000\006"),
+ 8);
+ code = px_dict_put(&pxs->font_dict, pfnv, pxfont);
+ if ( code >= 0 )
+ *ppxfont = pxfont;
+ }
+ }
+ }
+ return code;
+}
+
+/* Look for a partial match with a known font. */
+private int
+px_find_subst_font(px_font_flags_t flags, px_font_flags_t mask,
+ px_font_t **ppxfont, const char16 **psfname, px_state_t *pxs)
+{ uint i;
+
+ for ( i = 0; i < num_known_fonts; ++i )
+ { px_font_flags_t known_flags = known_fonts[i].descriptor.flags;
+ /* Require an exact match on typeface, but only inclusion */
+ /* on properties. */
+ if ( !((known_flags ^ flags) & pxff_typeface_mask & mask) &&
+ !(flags & ~known_flags & pxff_all_properties & mask)
+ )
+ { const char16 *sfname = known_fonts[i].descriptor.fname;
+ px_value_t fnv;
+ int code;
+
+ fnv.type = pxd_array | pxd_uint16 | pxd_native_byte_order;
+ fnv.value.array.data = (const byte *)sfname;
+ fnv.value.array.size = ustrlen(sfname);
+ code = px_find_existing_font(&fnv, ppxfont, pxs);
+ if ( code < 0 || *ppxfont == 0 )
+ continue;
+ *psfname = sfname;
+ return 0;
+ }
+ }
+ return_error(errorFontUndefinedNoSubstituteFound);
+}
+
+/* Look up a font name and return a font, possibly with substitution. */
+/* This procedure implements most of the SetFont operator. */
+/* This procedure may widen and/or byte-swap the font name. */
+int
+px_find_font(px_value_t *pfnv, uint symbol_set, px_font_t **ppxfont,
+ px_state_t *pxs)
+{ int code;
+ bool built_in;
+
+ /* Check if we know the font already. */
+ /* Note that px_find_existing_font normalizes the font name. */
+
+ code = px_find_existing_font(pfnv, ppxfont, pxs);
+ if ( code >= 0 )
+ { if ( *ppxfont != 0 )
+ return code;
+ built_in = true;
+ }
+ else if ( code == errorFontUndefined )
+ built_in = false;
+ else
+ return code;
+
+ /* If we're asking for an unknown symbol set, give up. */
+ { const pl_symbol_map_t **pmap = pl_built_in_symbol_maps;
+ const pl_symbol_map_t *map;
+
+ for ( ; (map = *pmap) != 0; ++pmap )
+ { uint map_set = (map->id[0] << 8) + map->id[1];
+ if ( map_set == symbol_set )
+ break;
+ }
+ if ( map == 0 )
+ return_error(built_in ? errorSymbolSetRemapUndefined :
+ errorFontUndefinedNoSubstituteFound);
+ }
+
+ /* Try to find a substitute font. */
+
+ { const char16 *fname = (const char16 *)pfnv->value.array.data;
+ uint fnsize = pfnv->value.array.size;
+ px_font_flags_t flags = 0;
+ const char16 *sfname;
+ int i;
+
+ if ( built_in )
+ flags = px_find_known_font(pfnv, pxs)->descriptor.flags;
+ else
+ { for ( i = 0; i < countof(subst_font_families); ++i )
+ { const char16 *pattern = subst_font_families[i].fname;
+ if ( ustrstr(fname, fnsize, pattern, ustrlen(pattern)) )
+ flags = subst_font_families[i].flags;
+ }
+ for ( i = 0; i < countof(subst_font_attributes); ++i )
+ { const char16 *pattern = subst_font_attributes[i].fname;
+ if ( ustrstr(fname, fnsize, pattern, ustrlen(pattern)) )
+ flags |= subst_font_attributes[i].flags;
+ }
+ }
+ if ( flags == 0 )
+ { /* This is a completely unknown font. Substitute Courier. */
+ flags = pxffCourier | pxff_fixed | pxff_serif | pxff_upright | pxff_plain;
+ }
+ if ( (code = px_find_subst_font(flags, pxff_all, ppxfont, &sfname, pxs)) >= 0 ||
+ (code = px_find_subst_font(flags, pxff_all_properties, ppxfont, &sfname, pxs)) >= 0 ||
+#define mask (pxff_serifs | pxff_posture | pxff_weight | pxff_symbolic)
+ (code = px_find_subst_font(flags, mask, ppxfont, &sfname, pxs)) >= 0 ||
+#undef mask
+#define mask (pxff_symbolic)
+ (code = px_find_subst_font(flags, mask, ppxfont, &sfname, pxs)) >= 0 ||
+#undef mask
+ (code = px_find_subst_font(0, 0, ppxfont, &sfname, pxs)) >= 0
+ )
+ { if ( code >= 0 && !built_in )
+ { /* Issue a warning. */
+ px_value_t sfnv;
+ static const char *mid_message = " substituted for ";
+ char message[px_max_error_line + 1];
+
+ message[0] = 0;
+ sfnv.type = pxd_array | pxd_uint16 | pxd_native_byte_order;
+ sfnv.value.array.data = (byte *)sfname; /* break const, but not really */
+ sfnv.value.array.size = ustrlen(sfname);
+ px_concat_font_name(message, px_max_error_line, &sfnv);
+ strncat(message, mid_message,
+ px_max_error_line - strlen(message));
+ message[px_max_error_line] = 0;
+ px_concat_font_name(message, px_max_error_line, pfnv);
+ code = px_record_warning(message, true, pxs);
+ }
+ }
+ return code;
+ }
+}