Blame lib/libpbmfont.c

Packit 78deda
/*
Packit 78deda
**
Packit 78deda
** Font routines.
Packit 78deda
**
Packit 78deda
** BDF font code Copyright 1993 by George Phillips.
Packit 78deda
**
Packit 78deda
** Copyright (C) 1991 by Jef Poskanzer.
Packit 78deda
**
Packit 78deda
** Permission to use, copy, modify, and distribute this software and its
Packit 78deda
** documentation for any purpose and without fee is hereby granted, provided
Packit 78deda
** that the above copyright notice appear in all copies and that both that
Packit 78deda
** copyright notice and this permission notice appear in supporting
Packit 78deda
** documentation.  This software is provided "as is" without express or
Packit 78deda
** implied warranty.
Packit 78deda
**
Packit 78deda
** BDF font specs available from:
Packit 78deda
** https://partners.adobe.com/public/developer/en/font/5005.BDF_Spec.pdf
Packit 78deda
** Glyph Bitmap Distribution Format (BDF) Specification
Packit 78deda
** Version 2.2
Packit 78deda
** 22 March 1993
Packit 78deda
** Adobe Developer Support
Packit 78deda
*/
Packit 78deda
Packit 78deda
#include <assert.h>
Packit 78deda
#include <string.h>
Packit 78deda
#include <ctype.h>
Packit 78deda
Packit 78deda
#include "netpbm/pm_c_util.h"
Packit 78deda
#include "netpbm/mallocvar.h"
Packit 78deda
#include "netpbm/nstring.h"
Packit 78deda
Packit 78deda
#include "pbmfont.h"
Packit 78deda
#include "pbm.h"
Packit 78deda
Packit 78deda
static unsigned int const firstCodePoint = 32;
Packit 78deda
    /* This is the code point of the first character in a pbmfont font.
Packit 78deda
       In ASCII, it is a space.
Packit 78deda
    */
Packit 78deda
Packit 78deda
static unsigned int const nCharsInFont = 96;
Packit 78deda
    /* The number of characters in a pbmfont font.  A pbmfont font defines
Packit 78deda
       characters at position 32 (ASCII space) through 127, so that's 96.
Packit 78deda
    */
Packit 78deda
Packit 78deda
Packit 78deda
struct font *
Packit 78deda
pbm_defaultfont(const char * const name) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Generate the built-in font with name 'name'.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    struct font * retval;
Packit 78deda
Packit 78deda
    if (streq(name, "bdf"))
Packit 78deda
        retval = &pbm_defaultBdffont;
Packit 78deda
    else if (streq(name, "fixed"))
Packit 78deda
        retval = &pbm_defaultFixedfont;
Packit 78deda
    else
Packit 78deda
        pm_error( "built-in font name unknown, try 'bdf' or 'fixed'");
