about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2020-05-28 21:04:14 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2020-05-28 21:04:14 +0000
commit780486818286799c8cce5f4e3ad3a312773e6f16 (patch)
treea5b2249c828b6a48526464bdf734862da15b4f0e
parentdd8aaea0561a2f1fa3d2818185900e92d17e4720 (diff)
downloadnetpbm-mirror-780486818286799c8cce5f4e3ad3a312773e6f16.tar.gz
netpbm-mirror-780486818286799c8cce5f4e3ad3a312773e6f16.tar.xz
netpbm-mirror-780486818286799c8cce5f4e3ad3a312773e6f16.zip
From a BDF font file, load only the characters that are needed instead of the full font
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@3811 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r--generator/pbmtext.c373
-rw-r--r--lib/libpbmfont0.c83
-rw-r--r--lib/libpbmfont1.c2
-rw-r--r--lib/libpbmfont2.c515
-rw-r--r--lib/pbmfont.h93
-rw-r--r--lib/pbmfontdata2.c2
6 files changed, 800 insertions, 268 deletions
diff --git a/generator/pbmtext.c b/generator/pbmtext.c
index a8d45837..7c7d2548 100644
--- a/generator/pbmtext.c
+++ b/generator/pbmtext.c
@@ -30,6 +30,7 @@
 #include "pbm.h"
 #include "pbmfont.h"
 
+
 /* Max length of input text.  Valid for text which is part of the
    command line and also for text fed from standard input.
    Note that newline is counted as a character.
@@ -55,6 +56,7 @@ struct CmdlineInfo {
     unsigned int nomargins;  /* -nomargins option specified  */
     unsigned int dryrun;     /* -dry-run option specified */
     unsigned int textdump;   /* -text-dump option specified */
+    unsigned int entirefont; /* -load-entire-font option specified */
     unsigned int verbose;    /* -verbose option specified */
         /* undocumented option */
     unsigned int dumpsheet; /* font data sheet in PBM format for -font */
