about summary refs log tree commit diff
path: root/lib/libpbmfont.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libpbmfont.c')
-rw-r--r--lib/libpbmfont.c476
1 files changed, 280 insertions, 196 deletions
diff --git a/lib/libpbmfont.c b/lib/libpbmfont.c
index 4ba812cd..d3551e78 100644
--- a/lib/libpbmfont.c
+++ b/lib/libpbmfont.c
@@ -16,12 +16,14 @@
 
 #include <assert.h>
 #include <string.h>
+#include <ctype.h>
+
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
 
-#include "pm_c_util.h"
-#include "nstring.h"
-#include "pbm.h"
 #include "pbmfont.h"
-#include "mallocvar.h"
+#include "pbm.h"
 
 static unsigned int const firstCodePoint = 32;
     /* This is the code point of the first character in a pbmfont font.
@@ -1072,12 +1074,48 @@ pbm_dumpfont( fn )
 /* Routines for loading a BDF font file */
 
 
-static unsigned int
-mk_argvn(char *        const s,
-         const char ** const vec,
-         unsigned int  const mk_max) {
+typedef struct {
+/*----------------------------------------------------------------------------
+   This is an object for reading lines of a font file.  It reads and tokenizes
+   them into words.
+-----------------------------------------------------------------------------*/
+    FILE * ifP;
 
-    int n;
+    char line[1024];
+        /* This is the storage space for the words of the line.  The
+           words go in here, one after another, separated by NULs.
+
+           It also functions as a work area for readline_read().
+        */
+    const char * arg[32];
+        /* These are the words; each entry is a pointer into line[] (above) */
+} readline;
+
+
+
+static void
+readline_init(readline * const readlineP,
+              FILE *     const ifP) {
+
+    readlineP->ifP = ifP;
+
+    readlineP->arg[0] = NULL;
+}
+
+
+
+static void
+tokenize(char *         const s,
+         const char **  const words,
+         unsigned int   const maxWordCt) {
+/*----------------------------------------------------------------------------
+   Chop up 's' into words by changing space characters to NUL.  Return
+   as 'words' pointer to the beginning of those words in 's'.
+
+   If there are more than 'maxWordCt' words in 's', ignore the excess on
+   the right.
+-----------------------------------------------------------------------------*/
+    unsigned int n;
     char * p;
 
     p = &s[0];
@@ -1087,30 +1125,27 @@ mk_argvn(char *        const s,
         if (ISSPACE(*p))
             *p++ = '\0';
         else {
-            vec[n++] = p;
-            if (n >= mk_max-1)
+            words[n++] = p;
+            if (n >= maxWordCt-1)
                 break;
             while (*p && !ISSPACE(*p))
                 ++p;
         }
     }
-    vec[n] = NULL;
-
-    return n;
+    words[n] = NULL;
 }
 
 
 
-static int
-readline(FILE *        const ifP,
-         char *        const line,
-         const char ** const wordList) {
+static void
+readline_read(readline * const readlineP,
+              bool *     const eofP) {
 /*----------------------------------------------------------------------------
-   Read a nonblank line from file *ifP.  Return the value of the whole line
-   in *buf (must be at least 1024 bytes long), and parse it into words
-   in *wordList (must have at least 32 entries).
+   Read a nonblank line from the file.  Make its contents available
+   as readlineP->arg[].
 
-   If there is no nonblank line before EOF, return rc == -1.
+   Return *eofP == true iff there is no nonblank line before EOF or we
+   are unable to read the file.
 -----------------------------------------------------------------------------*/
     bool gotLine;
     bool error;
@@ -1118,22 +1153,85 @@ readline(FILE *        const ifP,
     for (gotLine = false, error = false; !gotLine && !error; ) {
         char * rc;
 
-        rc = fgets(line, 1024, ifP);
+        rc = fgets(readlineP->line, 1024, readlineP->ifP);
         if (rc == NULL)
             error = true;
         else {
-            mk_argvn(line, wordList, 32);
-            if (wordList[0] != NULL)
+            tokenize(readlineP->line,
+                     readlineP->arg, ARRAY_SIZE(readlineP->arg));
+            if (readlineP->arg[0] != NULL)
                 gotLine = true;
         }
     }
-    return error ? -1 : 0;
+    *eofP = error;
+}
+
+
+
+static void
+parseBitmapRow(const char *    const hex,
+               unsigned int    const glyphWidth,
+               unsigned char * const bmap,
+               unsigned int    const origBmapIndex,
+               unsigned int *  const newBmapIndexP,
+               const char **   const errorP) {
+/*----------------------------------------------------------------------------
+   Parse one row of the bitmap for a glyph, from the hexadecimal string
+   for that row in the font file, 'hex'.  The glyph is 'glyphWidth'
+   pixels wide.
+
+   We place our result in 'bmap' at *bmapIndexP and advanced *bmapIndexP.
+-----------------------------------------------------------------------------*/
+    unsigned int bmapIndex;
+    int i;  /* dot counter */
+    const char * p;
+
+    bmapIndex = origBmapIndex;
+
+    for (i = glyphWidth, p = &hex[0], *errorP = NULL;
+         i > 0 && !*errorP;
+         i -= 4) {
+
+        if (*p == '\0')
+            pm_asprintf(errorP, "Not enough hexadecimal digits for glyph "
+                        "of width %u in '%s'",
+                        glyphWidth, hex);
+        else {
+            char const hdig = *p++;
+            unsigned int hdigValue;
+
+            if (hdig >= '0' && hdig <= '9')
+                hdigValue = hdig - '0';
+            else if (hdig >= 'a' && hdig <= 'f')
+                hdigValue = 10 + (hdig - 'a');
+            else if (hdig >= 'A' && hdig <= 'F')
+                hdigValue = 10 + (hdig - 'A');
+            else 
+                pm_asprintf(errorP,
+                            "Invalid hex digit x%02x (%c) in bitmap data '%s'",
+                            (unsigned int)(unsigned char)hdig, 
+                            isprint(hdig) ? hdig : '.',
+                            hex);
+
+            if (!*errorP) {
+                if (i > 0)
+                    bmap[bmapIndex++] = hdigValue & 0x8 ? 1 : 0;
+                if (i > 1)
+                    bmap[bmapIndex++] = hdigValue & 0x4 ? 1 : 0;
+                if (i > 2)
+                    bmap[bmapIndex++] = hdigValue & 0x2 ? 1 : 0;
+                if (i > 3)
+                    bmap[bmapIndex++] = hdigValue & 0x1 ? 1 : 0;
+            }
+        }
+    }
+    *newBmapIndexP = bmapIndex;
 }
 
 
 
 static void
-readBitmap(FILE *          const fp,
+readBitmap(readline *      const readlineP,
            unsigned int    const glyphWidth,
            unsigned int    const glyphHeight,
            const char *    const charName,
@@ -1145,48 +1243,26 @@ readBitmap(FILE *          const fp,
     bmapIndex = 0;
            
     for (n = glyphHeight; n > 0; --n) {
-        int i;  /* dot counter */
-        int rc;
-        char * hex;
-        char line[1024];
-        const char * arg[32];
+        bool eof;
+        const char * error;
 
-        rc = readline(fp, line, arg);
+        readline_read(readlineP, &eof);
 
-        if (rc < 0)
+        if (eof)
             pm_error("End of file in bitmap for character '%s' in BDF "
                      "font file.", charName);
 
-        hex = line;
-        for (i = glyphWidth; i > 0; i -= 4) {
-            if (*hex == '\0')
-                pm_error("Premature end of line in line '%s' of "
-                         "bitmap for character '%s' "
-                         "in BDF font file", line, charName);
-            else {
-                char const hdig = *hex++;
-                unsigned int hdigValue;
-
-                if (hdig >= '0' && hdig <= '9')
-                    hdigValue = hdig - '0';
-                else if (hdig >= 'a' && hdig <= 'f')
-                    hdigValue = 10 + (hdig - 'a');
-                else if (hdig >= 'A' && hdig <= 'F')
-                    hdigValue = 10 + (hdig - 'A');
-                else 
-                    pm_error("Invalid hex digit '%c' in line '%s' of "
-                             "bitmap for character '%s' "
-                             "in BDF font file", hdig, line, charName);
-                        
-                if (i > 0)
-                    bmap[bmapIndex++] = hdigValue & 0x8 ? 1 : 0;
-                if (i > 1)
-                    bmap[bmapIndex++] = hdigValue & 0x4 ? 1 : 0;
-                if (i > 2)
-                    bmap[bmapIndex++] = hdigValue & 0x2 ? 1 : 0;
-                if (i > 3)
-                    bmap[bmapIndex++] = hdigValue & 0x1 ? 1 : 0;
-            }
+        if (!readlineP->arg[0])
+            pm_error("A line that is supposed to contain bitmap data, "
+                     "in hexadecimal, for character '%s' is empty", charName);
+
+        parseBitmapRow(readlineP->arg[0], glyphWidth, bmap, bmapIndex,
+                       &bmapIndex, &error);
+
+        if (error) {
+            pm_error("Error in line %d of bitmap for character '%s': %s",
+                     n, charName, error);
+            pm_strfree(error);
         }
     }
 }
@@ -1196,15 +1272,13 @@ readBitmap(FILE *          const fp,
 static void
 createBmap(unsigned int  const glyphWidth,
            unsigned int  const glyphHeight,
-           FILE *        const fp,
+           readline *    const readlineP,
            const char *  const charName,
            const char ** const bmapP) {
 
-    char line[1024];
-    const char * arg[32];
     unsigned char * bmap;
-    int rc;
-
+    bool eof;
+    
     if (glyphWidth > 0 && UINT_MAX / glyphWidth < glyphHeight)
         pm_error("Ridiculously large glyph");
 
@@ -1213,24 +1287,26 @@ createBmap(unsigned int  const glyphWidth,
     if (!bmap)
         pm_error("no memory for font glyph byte map");
 
-    rc = readline(fp, line, arg);
-    if (rc < 0)
+    readline_read(readlineP, &eof);
+    if (eof)
         pm_error("End of file encountered reading font glyph byte map from "
                  "BDF font file.");
-
-    if (streq(arg[0], "ATTRIBUTES")) {
-        rc = readline(fp, line, arg);
-        if (rc < 0)
+    
+    if (streq(readlineP->arg[0], "ATTRIBUTES")) {
+        bool eof;
+        readline_read(readlineP, &eof);
+        if (eof)
             pm_error("End of file encountered after ATTRIBUTES in BDF "
                      "font file.");
     }                
-    if (!streq(arg[0], "BITMAP"))
+    if (!streq(readlineP->arg[0], "BITMAP"))
         pm_error("'%s' found where BITMAP expected in definition of "
-                 "character '%s' in BDF font file.", line, charName);
+                 "character '%s' in BDF font file.",
+                 readlineP->arg[0], charName);
 
-    assert(streq(arg[0], "BITMAP"));
+    assert(streq(readlineP->arg[0], "BITMAP"));
 
-    readBitmap(fp, glyphWidth, glyphHeight, charName, bmap);
+    readBitmap(readlineP, glyphWidth, glyphHeight, charName, bmap);
 
     *bmapP = (char *)bmap;
 }
@@ -1238,52 +1314,53 @@ createBmap(unsigned int  const glyphWidth,
 
 
 static void
-expect(FILE *        const fp,
-       const char *  const expected,
-       const char ** const arg,
-       char *        const line) {
-
-    int rc;
+readExpectedStatement(readline *    const readlineP,
+                      const char *  const expected) {
+/*----------------------------------------------------------------------------
+  Have the readline object *readlineP read the next line from the file, but
+  expect it to be a line of type 'expected' (i.e. the verb token at the
+  beginning of the line is that, e.g. "STARTFONT").  If it isn't, fail the
+  program.
+-----------------------------------------------------------------------------*/
+    bool eof;
 
-    rc = readline(fp, line, arg);
+    readline_read(readlineP, &eof);
 
-    if (rc < 0)
+    if (eof)
         pm_error("EOF in BDF font file where '%s' expected", expected);
-    else if (!streq(arg[0], expected))
-        pm_error("'%s' where '%s' expected in BDF font file",
-                 line, expected);
+    else if (!streq(readlineP->arg[0], expected))
+        pm_error("Statement of type '%s' where '%s' expected in BDF font file",
+                 readlineP->arg[0], expected);
 }
 
 
 
 static void
-skipCharacter(FILE * const fp) {
+skipCharacter(readline * const readlineP) {
 /*----------------------------------------------------------------------------
-  In BDF font file 'fp', skip through the end of the character we are
-  presently in.
+  In the BDF font file being read by readline object *readlineP, skip through
+  the end of the character we are presently in.
 -----------------------------------------------------------------------------*/
     bool endChar;
                         
     endChar = FALSE;
                         
     while (!endChar) {
-        char line[1024];
-        const char * arg[32];
-        int rc;
-        rc = readline(fp, line, arg);
-        if (rc < 0)
+        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(arg[0], "ENDCHAR");
+        endChar = streq(readlineP->arg[0], "ENDCHAR");
     }                        
 }
 
 
 
 static void
-validateEncoding(const char **  const arg,
-                 unsigned int * const codepointP,
-                 bool *         const badCodepointP) {
+interpEncoding(const char **  const arg,
+               unsigned int * const codepointP,
+               bool *         const badCodepointP) {
 /*----------------------------------------------------------------------------
    With arg[] being the ENCODING statement from the font, return as
    *codepointP the codepoint that it indicates (code point is the character
@@ -1323,44 +1400,57 @@ validateEncoding(const char **  const arg,
 
 
 
-
 static void
-processCharsLine(FILE *        const fp,
-                 const char ** const arg,
-                 struct font * const fontP) {
+readEncoding(readline *     const readlineP,
+             unsigned int * const codepointP,
+             bool *         const badCodepointP) {
+
+    readExpectedStatement(readlineP, "ENCODING");
+    
+    interpEncoding(readlineP->arg, codepointP, badCodepointP);
+}
+
 
+
+static void
+processChars(readline *    const readlineP,
+             struct font * const fontP) {
+/*----------------------------------------------------------------------------
+   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 *fontP.
+-----------------------------------------------------------------------------*/
     unsigned int nCharacters;
     unsigned int nCharsDone;
 
-    if (!arg[1])
+    if (!readlineP->arg[1])
         pm_error("Invalid CHARS line - no arguments");
 
-    nCharacters = atoi(arg[1]);
+    nCharacters = atoi(readlineP->arg[1]);
 
     nCharsDone = 0;
 
     while (nCharsDone < nCharacters) {
-        char line[1024];
-        const char * arg[32];
-        int rc;
+        bool eof;
 
-        rc = readline(fp, line, arg);
-        if (rc < 0)
+        readline_read(readlineP, &eof);
+        if (eof)
             pm_error("End of file after CHARS reading BDF font file");
 
-        if (streq(arg[0], "COMMENT")) {
+        if (streq(readlineP->arg[0], "COMMENT")) {
             /* ignore */
-        } else if (!streq(arg[0], "STARTCHAR"))
+        } else if (!streq(readlineP->arg[0], "STARTCHAR"))
             pm_error("no STARTCHAR after CHARS in BDF font file");
-        else if (!arg[1])
+        else if (!readlineP->arg[1])
             pm_error("Invalid STARTCHAR - no arguments");
         else {
-            const char * const charName = arg[1];
+            const char * const charName = readlineP->arg[1];
+
             struct glyph * glyphP;
             unsigned int codepoint;
             bool badCodepoint;
 
-            assert(streq(arg[0], "STARTCHAR"));
+            assert(streq(readlineP->arg[0], "STARTCHAR"));
 
             MALLOCVAR(glyphP);
 
@@ -1368,52 +1458,39 @@ processCharsLine(FILE *        const fp,
                 pm_error("no memory for font glyph for '%s' character",
                          charName);
 
-            {
-                const char * arg[32];
-                expect(fp, "ENCODING", arg, line);
+            readEncoding(readlineP, &codepoint, &badCodepoint);
 
-                validateEncoding(arg, &codepoint, &badCodepoint);
-            }
             if (badCodepoint)
-                skipCharacter(fp);
+                skipCharacter(readlineP);
             else {
-                {
-                    const char * arg[32];
-                    expect(fp, "SWIDTH", arg, line);
-                }
-                {
-                    const char * arg[32];
-                    
-                    expect(fp, "DWIDTH", arg, line);
-                    if (!arg[1])
-                        pm_error("Invalid DWIDTH statement - no arguments");
-                    glyphP->xadd = atoi(arg[1]);
-                }
-                {
-                    const char * arg[32];
+                readExpectedStatement(readlineP, "SWIDTH");
                     
-                    expect(fp, "BBX", arg, line);
-                    if (!arg[1])
-                        pm_error("Invalid BBX statement - no arguments");
-                    glyphP->width  = atoi(arg[1]);
-                    if (!arg[2])
-                        pm_error("Invalid BBX statement - only 1 argument");
-                    glyphP->height = atoi(arg[2]);
-                    if (!arg[3])
-                        pm_error("Invalid BBX statement - only 2 arguments");
-                    glyphP->x      = atoi(arg[3]);
-                    if (!arg[4])
-                        pm_error("Invalid BBX statement - only 3 arguments");
-                    glyphP->y      = atoi(arg[4]);
-                }
-                createBmap(glyphP->width, glyphP->height, fp, charName,
+                readExpectedStatement(readlineP, "DWIDTH");
+                if (!readlineP->arg[1])
+                    pm_error("Invalid DWIDTH statement - no arguments");
+                glyphP->xadd = atoi(readlineP->arg[1]);
+
+                readExpectedStatement(readlineP, "BBX");
+                if (!readlineP->arg[1])
+                    pm_error("Invalid BBX statement - no arguments");
+                glyphP->width  = atoi(readlineP->arg[1]);
+                if (!readlineP->arg[2])
+                    pm_error("Invalid BBX statement - only 1 argument");
+                glyphP->height = atoi(readlineP->arg[2]);
+                if (!readlineP->arg[3])
+                    pm_error("Invalid BBX statement - only 2 arguments");
+                glyphP->x      = atoi(readlineP->arg[3]);
+                if (!readlineP->arg[4])
+                    pm_error("Invalid BBX statement - only 3 arguments");
+                glyphP->y      = atoi(readlineP->arg[4]);
+
+                createBmap(glyphP->width, glyphP->height, readlineP, charName,
                            &glyphP->bmap);
                 
-                {
-                    const char * arg[32];
-                    expect(fp, "ENDCHAR", arg, line);
-                }
-                assert(codepoint < 256); /* Ensured by validateEncoding() */
+
+                readExpectedStatement(readlineP, "ENDCHAR");
+
+                assert(codepoint < 256); /* Ensured by readEncoding() */
 
                 fontP->glyph[codepoint] = glyphP;
             }
@@ -1425,51 +1502,57 @@ processCharsLine(FILE *        const fp,
 
 
 static void
-processFontLine(FILE *        const fp,
-                const char *  const line,
-                const char ** const arg,
-                struct font * const fontP,
-                bool *        const endOfFontP) {
+processBdfFontLine(readline *    const readlineP,
+                   struct font * const fontP,
+                   bool *        const endOfFontP) {
+/*----------------------------------------------------------------------------
+   Process a nonblank line just read from a BDF font file.
 
+   This processing may involve reading more lines.
+-----------------------------------------------------------------------------*/
     *endOfFontP = FALSE;  /* initial assumption */
 
-    if (streq(arg[0], "COMMENT")) {
+    assert(readlineP->arg[0] != NULL);  /* Entry condition */
+
+    if (streq(readlineP->arg[0], "COMMENT")) {
         /* ignore */
-    } else if (streq(arg[0], "SIZE")) {
+    } else if (streq(readlineP->arg[0], "SIZE")) {
         /* ignore */
-    } else if (streq(arg[0], "STARTPROPERTIES")) {
+    } else if (streq(readlineP->arg[0], "STARTPROPERTIES")) {
+        /* Read off the properties and ignore them all */
         unsigned int propCount;
         unsigned int i;
 
-        if (!arg[1])
+        if (!readlineP->arg[1])
             pm_error("Invalid STARTPROPERTIES statement - no arguments");
-        propCount = atoi(arg[1]);
+        propCount = atoi(readlineP->arg[1]);
 
         for (i = 0; i < propCount; ++i) {
-            char line[1024];
-            const char * arg[32];
-            int rc;
-            rc = readline(fp, line, arg);
-            if (rc < 0)
+            bool eof;
+            readline_read(readlineP, &eof);
+            if (eof)
                 pm_error("End of file after STARTPROPERTIES in BDF font file");
         }
-    } else if (streq(arg[0], "FONTBOUNDINGBOX")) {
-        if (!arg[1])
+    } else if (streq(readlineP->arg[0], "FONTBOUNDINGBOX")) {
+        if (!readlineP->arg[1])
             pm_error("Invalid FONTBOUNDINGBOX  statement - no arguments");
-        fontP->maxwidth  = atoi(arg[1]);
-        if (!arg[2])
+        fontP->maxwidth  = atoi(readlineP->arg[1]);
+        if (!readlineP->arg[2])
             pm_error("Invalid FONTBOUNDINGBOX  statement - only 1 argument");
-        fontP->maxheight = atoi(arg[2]);
-        if (!arg[3])
+        fontP->maxheight = atoi(readlineP->arg[2]);
+        if (!readlineP->arg[3])
             pm_error("Invalid FONTBOUNDINGBOX  statement - only 2 arguments");
-        fontP->x         = atoi(arg[3]);
-        if (!arg[4])
+        fontP->x         = atoi(readlineP->arg[3]);
+        if (!readlineP->arg[4])
             pm_error("Invalid FONTBOUNDINGBOX  statement - only 3 arguments");
-        fontP->y         = atoi(arg[4]);
-    } else if (streq(arg[0], "ENDFONT")) {
+        fontP->y         = atoi(readlineP->arg[4]);
+    } else if (streq(readlineP->arg[0], "ENDFONT")) {
         *endOfFontP = true;
-    } else if (!strcmp(arg[0], "CHARS"))
-        processCharsLine(fp, arg, fontP);
+    } else if (streq(readlineP->arg[0], "CHARS")) {
+        processChars(readlineP, fontP);
+    } else {
+        /* ignore */
+    }
 }
 
 
@@ -1477,18 +1560,17 @@ processFontLine(FILE *        const fp,
 struct font *
 pbm_loadbdffont(const char * const name) {
 
-    FILE * fp;
-    char line[1024];
-    const char * arg[32];
+    FILE * ifP;
+    readline readline;
     struct font * fontP;
     bool endOfFont;
 
-    fp = fopen(name, "rb");
-    if (!fp)
+    ifP = fopen(name, "rb");
+    if (!ifP)
         pm_error("Unable to open BDF font file name '%s'.  errno=%d (%s)",
                  name, errno, strerror(errno));
 
-    expect(fp, "STARTFONT", arg, line);
+    readline_init(&readline, ifP);
 
     MALLOCVAR(fontP);
     if (fontP == NULL)
@@ -1500,20 +1582,22 @@ pbm_loadbdffont(const char * const name) {
            find in the bdf file later.
         */
         unsigned int i;
-        for (i = 0; i < 256; i++) 
+        for (i = 0; i < 256; ++i) 
             fontP->glyph[i] = NULL;
     }
     fontP->x = fontP->y = 0;
 
+    readExpectedStatement(&readline, "STARTFONT");
+
     endOfFont = FALSE;
 
     while (!endOfFont) {
-        int rc;
-        rc = readline(fp, line, arg);
-        if (rc < 0)
+        bool eof;
+        readline_read(&readline, &eof);
+        if (eof)
             pm_error("End of file before ENDFONT statement in BDF font file");
 
-        processFontLine(fp, line, arg, fontP, &endOfFont);
+        processBdfFontLine(&readline, fontP, &endOfFont);
     }
     return fontP;
 }