about summary refs log tree commit diff
path: root/lib
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 /lib
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
Diffstat (limited to 'lib')
-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
5 files changed, 535 insertions, 160 deletions
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 */