Packit 78deda
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
findFirstBlankRow(const bit **   const font,
Packit 78deda
                  unsigned int   const fcols,
Packit 78deda
                  unsigned int   const frows,
Packit 78deda
                  unsigned int * const browP) {
Packit 78deda
Packit 78deda
    unsigned int row;
Packit 78deda
    bool foundBlank;
Packit 78deda
Packit 78deda
    for (row = 0, foundBlank = false; row < frows / 6 && !foundBlank; ++row) {
Packit 78deda
        unsigned int col;
Packit 78deda
        bit col0Value = font[row][0];
Packit 78deda
        bool rowIsBlank;
Packit 78deda
        rowIsBlank = true;  /* initial assumption */
Packit 78deda
        for (col = 1; col < fcols; ++col)
Packit 78deda
            if (font[row][col] != col0Value)
Packit 78deda
                rowIsBlank = false;
Packit 78deda
Packit 78deda
        if (rowIsBlank) {
Packit 78deda
            foundBlank = true;
Packit 78deda
            *browP = row;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (!foundBlank)
Packit 78deda
        pm_error("couldn't find blank pixel row in font");
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
findFirstBlankCol(const bit **   const font,
Packit 78deda
                  unsigned int   const fcols,
Packit 78deda
                  unsigned int   const frows,
Packit 78deda
                  unsigned int * const bcolP) {
Packit 78deda
Packit 78deda
    unsigned int col;
Packit 78deda
    bool foundBlank;
Packit 78deda
Packit 78deda
    for (col = 0, foundBlank = false; col < fcols / 6 && !foundBlank; ++col) {
Packit 78deda
        unsigned int row;
Packit 78deda
        bit row0Value = font[0][col];
Packit 78deda
        bool colIsBlank;
Packit 78deda
        colIsBlank = true;  /* initial assumption */
Packit 78deda
        for (row = 1; row < frows; ++row)
Packit 78deda
            if (font[row][col] != row0Value)
Packit 78deda
                colIsBlank = false;
Packit 78deda
Packit 78deda
        if (colIsBlank) {
Packit 78deda
            foundBlank = true;
Packit 78deda
            *bcolP = col;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (!foundBlank)
Packit 78deda
        pm_error("couldn't find blank pixel column in font");
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
computeCharacterSize(const bit **   const font,
Packit 78deda
                     unsigned int   const fcols,
Packit 78deda
                     unsigned int   const frows,
Packit 78deda
                     unsigned int * const cellWidthP,
Packit 78deda
                     unsigned int * const cellHeightP,
Packit 78deda
                     unsigned int * const charWidthP,
Packit 78deda
                     unsigned int * const charHeightP) {
Packit 78deda
Packit 78deda
    unsigned int firstBlankRow;
Packit 78deda
    unsigned int firstBlankCol;
Packit 78deda
    unsigned int heightLast11Rows;
Packit 78deda
Packit 78deda
    findFirstBlankRow(font, fcols, frows, &firstBlankRow);
Packit 78deda
Packit 78deda
    findFirstBlankCol(font, fcols, frows, &firstBlankCol);
Packit 78deda
Packit 78deda
    heightLast11Rows = frows - firstBlankRow;
Packit 78deda
Packit 78deda
    if (heightLast11Rows % 11 != 0)
Packit 78deda
        pm_error("The rows of characters in the font do not appear to "
Packit 78deda
                 "be all the same height.  The last 11 rows are %u pixel "
Packit 78deda
                 "rows high (from pixel row %u up to %u), "
Packit 78deda
                 "which is not a multiple of 11.",
Packit 78deda
                 heightLast11Rows, firstBlankRow, frows);
Packit 78deda
    else {
Packit 78deda
        unsigned int widthLast15Cols;
Packit 78deda
Packit 78deda
        *cellHeightP = heightLast11Rows / 11;
Packit 78deda
Packit 78deda
        widthLast15Cols = fcols - firstBlankCol;
Packit 78deda
Packit 78deda
        if (widthLast15Cols % 15 != 0)
Packit 78deda
            pm_error("The columns of characters in the font do not appear to "
Packit 78deda
                     "be all the same width.  "
Packit 78deda
                     "The last 15 columns are %u pixel "
Packit 78deda
                     "columns wide (from pixel col %u up to %u), "
Packit 78deda
                     "which is not a multiple of 15.",
Packit 78deda
                     widthLast15Cols, firstBlankCol, fcols);
Packit 78deda
        else {
Packit 78deda
            *cellWidthP = widthLast15Cols / 15;
Packit 78deda
Packit 78deda
            *charWidthP = firstBlankCol;
Packit 78deda
            *charHeightP = firstBlankRow;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
struct font*
Packit 78deda
pbm_dissectfont(const bit ** const font,
Packit 78deda
                unsigned int const frows,
Packit 78deda
                unsigned int const fcols) {
Packit 78deda
    /*
Packit 78deda
       This routine expects a font bitmap representing the following text:
Packit 78deda
      
Packit 78deda
       (0,0)
Packit 78deda
          M ",/^_[`jpqy| M
Packit 78deda
      
Packit 78deda
          /  !"#$%&'()*+ /
Packit 78deda
          < ,-./01234567 <
Packit 78deda
          > 89:;<=>?@ABC >
Packit 78deda
          @ DEFGHIJKLMNO @
Packit 78deda
          _ PQRSTUVWXYZ[ _
Packit 78deda
          { \]^_`abcdefg {
Packit 78deda
          } hijklmnopqrs }
Packit 78deda
          ~ tuvwxyz{|}~  ~
Packit 78deda
      
Packit 78deda
          M ",/^_[`jpqy| M
Packit 78deda
      
Packit 78deda
       The bitmap must be cropped exactly to the edges.
Packit 78deda
      
Packit 78deda
       The characters in the border you see are irrelevant except for
Packit 78deda
       character size compuations.  The 12 x 8 array in the center is
Packit 78deda
       the font.  The top left character there belongs to code point
Packit 78deda
       0, and the code points increase in standard reading order, so
Packit 78deda
       the bottom right character is code point 127.  You can't define
Packit 78deda
       code points < 32 or > 127 with this font format.
Packit 78deda
Packit 78deda
       The characters in the top and bottom border rows must include a
Packit 78deda
       character with the lowest reach of any in the font (e.g. "y",
Packit 78deda
       "_") and one with the highest reach (e.g. '"').  The characters
Packit 78deda
       in the left and right border columns must include characters
Packit 78deda
       with the rightmost and leftmost reach of any in the font
Packit 78deda
       (e.g. "M" for both).
Packit 78deda
Packit 78deda
       The border must be separated from the font by one blank text
Packit 78deda
       row or text column.
Packit 78deda
      
Packit 78deda
       The dissection works by finding the first blank row and column;
Packit 78deda
       i.e the lower right corner of the "M" in the upper left corner
Packit 78deda
       of the matrix.  That gives the height and width of the
Packit 78deda
       maximum-sized character, which is not too useful.  But the
Packit 78deda
       distance from there to the opposite side is an integral
Packit 78deda
       multiple of the cell size, and that's what we need.  Then it's
Packit 78deda
       just a matter of filling in all the coordinates.  */
Packit 78deda
    
Packit 78deda
    unsigned int cellWidth, cellHeight;
Packit 78deda
        /* Dimensions in pixels of each cell of the font -- that
Packit 78deda
           includes the glyph and the white space above and to the
Packit 78deda
           right of it.  Each cell is a tile of the font image.  The
Packit 78deda
           top character row and left character row don't count --
Packit 78deda
           those cells are smaller because they are missing the white
Packit 78deda
           space.
Packit 78deda
        */
Packit 78deda
    unsigned int charWidth, charHeight;
Packit 78deda
        /* Maximum dimensions of glyph itself, inside its cell */
Packit 78deda
Packit 78deda
    int row, col, ch, r, c, i;
Packit 78deda
    struct font * fn;
Packit 78deda
    struct glyph * glyph;
Packit 78deda
    char* bmap;
Packit 78deda
Packit 78deda
    computeCharacterSize(font, fcols, frows,
Packit 78deda
                         &cellWidth, &cellHeight, &charWidth, &charHeight);
Packit 78deda
Packit 78deda
    /* Now convert to a general font */
Packit 78deda
Packit 78deda
    MALLOCVAR(fn);
Packit 78deda
    if (fn == NULL)
Packit 78deda
        pm_error("out of memory allocating font structure");
Packit 78deda
Packit 78deda
    fn->maxwidth  = charWidth;
Packit 78deda
    fn->maxheight = charHeight;
Packit 78deda
    fn->x = fn->y = 0;
Packit 78deda
Packit 78deda
    fn->oldfont = font;
Packit 78deda
    fn->frows = frows;
Packit 78deda
    fn->fcols = fcols;
Packit 78deda
    
Packit 78deda
    /* Initialize all character positions to "undefined."  Those that
Packit 78deda
       are defined in the font will be filled in below.
Packit 78deda
    */
Packit 78deda
    for (i = 0; i < PM_FONT_MAXGLYPH + 1; i++)
Packit 78deda
        fn->glyph[i] = NULL;
Packit 78deda
Packit 78deda
    MALLOCARRAY(glyph, nCharsInFont);
Packit 78deda
    if ( glyph == NULL )
Packit 78deda
        pm_error( "out of memory allocating glyphs" );
Packit 78deda
    
Packit 78deda
    bmap = (char*) malloc( fn->maxwidth * fn->maxheight * nCharsInFont );
Packit 78deda
    if ( bmap == (char*) 0)
Packit 78deda
        pm_error( "out of memory allocating glyph data" );
Packit 78deda
Packit 78deda
    /* Now fill in the 0,0 coords. */
Packit 78deda
    row = cellHeight * 2;
Packit 78deda
    col = cellWidth * 2;
Packit 78deda
    for (i = 0; i < firstCodePoint; ++i)
Packit 78deda
        fn->glyph[i] = NULL;
Packit 78deda
Packit 78deda
    for ( ch = 0; ch < nCharsInFont; ++ch ) {
Packit 78deda
        glyph[ch].width = fn->maxwidth;
Packit 78deda
        glyph[ch].height = fn->maxheight;
Packit 78deda
        glyph[ch].x = glyph[ch].y = 0;
Packit 78deda
        glyph[ch].xadd = cellWidth;
Packit 78deda
Packit 78deda
        for ( r = 0; r < glyph[ch].height; ++r )
Packit 78deda
            for ( c = 0; c < glyph[ch].width; ++c )
Packit 78deda
                bmap[r * glyph[ch].width + c] = font[row + r][col + c];
Packit 78deda
    
Packit 78deda
        glyph[ch].bmap = bmap;
Packit 78deda
        bmap += glyph[ch].width * glyph[ch].height;
Packit 78deda
Packit 78deda
        fn->glyph[firstCodePoint + ch] = &glyph[ch];
Packit 78deda
Packit 78deda
        col += cellWidth;
Packit 78deda
        if ( col >= cellWidth * 14 ) {
Packit 78deda
            col = cellWidth * 2;
Packit 78deda
            row += cellHeight;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    for (i = firstCodePoint + nCharsInFont; i < PM_FONT_MAXGLYPH + 1; ++i)
Packit 78deda
        fn->glyph[i] = NULL;
Packit 78deda
    
Packit 78deda
    return fn;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
struct font *
Packit 78deda
pbm_loadfont(const char * const filename) {
Packit 78deda
Packit 78deda
    FILE * fileP;
Packit 78deda
    struct font * fontP;
Packit 78deda
    char line[10] = "\0\0\0\0\0\0\0\0\0\0";
Packit 78deda
        /* Initialize to suppress Valgrind error which is reported when file
Packit 78deda
           is empty or very small.
Packit 78deda
        */
Packit 78deda
Packit 78deda
    fileP = pm_openr(filename);
Packit 78deda
    fgets(line, 10, fileP);
Packit 78deda
    pm_close(fileP);
Packit 78deda
Packit 78deda
    if (line[0] == PBM_MAGIC1 && 
Packit 78deda
        (line[1] == PBM_MAGIC2 || line[1] == RPBM_MAGIC2)) {
Packit 78deda
        fontP = pbm_loadpbmfont(filename);
Packit 78deda
    } else if (!strncmp(line, "STARTFONT", 9)) {
Packit 78deda
        fontP = pbm_loadbdffont(filename);
Packit 78deda
        if (!fontP)
Packit 78deda
            pm_error("could not load BDF font file");
Packit 78deda
    } else {
Packit 78deda
        pm_error("font file not in a recognized format.  Does not start "
Packit 78deda
                 "with the signature of a PBM file or BDF font file");
Packit 78deda
        assert(false);
Packit 78deda
        fontP = NULL;  /* defeat compiler warning */
Packit 78deda
    }
Packit 78deda
    return fontP;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
struct font *
Packit 78deda
pbm_loadpbmfont(const char * const filename) {
Packit 78deda
Packit 78deda
    FILE * ifP;
Packit 78deda
    bit ** font;
Packit 78deda
    int fcols, frows;
Packit 78deda
Packit 78deda
    ifP = pm_openr(filename);
Packit 78deda
Packit 78deda
    font = pbm_readpbm(ifP, &fcols, &frows;;
Packit 78deda
Packit 78deda
    if ((fcols - 1) / 16 >= pbm_maxfontwidth() ||
Packit 78deda
       (frows - 1) / 12 >= pbm_maxfontheight())
Packit 78deda
        pm_error("Absurdly large PBM font file: %s", filename);
Packit 78deda
    else if (fcols < 31 || frows < 23) {
Packit 78deda
        /* Need at least one pixel per character, and this is too small to
Packit 78deda
           have that.
Packit 78deda
        */
Packit 78deda
        pm_error("PBM font file '%s' too small to be a font file: %u x %u.  "
Packit 78deda
                 "Minimum sensible size is 31 x 23",
Packit 78deda
                 filename, fcols, frows);
Packit 78deda
    }
Packit 78deda
Packit 78deda
    pm_close(ifP);
Packit 78deda
Packit 78deda
    return pbm_dissectfont((const bit **)font, frows, fcols);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pbm_dumpfont(struct font * const fontP,
Packit 78deda
             FILE *        const ofP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Dump out font as C source code.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int i;
Packit 78deda
    unsigned int ng;
Packit 78deda
Packit 78deda
    if (fontP->oldfont)
Packit 78deda
        pm_message("Netpbm no longer has the capability to generate "
Packit 78deda
                   "a font in long hexadecimal data format");
Packit 78deda
Packit 78deda
    for (i = 0, ng = 0; i < PM_FONT_MAXGLYPH +1; ++i) {
Packit 78deda
        if (fontP->glyph[i])
Packit 78deda
            ++ng;
Packit 78deda
    }
Packit 78deda
Packit 78deda
    printf("static struct glyph _g[%d] = {\n", ng);
Packit 78deda
Packit 78deda
    for (i = 0; i < PM_FONT_MAXGLYPH + 1; ++i) {
Packit 78deda
        struct glyph * const glyphP = fontP->glyph[i];
Packit 78deda
        if (glyphP) {
Packit 78deda
            unsigned int j;
Packit 78deda
            printf(" { %d, %d, %d, %d, %d, \"", glyphP->width, glyphP->height,
Packit 78deda
                   glyphP->x, glyphP->y, glyphP->xadd);
Packit 78deda
            
Packit 78deda
            for (j = 0; j < glyphP->width * glyphP->height; ++j) {
Packit 78deda
                if (glyphP->bmap[j])
Packit 78deda
                    printf("\\1");
Packit 78deda
                else
Packit 78deda
                    printf("\\0");
Packit 78deda
            }    
Packit 78deda
            --ng;
Packit 78deda
            printf("\" }%s\n", ng ? "," : "");
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    printf("};\n");
Packit 78deda
Packit 78deda
    printf("struct font XXX_font = { %d, %d, %d, %d, {\n",
Packit 78deda
           fontP->maxwidth, fontP->maxheight, fontP->x, fontP->y);
Packit 78deda
Packit 78deda
    {
Packit 78deda
        unsigned int i;
Packit 78deda
Packit 78deda
        for (i = 0; i < PM_FONT_MAXGLYPH + 1; ++i) {
Packit 78deda
            if (fontP->glyph[i])
Packit 78deda
                printf(" _g + %d", ng++);
Packit 78deda
            else
Packit 78deda
                printf(" NULL");
Packit 78deda
        
Packit 78deda
            if (i != PM_FONT_MAXGLYPH) printf(",");
Packit 78deda
            printf("\n");
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
Packit 78deda
    printf(" }\n};\n");
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Routines for loading a BDF font file
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
Packit 78deda
Packit 78deda
/* The following are not recognized in individual glyph data; library
Packit 78deda
   routines do a pm_error if they see one:
Packit 78deda
Packit 78deda
   Vertical writing systems: DWIDTH1, SWIDTH1, VVECTOR, METRICSET,
Packit 78deda
   CONTENTVERSION.
Packit 78deda
Packit 78deda
   The following is not recognized and is thus ignored at the global level:
Packit 78deda
   DWIDTH
Packit 78deda
*/
Packit 78deda
Packit 78deda
Packit 78deda
#define MAXBDFLINE 1024 
Packit 78deda
Packit 78deda
/* Official Adobe document says max length of string is 65535 characters.
Packit 78deda
   However the value 1024 is sufficient for practical uses.
Packit 78deda
*/
Packit 78deda
Packit 78deda
typedef struct {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   This is an object for reading lines of a font file.  It reads and tokenizes
Packit 78deda
   them into words.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    FILE * ifP;
Packit 78deda
Packit 78deda
    char line[MAXBDFLINE+1];
Packit 78deda
        /* This is the storage space for the words of the line.  The
Packit 78deda
           words go in here, one after another, separated by NULs.
Packit 78deda
Packit 78deda
           It also functions as a work area for readline_read().
Packit 78deda
        */
Packit 78deda
    const char * arg[6];
Packit 78deda
        /* These are the words; each entry is a pointer into line[] (above) */
Packit 78deda
} Readline;
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readline_init(Readline * const readlineP,
Packit 78deda
              FILE *     const ifP) {
Packit 78deda
Packit 78deda
    readlineP->ifP = ifP;
Packit 78deda
Packit 78deda
    readlineP->arg[0] = NULL;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
tokenize(char *         const s,
Packit 78deda
         const char **  const words,
Packit 78deda
         unsigned int   const wordsSz) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Chop up 's' into words by changing space characters to NUL.  Return as
Packit 78deda
   'words' an array of pointers to the beginnings of those words in 's'.
Packit 78deda
   Terminate the words[] list with a null pointer.
Packit 78deda
Packit 78deda
   'wordsSz' is the number of elements of space in 'words'.  If there are more
Packit 78deda
   words in 's' than will fit in that space (including the terminating null
Packit 78deda
   pointer), ignore the excess on the right.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int n;  /* Number of words in words[] so far */
Packit 78deda
    char * p;
Packit 78deda
Packit 78deda
    p = &s[0];
Packit 78deda
    n = 0;
Packit 78deda
Packit 78deda
    while (*p) {
Packit 78deda
        if (ISSPACE(*p))
Packit 78deda
            *p++ = '\0';
Packit 78deda
        else {
Packit 78deda
            words[n++] = p;
Packit 78deda
            if (n >= wordsSz - 1)
Packit 78deda
                break;
Packit 78deda
            while (*p && !ISSPACE(*p))
Packit 78deda
                ++p;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    assert(n <= wordsSz - 1);
Packit 78deda
    words[n] = NULL;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readline_read(Readline * const readlineP,
Packit 78deda
              bool *     const eofP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Read a nonblank line from the file.  Make its contents available
Packit 78deda
   as readlineP->arg[].
Packit 78deda
Packit 78deda
   Return *eofP == true iff there is no nonblank line before EOF or we
Packit 78deda
   are unable to read the file.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    bool gotLine;
Packit 78deda
    bool error;
Packit 78deda
Packit 78deda
    for (gotLine = false, error = false; !gotLine && !error; ) {
Packit 78deda
        char * rc;
Packit 78deda
Packit 78deda
        rc = fgets(readlineP->line, MAXBDFLINE+1, readlineP->ifP);
Packit 78deda
        if (rc == NULL)
Packit 78deda
            error = true;
Packit 78deda
        else {
Packit 78deda
            tokenize(readlineP->line,
Packit 78deda
                     readlineP->arg, ARRAY_SIZE(readlineP->arg));
Packit 78deda
            if (readlineP->arg[0] != NULL)
Packit 78deda
                gotLine = true;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    *eofP = error;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
parseBitmapRow(const char *    const hex,
Packit 78deda
               unsigned int    const glyphWidth,
Packit 78deda
               unsigned char * const bmap,
Packit 78deda
               unsigned int    const origBmapIndex,
Packit 78deda
               unsigned int *  const newBmapIndexP,
Packit 78deda
               const char **   const errorP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Parse one row of the bitmap for a glyph, from the hexadecimal string
Packit 78deda
   for that row in the font file, 'hex'.  The glyph is 'glyphWidth'
Packit 78deda
   pixels wide.
Packit 78deda
Packit 78deda
   We place our result in 'bmap' at *bmapIndexP and advanced *bmapIndexP.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int bmapIndex;
Packit 78deda
    int i;  /* dot counter */
Packit 78deda
    const char * p;
Packit 78deda
Packit 78deda
    bmapIndex = origBmapIndex;
Packit 78deda
Packit 78deda
    for (i = glyphWidth, p = &hex[0], *errorP = NULL;
Packit 78deda
         i > 0 && !*errorP;
Packit 78deda
         i -= 4) {
Packit 78deda
Packit 78deda
        if (*p == '\0')
Packit 78deda
            pm_asprintf(errorP, "Not enough hexadecimal digits for glyph "
Packit 78deda
                        "of width %u in '%s'",
Packit 78deda
                        glyphWidth, hex);
Packit 78deda
        else {
Packit 78deda
            char const hdig = *p++;
Packit 78deda
            unsigned int hdigValue;
Packit 78deda
Packit 78deda
            if (hdig >= '0' && hdig <= '9')
Packit 78deda
                hdigValue = hdig - '0';
Packit 78deda
            else if (hdig >= 'a' && hdig <= 'f')
Packit 78deda
                hdigValue = 10 + (hdig - 'a');
Packit 78deda
            else if (hdig >= 'A' && hdig <= 'F')
Packit 78deda
                hdigValue = 10 + (hdig - 'A');
Packit 78deda
            else 
Packit 78deda
                pm_asprintf(errorP,
Packit 78deda
                            "Invalid hex digit x%02x (%c) in bitmap data '%s'",
Packit 78deda
                            (unsigned int)(unsigned char)hdig, 
Packit 78deda
                            isprint(hdig) ? hdig : '.',
Packit 78deda
                            hex);
Packit 78deda
Packit 78deda
            if (!*errorP) {
Packit 78deda
                if (i > 0)
Packit 78deda
                    bmap[bmapIndex++] = hdigValue & 0x8 ? 1 : 0;
Packit 78deda
                if (i > 1)
Packit 78deda
                    bmap[bmapIndex++] = hdigValue & 0x4 ? 1 : 0;
Packit 78deda
                if (i > 2)
Packit 78deda
                    bmap[bmapIndex++] = hdigValue & 0x2 ? 1 : 0;
Packit 78deda
                if (i > 3)
Packit 78deda
                    bmap[bmapIndex++] = hdigValue & 0x1 ? 1 : 0;
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    *newBmapIndexP = bmapIndex;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readBitmap(Readline *      const readlineP,
Packit 78deda
           unsigned int    const glyphWidth,
Packit 78deda
           unsigned int    const glyphHeight,
Packit 78deda
           const char *    const charName,
Packit 78deda
           unsigned char * const bmap) {
Packit 78deda
Packit 78deda
    int n;
Packit 78deda
    unsigned int bmapIndex;
Packit 78deda
Packit 78deda
    bmapIndex = 0;
Packit 78deda
           
Packit 78deda
    for (n = glyphHeight; n > 0; --n) {
Packit 78deda
        bool eof;
Packit 78deda
        const char * error;
Packit 78deda
Packit 78deda
        readline_read(readlineP, &eof;;
Packit 78deda
Packit 78deda
        if (eof)
Packit 78deda
            pm_error("End of file in bitmap for character '%s' in BDF "
Packit 78deda
                     "font file.", charName);
Packit 78deda
Packit 78deda
        if (!readlineP->arg[0])
Packit 78deda
            pm_error("A line that is supposed to contain bitmap data, "
Packit 78deda
                     "in hexadecimal, for character '%s' is empty", charName);
Packit 78deda
Packit 78deda
        parseBitmapRow(readlineP->arg[0], glyphWidth, bmap, bmapIndex,
Packit 78deda
                       &bmapIndex, &error);
Packit 78deda
Packit 78deda
        if (error) {
Packit 78deda
            pm_error("Error in line %d of bitmap for character '%s': %s",
Packit 78deda
                     n, charName, error);
Packit 78deda
            pm_strfree(error);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
createBmap(unsigned int  const glyphWidth,
Packit 78deda
           unsigned int  const glyphHeight,
Packit 78deda
           Readline *    const readlineP,
Packit 78deda
           const char *  const charName,
Packit 78deda
           const char ** const bmapP) {
Packit 78deda
Packit 78deda
    unsigned char * bmap;
Packit 78deda
    bool eof;
Packit 78deda
    
Packit 78deda
    if (glyphWidth > 0 && UINT_MAX / glyphWidth < glyphHeight)
Packit 78deda
        pm_error("Ridiculously large glyph");
Packit 78deda
Packit 78deda
    MALLOCARRAY(bmap, glyphWidth * glyphHeight);
Packit 78deda
Packit 78deda
    if (!bmap)
Packit 78deda
        pm_error("no memory for font glyph byte map");
Packit 78deda
Packit 78deda
    readline_read(readlineP, &eof;;
Packit 78deda
    if (eof)
Packit 78deda
        pm_error("End of file encountered reading font glyph byte map from "
Packit 78deda
                 "BDF font file.");
Packit 78deda
    
Packit 78deda
    if (streq(readlineP->arg[0], "ATTRIBUTES")) {
Packit 78deda
        /* ATTRIBUTES is defined in Glyph Bitmap Distribution Format (BDF)
Packit 78deda
           Specification Version 2.1, but not in Version 2.2. 
Packit 78deda
        */
Packit 78deda
        bool eof;
Packit 78deda
        readline_read(readlineP, &eof;;
Packit 78deda
        if (eof)
Packit 78deda
            pm_error("End of file encountered after ATTRIBUTES in BDF "
Packit 78deda
                     "font file.");
Packit 78deda
    }                
Packit 78deda
    if (!streq(readlineP->arg[0], "BITMAP"))
Packit 78deda
        pm_error("'%s' found where BITMAP expected in definition of "
Packit 78deda
                 "character '%s' in BDF font file.",
Packit 78deda
                 readlineP->arg[0], charName);
Packit 78deda
Packit 78deda
    assert(streq(readlineP->arg[0], "BITMAP"));
Packit 78deda
Packit 78deda
    readBitmap(readlineP, glyphWidth, glyphHeight, charName, bmap);
Packit 78deda
Packit 78deda
    *bmapP = (char *)bmap;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readExpectedStatement(Readline *    const readlineP,
Packit 78deda
                      const char *  const expected) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Have the readline object *readlineP read the next line from the file, but
Packit 78deda
  expect it to be a line of type 'expected' (i.e. the verb token at the
Packit 78deda
  beginning of the line is that, e.g. "STARTFONT").  If it isn't, fail the
Packit 78deda
  program.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    bool eof;
Packit 78deda
Packit 78deda
    readline_read(readlineP, &eof;;
Packit 78deda
Packit 78deda
    if (eof)
Packit 78deda
        pm_error("EOF in BDF font file where '%s' expected", expected);
Packit 78deda
    else if (!streq(readlineP->arg[0], expected))
Packit 78deda
        pm_error("Statement of type '%s' where '%s' expected in BDF font file",
Packit 78deda
                 readlineP->arg[0], expected);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
skipCharacter(Readline * const readlineP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  In the BDF font file being read by readline object *readlineP, skip through
Packit 78deda
  the end of the character we are presently in.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    bool endChar;
Packit 78deda
                        
Packit 78deda
    endChar = FALSE;
Packit 78deda
                        
Packit 78deda
    while (!endChar) {
Packit 78deda
        bool eof;
Packit 78deda
        readline_read(readlineP, &eof;;
Packit 78deda
        if (eof)
Packit 78deda
            pm_error("End of file in the middle of a character (before "
Packit 78deda
                     "ENDCHAR) in BDF font file.");
Packit 78deda
        endChar = streq(readlineP->arg[0], "ENDCHAR");
Packit 78deda
    }                        
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
interpEncoding(const char **  const arg,
Packit 78deda
               unsigned int * const codepointP,
Packit 78deda
               bool *         const badCodepointP,
Packit 78deda
               PM_WCHAR       const maxglyph) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   With arg[] being the ENCODING statement from the font, return as
Packit 78deda
   *codepointP the codepoint that it indicates (code point is the character
Packit 78deda
   code, e.g. in ASCII, 48 is '0').
Packit 78deda
Packit 78deda
   But if the statement doesn't give an acceptable codepoint return
Packit 78deda
   *badCodepointP == TRUE.
Packit 78deda
Packit 78deda
   'maxglyph' is the maximum codepoint in the font.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    bool gotCodepoint;
Packit 78deda
    bool badCodepoint;
Packit 78deda
    unsigned int codepoint;
Packit 78deda
Packit 78deda
    if (atoi(arg[1]) >= 0) {
Packit 78deda
        codepoint = atoi(arg[1]);
Packit 78deda
        gotCodepoint = true;
Packit 78deda
    } else {
Packit 78deda
      if (atoi(arg[1]) == -1 && arg[2] != NULL) {
Packit 78deda
            codepoint = atoi(arg[2]);
Packit 78deda
            gotCodepoint = true;
Packit 78deda
        } else
Packit 78deda
            gotCodepoint = false;
Packit 78deda
    }
Packit 78deda
    if (gotCodepoint) {
Packit 78deda
        if (codepoint > maxglyph)
Packit 78deda
            badCodepoint = true;
Packit 78deda
        else
Packit 78deda
            badCodepoint = false;
Packit 78deda
    } else
Packit 78deda
        badCodepoint = true;
Packit 78deda
Packit 78deda
    *badCodepointP = badCodepoint;
Packit 78deda
    *codepointP    = codepoint;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readEncoding(Readline *     const readlineP,
Packit 78deda
             unsigned int * const codepointP,
Packit 78deda
             bool *         const badCodepointP,
Packit 78deda
             PM_WCHAR       const maxglyph) {
Packit 78deda
Packit 78deda
    readExpectedStatement(readlineP, "ENCODING");
Packit 78deda
    
Packit 78deda
    interpEncoding(readlineP->arg, codepointP, badCodepointP, maxglyph);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
validateFontLimits(const struct font2 * const fontP) {
Packit 78deda
Packit 78deda
    assert(pbm_maxfontheight() > 0 && pbm_maxfontwidth() > 0);
Packit 78deda
Packit 78deda
    if (fontP->maxwidth  <= 0 ||
Packit 78deda
        fontP->maxheight <= 0 ||
Packit 78deda
        fontP->maxwidth  > pbm_maxfontwidth()  ||
Packit 78deda
        fontP->maxheight > pbm_maxfontheight() ||
Packit 78deda
        -fontP->x + 1 > fontP->maxwidth ||
Packit 78deda
        -fontP->y + 1 > fontP->maxheight ||
Packit 78deda
        fontP->x > fontP->maxwidth  ||
Packit 78deda
        fontP->y > fontP->maxheight ||
Packit 78deda
        fontP->x + fontP->maxwidth  > pbm_maxfontwidth() || 
Packit 78deda
        fontP->y + fontP->maxheight > pbm_maxfontheight()
Packit 78deda
        ) {
Packit 78deda
Packit 78deda
        pm_error("Global font metric(s) out of bounds."); 
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (fontP->maxglyph > PM_FONT2_MAXGLYPH)
Packit 78deda
        pm_error("Internal error.  Glyph table too large: %u glyphs; "
Packit 78deda
                 "Maximum possible in Netpbm is %u",
Packit 78deda
                 fontP->maxglyph, PM_FONT2_MAXGLYPH);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
validateGlyphLimits(const struct font2 * const fontP,
Packit 78deda
                    const struct glyph * const glyphP,
Packit 78deda
                    const char *         const charName) {
Packit 78deda
Packit 78deda
    /* Some BDF files code space with zero width and height,
Packit 78deda
       no bitmap data and just the xadd value.
Packit 78deda
       We allow zero width and height, iff both are zero.
Packit 78deda
    */
Packit 78deda
Packit 78deda
    if (((glyphP->width == 0 || glyphP->height == 0) &&
Packit 78deda
         !(glyphP->width == 0 && glyphP->height == 0)) ||
Packit 78deda
        glyphP->width  > fontP->maxwidth  ||
Packit 78deda
        glyphP->height > fontP->maxheight ||
Packit 78deda
        glyphP->x < fontP->x ||
Packit 78deda
        glyphP->y < fontP->y ||
Packit 78deda
        glyphP->x + (int) glyphP->width  > fontP->x + fontP->maxwidth  ||
Packit 78deda
        glyphP->y + (int) glyphP->height > fontP->y + fontP->maxheight ||
Packit 78deda
        glyphP->xadd > pbm_maxfontwidth() ||
Packit 78deda
        glyphP->xadd + MAX(glyphP->x,0) + (int) glyphP->width >
Packit 78deda
        pbm_maxfontwidth()
Packit 78deda
        ) {
Packit 78deda
Packit 78deda
        pm_error("Font metric(s) for char '%s' out of bounds.\n", charName);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
processChars(Readline *     const readlineP,
Packit 78deda
             struct font2 * const fontP,
Packit 78deda
             PM_WCHAR       const maxglyph ) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Process the CHARS block in a BDF font file, assuming the file is positioned
Packit 78deda
   just after the CHARS line.  Read the rest of the block and apply its
Packit 78deda
   contents to *fontP.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int const nCharacters = atoi(readlineP->arg[1]);
Packit 78deda
Packit 78deda
    unsigned int nCharsDone;
Packit 78deda
Packit 78deda
    nCharsDone = 0;
Packit 78deda
Packit 78deda
    while (nCharsDone < nCharacters) {
Packit 78deda
        bool eof;
Packit 78deda
Packit 78deda
        readline_read(readlineP, &eof;;
Packit 78deda
        if (eof)
Packit 78deda
            pm_error("End of file after CHARS reading BDF font file");
Packit 78deda
Packit 78deda
        if (streq(readlineP->arg[0], "COMMENT")) {
Packit 78deda
            /* ignore */
Packit 78deda
        } else if (!streq(readlineP->arg[0], "STARTCHAR"))
Packit 78deda
            pm_error("no STARTCHAR after CHARS in BDF font file");
Packit 78deda
        else {
Packit 78deda
            const char * const charName = pm_strdup(readlineP->arg[1]);
Packit 78deda
Packit 78deda
            struct glyph * glyphP;
Packit 78deda
            unsigned int codepoint;
Packit 78deda
            bool badCodepoint;
Packit 78deda
Packit 78deda
            assert(streq(readlineP->arg[0], "STARTCHAR"));
Packit 78deda
Packit 78deda
            MALLOCVAR(glyphP);
Packit 78deda
Packit 78deda
            if (glyphP == NULL)
Packit 78deda
                pm_error("no memory for font glyph for '%s' character",
Packit 78deda
                         charName);
Packit 78deda
Packit 78deda
            readEncoding(readlineP, &codepoint, &badCodepoint, maxglyph);
Packit 78deda
Packit 78deda
            if (badCodepoint)
Packit 78deda
                skipCharacter(readlineP);
Packit 78deda
            else if (fontP->glyph[codepoint] != NULL)
Packit 78deda
                pm_error("Multiple definition of code point %d "
Packit 78deda
                         "in font file", (unsigned int) codepoint); 
Packit 78deda
            else {
Packit 78deda
                readExpectedStatement(readlineP, "SWIDTH");
Packit 78deda
                    
Packit 78deda
                readExpectedStatement(readlineP, "DWIDTH");
Packit 78deda
                glyphP->xadd = atoi(readlineP->arg[1]);
Packit 78deda
Packit 78deda
                readExpectedStatement(readlineP, "BBX");
Packit 78deda
                glyphP->width  = atoi(readlineP->arg[1]);
Packit 78deda
                glyphP->height = atoi(readlineP->arg[2]);
Packit 78deda
                glyphP->x      = atoi(readlineP->arg[3]);
Packit 78deda
                glyphP->y      = atoi(readlineP->arg[4]);
Packit 78deda
Packit 78deda
                validateGlyphLimits(fontP, glyphP, charName);
Packit 78deda
Packit 78deda
                createBmap(glyphP->width, glyphP->height, readlineP, charName,
Packit 78deda
                           &glyphP->bmap);
Packit 78deda
                
Packit 78deda
Packit 78deda
                readExpectedStatement(readlineP, "ENDCHAR");
Packit 78deda
Packit 78deda
                assert(codepoint <= maxglyph); /* Ensured by readEncoding() */
Packit 78deda
Packit 78deda
                fontP->glyph[codepoint] = glyphP;
Packit 78deda
                pm_strfree(charName);
Packit 78deda
            }
Packit 78deda
            ++nCharsDone;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
processBdfFontLine(Readline     * const readlineP,
Packit 78deda
                   struct font2 * const fontP,
Packit 78deda
                   bool         * const endOfFontP,
Packit 78deda
                   PM_WCHAR       const maxglyph) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Process a nonblank line just read from a BDF font file.
Packit 78deda
Packit 78deda
   This processing may involve reading more lines.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    *endOfFontP = FALSE;  /* initial assumption */
Packit 78deda
Packit 78deda
    assert(readlineP->arg[0] != NULL);  /* Entry condition */
Packit 78deda
Packit 78deda
    if (streq(readlineP->arg[0], "COMMENT")) {
Packit 78deda
        /* ignore */
Packit 78deda
    } else if (streq(readlineP->arg[0], "SIZE")) {
Packit 78deda
        /* ignore */
Packit 78deda
    } else if (streq(readlineP->arg[0], "STARTPROPERTIES")) {
Packit 78deda
        /* Read off the properties and ignore them all */
Packit 78deda
        unsigned int const propCount = atoi(readlineP->arg[1]);
Packit 78deda
Packit 78deda
        unsigned int i;
Packit 78deda
        for (i = 0; i < propCount; ++i) {
Packit 78deda
            bool eof;
Packit 78deda
            readline_read(readlineP, &eof;;
Packit 78deda
            if (eof)
Packit 78deda
                pm_error("End of file after STARTPROPERTIES in BDF font file");
Packit 78deda
        }
Packit 78deda
    } else if (streq(readlineP->arg[0], "FONTBOUNDINGBOX")) {
Packit 78deda
        fontP->maxwidth  = atoi(readlineP->arg[1]);
Packit 78deda
        fontP->maxheight = atoi(readlineP->arg[2]);
Packit 78deda
        fontP->x = atoi(readlineP->arg[3]);
Packit 78deda
        fontP->y = atoi(readlineP->arg[4]);
Packit 78deda
        validateFontLimits(fontP);
Packit 78deda
    } else if (streq(readlineP->arg[0], "ENDPROPERTIES")) {
Packit 78deda
      if (fontP->maxwidth ==0)
Packit 78deda
      pm_error("Encountered ENDPROPERTIES before FONTBOUNDINGBOX " 
Packit 78deda
                   "in BDF font file");
Packit 78deda
    } else if (streq(readlineP->arg[0], "ENDFONT")) {
Packit 78deda
        *endOfFontP = true;
Packit 78deda
    } else if (streq(readlineP->arg[0], "CHARS")) {
Packit 78deda
      if (fontP->maxwidth ==0)
Packit 78deda
      pm_error("Encountered CHARS before FONTBOUNDINGBOX " 
Packit 78deda
                   "in BDF font file");
Packit 78deda
      else
Packit 78deda
        processChars(readlineP, fontP, maxglyph);
Packit 78deda
    } else {
Packit 78deda
        /* ignore */
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
struct font2 *
Packit 78deda
pbm_loadbdffont2(const char * const filename,
Packit 78deda
                 PM_WCHAR     const maxglyph) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Read a BDF font file "filename" as a 'font2' structure.  A 'font2'
Packit 78deda
   structure is more expressive than a 'font' structure, most notably in that
Packit 78deda
   it can handle wide code points and many more glyphs.
Packit 78deda
Packit 78deda
   Codepoints up to maxglyph inclusive are valid in the file.
Packit 78deda
Packit 78deda
   The returned object is in new malloc'ed storage, in many pieces, and
Packit 78deda
   cannot be destroyed.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    /* For our return object to be destroyable, we need to supply a destroy
Packit 78deda
       function, and it needs to return glyph and raster memory, and
Packit 78deda
       struct font needs to manage glyph memory separately from mapping
Packit 78deda
       code points to glyphs.
Packit 78deda
    */
Packit 78deda
    FILE *         ifP;
Packit 78deda
    Readline       readline;
Packit 78deda
    struct font2 * font2P;
Packit 78deda
    bool           endOfFont;
Packit 78deda
Packit 78deda
    ifP = fopen(filename, "rb");
Packit 78deda
    if (!ifP)
Packit 78deda
        pm_error("Unable to open BDF font file name '%s'.  errno=%d (%s)",
Packit 78deda
                 filename, errno, strerror(errno));
Packit 78deda
Packit 78deda
    readline_init(&readline, ifP);
Packit 78deda
Packit 78deda
    MALLOCVAR(font2P);
Packit 78deda
    if (font2P == NULL)
Packit 78deda
        pm_error("no memory for font");
Packit 78deda
Packit 78deda
    MALLOCARRAY(font2P->glyph, maxglyph + 1);
Packit 78deda
    if (font2P->glyph == NULL)
Packit 78deda
        pm_error("no memory for font glyphs");
Packit 78deda
Packit 78deda
    font2P->maxglyph = maxglyph;
Packit 78deda
Packit 78deda
    font2P->oldfont = NULL;
Packit 78deda
    {
Packit 78deda
        /* Initialize all characters to nonexistent; we will fill the ones we
Packit 78deda
           find in the bdf file later.
Packit 78deda
        */
Packit 78deda
        PM_WCHAR i;
Packit 78deda
        for (i = 0; i <= maxglyph; ++i)
Packit 78deda
            font2P->glyph[i] = NULL;
Packit 78deda
    }
Packit 78deda
Packit 78deda
    font2P->maxwidth = font2P->maxheight = font2P->x = font2P->y = 0;
Packit 78deda
Packit 78deda
    readExpectedStatement(&readline, "STARTFONT");
Packit 78deda
Packit 78deda
    endOfFont = FALSE;
Packit 78deda
Packit 78deda
    while (!endOfFont) {
Packit 78deda
        bool eof;
Packit 78deda
        readline_read(&readline, &eof;;
Packit 78deda
        if (eof)
Packit 78deda
            pm_error("End of file before ENDFONT statement in BDF font file");
Packit 78deda
Packit 78deda
        processBdfFontLine(&readline, font2P, &endOfFont, maxglyph);
Packit 78deda
    }
Packit 78deda
    return font2P;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
struct font *
Packit 78deda
pbm_loadbdffont(const char * const filename) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Read a BDF font file "filename" into a traditional font structure.
Packit 78deda
Packit 78deda
   Codepoints up to 255 (PM_FONT_MAXGLYPH) are valid.
Packit 78deda
Packit 78deda
   Can handle ASCII, ISO-8859-1, ISO-8859-2, ISO-8859-15, etc.
Packit 78deda
Packit 78deda
   The returned object is in new malloc'ed storage, in many pieces, and
Packit 78deda
   cannot be destroyed.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    /* For our return object to deep copy the glyphs and fonts from
Packit 78deda
       the struct font2be destroyable, we need to supply a destroy
Packit 78deda
       function, and it needs to return glyph and raster memory, and
Packit 78deda
       struct font needs to manage glyph memory separately from mapping
Packit 78deda
       code points to glyphs.
Packit 78deda
    */
Packit 78deda
    struct font  * fontP;
Packit 78deda
    struct font2 * font2P;
Packit 78deda
    unsigned int   codePoint;
Packit 78deda
Packit 78deda
    MALLOCVAR(fontP);
Packit 78deda
    if (fontP == NULL)
Packit 78deda
        pm_error("no memory for font");
Packit 78deda
Packit 78deda
    font2P = pbm_loadbdffont2(filename, PM_FONT_MAXGLYPH);
Packit 78deda
Packit 78deda
    fontP->maxwidth  = font2P->maxwidth;
Packit 78deda
    fontP->maxheight = font2P->maxheight;
Packit 78deda
Packit 78deda
    fontP->x = font2P->x;
Packit 78deda
    fontP->y = font2P->y;
Packit 78deda
Packit 78deda
    for (codePoint = 0; codePoint < PM_FONT_MAXGLYPH + 1; ++codePoint)
Packit 78deda
        fontP->glyph[codePoint] = font2P->glyph[codePoint];
Packit 78deda
Packit 78deda
    fontP->oldfont = NULL;
Packit 78deda
Packit 78deda
    fontP->fcols = 0;
Packit 78deda
    fontP->frows = 0;
Packit 78deda
Packit 78deda
    /* Note that *fontP2 is unfreeable.  See pbm_loadbdffont2.  And even if it
Packit 78deda
       were, we hooked *fontP into it above, so that would have to turn into a
Packit 78deda
       deep copy before we could free *fontP2.
Packit 78deda
    */
Packit 78deda
Packit 78deda
    return fontP;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
struct font2 *
Packit 78deda
pbm_expandbdffont(const struct font * const fontP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Convert a traditional font structure into an expanded font2 structure.
Packit 78deda
Packit 78deda
  This function depends upon the fact that *fontP, like any struct font,
Packit 78deda
  cannot be destroyed.  The returned object refers to memory that belongs
Packit 78deda
  to *fontP.
Packit 78deda
Packit 78deda
  The returned object is in new malloc'ed storage, in many pieces, and
Packit 78deda
  cannot be destroyed.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    /* If we ever make struct font destroyable, this function needs to
Packit 78deda
       copy the glyphs and rasters, and struct font and struct font2 need
Packit 78deda
       to manage glyph memory separately from mapping code points to the
Packit 78deda
       glyphs.
Packit 78deda
    */
Packit 78deda
    PM_WCHAR const maxglyph = PM_FONT_MAXGLYPH;
Packit 78deda
Packit 78deda
    struct font2 * font2P;
Packit 78deda
    unsigned int   codePoint;
Packit 78deda
Packit 78deda
    MALLOCVAR(font2P);
Packit 78deda
    if (font2P == NULL)
Packit 78deda
        pm_error("no memory for font");
Packit 78deda
Packit 78deda
    MALLOCARRAY(font2P->glyph, maxglyph + 1);
Packit 78deda
    if (font2P->glyph == NULL)
Packit 78deda
        pm_error("no memory for font glyphs");
Packit 78deda
Packit 78deda
    font2P->maxwidth  = fontP->maxwidth;
Packit 78deda
    font2P->maxheight = fontP->maxheight;
Packit 78deda
Packit 78deda
    font2P->x = fontP->x;
Packit 78deda
    font2P->y = fontP->y;
Packit 78deda
Packit 78deda
    font2P->maxglyph = maxglyph;
Packit 78deda
Packit 78deda
    for (codePoint = 0; codePoint < maxglyph + 1; ++codePoint)
Packit 78deda
        font2P->glyph[codePoint] = fontP->glyph[codePoint];
Packit 78deda
Packit 78deda
    font2P->oldfont = fontP->oldfont;
Packit 78deda
Packit 78deda
    font2P->fcols = fontP->fcols;
Packit 78deda
    font2P->frows = fontP->frows;
Packit 78deda
Packit 78deda
    return font2P;
Packit 78deda
}
Packit 78deda
Packit 78deda