diff options
Diffstat (limited to 'lib/libpbmfont.c')
-rw-r--r-- | lib/libpbmfont.c | 517 |
1 files changed, 376 insertions, 141 deletions
diff --git a/lib/libpbmfont.c b/lib/libpbmfont.c index d79b9262..708205a2 100644 --- a/lib/libpbmfont.c +++ b/lib/libpbmfont.c @@ -14,6 +14,7 @@ ** implied warranty. */ +#include <assert.h> #include <string.h> #include "pm_c_util.h" @@ -1070,166 +1071,400 @@ pbm_dumpfont( fn ) /* Routines for loading a BDF font file */ -static int readline ARGS((FILE* fp, char* buf, char* arg[])); -#define expect(str) if (readline(fp, line, arg) < 0 || strcmp(arg[0], (str))) \ - { fclose(fp); return 0; } +static unsigned int +mk_argvn(char * const s, + const char ** const vec, + unsigned int const mk_max) { -struct font* pbm_loadbdffont(const char * const name) -{ - FILE* fp; - char line[1024], *arg[32], *hex; - char *b; - int n, numchar, hdig, encoding; - struct font* font; - struct glyph* glyph; - - if (!(fp = fopen(name, "rb"))) - return 0; + int n; + char * p; - expect("STARTFONT"); + p = &s[0]; + n = 0; - MALLOCVAR(font); - if (font == NULL) - pm_error("no memory for font"); - font->oldfont = 0; - { - /* Initialize all characters to nonexistent; we will fill the ones we - find in the bdf file later. - */ - int i; - for (i = 0; i < 256; i++) - font->glyph[i] = NULL; + while (*p) { + if (ISSPACE(*p)) + *p++ = '\0'; + else { + vec[n++] = p; + if (n >= mk_max) + break; + while (*p && !ISSPACE(*p)) + ++p; + } } + vec[n] = NULL; - while (readline(fp, line, arg) >= 0) { - if (!strcmp(arg[0], "COMMENT")) - continue; - if (!strcmp(arg[0], "SIZE")) - continue; - - if (!strcmp(arg[0], "STARTPROPERTIES")) { - n = atoi(arg[1]); - for (; n > 0 && readline(fp, line, arg) >= 0; n--) - ; - } - else if (!strcmp(arg[0], "FONTBOUNDINGBOX")) { - font->maxwidth = atoi(arg[1]); - font->maxheight = atoi(arg[2]); - font->x = atoi(arg[3]); - font->y = atoi(arg[4]); - } - else if (!strcmp(arg[0], "ENDFONT")) { - fclose(fp); - return font; + return n; +} + + + +static int +readline(FILE * const fp, + char * const buf, + const char ** const arg) { + + int retval; + char * rc; + + rc = fgets(buf, 1024, fp); + if (rc == NULL) + retval = -1; + else + retval = mk_argvn(buf, arg, 32); + + return retval; +} + + + +static void +readBitmap(FILE * const fp, + unsigned int const glyphWidth, + unsigned int const glyphHeight, + const char * const charName, + unsigned char * const bmap) { + + int n; + unsigned int bmapIndex; + + bmapIndex = 0; + + for (n = glyphHeight; n > 0; --n) { + int i; /* dot counter */ + int rc; + char * hex; + char line[1024]; + const char * arg[32]; + + rc = readline(fp, line, arg); + + if (rc < 0) + pm_error("End of file in bitmap for character '%s' in BDF " + "font file.", charName); + + hex = line; + for (i = glyphWidth; i > 0; i -= 4) { + char hdig; + unsigned int hdigValue; + hdig = *hex++; + 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; } - else if (!strcmp(arg[0], "CHARS")) { - numchar = atoi(arg[1]); - while (numchar > 0) { - if (readline(fp, line, arg) < 0) { fclose(fp); return 0; } - if (!strcmp(arg[0], "COMMENT")) - continue; - if (strcmp(arg[0], "STARTCHAR")) { fclose(fp); return 0; } - MALLOCVAR(glyph); - if (glyph == NULL) - pm_error("no memory for font glyph"); - - expect("ENCODING"); - if ((encoding = atoi(arg[1])) < 0) { - if (arg[2]) - encoding = atoi(arg[2]); - else { - while (readline(fp, line, arg) >= 0) - if (!strcmp(arg[0], "ENDCHAR")) - break; + } +} + + + +static void +createBmap(unsigned int const glyphWidth, + unsigned int const glyphHeight, + FILE * const fp, + const char * const charName, + const char ** const bmapP) { + + char line[1024]; + const char * arg[32]; + unsigned char * bmap; + int rc; + + if (UINT_MAX / glyphWidth < glyphHeight) + pm_error("Ridiculously large glyph"); + + MALLOCARRAY(bmap, glyphWidth * glyphHeight); + + if (!bmap) + pm_error("no memory for font glyph byte map"); + + rc = readline(fp, line, arg); + if (rc < 0) + 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) + pm_error("End of file encountered after ATTRIBUTES in BDF " + "font file."); + } + if (!streq(arg[0], "BITMAP")) + pm_error("'%s' found where BITMAP expected in definition of " + "character '%s' in BDF font file.", line, charName); + + assert(streq(arg[0], "BITMAP")); + + readBitmap(fp, glyphWidth, glyphHeight, charName, bmap); + + *bmapP = (char *)bmap; +} + + + +static void +expect(FILE * const fp, + const char * const expected, + const char ** const arg) { + + char line[1024]; + int rc; + + rc = readline(fp, line, arg); + + if (rc < 0) + 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); +} + + + +static void +skipCharacter(FILE * const fp) { +/*---------------------------------------------------------------------------- + In BDF font file 'fp', skip through the end of the character we are + presently in. +-----------------------------------------------------------------------------*/ + bool endChar; - numchar--; - continue; - } - } - expect("SWIDTH"); - expect("DWIDTH"); - glyph->xadd = atoi(arg[1]); - expect("BBX"); - glyph->width = atoi(arg[1]); - glyph->height = atoi(arg[2]); - glyph->x = atoi(arg[3]); - glyph->y = atoi(arg[4]); - - b = (char*)malloc(glyph->width * glyph->height * sizeof(char)); - glyph->bmap = b; - if (!glyph->bmap) - pm_error("no memory for font glyph byte map"); - - if (readline(fp, line, arg) < 0) { fclose(fp); return 0; } - if (!strcmp(arg[0], "ATTRIBUTES")) - if (readline(fp, line, arg) < 0) { fclose(fp); return 0; } - - for (n = glyph->height; n > 0; n--) { - int i; /* dot counter */ - if (readline(fp, line, arg) < 0) { fclose(fp); return 0; } - hex = line; - for (i = glyph->width; i > 0; i -= 4) { - hdig = *hex++; - if (hdig >= '0' && hdig <= '9') - hdig -= '0'; - else if (hdig >= 'a' && hdig <= 'f') - hdig -= 'a' - 10; - else if (hdig >= 'A' && hdig <= 'F') - hdig -= 'A' - 10; + endChar = FALSE; - *b++ = hdig & 8 ? 1 : 0; - if (i > 1) *b++ = hdig & 4 ? 1 : 0; - if (i > 2) *b++ = hdig & 2 ? 1 : 0; - if (i > 3) *b++ = hdig & 1; - } - } + while (!endChar) { + char line[1024]; + const char * arg[32]; + int rc; + rc = readline(fp, line, arg); + if (rc < 0) + pm_error("End of file in the middle of a character (before " + "ENDCHAR) in BDF font file."); + endChar = streq(arg[0], "ENDCHAR"); + } +} + + + +static void +validateEncoding(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 + code, e.g. in ASCII, 48 is '0'). + + But if the statement doesn't give an acceptable codepoint return + *badCodepointP == TRUE. +-----------------------------------------------------------------------------*/ + + bool gotCodepoint; + bool badCodepoint; + unsigned int codepoint; + + if (atoi(arg[1]) >= 0) { + codepoint = atoi(arg[1]); + gotCodepoint = true; + } else { + if (arg[2]) { + codepoint = atoi(arg[2]); + gotCodepoint = true; + } else + gotCodepoint = false; + } + if (gotCodepoint) { + if (codepoint > 255) + badCodepoint = true; + else + badCodepoint = false; + } else + badCodepoint = true; + + *badCodepointP = badCodepoint; + *codepointP = codepoint; +} - expect("ENDCHAR"); - if (encoding < 256) - /* We ignore any characters with codes that don't fit - in 8 bits. We may want to change this someday. - */ - font->glyph[encoding] = glyph; - numchar--; + +static void +processCharsLine(FILE * const fp, + const char ** const arg, + struct font * const fontP) { + + unsigned int const nCharacters = atoi(arg[1]); + + unsigned int nCharsDone; + + nCharsDone = 0; + + while (nCharsDone < nCharacters) { + char line[1024]; + const char * arg[32]; + int rc; + + rc = readline(fp, line, arg); + if (rc < 0) + pm_error("End of file after CHARS reading BDF font file"); + + if (streq(arg[0], "COMMENT")) { + /* ignore */ + } else if (!streq(arg[0], "STARTCHAR")) + pm_error("no STARTCHAR after CHARS in BDF font file"); + else { + const char * const charName = arg[1]; + struct glyph * glyphP; + unsigned int codepoint; + bool badCodepoint; + + assert(streq(arg[0], "STARTCHAR")); + + MALLOCVAR(glyphP); + + if (glyphP == NULL) + pm_error("no memory for font glyph for '%s' character", + charName); + + { + const char * arg[32]; + expect(fp, "ENCODING", arg); + + validateEncoding(arg, &codepoint, &badCodepoint); + } + if (badCodepoint) + skipCharacter(fp); + else { + { + const char * arg[32]; + expect(fp, "SWIDTH", arg); + } + { + const char * arg[32]; + + expect(fp, "DWIDTH", arg); + glyphP->xadd = atoi(arg[1]); + } + { + const char * arg[32]; + + expect(fp, "BBX", arg); + glyphP->width = atoi(arg[1]); + glyphP->height = atoi(arg[2]); + glyphP->x = atoi(arg[3]); + glyphP->y = atoi(arg[4]); + } + createBmap(glyphP->width, glyphP->height, fp, charName, + &glyphP->bmap); + + { + const char * arg[32]; + expect(fp, "ENDCHAR", arg); + } + assert(codepoint < 256); /* Ensured by validateEncoding() */ + + fontP->glyph[codepoint] = glyphP; } + ++nCharsDone; } } - return font; } -static int readline(fp, buf, arg) -FILE* fp; -char* buf; -char* arg[]; -{ - if (!fgets(buf, 1024, fp)) - return -1; - - return mk_argvn(buf, arg, 32); -} -int mk_argvn(s, vec, mk_max) -char* s; -char* vec[]; -int mk_max; -{ - int n; - n = 0; - while (*s) { - if (ISSPACE(*s)) { - *s++ = '\0'; - continue; +static void +processFontLine(FILE * const fp, + const char * const line, + const char ** const arg, + struct font * const fontP, + bool * const endOfFontP) { + + *endOfFontP = FALSE; /* initial assumption */ + + if (streq(arg[0], "COMMENT")) { + /* ignore */ + } else if (streq(arg[0], "SIZE")) { + /* ignore */ + } else if (streq(arg[0], "STARTPROPERTIES")) { + unsigned int const propCount = atoi(arg[1]); + unsigned int i; + for (i = 0; i < propCount; ++i) { + char line[1024]; + const char * arg[32]; + int rc; + rc = readline(fp, line, arg); + if (rc < 0) + pm_error("End of file after STARTPROPERTIES in BDF font file"); } - vec[n++] = s; - if (n >= mk_max) - break; - while (*s && !ISSPACE(*s)) - s++; + } else if (streq(arg[0], "FONTBOUNDINGBOX")) { + fontP->maxwidth = atoi(arg[1]); + fontP->maxheight = atoi(arg[2]); + fontP->x = atoi(arg[3]); + fontP->y = atoi(arg[4]); + } else if (streq(arg[0], "ENDFONT")) { + *endOfFontP = true; + } else if (!strcmp(arg[0], "CHARS")) + processCharsLine(fp, arg, fontP); +} + + + +struct font * +pbm_loadbdffont(const char * const name) { + + FILE * fp; + char line[1024]; + const char * arg[32]; + struct font * fontP; + bool endOfFont; + + fp = fopen(name, "rb"); + if (!fp) + pm_error("Unable to open BDF font file name '%s'. errno=%d (%s)", + name, errno, strerror(errno)); + + expect(fp, "STARTFONT", arg); + + MALLOCVAR(fontP); + if (fontP == NULL) + pm_error("no memory for font"); + + fontP->oldfont = 0; + { + /* Initialize all characters to nonexistent; we will fill the ones we + find in the bdf file later. + */ + unsigned int i; + for (i = 0; i < 256; i++) + fontP->glyph[i] = NULL; } - vec[n] = 0; - return n; + fontP->x = fontP->y = 0; + + endOfFont = FALSE; + + while (!endOfFont) { + int rc; + rc = readline(fp, line, arg); + if (rc < 0) + pm_error("End of file before ENDFONT statement in BDF font file"); + + processFontLine(fp, line, arg, fontP, &endOfFont); + } + return fontP; } |