/*
* Copyright 1993,1994,1995,2005 by Ross Paterson
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* Ross Paterson <
[email protected]>
* 17 October 1995
*
* The following people have supplied bug fixes:
*
* Simon Chow <
[email protected]>
* Fung Fung Lee <
[email protected]>
* Man-Chi Pong <
[email protected]>
* Steven Simpson <
[email protected]>
* Charles Wang <
[email protected]>
* Werner Lemberg <
[email protected]>
*
* Ross no longer maintains this code. Please send bug reports to
* Werner Lemberg <
[email protected]>.
*
*/
/*
* Two C interfaces to HBF files.
*
* The multiple interfaces make this code rather messy; I intend
* to clean it up as experience is gained on what is really needed.
*
* There are also two modes of operation:
* - the default is to read each bitmap from its file as demanded
* - if IN_MEMORY is defined, the whole bitmap file is held in memory.
* In this case, if running under Unix, the bitmap files may be gzipped
* (but the filename used in the HBF file should be the name of the
* file before it was gzipped).
*/
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "hbf.h"
#ifdef __MSDOS__
#define msdos
#endif
/*
* if the linker complains about an unresolved identifier '_strdup',
* uncomment the following definition.
*/
/* #define NO_STRDUP */
#ifdef __STDC__
# define _(x) x
#else
# define _(x) ()
#endif
#define reg register
typedef int bool;
#define TRUE 1
#define FALSE 0
#define Bit(n) (1<<(7 - (n)))
/*
* Messy file system issues
*/
#ifdef unix
#define PATH_DELIMITER ':'
#define RelativeFileName(fn) ((fn)[0] != '/')
#define LocalFileName(fn) (strchr(fn, '/') == NULL)
#endif /* unix */
#ifdef msdos
#define PATH_DELIMITER ';'
#define HasDrive(fn) (isalpha((fn)[0]) && (fn)[1] == ':')
#ifdef __EMX__
#define RelativeFileName(fn) (! HasDrive(fn) && \
!((fn)[0] == '\\' || (fn)[0] == '/'))
#define LocalFileName(fn) (! HasDrive(fn) && \
strchr(fn, '\\') == NULL && \
strchr(fn, '/') == NULL)
#else
#define RelativeFileName(fn) (! HasDrive(fn) && (fn)[0] != '\\')
#define LocalFileName(fn) (! HasDrive(fn) && strchr(fn, '\\') == NULL)
#endif /* __EMX__ */
#define READ_BINARY "rb"
#endif /* msdos */
#ifdef vms
#define PATH_DELIMITER ','
#define RelativeFileName(fn) (strchr(fn, ':') == NULL && ((fn)[0] != '[' || (fn)[1] == '.' || (fn)[1] == '-'))
#define LocalFileName(fn) (strchr(fn, ':') == NULL && strchr(fn, ']') == NULL)
#endif
#ifndef RelativeFileName
#define RelativeFileName(fn) FALSE
#endif
#ifndef LocalFileName
#define LocalFileName(fn) FALSE
#endif
#ifndef READ_BINARY
#define READ_BINARY "r"
#endif
#define MAX_FILENAME 1024
/*
* Internal structures
*/
typedef unsigned char byte;
#define PROPERTY struct _PROPERTY
#define BM_FILE struct _BM_FILE
#define B2_RANGE struct _B2_RANGE
#define CODE_RANGE struct _CODE_RANGE
PROPERTY {
char *prop_name;
char *prop_value;
PROPERTY *prop_next;
};
BM_FILE {
char *bmf_name;
#ifdef IN_MEMORY
byte *bmf_contents;
#else
FILE *bmf_file;
#endif
long bmf_size;
BM_FILE *bmf_next;
};
B2_RANGE {
byte b2r_start;
byte b2r_finish;
B2_RANGE *b2r_next;
};
typedef unsigned short CHAR;
typedef unsigned int CHAR_INDEX; /* character index in file */
#define BAD_CHAR_INDEX 0xffff
CODE_RANGE {
CHAR code_start;
CHAR code_finish;
BM_FILE *code_bm_file;
long code_offset;
CHAR_INDEX code_pos;
bool code_transposed;
bool code_inverted;
CODE_RANGE *code_next;
};
/*
* Extended internal version of HBF
*/
typedef struct {
/* fields corresponding to the definition */
HBF public;
/* plus internal stuff */
char *filename;
byte *bitmap_buffer;
unsigned int b2_size; /* number of legal byte-2's */
PROPERTY *property;
B2_RANGE *byte_2_range;
CODE_RANGE *code_range;
BM_FILE *bm_file;
} HBF_STRUCT;
#define FirstByte(code) ((code)>>8)
#define SecondByte(code) ((code)&0xff)
#define MakeCode(byte1,byte2) (((byte1)<<8)|(byte2))
/* size of a bitmap in the file (may be affected by transposition) */
#define FileBitmapSize(hbfFile,cp) \
((cp)->code_transposed ? \
(hbfBitmapBBox(hbfFile)->hbf_height + 7)/8 * \
hbfBitmapBBox(hbfFile)->hbf_width : \
HBF_BitmapSize(hbfFile))
#define NEW(type) ((type *)malloc((unsigned)(sizeof(type))))
#define QUOTE '"'
#define MAXLINE 1024
#ifdef WIN32
#define strdup(x) _strdup(x)
#else
extern char *strdup _((const char *s));
#endif
static void add_b2r _((B2_RANGE **last_b2r, int start, int finish));
static bool add_code_range _((HBF_STRUCT *hbf, const char *line));
static void add_property _((HBF_STRUCT *hbf, const char *lp));
static CHAR_INDEX b2_pos _((HBF_STRUCT *hbf, HBF_CHAR code));
static int b2_size _((B2_RANGE *b2r));
static void clear_bbox _((HBF_BBOX *bbox));
static void clear_record _((HBF_STRUCT *hbf));
static char *concat _((const char *dir, int dirlen, const char *stem));
static char *expand_filename _((const char *name, const char *filename));
static const byte *get_bitmap
_((HBF_STRUCT *hbf, HBF_CHAR code, byte *buffer));
static byte *local_buffer _((HBF_STRUCT *hbf));
static void invert _((byte *buffer, unsigned length));
#ifdef IN_MEMORY
static bool read_bitmap_file _((BM_FILE *bmf, FILE *f));
static bool copy_transposed
_((HBF *hbf, byte *bitmap, const byte *source));
#else
static bool get_transposed _((HBF *hbf, FILE *f, byte *bitmap));
#endif
static bool match _((const char *lp, const char *sp));
static bool parse_file _((FILE *f, HBF_STRUCT *hbf));
static FILE *path_open
_((const char *path, const char *filename, char **fullp));
static bool real_open _((const char *filename, HBF_STRUCT *hbf));
/* Error reporting */
int hbfDebug; /* set this for error reporting */
#ifdef __STDC__
#include <stdarg.h>
static void
eprintf(const char *fmt, ...)
{
if (hbfDebug) {
va_list args;
(void)fprintf(stderr, "HBF: ");
va_start(args, fmt);
(void)vfprintf(stderr, fmt, args);
va_end(args);
(void)fprintf(stderr, "\n");
}
}
#else /* ! __STDC__ */
/* poor man's variable-length argument list */
static void
eprintf(fmt, x1, x2, x3, x4, x5, x6, x7, x8, x9)
const char *fmt;
int x1, x2, x3, x4, x5, x6, x7, x8, x9;
{
if (hbfDebug) {
(void)fprintf(stderr, "HBF: ");
(void)fprintf(stderr, fmt, x1, x2, x3, x4, x5, x6, x7, x8, x9);
(void)fprintf(stderr, "\n");
}
}
#endif /* __STDC__ */
static void
clear_bbox(HBF_BBOX *bbox)
{
bbox->hbf_width = bbox->hbf_height = 0;
bbox->hbf_xDisplacement = bbox->hbf_yDisplacement = 0;
}
static void
clear_record(HBF_STRUCT *hbf)
{
clear_bbox(&(hbf->public.hbf_bitmap_bbox));
clear_bbox(&(hbf->public.hbf_font_bbox));
hbf->property = NULL;
hbf->filename = NULL;
hbf->bitmap_buffer = NULL;
hbf->byte_2_range = NULL;
hbf->code_range = NULL;
hbf->bm_file = NULL;
}
/*
* Byte-2 ranges
*/
static void
add_b2r(reg B2_RANGE **last_b2r, int start, int finish)
{
reg B2_RANGE *b2r;
b2r = NEW(B2_RANGE);
while (*last_b2r != NULL && (*last_b2r)->b2r_start < start)
last_b2r = &((*last_b2r)->b2r_next);
b2r->b2r_next = *last_b2r;
b2r->b2r_start = start;
b2r->b2r_finish = finish;
*last_b2r = b2r;
}
static CHAR_INDEX
b2_pos(HBF_STRUCT *hbf, HBF_CHAR code)
{
reg B2_RANGE *b2r;
reg unsigned c;
reg CHAR_INDEX pos;
c = SecondByte(code);
pos = 0;
for (b2r = hbf->byte_2_range; b2r != NULL; b2r = b2r->b2r_next)
if (b2r->b2r_start <= c && c <= b2r->b2r_finish)
return pos + c - b2r->b2r_start;
else
pos += b2r->b2r_finish - b2r->b2r_start + 1;
return BAD_CHAR_INDEX;
}
static int
b2_size(reg B2_RANGE *b2r)
{
reg int size;
size = 0;
for ( ; b2r != NULL; b2r = b2r->b2r_next)
size += b2r->b2r_finish - b2r->b2r_start + 1;
return size;
}
/* map a position to a character code */
static long
code_of(HBF_STRUCT *hbf, long pos)
{
long code;
int residue;
reg B2_RANGE *b2r;
code = pos / hbf->b2_size * 256;
residue = pos % hbf->b2_size;
for (b2r = hbf->byte_2_range; b2r != NULL; b2r = b2r->b2r_next)
if (b2r->b2r_start + residue <= b2r->b2r_finish)
return code + b2r->b2r_start + residue;
else
residue -= b2r->b2r_finish - b2r->b2r_start + 1;
/* should never get here */
return 0L;
}
/*
* String stuff
*/
static bool
match(reg const char *lp, reg const char *sp)
{
while (*lp == *sp && *sp != '\0') {
lp++;
sp++;
}
return (*lp == '\0' || isspace((unsigned char)*lp)) && *sp == '\0';
}
#ifdef NO_STRDUP
char *
strdup(const char *s)
{
char *new_s;
new_s = malloc((unsigned)strlen(s) + 1);
strcpy(new_s, s);
return new_s;
}
#endif
/*
* Properties
*/
static void
add_property(reg HBF_STRUCT *hbf, const char *lp)
{
reg PROPERTY *prop;
char tmp[MAXLINE];
reg char *tp;
prop = NEW(PROPERTY);
tp = tmp;
while (*lp != '\0' && ! isspace((unsigned char)*lp))
*tp++ = *lp++;
*tp = '\0';
prop->prop_name = strdup(tmp);
while (*lp != '\0' && isspace((unsigned char)*lp))
lp++;
tp = tmp;
if (*lp == QUOTE) {
lp++;
while (*lp != '\0' && ! (*lp == QUOTE && *++lp != QUOTE))
*tp++ = *lp++;
}
else
for (;;) {
while (*lp != '\0' && ! isspace((unsigned char)*lp))
*tp++ = *lp++;
while (*lp != '\0' && isspace((unsigned char)*lp))
lp++;
if (*lp == '\0')
break;
*tp++ = ' ';
}
*tp = '\0';
prop->prop_value = strdup(tmp);
prop->prop_next = hbf->property;
hbf->property = prop;
}
const char *
hbfProperty(HBF *hbfFile, const char *propName)
{
reg HBF_STRUCT *hbf;
reg PROPERTY *prop;
hbf = (HBF_STRUCT *)hbfFile;
for (prop = hbf->property; prop != NULL; prop = prop->prop_next)
if (strcmp(prop->prop_name, propName) == 0)
return prop->prop_value;
return NULL;
}
/*
* Compatability routines
*/
const char *
HBF_GetProperty(HBF *handle, const char *propertyName)
{
return hbfProperty(handle, propertyName);
}
int
HBF_GetFontBoundingBox(HBF_Handle handle,
unsigned int *width, unsigned int *height,
int *xDisplacement, int *yDisplacement)
{
if (width != NULL)
*width = hbfFontBBox(handle)->hbf_width;
if (height != NULL)
*height = hbfFontBBox(handle)->hbf_height;
if (xDisplacement != NULL)
*xDisplacement = hbfFontBBox(handle)->hbf_xDisplacement;
if (yDisplacement != NULL)
*yDisplacement = hbfFontBBox(handle)->hbf_yDisplacement;
return 0;
}
int
HBF_GetBitmapBoundingBox(HBF_Handle handle,
unsigned int *width, unsigned int *height,
int *xDisplacement, int *yDisplacement)
{
if (width != NULL)
*width = hbfBitmapBBox(handle)->hbf_width;
if (height != NULL)
*height = hbfBitmapBBox(handle)->hbf_height;
if (xDisplacement != NULL)
*xDisplacement = hbfBitmapBBox(handle)->hbf_xDisplacement;
if (yDisplacement != NULL)
*yDisplacement = hbfBitmapBBox(handle)->hbf_yDisplacement;
return 0;
}
/*
* Prepend a directory to a relative filename.
*/
static char *
concat(const char *dir, /* not necessarily null-terminated */
int dirlen, /* number of significant chars in dir */
const char *stem) /* relative filename */
{
char *fullname;
if (dirlen == 0) /* null: current directory */
return strdup(stem);
#ifdef unix
fullname = malloc(dirlen + strlen(stem) + 2);
(void)sprintf(fullname, "%.*s/%s", dirlen, dir, stem);
#else
#ifdef msdos
fullname = malloc(dirlen + strlen(stem) + 2);
(void)sprintf(fullname, "%.*s\\%s", dirlen, dir, stem);
#else
#ifdef vms
if (dir[dirlen-1] == ']' && stem[0] == '[' && stem[1] == '-') {
dirlen--;
stem++;
fullname = malloc(dirlen + strlen(stem) + 2);
(void)sprintf(fullname, "%.*s.%s", dirlen, dir, stem);
}
else {
if (dir[dirlen-1] == ']' && stem[0] == '[' && stem[1] == '.') {
dirlen--;
stem++;
}
fullname = malloc(dirlen + strlen(stem) + 1);
(void)sprintf(fullname, "%.*s%s", dirlen, dir, stem);
}
#else
fullname = strdup(stem);
#endif /* vms */
#endif /* msdos */
#endif /* unix */
return fullname;
}
/*
* Bitmap files
*
* If the host operating system has a heirarchical file system and
* the bitmap file name is relative, it is relative to the directory
* containing the HBF file.
*/
static char *
expand_filename(const char *name, const char *hbf_name)
{
#ifdef unix
reg char *s;
reg int size;
size = name[0] != '/' && (s = strrchr(hbf_name, '/')) != NULL ?
s - hbf_name + 1 : 0;
s = malloc((unsigned)size + strlen(name) + 1);
(void)sprintf(s, "%.*s%s", size, hbf_name, name);
return s;
#else
#ifdef msdos
reg char *s;
reg int size;
#ifdef __EMX__
s = (unsigned char *)hbf_name + strlen((unsigned char *)hbf_name) - 1;
for(;;) {
if (*s == '\\' || *s == '/')
break;
if (s == hbf_name) {
s = NULL;
break;
}
s--;
}
size = HasDrive(name) ? 0 :
(name[0] == '\\' || name[0] == '/') ?
(HasDrive(hbf_name) ? 2 : 0) :
s != NULL ? s - hbf_name + 1 : 0;
#else
size = HasDrive(name) ? 0 :
name[0] == '\\' ? (HasDrive(hbf_name) ? 2 : 0) :
(s = strrchr(hbf_name, '\\')) != NULL ?
s - hbf_name + 1 : 0;
#endif /* __EMX__ */
s = malloc((unsigned)size + strlen(name) + 1);
(void)sprintf(s, "%.*s%s", size, hbf_name, name);
return s;
#else
#ifdef vms
reg char *s;
reg const char *copyto;
reg int size;
if ((s = strchr(hbf_name, ']')) != NULL && RelativeFileName(name))
return concat(hbf_name, (s - hbf_name) + 1, name);
copyto = hbf_name;
if ((s = strstr(copyto, "::")) != NULL && strstr(name, "::") == NULL)
copyto = s+2;
if ((s = strchr(copyto, ':')) != NULL && strchr(name, ':') == NULL)
copyto = s+1;
size = copyto - hbf_name;
s = malloc((unsigned)size + strlen(name) + 1);
(void)sprintf(s, "%.*s%s", size, hbf_name, name);
return s;
#else
return strdup(name);
#endif /* vms */
#endif /* msdos */
#endif /* unix */
}
static BM_FILE *
find_file(HBF_STRUCT *hbf, const char *filename)
{
BM_FILE **fp;
reg BM_FILE *file;
FILE *f;
char *bmfname;
#ifdef IN_MEMORY
#ifdef unix
bool from_pipe;
#endif
#endif
for (fp = &(hbf->bm_file); *fp != NULL; fp = &((*fp)->bmf_next)) {
bmfname = strrchr((*fp)->bmf_name, '/');
bmfname = (bmfname) ? bmfname + 1 : (*fp)->bmf_name;
if (strcmp(bmfname, filename) == 0)
return *fp;
}
file = NEW(BM_FILE);
if (file == NULL) {
eprintf("out of memory");
return NULL;
}
file->bmf_name = expand_filename(filename, hbf->filename);
if (file->bmf_name == NULL) {
free((char *)file);
return NULL;
}
f = fopen(file->bmf_name, READ_BINARY);
#ifdef IN_MEMORY
#ifdef unix
from_pipe = FALSE;
if (f == NULL) {
char tmp[400];
sprintf(tmp, "%s.gz", file->bmf_name);
if ((f = fopen(tmp, "r")) != NULL) {
fclose(f);
sprintf(tmp, "gzcat %s.gz", file->bmf_name);
if ((f = popen(tmp, "r")) != NULL)
from_pipe = TRUE;
}
}
#endif /* unix */
#endif /* IN_MEMORY */
if (f == NULL) {
eprintf("can't open bitmap file '%s'", file->bmf_name);
free(file->bmf_name);
free((char *)file);
return NULL;
}
#ifdef IN_MEMORY
if (! read_bitmap_file(file, f)) {
free(file->bmf_name);
free((char *)file);
return NULL;
}
#ifdef unix
if (from_pipe)
pclose(f);
else
fclose(f);
#else /* ! unix */
fclose(f);
#endif /* ! unix */
#else /* ! IN_MEMORY */
file->bmf_file = f;
fseek(f, 0L, 2);
file->bmf_size = ftell(f);
#endif /* ! IN_MEMORY */
file->bmf_next = NULL;
*fp = file;
return file;
}
#ifdef IN_MEMORY
#define GRAIN_SIZE 512
static bool
read_bitmap_file(BM_FILE *bmf, FILE *f)
{
byte *contents, *cp;
long size;
int c;
size = 0;
cp = contents = (byte *)malloc((unsigned)GRAIN_SIZE);
if (contents == NULL) {
eprintf("not enough space for bitmap file");
return NULL;
}
while ((c = getc(f)) != EOF) {
if (size%GRAIN_SIZE == 0) {
contents = (byte *)realloc((char *)contents,
(unsigned)(size + GRAIN_SIZE));
if (contents == NULL) {
eprintf("not enough space for bitmap file");
return NULL;
}
cp = contents + size;
}
*cp++ = c;
size++;
}
bmf->bmf_size = size;
bmf->bmf_contents = (byte *)realloc((char *)contents, (unsigned)size);
return TRUE;
}
#endif /* IN_MEMORY */
/*
* Code ranges
*/
/* check that a code range fits within its bitmap file */
static bool
too_short(HBF_STRUCT *hbf, CODE_RANGE *cp)
{
int bm_size;
long offset, end_offset;
BM_FILE *bmf;
long start, finish;
bm_size = FileBitmapSize(&(hbf->public), cp);
offset = cp->code_offset;
start = cp->code_start;
finish = cp->code_finish;
end_offset = offset + bm_size *
(hbf->b2_size*(long)FirstByte(finish) +
b2_pos(hbf, finish) - cp->code_pos + 1);
bmf = cp->code_bm_file;
if (end_offset <= bmf->bmf_size)
return FALSE;
/* bitmap file is too short: produce a specific error message */
if (offset > bmf->bmf_size)
eprintf("bitmap file '%s' is shorter than offset 0x%04lx",
bmf->bmf_name, offset);
else if (offset + bm_size > bmf->bmf_size)
eprintf("bitmap file '%s' too short: no room for any bitmaps at offset 0x%04lx",
bmf->bmf_name, offset);
else
eprintf("bitmap file '%s' is too short - code range appears to be 0x%04lx-0x%04lx",
bmf->bmf_name,
start,
code_of(hbf, cp->code_pos +
(bmf->bmf_size - offset)/bm_size) - 1);
return TRUE;
}
static const char *
skip_word(int n, const char *s)
{
for ( ; n > 0; n--) {
while (*s != '\0' && ! isspace((unsigned char)*s))
s++;
while (*s != '\0' && isspace((unsigned char)*s))
s++;
}
return s;
}
/* optional keywords at the end of a CODE_RANGE line */
static void
parse_keywords(CODE_RANGE *cp, const char *s)
{
for (s = skip_word(4, s) ; *s != '\0'; s = skip_word(1, s)) {
switch (*s) {
case 's': case 'S': case 't': case 'T':
/* keyword "sideways" or "transposed" */
cp->code_transposed = TRUE;
break;
case 'i': case 'I':
/* keyword "inverted" */
cp->code_inverted = TRUE;
}
}
}
static bool
add_code_range(HBF_STRUCT *hbf, const char *line)
{
CODE_RANGE *cp;
CODE_RANGE **cpp;
long start, finish;
long offset;
char filename[MAXLINE];
BM_FILE *bmf;
CHAR_INDEX b2pos;
if (sscanf(line, "HBF_CODE_RANGE %li-%li %s %li",
&start, &finish, filename, &offset) != 4) {
eprintf("syntax error in HBF_CODE_RANGE");
return FALSE;
}
/* code ranges are checked in real_open() */
if ((bmf = find_file(hbf, filename)) == NULL)
return FALSE;
if ((cp = NEW(CODE_RANGE)) == NULL) {
eprintf("out of memory");
return FALSE;
}
cp->code_start = (CHAR)start;
cp->code_finish = (CHAR)finish;
cp->code_bm_file = bmf;
cp->code_offset = offset;
cp->code_transposed = cp->code_inverted = FALSE;
parse_keywords(cp, line);
/* insert it in order */
for (cpp = &hbf->code_range;
*cpp != NULL && (*cpp)->code_finish < start;
cpp = &((*cpp)->code_next))
;
if (*cpp != NULL && (*cpp)->code_start <= finish) {
eprintf("code ranges overlap");
return FALSE;
}
cp->code_next = *cpp;
*cpp = cp;
/* set code_pos, and check range */
if (start > finish) {
eprintf("illegal code range 0x%04lx-0x%04lx", start, finish);
return FALSE;
}
if ((b2pos = b2_pos(hbf, start)) == BAD_CHAR_INDEX) {
eprintf("illegal start code 0x%04lx", start);
return FALSE;
}
cp->code_pos = hbf->b2_size*(long)FirstByte(start) + b2pos;
if ((b2pos = b2_pos(hbf, finish)) == BAD_CHAR_INDEX) {
eprintf("illegal finish code 0x%04lx", finish);
return FALSE;
}
/* check that the bitmap file has enough bitmaps */
return ! too_short(hbf, cp);
}
/*
* Reading and parsing of an HBF file
*/
/* get line, truncating to len, and trimming trailing spaces */
static bool
get_line(char *buf, int len, FILE *f)
{
int c;
char *bp;
bp = buf;
for (;;) {
if ((c = getc(f)) == EOF) {
eprintf("unexpected end of file");
return FALSE;
}
if (c == '\n' || c == '\r') {
/* trim trailing space */
while (bp > buf && isspace((unsigned char)*(bp-1)))
bp--;
*bp = '\0';
return TRUE;
}
if (len > 0) {
*bp++ = c;
len--;
}
}
}
/* get next non-COMMENT line */
static bool
get_text_line(char *buf, int len, FILE *f)
{
while (get_line(buf, len, f))
if (*buf != '\0' && ! match(buf, "COMMENT"))
return TRUE;
return FALSE;
}
static bool
get_property(const char *line, const char *keyword, HBF_STRUCT *hbf)
{
if (! match(line, keyword)) {
eprintf("%s expected", keyword);
return FALSE;
}
add_property(hbf, line);
return TRUE;
}
static bool
get_bbox(const char *line, const char *keyword, HBF_BBOX *bbox)
{
int w, h, xd, yd;
if (! match(line, keyword) ||
sscanf(line + strlen(keyword), "%i %i %i %i",
&w, &h, &xd, &yd) != 4) {
eprintf("%s expected", keyword);
return FALSE;
}
if (w <= 0 || h <= 0) {
eprintf("illegal %s dimensions %dx%d", keyword, w, h);
return FALSE;
}
bbox->hbf_width = w;
bbox->hbf_height = h;
bbox->hbf_xDisplacement = xd;
bbox->hbf_yDisplacement = yd;
return TRUE;
}
/*
* HBFHeaderFile ::=
* 'HBF_START_FONT' version EOLN
* 'HBF_CODE_SCHEME' word ... EOLN
* 'FONT' fontName EOLN
* 'SIZE' ptsize xres yres EOLN
* 'HBF_BITMAP_BOUNDING_BOX' w h xd yd EOLN
* 'FONTBOUNDINGBOX' w h xd yd EOLN
* X11R5FontPropertySection
* 'CHARS' n EOLN
* HBFByte2RangeSection
* HBFCodeRangeSection
* 'HBF_END_FONT' EOLN .
*
* This implementation allows extra lines before HBF_END_FONT.
* Anything after HBF_END_FONT is ignored.
*/
static bool
parse_file(FILE *f, reg HBF_STRUCT *hbf)
{
char line[MAXLINE];
int start, finish;
if (! get_text_line(line, MAXLINE, f) ||
! get_property(line, "HBF_START_FONT", hbf))
return FALSE;
if (! get_text_line(line, MAXLINE, f) ||
! get_property(line, "HBF_CODE_SCHEME", hbf))
return FALSE;
if (! get_text_line(line, MAXLINE, f) ||
! get_property(line, "FONT", hbf))
return FALSE;
if (! get_text_line(line, MAXLINE, f) ||
! get_property(line, "SIZE", hbf))
return FALSE;
if (! get_text_line(line, MAXLINE, f) ||
! get_bbox(line, "HBF_BITMAP_BOUNDING_BOX",
&(hbf->public.hbf_bitmap_bbox)))
return FALSE;
if (! get_text_line(line, MAXLINE, f) ||
! get_bbox(line, "FONTBOUNDINGBOX", &(hbf->public.hbf_font_bbox)))
return FALSE;
if (! get_text_line(line, MAXLINE, f))
return FALSE;
if (match(line, "STARTPROPERTIES")) {
for (;;) {
if (! get_text_line(line, MAXLINE, f))
return FALSE;
if (match(line, "ENDPROPERTIES"))
break;
add_property(hbf, line);
}
if (! get_text_line(line, MAXLINE, f))
return FALSE;
}
if (match(line, "CHARS"))
if (! get_text_line(line, MAXLINE, f))
return FALSE;
if (match(line, "HBF_START_BYTE_2_RANGES")) {
for (;;) {
if (! get_text_line(line, MAXLINE, f))
return FALSE;
if (match(line, "HBF_END_BYTE_2_RANGES"))
break;
if (sscanf(line, "HBF_BYTE_2_RANGE %i-%i",
&start, &finish) != 2) {
eprintf("HBF_BYTE_2_RANGE expected");
return FALSE;
}
add_b2r(&(hbf->byte_2_range), start, finish);
}
if (! get_text_line(line, MAXLINE, f))
return FALSE;
}
else
add_b2r(&(hbf->byte_2_range), 0, 0xff);
hbf->b2_size = b2_size(hbf->byte_2_range);
if (! match(line, "HBF_START_CODE_RANGES")) {
eprintf("HBF_START_CODE_RANGES expected");
return FALSE;
}
for (;;) {
if (! get_text_line(line, MAXLINE, f))
return FALSE;
if (match(line, "HBF_END_CODE_RANGES"))
break;
if (! add_code_range(hbf, line))
return FALSE;
}
for (;;) {
if (! get_text_line(line, MAXLINE, f))
return FALSE;
if (match(line, "HBF_END_FONT"))
break;
/* treat extra lines as properties (for private extensions) */
add_property(hbf, line);
}
return TRUE;
}
static FILE *
path_open(const char *path, const char *filename, char **fullp)
{
if (LocalFileName(filename) && path != NULL) {
#ifdef PATH_DELIMITER
char *fullname;
FILE *f;
const char *p_next;
for (;;) {
p_next = strchr(path, PATH_DELIMITER);
if (p_next == NULL)
p_next = path + strlen(path);
fullname = concat(path, p_next - path, filename);
if ((f = fopen(fullname, "r")) != NULL) {
*fullp = fullname;
return f;
}
free(fullname);
if (*p_next == '\0')
break;
path = p_next + 1;
}
#endif
return NULL;
}
else {
*fullp = strdup(filename);
return fopen(*fullp, "r");
}
}
static bool
real_open(const char *filename, reg HBF_STRUCT *hbf)
{
FILE *f;
f = path_open(getenv("HBFPATH"), filename, &(hbf->filename));
if (f == NULL) {
eprintf("can't read file '%s'", filename);
return FALSE;
}
if (! parse_file(f, hbf)) {
fclose(f);
return FALSE;
}
fclose(f);
return TRUE;
}
HBF *
hbfOpen(const char *filename)
{
reg HBF_STRUCT *hbf;
if ((hbf = NEW(HBF_STRUCT)) == NULL) {
eprintf("can't allocate HBF structure");
return NULL;
}
clear_record(hbf);
if (real_open(filename, hbf))
return &(hbf->public);
hbfClose(&(hbf->public));
return NULL;
}
int
HBF_OpenFont(const char *filename, HBF **ptrHandleStorage)
{
return (*ptrHandleStorage = hbfOpen(filename)) == NULL ? -1 : 0;
}
/*
* Close files, free everything associated with the HBF.
*/
int
HBF_CloseFont(HBF *hbfFile)
{
reg HBF_STRUCT *hbf;
PROPERTY *prop_ptr, *prop_next;
B2_RANGE *b2r_ptr, *b2r_next;
CODE_RANGE *code_ptr, *code_next;
BM_FILE *bmf_ptr, *bmf_next;
int status;
status = 0;
hbf = (HBF_STRUCT *)hbfFile;
if (hbf->filename != NULL)
free(hbf->filename);
if (hbf->bitmap_buffer != NULL)
free(hbf->bitmap_buffer);
for (prop_ptr = hbf->property;
prop_ptr != NULL;
prop_ptr = prop_next) {
prop_next = prop_ptr->prop_next;
free(prop_ptr->prop_name);
free(prop_ptr->prop_value);
free((char *)prop_ptr);
}
for (b2r_ptr = hbf->byte_2_range;
b2r_ptr != NULL;
b2r_ptr = b2r_next) {
b2r_next = b2r_ptr->b2r_next;
free((char *)b2r_ptr);
}
for (code_ptr = hbf->code_range;
code_ptr != NULL;
code_ptr = code_next) {
code_next = code_ptr->code_next;
free((char *)code_ptr);
}
for (bmf_ptr = hbf->bm_file;
bmf_ptr != NULL;
bmf_ptr = bmf_next) {
bmf_next = bmf_ptr->bmf_next;
#ifdef IN_MEMORY
free((char *)(bmf_ptr->bmf_contents));
#else
if (bmf_ptr->bmf_file != NULL &&
fclose(bmf_ptr->bmf_file) < 0)
status = -1;
#endif
free(bmf_ptr->bmf_name);
free((char *)bmf_ptr);
}
free((char *)hbf);
return status;
}
void
hbfClose(HBF *hbfFile)
{
(void)HBF_CloseFont(hbfFile);
}
/*
* Fetch a bitmap
*/
const byte *
hbfGetBitmap(HBF *hbf, HBF_CHAR code)
{
return get_bitmap((HBF_STRUCT *)hbf, code, (byte *)NULL);
}
int
HBF_GetBitmap(HBF *hbf, HBF_CHAR code, byte *buffer)
{
return get_bitmap((HBF_STRUCT *)hbf, code, buffer) == NULL ? -1 : 0;
}
/*
* Internal function to fetch a bitmap.
* If buffer is non-null, it must be used.
*/
static const byte *
get_bitmap(reg HBF_STRUCT *hbf, HBF_CHAR code, byte *buffer)
{
CHAR_INDEX pos, b2pos;
reg CODE_RANGE *cp;
BM_FILE *bmf;
int bm_size;
long offset;
if ((b2pos = b2_pos(hbf, code)) == BAD_CHAR_INDEX)
return NULL;
pos = hbf->b2_size*FirstByte(code) + b2pos;
for (cp = hbf->code_range; cp != NULL; cp = cp->code_next)
if (cp->code_start <= code && code <= cp->code_finish) {
bmf = cp->code_bm_file;
bm_size = FileBitmapSize(&(hbf->public), cp);
offset = cp->code_offset +
(long)(pos - cp->code_pos) * bm_size;
#ifdef IN_MEMORY
if (buffer == NULL &&
! cp->code_transposed && ! cp->code_inverted)
return bmf->bmf_contents + offset;
#endif /* IN_MEMORY */
if (buffer == NULL &&
((buffer = local_buffer(hbf)) == NULL))
return NULL;
#ifdef IN_MEMORY
if (cp->code_transposed)
copy_transposed(&(hbf->public),
buffer,
bmf->bmf_contents + offset);
else
memcpy((char *)buffer,
(char *)(bmf->bmf_contents + offset),
bm_size);
#else /* ! IN_MEMORY */
if (fseek(bmf->bmf_file, offset, 0) != 0) {
eprintf("seek error on code 0x%04x", code);
return NULL;
}
if (cp->code_transposed ?
! get_transposed(&(hbf->public), bmf->bmf_file,
buffer) :
fread((char *)buffer,
bm_size, 1, bmf->bmf_file) != 1) {
eprintf("read error on code 0x%04x", code);
return NULL;
}
#endif /* IN_MEMORY */
if (cp->code_inverted)
invert(buffer, HBF_BitmapSize(&(hbf->public)));
return buffer;
}
eprintf("code 0x%04x out of range", code);
return NULL;
}
static byte *
local_buffer(HBF_STRUCT *hbf)
{
if (hbf->bitmap_buffer == NULL &&
(hbf->bitmap_buffer = (byte *)malloc(HBF_BitmapSize(&(hbf->public)))) == NULL) {
eprintf("out of memory");
return NULL;
}
return hbf->bitmap_buffer;
}
static void
invert(byte *buffer, unsigned int length)
{
for ( ; length > 0; length--)
*buffer++ ^= 0xff;
}
#ifdef IN_MEMORY
static bool
copy_transposed(HBF *hbf, reg byte *bitmap, reg const byte *source)
{
reg byte *pos;
reg byte *bm_end;
int x;
int width;
reg int row_size;
reg int c;
reg int imask, omask;
width = hbfBitmapBBox(hbf)->hbf_width;
row_size = HBF_RowSize(hbf);
bm_end = bitmap + HBF_BitmapSize(hbf);
(void)memset((char *)bitmap, '\0', HBF_BitmapSize(hbf));
for (x = 0; x < width; x++) {
pos = bitmap + x/8;
omask = Bit(x%8);
/* y = 0 */
for (;;) {
c = *source++;
for (imask = Bit(0); imask != 0; imask >>= 1) {
/*
* At this point,
*
* imask == Bit(y%8)
* pos == bitmap + y*row_size + x/8
*
* We examine bit y of row x of the input,
* setting bit x of row y of the output if
* required, by applying omask to *pos.
*/
if ((c & imask) != 0)
*pos |= omask;
/* if (++y > height) goto end_column */
pos += row_size;
if (pos >= bm_end)
goto end_column;
}
}
end_column:
;
}
return TRUE;
}
#else /* ! IN_MEMORY */
static bool
get_transposed(HBF *hbf, FILE *f, reg byte *bitmap)
{
reg byte *pos;
reg byte *bm_end;
int x;
int width;
reg int row_size;
reg int c;
reg int imask, omask;
width = hbfBitmapBBox(hbf)->hbf_width;
row_size = HBF_RowSize(hbf);
bm_end = bitmap + HBF_BitmapSize(hbf);
(void)memset((char *)bitmap, '\0', HBF_BitmapSize(hbf));
for (x = 0; x < width; x++) {
pos = bitmap + x/8;
omask = Bit(x%8);
/* y = 0 */
for (;;) {
if ((c = getc(f)) == EOF)
return FALSE;
for (imask = Bit(0); imask != 0; imask >>= 1) {
/*
* At this point,
*
* imask == Bit(y%8)
* pos == bitmap + y*row_size + x/8
*
* We examine bit y of row x of the input,
* setting bit x of row y of the output if
* required, by applying omask to *pos.
*/
if ((c & imask) != 0)
*pos |= omask;
/* if (++y > height) goto end_column */
pos += row_size;
if (pos >= bm_end)
goto end_column;
}
}
end_column:
;
}
return TRUE;
}
#endif /* ! IN_MEMORY */
/*
* Call function on each valid code in ascending order.
*/
void
hbfForEach(reg HBF *hbfFile, void (*func)(HBF *, HBF_CHAR))
{
HBF_STRUCT *hbf;
CODE_RANGE *cp;
reg B2_RANGE *b2r;
reg unsigned byte1, byte2;
reg unsigned finish;
hbf = (HBF_STRUCT *)hbfFile;
for (cp = hbf->code_range; cp != NULL; cp = cp->code_next) {
byte1 = FirstByte(cp->code_start);
byte2 = SecondByte(cp->code_start);
while (MakeCode(byte1, byte2) <= cp->code_finish) {
for (b2r = hbf->byte_2_range;
b2r != NULL;
b2r = b2r->b2r_next) {
if (byte2 < b2r->b2r_start)
byte2 = b2r->b2r_start;
finish = b2r->b2r_finish;
if (byte1 == FirstByte(cp->code_finish) &&
finish > SecondByte(cp->code_finish))
finish = SecondByte(cp->code_finish);
while (byte2 <= finish) {
(*func)(hbfFile,
MakeCode(byte1, byte2));
byte2++;
}
}
byte1++;
byte2 = 0;
}
}
}
const char *
hbfFileName(HBF *hbf)
{
return ((HBF_STRUCT *)hbf)->filename;
}
long
hbfChars(HBF *hbfFile)
{
HBF_STRUCT *hbf;
CODE_RANGE *cp;
long num_chars;
hbf = (HBF_STRUCT *)hbfFile;
num_chars = 0;
for (cp = hbf->code_range; cp != NULL; cp = cp->code_next)
num_chars +=
hbf->b2_size*FirstByte(cp->code_finish) +
b2_pos(hbf, cp->code_finish) -
(hbf->b2_size*FirstByte(cp->code_start) +
b2_pos(hbf, cp->code_start)) + 1;
return num_chars;
}
/*
* Functions also implemented as macros
*/
#ifdef hbfBitmapBBox
#undef hbfBitmapBBox
#endif
HBF_BBOX *
hbfBitmapBBox(HBF *hbf)
{
return &(hbf->hbf_bitmap_bbox);
}
#ifdef hbfFontBBox
#undef hbfFontBBox
#endif
HBF_BBOX *
hbfFontBBox(HBF *hbf)
{
return &(hbf->hbf_font_bbox);
}
const void *
hbfGetByte2Range(HBF *hbfFile, const void *b2r_pointer,
byte *startp, byte *finishp)
{
HBF_STRUCT *hbf;
const B2_RANGE *b2r;
hbf = (HBF_STRUCT *)hbfFile;
if (b2r_pointer == NULL)
b2r = hbf->byte_2_range;
else
b2r = ((const B2_RANGE *)b2r_pointer)->b2r_next;
if(b2r == NULL)
return NULL;
*startp = b2r->b2r_start;
*finishp = b2r->b2r_finish;
return (const void *)b2r;
}
const void *
hbfGetCodeRange(HBF *hbfFile, const void *code_pointer,
HBF_CHAR *startp, HBF_CHAR *finishp)
{
HBF_STRUCT *hbf;
const CODE_RANGE *cp;
hbf = (HBF_STRUCT *)hbfFile;
if (code_pointer == NULL)
cp = hbf->code_range;
else
cp = ((const CODE_RANGE *)code_pointer)->code_next;
if(cp == NULL)
return NULL;
*startp = cp->code_start;
*finishp = cp->code_finish;
return (const void *)cp;
}