|
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 |
|