@@ -133,6 +135,7 @@ parseCommandLine(int argc, const char ** argv,
     OPTENT3(0, "dry-run",    OPT_FLAG,   NULL, &cmdlineP->dryrun,    0);
     OPTENT3(0, "text-dump",  OPT_FLAG,   NULL, &cmdlineP->textdump,  0);
     OPTENT3(0, "dump-sheet", OPT_FLAG,   NULL, &cmdlineP->dumpsheet, 0);
+    OPTENT3(0, "load-entire-font", OPT_FLAG,   NULL, &cmdlineP->entirefont, 0);
 
     /* Set the defaults */
     cmdlineP->font    = NULL;
@@ -166,6 +169,8 @@ parseCommandLine(int argc, const char ** argv,
 
     if (cmdlineP->font != NULL && cmdlineP->builtin != NULL)
         pm_error("You cannot specify both -font and -builtin");
+    else if (cmdlineP->font == NULL && cmdlineP->entirefont)
+        pm_error("You cannot specify -load-entire-font without -font");
 
     if (cmdlineP->textdump) {
         if (cmdlineP->dryrun)
@@ -206,12 +211,10 @@ reportFont(const struct font2 * const fontP) {
 
 
 
-
-
-
 static struct font2 *
-font2FromFile(const char * const fileName,
-              PM_WCHAR     const maxmaxglyph) {
+font2FromFile(const char *               const fileName,
+              PM_WCHAR                   const maxmaxglyph,
+              const struct pm_selector * const selectorP) {
 
     struct font2 * font2P;
 
@@ -224,7 +227,7 @@ font2FromFile(const char * const fileName,
         /* This is the normal program flow */
         pm_setjmpbuf(&jmpbuf);
 
-        font2P = pbm_loadfont2(fileName, maxmaxglyph);
+        font2P = pbm_loadfont2select(fileName, maxmaxglyph, selectorP);
 
         pm_setjmpbuf(NULL);
     } else {
@@ -241,17 +244,163 @@ font2FromFile(const char * const fileName,
 
 
 
+static bool
+codepointIsValid(struct font2 * const fontP,
+                 PM_WCHAR       const codepoint) {
+/*----------------------------------------------------------------------------
+  'codepoint' is a valid entry in the font indicated by 'fontP'.
+-----------------------------------------------------------------------------*/
+    bool retval;
+
+    assert(pm_selector_is_marked(fontP->selectorP, codepoint));
+
+    if (codepoint > fontP->maxglyph || fontP->glyph[codepoint] == NULL)
+        retval = false;
+    else retval = true;
+
+    return (retval);
+
+}
+
+
+
+static const char *
+charDescription(PM_WCHAR const codepoint) {
+/*----------------------------------------------------------------------------
+   Descriptive string for codepoint 'codepoint'.
+
+   Certain codepoints appear frequently in text files and cause problems when
+   missing in the font set, so we give those descriptions.  For other
+   codepoint, we just return a null string.
+-----------------------------------------------------------------------------*/
+
+  const char * name;
+
+  switch (codepoint) {
+  case '\r' : name="carriage return";  break;
+  case '\n' : name="line feed";        break; /* for future use */
+  case '\t' : name="tab";              break; /* for future use */
+  case ' '  : name="space";            break;
+  case 0xFEFF: name="byte order mark"; break;
+  default : name=""; break;
+  }
+
+  return name;
+}
+
+
+
+enum FixMode {SILENT, /* convert silently */
+              WARN,   /* output message to stderr */
+              QUIT    /* abort */ };
+
+
+
 static void
-computeFont(struct CmdlineInfo const cmdline,
-            struct font2 **    const fontPP) {
+reportAbsentGlyphs(bool                       const wchar,
+                   struct font2 *             const fontP,
+                   const struct pm_selector * const textSelectorP,
+                   unsigned int *             const missingCharCtP) {
+/*----------------------------------------------------------------------------
+   Compare the glyph entries in *fontP with the requests in *textSelectorP.
+
+   Note that we may need the space character as a substitute for missing
+   glyphs while the input text has no spaces.  In rare cases the font may not
+   have a space character.
+
+   Currently, this program reads the font file only once.  A future version
+   may opt to read it a second time to load the substitute glyph.
+-----------------------------------------------------------------------------*/
+    PM_WCHAR     codepoint;
+    unsigned int missingCharCt;
+
+    for (codepoint = textSelectorP->min, missingCharCt = 0;
+         codepoint <= textSelectorP->max; ++codepoint) {
+
+        if (pm_selector_is_marked(textSelectorP, codepoint) &&
+            !codepointIsValid(fontP, codepoint)) {
+            ++missingCharCt;
+            if (missingCharCt == 1)  { /* initial */
+                pm_message ("%u characters found in text",
+                            textSelectorP->count);;
+                pm_message ("failed to load glyph data for:");
+            }
+
+            pm_message(wchar ? "+%05X %s" : "%02X %s",
+                       (unsigned int) codepoint,
+                       charDescription(codepoint));
+        }
+    }
+    if (missingCharCt > 0)
+        pm_message("total %u chararcters absent in font", missingCharCt);
+
+    *missingCharCtP = missingCharCt;
+}
+
+
+
+static void
+validateFont(bool                       const wchar,
+             struct font2 *             const fontP,
+             const struct pm_selector * const textSelectorP,
+             enum   FixMode             const fixmode,
+             bool *                     const hasAllCharsP) {
+/*----------------------------------------------------------------------------
+   If any glyphs required by the text indicated by *textSelectorP are missing
+   from font *fontP, issue a warning message or abort the program according to
+   'fixmode'.
+
+   Abort the program if one or more characters are missing and the space
+   character is one of them.
+
+   Return (if we return) as *hasAllCharsP whether the font has all the glyphs.
+-----------------------------------------------------------------------------*/
+    unsigned int missingCharCt;
+
+    assert (textSelectorP != NULL);
+    assert (textSelectorP->count >= 0);
+
+    reportAbsentGlyphs(wchar, fontP, textSelectorP, &missingCharCt);
+
+    if (missingCharCt > 0) {
+
+        if (fixmode == QUIT)
+            pm_error("aborting");
+        else if (!codepointIsValid(fontP, L' '))
+            pm_error ("replacement character (space) absent; aborting");
+        else
+            pm_message("undefined code points will be converted to space");
+    }
+
+    *hasAllCharsP = (missingCharCt == 0);
+}
 
-    struct font2 * font2P;
 
-    if (cmdline.font)
-        font2P = font2FromFile(cmdline.font,
-                               cmdline.wchar ? PM_FONT2_MAXGLYPH :
-                                               PM_FONT_MAXGLYPH);
-    else if (cmdline.builtin)
+
+static void
+computeFont(struct CmdlineInfo         const cmdline,
+            struct font2 **            const fontPP,
+            const struct pm_selector * const textSelectorP,
+            enum   FixMode             const fixmode,
+            bool *                     const fontHasAllCharsP) {
+
+    struct font2 *       font2P;
+    struct pm_selector * fontSelectorP;
+
+    if (cmdline.font) {
+        if(cmdline.entirefont)
+            fontSelectorP = NULL;
+        else if(!pm_selector_is_marked(textSelectorP, L' ')) {
+            pm_selector_copy(MAX(textSelectorP->max, L' '),
+                             textSelectorP, &fontSelectorP);
+            pm_selector_mark(fontSelectorP, L' ');
+        } else
+            fontSelectorP = (struct pm_selector *) textSelectorP;
+
+        font2P = font2FromFile(cmdline.font, cmdline.wchar ?
+                               PM_FONT2_MAXGLYPH : PM_FONT_MAXGLYPH,
+                               fontSelectorP);
+    } else if (cmdline.builtin)
         font2P = pbm_defaultfont2(cmdline.builtin);
     else
         font2P = pbm_defaultfont2(cmdline.wchar ? "bdf" : "bdf");
@@ -259,6 +408,9 @@ computeFont(struct CmdlineInfo const cmdline,
     if (cmdline.verbose)
         reportFont(font2P);
 
+    validateFont(cmdline.wchar, font2P, textSelectorP, fixmode,
+                 fontHasAllCharsP);
+
     *fontPP = font2P;
 }
 
@@ -266,9 +418,6 @@ computeFont(struct CmdlineInfo const cmdline,
 
 struct Text {
     PM_WCHAR **  textArray;  /* malloc'ed */
-        /* This is strictly characters that are in user's font - no control
-           characters, no undefined code points.
-        */
     unsigned int allocatedLineCount;
     unsigned int lineCount;
 };
@@ -309,28 +458,19 @@ freeTextArray(struct Text const text) {
 
 
 
-enum FixMode {SILENT, /* convert silently */
-              WARN,   /* output message to stderr */
-              QUIT    /* abort */ };
-
 
 static void
-fixControlChars(const PM_WCHAR  * const input,
-                struct font2    * const fontP,
-                const PM_WCHAR ** const outputP,
-                enum FixMode      const fixMode) {
+setupSelector(const PM_WCHAR *     const input,
+              const PM_WCHAR **    const outputP,
+              struct pm_selector * const selectorP) {
 /*----------------------------------------------------------------------------
-   Return a translation of input[] that can be rendered as glyphs in
-   the font 'fontP'.  Return it as newly malloced *outputP.
+   Read through input[] and record the codepoints encountered.  Return it as
+   newly malloced *outputP.
 
    Expand tabs to spaces.
 
    Remove any trailing newline.  (But leave intermediate ones as line
    delimiters).
-
-   Depending on value of fixMode, turn anything that isn't a code point
-   in the font to a single space (which isn't guaranteed to be in the
-   font either, of course).
 -----------------------------------------------------------------------------*/
     /* We don't know in advance how big the output will be because of the
        tab expansions.  So we make sure before processing each input
@@ -370,33 +510,19 @@ fixControlChars(const PM_WCHAR  * const input,
             unsigned int const nextTabStop =
                 (outCursor + tabSize) / tabSize * tabSize;
 
-            if (fontP->glyph[L' '] == NULL)
-                pm_error("space character not defined in font");
-
             while (outCursor < nextTabStop)
                 output[outCursor++] = L' ';
-        } else if (currentChar > fontP->maxglyph ||
-                   !fontP->glyph[currentChar]) {
-            if (currentChar > PM_FONT2_MAXGLYPH)
+
+            pm_selector_mark(selectorP, L' ');
+
+        } else if (currentChar > PM_FONT2_MAXGLYPH)
                 pm_message("code point %X is beyond what this program "
                            "can handle.  Max=%X",
                            (unsigned int)currentChar, PM_FONT2_MAXGLYPH);
-
-            /* Turn this unknown char into a single space. */
-            if (fontP->glyph[L' '] == NULL)
-                pm_error("space character not defined in font");
-            else if (fixMode == QUIT)
-                pm_error("code point %X not defined in font",
-                         (unsigned int) currentChar );
-            else {
-                if (fixMode == WARN)
-                    pm_message("converting code point %X to space",
-                               (unsigned int) currentChar );
-                output[outCursor++] = ' ';
-            }
-        } else
+        else {
             output[outCursor++] = input[inCursor];
-
+            pm_selector_mark(selectorP, currentChar);
+        }
         assert(outCursor <= outputSize);
     }
     output[outCursor++] = L'\0';
@@ -437,7 +563,8 @@ getEdges(double               const currentPosition,
     double rightEdge;
 
     if (glyphP == NULL)
-        pm_error("Unrenderable char: %04X", (unsigned int) currentChar);
+        pm_error("encountered unrenderable char: %04X",
+                  (unsigned int) currentChar);
     else {
         leftEdge  =  (int) MIN(currentPosition + glyphP->x, currLeftEdge);
         rightEdge =  MAX(currentPosition + glyphP->x + glyphP->width,
@@ -475,8 +602,8 @@ advancePosition(double               const currentPosition,
             pm_error("Negative -space value too large");
         else
             pm_error("Abnormal horizontal advance value %d "
-                     "for code point 0x%lx.",
-                     glyphP->xadd, (unsigned long int) currentChar);
+                     "for code point +%05X",
+                     glyphP->xadd, (unsigned int) currentChar);
     }
     else if (currentPosition + advance > INT_MAX)
         pm_error("Image is too wide");
@@ -535,8 +662,8 @@ getLineDimensions(PM_WCHAR             const line[],
 
     for (cursor = 0; line[cursor] != L'\0'; ++cursor) {
         PM_WCHAR          const currentChar = line[cursor];
-        unsigned long int const glyphIndex  = (unsigned long int) currentChar;
-        struct glyph *    const glyphP      = fontP->glyph[glyphIndex];
+        unsigned int      const index       = (unsigned int) currentChar;
+        struct glyph *    const glyphP      = fontP->glyph[index];
 
         getEdges(currentPosition, currentChar, glyphP, leftEdge, rightEdge,
                  &leftEdge, &rightEdge);
@@ -593,9 +720,8 @@ getCharsWithinWidth(PM_WCHAR             const line[],
              currentWidth <= targetWidth && line[cursor] != L'\0';
              ++cursor) {
             PM_WCHAR const currentChar = line[cursor];
-            unsigned long int const glyphIndex =
-              (unsigned long int) currentChar;
-            struct glyph * const glyphP = fontP->glyph[glyphIndex];
+            unsigned int const index = (unsigned int) currentChar;
+            struct glyph * const glyphP = fontP->glyph[index];
 
             getEdges(currentPosition, currentChar, glyphP, leftEdge, rightEdge,
                      &leftEdge, &rightEdge);
@@ -694,9 +820,8 @@ insertCharacters(bit **         const bits,
 
         for (cursor = 0; lp.textArray[line][cursor] != '\0'; ++cursor) {
             PM_WCHAR const currentChar = lp.textArray[line][cursor];
-            unsigned long int const glyphIndex =
-                (unsigned long int)currentChar;
-            struct glyph * const glyphP = fontP->glyph[glyphIndex];
+            unsigned int const index = (unsigned int) currentChar;
+            struct glyph * const glyphP = fontP->glyph[index];
             int const toprow =
                 row + fontP->maxheight + fontP->y - glyphP->height - glyphP->y;
                 /* row number in image of top row in glyph */
@@ -943,10 +1068,9 @@ fgetNarrowWideString(PM_WCHAR *    const widestring,
 
 
 static void
-getText(PM_WCHAR       const cmdlineText[],
-        struct font2 * const fontP,
-        struct Text  * const inputTextP,
-        enum FixMode   const fixMode) {
+getText(PM_WCHAR             const cmdlineText[],
+        struct Text *        const inputTextP,
+        struct pm_selector * const selectorP) {
 /*----------------------------------------------------------------------------
    Get as *inputTextP the text to format, given that the text on the
    command line (one word per command line argument, separated by spaces),
@@ -969,8 +1093,8 @@ getText(PM_WCHAR       const cmdlineText[],
         MALLOCARRAY_NOFAIL(inputText.textArray, 1);
         inputText.allocatedLineCount = 1;
         inputText.lineCount = 1;
-        fixControlChars(cmdlineText, fontP,
-                        (const PM_WCHAR**)&inputText.textArray[0], fixMode);
+        setupSelector(cmdlineText, (const PM_WCHAR**) &inputText.textArray[0],
+                      selectorP);
         free((void *) cmdlineText);
     } else {
         /* Read text from stdin. */
@@ -1021,9 +1145,9 @@ getText(PM_WCHAR       const cmdlineText[],
                         if (textArray == NULL)
                             pm_error("out of memory");
                     }
-                    fixControlChars(buf, fontP,
-                                    (const PM_WCHAR **)&textArray[lineCount],
-                                    fixMode);
+                    setupSelector(buf,
+                                  (const PM_WCHAR **) &textArray[lineCount],
+                                  selectorP);
                     if (textArray[lineCount] == NULL)
                         pm_error("out of memory");
                     ++lineCount;
@@ -1064,6 +1188,33 @@ computeMargins(struct CmdlineInfo const cmdline,
 
 
 static void
+refineText(struct Text        const inputText,
+           struct font2 *     const fontP) {
+/*----------------------------------------------------------------------------
+   Replace missing characters with space
+
+   A future version of this program may provide various alternatives
+   here including simply deleting the offending character, based on a
+   command-line option
+-----------------------------------------------------------------------------*/
+    PM_WCHAR ** const textArray = inputText.textArray;
+
+    unsigned int lineNum;
+
+    for (lineNum = 0; lineNum < inputText.lineCount; ++lineNum) {
+        PM_WCHAR * const line = textArray[lineNum];
+
+        unsigned int cursor;
+
+        for (cursor = 0; line[cursor] != L'\0'; ++cursor)
+            if ( !codepointIsValid(fontP, line[cursor]) )
+                line[cursor] = L' ';
+    }
+}
+
+
+
+static void
 formatText(struct CmdlineInfo const cmdline,
            struct Text        const inputText,
            struct font2 *     const fontP,
@@ -1191,7 +1342,7 @@ renderText(unsigned int   const cols,
                      space, cols, rows, lspace, fixedAdvance);
 
     /* Free all font data */
-    pbm_destroybdffont2(fontP); 
+    pbm_destroybdffont2(fontP);
 
     {
         unsigned int row;
@@ -1224,36 +1375,32 @@ L"M \",/^_[`jpqy| M" };
 
 
 static void
-validateText(const PM_WCHAR ** const textArray,
-             struct font2    * const fontP) {
-/*----------------------------------------------------------------------------
-   Abort the program if there are characters in 'textArray' which cannot be
-   rendered in font *fontP.
------------------------------------------------------------------------------*/
-    const PM_WCHAR * output;
-    unsigned int textRow;
+renderSheet(struct CmdlineInfo const cmdline,
+            FILE *             const ofP) {
 
-    for (textRow = 0; textRow < 12; ++textRow)
-        fixControlChars(textArray[textRow], fontP, &output, QUIT);
-
-    free((PM_WCHAR *)output);
-}
+    struct Text const sheetText =
+        { (PM_WCHAR ** const) sheetTextArray, 12, 12};
+    static unsigned char const sheetRequestArray[16] = {
+         0x00, 0x00, 0x00, 0x00,  0xff, 0xff, 0xff, 0xff,
+         0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xfe};
 
+    struct pm_selector * selectorP;
 
+    pm_selector_create_fixed(sheetRequestArray, 32, 126,95, &selectorP);
 
-static void
-renderSheet(struct font2 * const fontP,
-            FILE *         const ofP) {
+    struct font2 * fontP;
+    bool fontIsComplete;
 
-    int const cols  = fontP->maxwidth  * 16;
-    int const rows  = fontP->maxheight * 12;
-    struct Text const sheetText =
-        { (PM_WCHAR ** const) sheetTextArray, 12, 12};
+    computeFont(cmdline, &fontP, selectorP, QUIT, &fontIsComplete);
 
-    validateText(sheetTextArray, fontP);
+    {
+        unsigned int const cols  = fontP->maxwidth  * 16;
+        unsigned int const rows  = fontP->maxheight * 12;
 
-    renderText(cols, rows, fontP, 0, 0, sheetText, MAX(-(fontP->x),0),
-               0.0, 0, TRUE, ofP);
+        renderText(cols, rows, fontP, 0, 0, sheetText, MAX(-(fontP->x),0),
+                   0.0, 0, TRUE, ofP);
+    }
+    pm_selector_destroy(selectorP);
 }
 
 
@@ -1304,8 +1451,8 @@ textDumpOutput(struct Text   const lp,
 
 static void
 pbmtext(struct CmdlineInfo const cmdline,
-        struct font2 *     const fontP,
-        FILE *             const ofP) {
+        FILE *             const ofP,
+        bool               const wchar) {
 
     unsigned int rows, cols;
         /* Dimensions in pixels of the output image */
@@ -1315,19 +1462,30 @@ pbmtext(struct CmdlineInfo const cmdline,
     unsigned int hmargin0;
     struct Text inputText;
     struct Text formattedText;
+    struct font2 * fontP;
+    struct pm_selector * selectorP;
     unsigned int maxleftb, maxleftb0;
+    bool fontIsComplete;
+
+    pm_selector_create(wchar ? PM_FONT2_MAXGLYPH : PM_FONT_MAXGLYPH,
+                       &selectorP);
 
-    getText(cmdline.text, fontP, &inputText,
-            cmdline.verbose ? WARN : SILENT);
+    getText(cmdline.text, &inputText, selectorP);
+
+    if (pm_selector_marked_ct(selectorP) == 0)
+        pm_error("No input text.  Aborting.");
+
+    computeFont(cmdline, &fontP, selectorP, cmdline.verbose ? WARN : SILENT,
+                &fontIsComplete);
 
     computeMargins(cmdline, inputText, fontP, &vmargin, &hmargin0);
 
+    if (!fontIsComplete)
+        refineText(inputText, fontP);
+
     formatText(cmdline, inputText, fontP, hmargin0,
                &formattedText, &maxleftb0);
 
-    if (formattedText.lineCount == 0)
-        pm_error("No input text");
-
     computeImageHeight(formattedText, fontP, cmdline.lspace, vmargin, &rows);
 
     computeImageWidth(formattedText, fontP, cmdline.space,
@@ -1361,6 +1519,8 @@ pbmtext(struct CmdlineInfo const cmdline,
                    maxleftb, cmdline.space, cmdline.lspace, FALSE, ofP);
 
     freeTextArray(formattedText);
+
+    pm_selector_destroy(selectorP);
 }
 
 
@@ -1369,7 +1529,6 @@ int
 main(int argc, const char *argv[]) {
 
     struct CmdlineInfo cmdline;
-    struct font2 * fontP;
 
     pm_proginit(&argc, argv);
 
@@ -1389,12 +1548,10 @@ main(int argc, const char *argv[]) {
     if (cmdline.verbose)
         pm_message("LC_CTYPE is set to '%s'", setlocale(LC_CTYPE, NULL) );
 
-    computeFont(cmdline, &fontP);
-
     if (cmdline.dumpsheet)
-        renderSheet(fontP, stdout);
+        renderSheet(cmdline, stdout);
     else
-        pbmtext(cmdline, fontP, stdout);
+        pbmtext(cmdline, stdout, cmdline.wchar);
 
     pm_close(stdout);
 
diff --git a/lib/libpbmfont0.c b/lib/libpbmfont0.c
index 503c7ee7..a8027a38 100644
--- a/lib/libpbmfont0.c
+++ b/lib/libpbmfont0.c
@@ -27,7 +27,6 @@
 #include "pbmfont.h"
 #include "pbmfontdata.h"
 
-
 struct font *
 pbm_defaultfont(const char * const name) {
 /*----------------------------------------------------------------------------
@@ -77,11 +76,12 @@ pbm_defaultfont2(const char * const requestedFontName) {
 
 
 static void
-selectFontType(const    char * const filename,
-               PM_WCHAR        const maxmaxglyph,
-               unsigned int    const isWide,
-               struct font  ** const fontPP,
-               struct font2 ** const font2PP) {
+selectFontType(const    char *            const filename,
+               PM_WCHAR                   const maxmaxglyph,
+               unsigned int               const isWide,
+               struct font  **            const fontPP,
+               struct font2 **            const font2PP,
+               const struct pm_selector * const selectorP) {
 
     FILE * fileP;
     struct font  * fontP  = NULL; /* initial value */
@@ -106,7 +106,7 @@ selectFontType(const    char * const filename,
 
     } else if (!strncmp(line, "STARTFONT", 9)) {
         if (isWide == TRUE)
-            font2P = pbm_loadbdffont2(filename, maxmaxglyph);
+            font2P = pbm_loadbdffont2select(filename, maxmaxglyph, selectorP);
         else
             fontP = pbm_loadbdffont(filename);
         if (fontP == NULL && font2P == NULL)
@@ -128,25 +128,49 @@ selectFontType(const    char * const filename,
 
 
 struct font *
-pbm_loadfont(const    char * const filename) {
-
+pbm_loadfont(const char * const filename) {
+/*----------------------------------------------------------------------------
+   Load font file named 'filename'.
+   Font file may be either a PBM sheet or BDF.
+   Supports 8 bit codepoints.
+-----------------------------------------------------------------------------*/
     struct font  * fontP;
     struct font2 * font2P;
 
-    selectFontType(filename, PM_FONT_MAXGLYPH, FALSE, &fontP, &font2P);
+    selectFontType(filename, PM_FONT_MAXGLYPH, FALSE, &fontP, &font2P, NULL);
     return fontP;
 }
 
 
 
 struct font2 *
-pbm_loadfont2(const    char * const filename,
-              PM_WCHAR        const maxmaxglyph) {
+pbm_loadfont2(const char * const filename,
+              PM_WCHAR     const maxmaxglyph) {
+/*----------------------------------------------------------------------------
+   Load font file named 'filename'.
+   Font file may be either a PBM sheet or BDF.
+   Supports codepoints above 256.
+-----------------------------------------------------------------------------*/
+    struct font  * fontP;
+    struct font2 * font2P;
 
+    selectFontType(filename, maxmaxglyph, TRUE, &fontP, &font2P, NULL);
+    return font2P;
+}
+
+
+struct font2 *
+pbm_loadfont2select(const  char *              const filename,
+                    PM_WCHAR                   const maxmaxglyph,
+                    const struct pm_selector * const selectorP) {
+/*----------------------------------------------------------------------------
+   Same as pbm_loadfont2(), but load only glyphs indicated by *selectorP
+-----------------------------------------------------------------------------*/
     struct font  * fontP;
     struct font2 * font2P;
 
-    selectFontType(filename, maxmaxglyph, TRUE, &fontP, &font2P);
+    selectFontType(filename, maxmaxglyph, TRUE, &fontP, &font2P, selectorP);
+
     return font2P;
 }
 
@@ -173,7 +197,7 @@ pbm_createbdffont2_base(struct font2 ** const font2PP,
     /*  Caller should overwrite following fields as necessary */
     font2P->oldfont = NULL;
     font2P->fcols = font2P->frows = 0;
-    font2P->selector = NULL;
+    font2P->selectorP = NULL;
     font2P->default_char = 0;
     font2P->default_char_defined = FALSE;
     font2P->total_chars = font2P->chars = 0;
@@ -187,23 +211,32 @@ pbm_createbdffont2_base(struct font2 ** const font2PP,
 
 
 static void
-destroyGlyphData(struct glyph ** const glyph,
-                 PM_WCHAR        const maxglyph) {
+destroyGlyphData(struct glyph **            const glyph,
+                 PM_WCHAR                   const maxglyph,
+                 const struct pm_selector * const selectorP) {
 /*----------------------------------------------------------------------------
   Free glyph objects and bitmap objects.
 
   This does not work when an object is "shared" through multiple pointers
   referencing an identical address and thus pointing to a common glyph
   or bitmap object.
+
+  If 'selectorP' is NULL, free all glyph and bitmap objects in the range
+  0 ... maxglyph.  If not, free only the objects which the selector
+  indicates as present.
 -----------------------------------------------------------------------------*/
 
+    PM_WCHAR const min = (selectorP != NULL) ? selectorP->min : 0;
+    PM_WCHAR const max =
+        (selectorP != NULL) ? MIN(selectorP->max, maxglyph) : maxglyph;
+
     PM_WCHAR i;
 
-    for(i = 0; i <= maxglyph; ++i) {
-        if (glyph[i]!=NULL) {
+    for (i = min; i <= max; ++i) {
+        if (pm_selector_is_marked(selectorP, i) && glyph[i]) {
             free((void *) (glyph[i]->bmap));
             free(glyph[i]);
-      }
+        }
     }
 }
 
@@ -214,11 +247,10 @@ pbm_destroybdffont2_base(struct font2 * const font2P) {
   Free font2 structure, but not the glyph data
 ---------------------------------------------------------------------------- */
 
-    free(font2P->selector);
-
     pm_strfree(font2P->name);
     pm_strfree(font2P->charset_string);
     free(font2P->glyph);
+    pm_selector_destroy(font2P->selectorP);
 
     if (font2P->oldfont !=NULL)
        pbm_freearray(font2P->oldfont, font2P->frows);
@@ -239,8 +271,8 @@ pbm_destroybdffont2(struct font2 * const font2P) {
 ---------------------------------------------------------------------------- */
 
     if (font2P->load_fn != FIXED_DATA) {
-        destroyGlyphData(font2P->glyph, font2P->maxglyph);
-        pbm_destroybdffont2_base(font2P);
+        destroyGlyphData(font2P->glyph, font2P->maxglyph, font2P->selectorP);
+                         pbm_destroybdffont2_base(font2P);
     }
 }
 
@@ -254,7 +286,7 @@ pbm_destroybdffont(struct font * const fontP) {
   For freeing a structure created by pbm_loadbdffont() or pbm_loadpbmfont().
 ---------------------------------------------------------------------------- */
 
-    destroyGlyphData(fontP->glyph, PM_FONT_MAXGLYPH);
+    destroyGlyphData(fontP->glyph, PM_FONT_MAXGLYPH, NULL);
 
     if (fontP->oldfont !=NULL)
        pbm_freearray(fontP->oldfont, fontP->frows);
@@ -326,8 +358,7 @@ pbm_expandbdffont(const struct font * const fontP) {
     font2P->bit_format = PBM_FORMAT;
     font2P->total_chars = font2P->chars = nCharacters;
     font2P->load_fn = CONVERTED_TYPE1_FONT;
-    /* Caller should be overwrite the above to a more descriptive
-       value */
+    /* Caller should overwrite the above to a more descriptive value */
     return font2P;
 }
 
diff --git a/lib/libpbmfont1.c b/lib/libpbmfont1.c
index 4189f150..fe014150 100644
--- a/lib/libpbmfont1.c
+++ b/lib/libpbmfont1.c
@@ -47,7 +47,7 @@
   The characters in the border you see are irrelevant except for
   character size compuations.  The 12 x 8 array in the center is
   the font.  The top left character there belongs to code point
-  0, and the code points increase in standard reading order, so
+  32, and the code points increase in standard reading order, so
   the bottom right character is code point 127.  You can't define
   code points < 32 or > 127 with this font format.
 
diff --git a/lib/libpbmfont2.c b/lib/libpbmfont2.c
index b4cf4c2a..1560b407 100644
--- a/lib/libpbmfont2.c
+++ b/lib/libpbmfont2.c
@@ -33,6 +33,208 @@
 #include "pbm.h"
 
 /*----------------------------------------------------------------------------
+  Font selector routines
+
+  The selector is a device consisting of a bitmap, min value, max value and
+  count.  It is used here to specify necessary fonts and record what entries
+  are valid in the glyph array.
+
+  This device may be used for other purposes.  In that case the code should
+  be put into an independent source file in the lib/util subdirectory.
+-----------------------------------------------------------------------------*/
+
+static void
+allocRecord(struct pm_selector * const selectorP,
+            unsigned int         const max) {
+
+    unsigned int const size = (max + 8) / 8;
+
+    MALLOCARRAY(selectorP->localRecord, size);
+
+    if (!selectorP->localRecord)
+        pm_error("Failed to allocate %u bytes of memory for font selector "
+                 "bitmap", size);
+
+    selectorP->record = selectorP->localRecord;
+}
+
+
+
+void
+pm_selector_create(unsigned int          const max,
+                   struct pm_selector ** const selectorPP) {
+
+    struct pm_selector * selectorP;
+
+    MALLOCVAR_NOFAIL(selectorP);
+
+    allocRecord(selectorP, max);
+
+    {
+        unsigned int byteIndex;
+        for (byteIndex = 0; byteIndex <= max/8; ++byteIndex)
+            selectorP->localRecord[byteIndex]= 0x00;
+    }
+
+    selectorP->maxmax = selectorP->min = max;
+    selectorP->max = 0;
+    selectorP->count = 0;
+
+    *selectorPP = selectorP;
+}
+
+
+
+void
+pm_selector_create_fixed(const unsigned char * const record,
+                         unsigned int          const min,
+                         unsigned int          const max,
+                         unsigned int          const count,
+                         struct pm_selector ** const selectorPP) {
+
+    struct pm_selector * selectorP;
+
+    MALLOCVAR_NOFAIL(selectorP);
+
+    selectorP->localRecord = NULL;
+    selectorP->record      = record;
+    selectorP->min         = min;
+    selectorP->max         = max;
+    selectorP->maxmax      = max;
+    selectorP->count       = count;
+
+    *selectorPP = selectorP;
+}
+
+
+
+void
+pm_selector_destroy(struct pm_selector * const selectorP) {
+
+    if (selectorP->localRecord)
+        free(selectorP->localRecord);
+
+    free(selectorP);
+}
+
+
+
+void
+pm_selector_copy(unsigned int               const max,
+                 const struct pm_selector * const srcSelectorP,
+                 struct pm_selector      ** const destSelectorPP) {
+
+    /* Create a new selector and copy into it the content of another */
+
+    struct pm_selector * destSelectorP;
+
+    if (max < srcSelectorP->max)
+        pm_error("internal error: attempt to copy a selector as "
+                 "another with a smaller max value %u -> %u",
+                 srcSelectorP->max, max);
+
+    MALLOCVAR_NOFAIL(destSelectorP);
+
+    destSelectorP->maxmax     = max;
+    destSelectorP->max        = srcSelectorP->max;
+    destSelectorP->min        = srcSelectorP->min;
+    destSelectorP->count      = srcSelectorP->count;
+
+    allocRecord(destSelectorP, max);
+
+    {
+        unsigned int const minByteIndex = srcSelectorP->min / 8;
+        unsigned int const maxByteIndex = srcSelectorP->max / 8;
+        unsigned int const maxmaxByteIndex = max / 8;
+
+        unsigned int byteIndex;
+
+        for (byteIndex = 0 ; byteIndex < minByteIndex; ++byteIndex)
+            destSelectorP->localRecord[byteIndex] = 0x00;
+        for (byteIndex = maxByteIndex + 1 ; byteIndex <= maxmaxByteIndex;
+             ++byteIndex)
+            destSelectorP->localRecord[byteIndex] = 0x00;
+        for (byteIndex = minByteIndex; byteIndex <= maxByteIndex; ++byteIndex)
+            destSelectorP->localRecord[byteIndex] =
+                srcSelectorP->record[byteIndex];
+    }
+
+    *destSelectorPP = destSelectorP;
+}
+
+
+
+void
+pm_selector_mark(struct pm_selector * const selectorP,
+                 unsigned int         const index) {
+/*----------------------------------------------------------------------------
+   Mark index 'index'.
+-----------------------------------------------------------------------------*/
+    unsigned int  byteIndex = index / 8;
+    unsigned int  bitIndex  = index % 8;
+    unsigned char mask = (0x01 <<7) >> bitIndex;
+
+    /* set bit on to indicate presence of this index */
+
+    if (!selectorP->localRecord)
+        pm_error("INTERNAL ERROR: attempt to mark in a fixed pm_selector");
+
+    if ((selectorP->localRecord[byteIndex] & mask) == 0x00) {
+        /* if bit is not already set */
+
+        selectorP->localRecord[byteIndex] |= mask;
+        ++selectorP->count;  /* increment count */
+
+        /* reset min and max */
+        if (selectorP->min > index)
+            selectorP->min = index;
+        if (selectorP->max < index)
+            selectorP->max = index;
+    }
+}
+
+/* There is no function for erasing a marked bit */
+
+
+int  /* boolean */
+pm_selector_is_marked(const struct pm_selector * const selectorP,
+                      unsigned int               const index) {
+/*----------------------------------------------------------------------------
+  Index 'index' is marked.
+-----------------------------------------------------------------------------*/
+    bool retval;
+
+    if (selectorP) {
+        unsigned int  const byteIndex = index / 8;
+        unsigned int  const bitIndex  = index % 8;
+        unsigned char const mask = (0x01 <<7) >> bitIndex;
+
+        if ( index < selectorP->min || index > selectorP->max)
+            retval = false;
+        else if ((selectorP->record[byteIndex] & mask) != 0x00)
+            retval = true;
+        else
+            retval = false;
+    } else {
+        /* All entries are set to "exist" */
+        retval = true;
+    }
+    return retval ? 1 : 0;
+}
+
+
+
+unsigned int
+pm_selector_marked_ct(const struct pm_selector * const selectorP) {
+/*----------------------------------------------------------------------------
+   Number of indices that are marked.
+-----------------------------------------------------------------------------*/
+    return selectorP->count;
+}
+
+
+
+/*----------------------------------------------------------------------------
   Routines for loading a BDF font file
 -----------------------------------------------------------------------------*/
 
@@ -354,21 +556,23 @@ readExpectedStatement(Readline *    const readlineP,
 static void
 skipCharacter(Readline * const readlineP) {
 /*----------------------------------------------------------------------------
-  In the BDF font file being read by readline object *readlineP, skip through
-  the end of the character we are currently in.
+  In the BDF font file being read by readline object *readlineP, skip
+  through to the end of the data for the character we are presently in.
+
+  At entry the stream must be positioned at the end of the ENCODING line.
 -----------------------------------------------------------------------------*/
-    bool endChar;
 
-    endChar = FALSE;
+    char * rc;
+    do {
+        rc = fgets(readlineP->line, MAXBDFLINE+1, readlineP->ifP);
+        readlineP->line[7] = '\0';
+
+    } while (rc != NULL && !streq(readlineP->line, "ENDCHAR"));
+
+    if (rc == NULL)
+        pm_error("End of file in the middle of a character (before "
+                 "ENDCHAR) in BDF font file.");
 
-    while (!endChar) {
-        bool eof;
-        readline_read(readlineP, &eof);
-        if (eof)
-            pm_error("End of file in the middle of a character (before "
-                     "ENDCHAR) in BDF font file.");
-        endChar = streq(readlineP->arg[0], "ENDCHAR");
-    }
 }
 
 
@@ -421,7 +625,7 @@ interpEncoding(const char **  const arg,
 
    'maxmaxglyph' is the maximum codepoint in the font.
 -----------------------------------------------------------------------------*/
-    bool gotCodepoint;
+    bool gotCodepoint = false;   /* initial value */
     bool badCodepoint;
     unsigned int codepoint;
 
@@ -429,12 +633,15 @@ interpEncoding(const char **  const arg,
         codepoint = wordToInt(arg[1]);
         gotCodepoint = true;
     } else {
-      if (wordToInt(arg[1]) == -1 && arg[2] != NULL) {
-            codepoint = wordToInt(arg[2]);
-            gotCodepoint = true;
-        } else
-            gotCodepoint = false;
+        if (wordToInt(arg[1]) == -1 && arg[2] != NULL) {
+            int const codepoint0 = wordToInt(arg[2]);
+            if (codepoint0 >= 0) {
+                codepoint = codepoint0;
+                gotCodepoint = true;
+            }
+        }
     }
+
     if (gotCodepoint) {
         if (codepoint > maxmaxglyph)
             badCodepoint = true;
@@ -538,112 +745,147 @@ validateGlyphLimits(const struct font2 * const font2P,
 
 
 static void
-processChars(Readline *     const readlineP,
-             struct font2 * const font2P) {
-/*----------------------------------------------------------------------------
-   Process the CHARS block in a BDF font file, assuming the file is positioned
-   just after the CHARS line.  Read the rest of the block and apply its
-   contents to *font2P.
------------------------------------------------------------------------------*/
-    unsigned int const nCharacters = wordToInt(readlineP->arg[1]);
-
-    unsigned int nCharsDone;
-    unsigned int nCharsValid;
-
-    for (nCharsDone = 0, nCharsValid = 0;
-         nCharsDone < nCharacters; ) {
-
+readStartchar(Readline * const readlineP,
+              const char ** charNameP) {
+        
+        const char * charName;
         bool eof;
 
         readline_read(readlineP, &eof);
         if (eof)
             pm_error("End of file after CHARS reading BDF font file");
 
-        if (streq(readlineP->arg[0], "COMMENT")) {
+        while (streq(readlineP->arg[0], "COMMENT")) {
+            readline_read(readlineP, &eof);
+            if (eof)
+                 pm_error("End of file after CHARS reading BDF font file");
             /* ignore */
-        } else if (!streq(readlineP->arg[0], "STARTCHAR"))
+        }
+
+        if (!streq(readlineP->arg[0], "STARTCHAR"))
             pm_error("%s detected where \'STARTCHAR\' expected "
                      "in BDF font file", readlineP->arg[0] );
-        else {
-            const char * charName;
-
-            struct glyph * glyphP;
-            unsigned int codepoint;
-            bool badCodepoint;
-
-            if (readlineP->wordCt < 2)
-                pm_error("Wrong number of arguments in STARTCHAR line "
-                         "in BDF font file");
+        else if (readlineP->wordCt < 2)
+            pm_error("Wrong number of arguments in STARTCHAR line "
+                      "in BDF font file");
             /* Character name may contain spaces: there may be more than
                three words in the line.
              */
+        else
             charName = pm_strdup(readlineP->arg[1]);
 
-            assert(streq(readlineP->arg[0], "STARTCHAR"));
+        *charNameP = charName;
+}
 
-            MALLOCVAR(glyphP);
 
-            if (glyphP == NULL)
-                pm_error("no memory for font glyph for '%s' character",
-                         charName);
 
-            readEncoding(readlineP, &codepoint, &badCodepoint,
-                         font2P->maxmaxglyph);
+static void
+readGlyph(Readline * const readlineP,
+          const char * const charName,
+          const struct font2 * const font2P,
+          struct glyph ** const glyphPP) {
 
-            if (badCodepoint)
-                skipCharacter(readlineP);
-            else {
-                if (codepoint < font2P->maxglyph) {
-                    if (font2P->glyph[codepoint] != NULL)
-                        pm_error("Multiple definition of code point %u "
-                                 "in BDF font file", (unsigned int) codepoint);
-                    else
-                        pm_message("Reverse order detected in BDF file. "
-                                   "Code point %u defined after %u",
-                                    (unsigned int) codepoint,
-                                    (unsigned int) font2P->maxglyph);
-                } else {
-                    /* Initialize all characters in the gap to nonexistent */
-                    unsigned int i;
-                    unsigned int const oldMaxglyph = font2P->maxglyph;
-                    unsigned int const newMaxglyph = codepoint;
+    struct glyph * glyphP;
+    MALLOCVAR(glyphP);
+    if (glyphP == NULL)
+        pm_error("no memory for font glyph for '%s' character",
+             charName);
 
-                    for (i = oldMaxglyph + 1; i < newMaxglyph; ++i)
-                        font2P->glyph[i] = NULL;
+    readExpectedStatement(readlineP, "SWIDTH", 3);
 
-                    font2P->maxglyph = newMaxglyph;
-                    }
+    readExpectedStatement(readlineP, "DWIDTH", 3);
+    glyphP->xadd = wordToInt(readlineP->arg[1]);
 
-                readExpectedStatement(readlineP, "SWIDTH", 3);
+    readExpectedStatement(readlineP, "BBX", 5);
+    glyphP->width  = wordToInt(readlineP->arg[1]);
+    glyphP->height = wordToInt(readlineP->arg[2]);
+    glyphP->x      = wordToInt(readlineP->arg[3]);
+    glyphP->y      = wordToInt(readlineP->arg[4]);
 
-                readExpectedStatement(readlineP, "DWIDTH", 3);
-                glyphP->xadd = wordToInt(readlineP->arg[1]);
+    validateGlyphLimits(font2P, glyphP, charName);
 
-                readExpectedStatement(readlineP, "BBX", 5);
-                glyphP->width  = wordToInt(readlineP->arg[1]);
-                glyphP->height = wordToInt(readlineP->arg[2]);
-                glyphP->x      = wordToInt(readlineP->arg[3]);
-                glyphP->y      = wordToInt(readlineP->arg[4]);
+    createBmap(glyphP->width, glyphP->height, readlineP, charName,
+               &glyphP->bmap);
 
-                validateGlyphLimits(font2P, glyphP, charName);
+    *glyphPP = glyphP;
+}
+
+
+
+static void
+processChars(Readline *     const readlineP,
+             struct font2 * const font2P) {
+/*----------------------------------------------------------------------------
+   Process the CHARS block in a BDF font file, assuming the file is positioned
+   just after the CHARS line.  Read the rest of the block and apply its
+   contents to *font2P.
+-----------------------------------------------------------------------------*/
+    unsigned int const nCharacters = wordToInt(readlineP->arg[1]);
+    unsigned int const nCharsWanted = (font2P->selectorP != NULL) ?
+        font2P->selectorP->count : nCharacters;
+
+    unsigned int nCharsEncountered;
+    unsigned int nCharsLoaded;
+
+    for (nCharsEncountered = 0, nCharsLoaded = 0;
+         nCharsEncountered < nCharacters && nCharsLoaded < nCharsWanted;
+         ++nCharsEncountered ) {
+
+        const char * charName;
+        unsigned int codepoint;
+        bool badCodepoint;
+
+        readStartchar(readlineP, &charName);
+
+        readEncoding(readlineP, &codepoint, &badCodepoint,
+                         font2P->maxmaxglyph);
+
+        if ( badCodepoint ) {
+            skipCharacter(readlineP);
+            pm_strfree (charName);
+            }
+        else if (!pm_selector_is_marked(font2P->selectorP, codepoint) ) {
+            skipCharacter(readlineP);
+            pm_strfree (charName);
+            font2P->maxglyph = codepoint;
+            }
+        else {
+            if (codepoint < font2P->maxglyph) {
+                if (font2P->glyph[codepoint] != NULL )
+                       pm_error("Multiple definition of code point %u "
+                                "in BDF font file", (unsigned int) codepoint);
+                else
+                       pm_message("Reverse order detected in BDF file. "
+                                  "Code point %u defined after %u",
+                                   (unsigned int) codepoint,
+                                   (unsigned int) font2P->maxglyph);
+            }
 
-                createBmap(glyphP->width, glyphP->height, readlineP, charName,
-                           &glyphP->bmap);
+            {
+            struct glyph * glyphP;
 
-                readExpectedStatement(readlineP, "ENDCHAR", 1);
+            readGlyph(readlineP, charName, font2P, &glyphP);
+            readExpectedStatement(readlineP, "ENDCHAR", 1);
 
-                assert(codepoint <= font2P->maxmaxglyph);
-                /* Ensured by readEncoding() */
+            assert(codepoint <= font2P->maxmaxglyph);
+            /* Ensured by readEncoding() */
 
-                font2P->glyph[codepoint] = glyphP;
-                pm_strfree(charName);
+            font2P->glyph[codepoint] = glyphP;
+            pm_strfree(charName);
 
-                ++nCharsValid;
+            font2P->maxglyph = codepoint;
+            ++nCharsLoaded;
             }
-            ++nCharsDone;
         }
     }
-    font2P->chars = nCharsValid;
+
+    /* Note that BDF file may have reached end before the largest entry
+       in selector was checked.  */
+    /*
+    if (font2P->selectorP->max > font2P->maxglyph)
+       font2P->maxglyph = font2P->selectorP->max;
+    */
+    font2P->chars       = nCharsLoaded;
     font2P->total_chars = nCharacters;
 }
 
@@ -717,12 +959,8 @@ loadCharsetString(const char *  const registry,
 }
 
 
-
-
 static unsigned int const maxTokenLen = 60;
 
-
-
 static void
 doCharsetRegistry(Readline *    const readlineP,
                   bool *        const gotRegistryP,
@@ -904,6 +1142,9 @@ processBdfFontLine(Readline     * const readlineP,
       else {
         validateWordCount(readlineP, 2);  /* CHARS n */
         processChars(readlineP, font2P);
+        if (font2P->selectorP != NULL &&
+            font2P->selectorP->count == font2P->chars)
+               *endOfFontP = true;
       }
     } else {
         /* ignore */
@@ -913,9 +1154,39 @@ processBdfFontLine(Readline     * const readlineP,
 
 
 
+static void
+initializeGlyphArray(struct font2 * const font2P,
+                     unsigned int   const maxmaxglyph) {
+/*----------------------------------------------------------------------------
+  Initialize glyph array based on entries in selector.
+  Note that only valid codepoints are set to NULL.
+  Entries for unused glyphs are left untouched.
+-----------------------------------------------------------------------------*/
+    const struct pm_selector * const selectorP = font2P->selectorP;
+    unsigned int const min = (selectorP == NULL) ? 0 : selectorP->min;
+    unsigned int const max =
+        (selectorP == NULL) ? font2P->maxglyph : selectorP->max;
+
+    unsigned int codepoint;
+
+    for (codepoint = min; codepoint <= max ; ++codepoint)
+        if (pm_selector_is_marked(selectorP, codepoint) == true)
+             font2P->glyph[codepoint] = NULL;
+
+    font2P->glyph[L' '] = NULL;
+        /* Clear the slot for space character.
+           It may not be defined in the font, but the program may try
+           to use space as a substitute char
+        */
+
+}
+
+
+
 struct font2 *
-pbm_loadbdffont2(const char * const filename,
-                 PM_WCHAR     const maxmaxglyph) {
+pbm_loadbdffont2select(const char *               const filename,
+                       PM_WCHAR                   const maxmaxglyph,
+                       const struct pm_selector * const selectorP) {
 /*----------------------------------------------------------------------------
    Read a BDF font file "filename" as a 'font2' structure.  A 'font2'
    structure is more expressive than a 'font' structure, most notably in that
@@ -943,8 +1214,14 @@ pbm_loadbdffont2(const char * const filename,
 
     font2P->maxglyph = 0;
         /* Initial value.  Increases as new characters are loaded */
-    font2P->glyph[0] = NULL;
-        /* Initial value.  Overwrite later if codepoint 0 is defined. */
+
+    if (font2P->selectorP == NULL) {
+        PM_WCHAR i;
+
+        for(i = 0; i <= maxmaxglyph; ++i)
+            font2P->glyph[i] = NULL;
+            /* Initial value.  Overwrite later if codepoint i is defined. */
+    }
 
     font2P->maxmaxglyph = maxmaxglyph;
 
@@ -955,6 +1232,9 @@ pbm_loadbdffont2(const char * const filename,
     font2P->chars = font2P->total_chars = 0;
     font2P->default_char = 0;
     font2P->default_char_defined = FALSE;
+    font2P->selectorP = (struct pm_selector * const) selectorP;
+
+    initializeGlyphArray(font2P, maxmaxglyph);
 
     readExpectedStatement(&readline, "STARTFONT", 2);
 
@@ -970,9 +1250,13 @@ pbm_loadbdffont2(const char * const filename,
     }
     fclose(ifP);
 
-    if(font2P->chars == 0)
+    if(font2P->total_chars == 0)
         pm_error("No glyphs found in BDF font file "
                  "in codepoint range 0 - %u", (unsigned int) maxmaxglyph);
+    if(font2P->chars == 0)
+        pm_error("Not any requested glyphs found in BDF font file "
+                 "in codepoint range 0 - %u", (unsigned int) maxmaxglyph);
+
 
     REALLOCARRAY(font2P->glyph, font2P->maxglyph + 1);
 
@@ -986,8 +1270,21 @@ pbm_loadbdffont2(const char * const filename,
 }
 
 
+struct font2 *
+pbm_loadbdffont2(const char * const filename,
+                 PM_WCHAR     const maxmaxglyph) {
+
+  return (pbm_loadbdffont2select(filename, maxmaxglyph, NULL));
+
+  return(0);
+}
+
+
+
+
 static struct font *
 font2ToFont(const struct font2 * const font2P) {
+
             struct font  * fontP;
             unsigned int   codePoint;
 
@@ -1001,18 +1298,10 @@ font2ToFont(const struct font2 * const font2P) {
     fontP->x = font2P->x;
     fontP->y = font2P->y;
 
-    for (codePoint = 0; codePoint <= font2P->maxglyph; ++codePoint)
-        fontP->glyph[codePoint] = font2P->glyph[codePoint];
-
-    /* font2P->maxglyph is typically 255 (PM_FONT_MAXGLYPH) or larger.
-       But in some rare cases it is smaller.
-       If an ASCII-only font is read, it will be 126 or 127.
-
-       Set remaining codepoints up to PM_FONT_MAXGLYPH, if any, to NULL
-    */
-
-    for ( ; codePoint <= PM_FONT_MAXGLYPH; ++codePoint)
-        fontP->glyph[codePoint] = NULL;
+    for (codePoint = 0; codePoint <= PM_FONT_MAXGLYPH; ++codePoint)
+        fontP->glyph[codePoint] =
+             pm_selector_is_marked(font2P->selectorP, codePoint) ?
+             font2P->glyph[codePoint] : NULL;
 
     /* Give values to legacy fields */
     fontP->oldfont = font2P->oldfont;
diff --git a/lib/pbmfont.h b/lib/pbmfont.h
index c8b3934b..6adbe814 100644
--- a/lib/pbmfont.h
+++ b/lib/pbmfont.h
@@ -115,12 +115,12 @@ struct glyph {
 
 struct font {
     /* This describes a combination of font and character set.  Given
-       an code point in the range 0..255, this structure describes the
+       a code point in the range 0..255, this structure describes the
        glyph for that character.
     */
     unsigned int maxwidth, maxheight;
     int x;
-        /* The minimum value of glyph.font.  The left edge of the glyph
+        /* The minimum value of glyph.x .  The left edge of the glyph
            in the glyph set which advances furthest to the left. */
     int y;
         /* Amount of white space that should be added between lines of
@@ -137,10 +137,29 @@ struct font {
 };
 
 
+
+struct pm_selector {
+    unsigned int min;     /* smallest index requested */
+    unsigned int max;     /* largest index requested  */
+    unsigned int maxmax;  /* largest index possible  */
+    unsigned int count;   /* number bits set to 1 in 'record' */
+    const unsigned char * record;
+        /* Bit array 0: absent 1: existent size calculated from maxmax
+           This is for reading only; not updating.
+           Might be 'localRecord'; might be fixed object elsewhere.
+        */
+    unsigned char * localRecord;
+        /* Updateable record optionally pointed to by 'record'.
+           Null if none.  Malloc'ed storage we own otherwise.
+        */
+};
+
+
+
 struct font2 {
     /* Font structure for expanded character set.
-       Code points in the range 0...maxmaxglyph are loaded.
-       Loaded code point is in the range 0..maxglyph .
+       Code points in the range 0...maxmaxglyph may be loaded.
+       Actually loaded code point is in the range 0..maxglyph .
      */
 
     /* 'size' and 'len' are necessary in order to provide forward and
@@ -159,7 +178,7 @@ struct font2 {
     int maxwidth, maxheight;
 
     int x;
-         /* The minimum value of glyph.font.  The left edge of the glyph in
+         /* The minimum value of glyph.x .  The left edge of the glyph in
             the glyph set which advances furthest to the left.
          */
     int y;
@@ -177,14 +196,12 @@ struct font2 {
          */
 
     PM_WCHAR maxglyph;
-        /* max code point for glyphs, including vacant slots max value of
-           above i
+        /* max code point for glyphs, including vacant slots
+           max value of above i
         */
 
-    void * selector;
-        /* Reserved
-
-           Bit array or structure indicating which code points to load.
+    struct pm_selector * selectorP;
+        /* Structure with bit array indicating which code points to load.
 
            When NULL, all available code points up to maxmaxglyph, inclusive
            are loaded.
@@ -206,14 +223,15 @@ struct font2 {
         */
 
     unsigned int bit_format;
-        /* PBM_FORMAT:   glyph data: 1 byte per pixel (like P1, but not ASCII)
-           RPBM_FORMAT:  glyph data: 1 bit per pixel
+        /* PBM_FORMAT:  glyph data: 1 byte per pixel (like P1, but not ASCII)
+           RPBM_FORMAT: glyph data: 1 bit per pixel
            Currently only PBM_FORMAT is possible
         */
 
     unsigned int total_chars;
-        /* Number of glyphs defined in font file, as stated in the CHARS line
-           of the BDF file PBM sheet font.  Always 96
+        /* Number of glyphs defined in font file.
+           BDF file: as stated in the CHARS line.
+           PBM sheet font: always 96.
         */
 
     unsigned int chars;
@@ -237,7 +255,7 @@ struct font2 {
 
     PM_WCHAR default_char;
         /* Code index of what to show when there is no glyph for a requested
-           code Available in many BDF fonts between STARPROPERTIES -
+           code.  Available in many BDF fonts between STARPROPERTIES -
            ENDPROPERTIES.
 
            Set to value read from BDF font file.
@@ -306,6 +324,11 @@ struct font2 *
 pbm_loadfont2(const    char * const filename,
               PM_WCHAR        const maxmaxglyph);
 
+struct font2 *
+pbm_loadfont2select(const    char *            const filename,
+                    PM_WCHAR                   const maxmaxglyph,
+                    const struct pm_selector * const selectorP);
+
 struct font *
 pbm_loadpbmfont(const char * const filename);
 
@@ -320,9 +343,9 @@ pbm_loadbdffont2(const char * const filename,
                  PM_WCHAR     const maxmaxglyph);
 
 struct font2 *
-pbm_loadbdffont2_select(const char * const filename,
-                        PM_WCHAR     const maxmaxglyph,
-                        const void * const selector);
+pbm_loadbdffont2select(const char *               const filename,
+                       PM_WCHAR                   const maxmaxglyph,
+                       const struct pm_selector * const selectorP);
 
 void
 pbm_createbdffont2_base(struct font2 ** const font2P,
@@ -344,6 +367,38 @@ void
 pbm_dumpfont(struct font * const fontP,
              FILE *        const ofP);
 
+/* selector functions */
+
+void
+pm_selector_create(unsigned int          const max,
+                   struct pm_selector ** const selectorPP);
+
+void
+pm_selector_create_fixed(const unsigned char * const record,
+                         unsigned int          const min,
+                         unsigned int          const max,
+                         unsigned int          const count,
+                         struct pm_selector ** const selectorPP);
+
+void
+pm_selector_destroy(struct pm_selector * const selectorP);
+
+void
+pm_selector_copy(unsigned int               const max,
+                 const struct pm_selector * const srcSelectorP,
+                 struct pm_selector **      const destSelectorPP);
+
+void
+pm_selector_mark(struct pm_selector * const selectorP,
+                 unsigned int         const index);
+
+int  /* boolean */
+pm_selector_is_marked(const struct pm_selector * const selectorP,
+                      unsigned int               const index);
+
+unsigned int
+pm_selector_marked_ct(const struct pm_selector * const selectorP);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/pbmfontdata2.c b/lib/pbmfontdata2.c
index 11dd84e6..63dd36be 100644
--- a/lib/pbmfontdata2.c
+++ b/lib/pbmfontdata2.c
@@ -452,7 +452,7 @@ struct font2 const pbm_defaultBdffont2 = {
   255, NULL, 255,                 /* maxglyph, selector, maxmaxglyph */
   NULL, 0, 0,                     /* oldfont, fcols, frows */
   PBM_FORMAT,                     /* bit_format */
-  190, 190,                       /* total_chars, chars */
+  191, 191,                       /* total_chars, chars */
   FIXED_DATA,                     /* load_fn */
   32, 1,                          /* default_char, default_char_defined */
   (char *) "builtin bdf",         /* name */