/* * 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 * 17 October 1995 * * The following people have supplied bug fixes: * * Simon Chow * Fung Fung Lee * Man-Chi Pong * Steven Simpson * Charles Wang * Werner Lemberg * * Ross no longer maintains this code. Please send bug reports to * Werner Lemberg . * */ /* * 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 #include #include #include #include #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 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; }