/* * Copyright 2000 Computing Research Labs, New Mexico State University * Copyright 2001 Francesco Zappa Nardelli * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* static char rcsid[] = "$Id$"; */ #include #include FT_INTERNAL_DEBUG_H #include FT_INTERNAL_STREAM_H #include FT_INTERNAL_OBJECTS_H #include "bdf.h" #include "bdferror.h" #undef MAX #define MAX(h, i) ((h) > (i) ? (h) : (i)) #undef MIN #define MIN(l, o) ((l) < (o) ? (l) : (o)) /************************************************************************** * * Masks used for checking different bits per pixel cases. * **************************************************************************/ static const unsigned char onebpp[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; static const unsigned char twobpp[] = { 0xc0, 0x30, 0x0c, 0x03 }; static const unsigned char fourbpp[] = { 0xf0, 0x0f }; static const unsigned char eightbpp[] = { 0xff }; /************************************************************************** * * Default BDF font options. * **************************************************************************/ static const bdf_options_t _bdf_opts = { 1, /* Hint TTF glyphs. */ 1, /* Correct metrics. */ 1, /* Preserve unencoded glyphs. */ 1, /* Preserve comments. */ 1, /* Pad character-cells. */ BDF_PROPORTIONAL, /* Default spacing. */ 12, /* Default point size. */ 0, /* Default horizontal resolution. */ 0, /* Default vertical resolution. */ 1, /* Bits per pixel. */ BDF_UNIX_EOL, /* Line separator. */ }; /************************************************************************** * * Builtin BDF font properties. * **************************************************************************/ /* * List of most properties that might appear in a font. Doesn't include the * RAW_* and AXIS_* properties in X11R6 polymorphic fonts. */ static const bdf_property_t _bdf_properties[] = { {"ADD_STYLE_NAME", BDF_ATOM, 1}, {"AVERAGE_WIDTH", BDF_INTEGER, 1}, {"AVG_CAPITAL_WIDTH", BDF_INTEGER, 1}, {"AVG_LOWERCASE_WIDTH", BDF_INTEGER, 1}, {"CAP_HEIGHT", BDF_INTEGER, 1}, {"CHARSET_COLLECTIONS", BDF_ATOM, 1}, {"CHARSET_ENCODING", BDF_ATOM, 1}, {"CHARSET_REGISTRY", BDF_ATOM, 1}, {"COMMENT", BDF_ATOM, 1}, {"COPYRIGHT", BDF_ATOM, 1}, {"DEFAULT_CHAR", BDF_CARDINAL, 1}, {"DESTINATION", BDF_CARDINAL, 1}, {"DEVICE_FONT_NAME", BDF_ATOM, 1}, {"END_SPACE", BDF_INTEGER, 1}, {"FACE_NAME", BDF_ATOM, 1}, {"FAMILY_NAME", BDF_ATOM, 1}, {"FIGURE_WIDTH", BDF_INTEGER, 1}, {"FONT", BDF_ATOM, 1}, {"FONTNAME_REGISTRY", BDF_ATOM, 1}, {"FONT_ASCENT", BDF_INTEGER, 1}, {"FONT_DESCENT", BDF_INTEGER, 1}, {"FOUNDRY", BDF_ATOM, 1}, {"FULL_NAME", BDF_ATOM, 1}, {"ITALIC_ANGLE", BDF_INTEGER, 1}, {"MAX_SPACE", BDF_INTEGER, 1}, {"MIN_SPACE", BDF_INTEGER, 1}, {"NORM_SPACE", BDF_INTEGER, 1}, {"NOTICE", BDF_ATOM, 1}, {"PIXEL_SIZE", BDF_INTEGER, 1}, {"POINT_SIZE", BDF_INTEGER, 1}, {"QUAD_WIDTH", BDF_INTEGER, 1}, {"RAW_ASCENT", BDF_INTEGER, 1}, {"RAW_AVERAGE_WIDTH", BDF_INTEGER, 1}, {"RAW_AVG_CAPITAL_WIDTH", BDF_INTEGER, 1}, {"RAW_AVG_LOWERCASE_WIDTH", BDF_INTEGER, 1}, {"RAW_CAP_HEIGHT", BDF_INTEGER, 1}, {"RAW_DESCENT", BDF_INTEGER, 1}, {"RAW_END_SPACE", BDF_INTEGER, 1}, {"RAW_FIGURE_WIDTH", BDF_INTEGER, 1}, {"RAW_MAX_SPACE", BDF_INTEGER, 1}, {"RAW_MIN_SPACE", BDF_INTEGER, 1}, {"RAW_NORM_SPACE", BDF_INTEGER, 1}, {"RAW_PIXEL_SIZE", BDF_INTEGER, 1}, {"RAW_POINT_SIZE", BDF_INTEGER, 1}, {"RAW_PIXELSIZE", BDF_INTEGER, 1}, {"RAW_POINTSIZE", BDF_INTEGER, 1}, {"RAW_QUAD_WIDTH", BDF_INTEGER, 1}, {"RAW_SMALL_CAP_SIZE", BDF_INTEGER, 1}, {"RAW_STRIKEOUT_ASCENT", BDF_INTEGER, 1}, {"RAW_STRIKEOUT_DESCENT", BDF_INTEGER, 1}, {"RAW_SUBSCRIPT_SIZE", BDF_INTEGER, 1}, {"RAW_SUBSCRIPT_X", BDF_INTEGER, 1}, {"RAW_SUBSCRIPT_Y", BDF_INTEGER, 1}, {"RAW_SUPERSCRIPT_SIZE", BDF_INTEGER, 1}, {"RAW_SUPERSCRIPT_X", BDF_INTEGER, 1}, {"RAW_SUPERSCRIPT_Y", BDF_INTEGER, 1}, {"RAW_UNDERLINE_POSITION", BDF_INTEGER, 1}, {"RAW_UNDERLINE_THICKNESS", BDF_INTEGER, 1}, {"RAW_X_HEIGHT", BDF_INTEGER, 1}, {"RELATIVE_SETWIDTH", BDF_CARDINAL, 1}, {"RELATIVE_WEIGHT", BDF_CARDINAL, 1}, {"RESOLUTION", BDF_INTEGER, 1}, {"RESOLUTION_X", BDF_CARDINAL, 1}, {"RESOLUTION_Y", BDF_CARDINAL, 1}, {"SETWIDTH_NAME", BDF_ATOM, 1}, {"SLANT", BDF_ATOM, 1}, {"SMALL_CAP_SIZE", BDF_INTEGER, 1}, {"SPACING", BDF_ATOM, 1}, {"STRIKEOUT_ASCENT", BDF_INTEGER, 1}, {"STRIKEOUT_DESCENT", BDF_INTEGER, 1}, {"SUBSCRIPT_SIZE", BDF_INTEGER, 1}, {"SUBSCRIPT_X", BDF_INTEGER, 1}, {"SUBSCRIPT_Y", BDF_INTEGER, 1}, {"SUPERSCRIPT_SIZE", BDF_INTEGER, 1}, {"SUPERSCRIPT_X", BDF_INTEGER, 1}, {"SUPERSCRIPT_Y", BDF_INTEGER, 1}, {"UNDERLINE_POSITION", BDF_INTEGER, 1}, {"UNDERLINE_THICKNESS", BDF_INTEGER, 1}, {"WEIGHT", BDF_CARDINAL, 1}, {"WEIGHT_NAME", BDF_ATOM, 1}, {"X_HEIGHT", BDF_INTEGER, 1}, {"_MULE_BASELINE_OFFSET", BDF_INTEGER, 1}, {"_MULE_RELATIVE_COMPOSE", BDF_INTEGER, 1}, }; static const FT_ULong _num_bdf_properties = FT_NUM_ELEMENT(_bdf_properties); /* * User defined properties. */ /*static bdf_property_t *user_props; static unsigned long nuser_props = 0;*/ /************************************************************************** * * Hash table utilities for the properties. * **************************************************************************/ #define INITIAL_HT_SIZE 241 typedef void (*hash_free_func)(hashnode node); static hashnode* hash_bucket(char *key, hashtable *ht) { char* kp = key; unsigned long res = 0; hashnode* bp = ht->table, *ndp; /* * Mocklisp hash function. */ while (*kp) res = (res << 5) - res + *kp++; ndp = bp + (res % ht->size); while (*ndp) { kp = (*ndp)->key; if (kp[0] == key[0] && ft_strcmp(kp, key) == 0) break; ndp--; if (ndp < bp) ndp = bp + (ht->size - 1); } return ndp; } static FT_Error hash_rehash ( hashtable* ht, FT_Memory memory ) { hashnode *obp = ht->table, *bp, *nbp; int i, sz = ht->size; FT_Error error; ht->size <<= 1; ht->limit = ht->size / 3; if ( FT_NEW_ARRAY( ht->table , ht->size ) ) return error; for (i = 0, bp = obp; i < sz; i++, bp++) { if (*bp) { nbp = hash_bucket((*bp)->key, ht); *nbp = *bp; } } FT_FREE(obp); return FT_Err_Ok; } static FT_Error hash_init ( hashtable* ht, FT_Memory memory ) { int sz = INITIAL_HT_SIZE; FT_Error error; ht->size = sz; ht->limit = sz / 3; ht->used = 0; if ( FT_NEW_ARRAY( ht->table, size ) ) return error; return FT_Err_Ok; } static void hash_free( hashtable* ht, FT_Memory memory ) { /* FT_Error error; */ if ( ht != 0 ) { int i, sz = ht->size; hashnode* bp = ht->table; for (i = 0; i < sz; i++, bp++) { if (*bp) FT_FREE(*bp); } if (sz > 0) FT_FREE(ht->table); } } static FT_Error hash_insert ( char* key, void* data, hashtable* ht, FT_Memory memory ) { FT_Error error = FT_Err_Ok; hashnode nn, *bp = hash_bucket(key, ht); nn = *bp; if (!nn) { if ( FT_NEW( nn ) ) return error; *bp = nn; nn->key = key; nn->data = data; if (ht->used >= ht->limit) error = hash_rehash(ht, memory); ht->used++; } else nn->data = data; return error; } static hashnode hash_lookup(char *key, hashtable *ht) { hashnode *np = hash_bucket(key, ht); return *np; } #ifdef 0 static void hash_delete(char *name, hashtable *ht , FT_Memory memory) { hashnode *hp; /* FT_Error error; */ hp = hash_bucket(name, ht); FT_FREE( *hp ); } #endif /* * The builtin property table. */ /*static hashtable proptbl; */ /* XXX eliminate this */ /************************************************************************** * * Utility types and functions. * **************************************************************************/ /* * Function type for parsing lines of a BDF font. */ typedef int (*_bdf_line_func_t)( char* line, unsigned long linelen, unsigned long lineno, void* call_data, void* client_data ); /* * List structure for splitting lines into fields. */ typedef struct { char** field; unsigned long size; unsigned long used; char* bfield; unsigned long bsize; unsigned long bused; } _bdf_list_t; /* * Structure used while loading BDF fonts. */ typedef struct { unsigned long flags; unsigned long cnt; unsigned long row; unsigned long bpr; short minlb; short maxlb; short maxrb; short maxas; short maxds; short rbearing; char* glyph_name; long glyph_enc; bdf_font_t* font; bdf_options_t* opts; void* client_data; bdf_callback_t callback; bdf_callback_struct_t cb; unsigned long have[2048]; _bdf_list_t list; FT_Memory memory; } _bdf_parse_t; #define setsbit(m, cc) (m[(cc) >> 3] |= (1 << ((cc) & 7))) #define sbitset(m, cc) (m[(cc) >> 3] & (1 << ((cc) & 7))) /* * An empty string for empty fields. */ static const char empty[1] = { 0 }; /* XXX eliminate this */ /* * Assume the line is NULL terminated and that the `list' parameter was * initialized the first time it was used. */ static FT_Error _bdf_split ( char* separators, char* line, unsigned long linelen, _bdf_list_t* list, FT_Memory memory ) { int mult, final_empty; char *sp, *ep, *end; unsigned char seps[32]; FT_Error error; /* * Initialize the list. */ list->used = list->bused = 0; /* * If the line is empty, then simply return. */ if ( linelen == 0 || line[0] == 0 ) return FT_Err_Ok; /* * If the `separators' parameter is NULL or empty, split the list into * individual bytes. */ if ( separators == 0 || *separators == 0 ) { if ( linelen > list->bsize ) { if ( list->bsize ) { if ( FT_ALLOC ( list->bfield , linelen) ) return error; } else { if ( FT_REALLOC ( list->bfield , list->bsize, linelen) ) return error; } list->bsize = linelen; } list->bused = linelen; FT_MEM_COPY (list->bfield, line, linelen); return FT_Err_Ok; } /* * Prepare the separator bitmap. */ FT_MEM_ZERO( seps, 32 ); /* * If the very last character of the separator string is a plus, then set * the `mult' flag to indicate that multiple separators should be * collapsed into one. */ for ( mult = 0, sp = separators; sp && *sp; sp++ ) { if ( sp[0] == '+' && sp[1] == 0) mult = 1; else setsbit( seps, sp[0] ); } /* * Break the line up into fields. */ final_empty = 0; sp = ep = line; end = sp + linelen; for ( ; sp < end && *sp;) { /* * Collect everything that is not a separator. */ for ( ; *ep && !sbitset( seps, *ep ); ep++ ) ; /* * Resize the list if necessary. */ if ( list->used == list->size ) { if ( list->size == 0 ) { if ( FT_NEW_ARRAY( list->field , 5) ) return error; } else { if ( FT_RENEW_ARRAY( list->field , list->size, list->size+5 ) return error; } list->size += 5; } /* * Assign the field appropriately. */ list->field[ list->used++ ] = (ep > sp) ? sp : empty; sp = ep; if (mult) { /* * If multiple separators should be collapsed, do it now by * setting all the separator characters to 0. */ for ( ; *ep && sbitset(seps, *ep); ep++ ) *ep = 0; } else if (*ep != 0) { /* * Don't collapse multiple separators by making them 0, so just * make the one encountered 0. */ *ep++ = 0; } final_empty = ( ep > sp && *ep == 0 ); sp = ep; } /* * Finally, NULL terminate the list. */ if ( list->used + final_empty + 1 >= list->size ) { if ( list->used == list->size ) { if ( list->size == 0 ) { if ( FT_NEW_ARRAY( list->field, 5 ) ) return error; } else { if ( FT_RENEW_ARRAY( list->field , list->size, list->size+5 ) ) return error; } list->size += 5; } } if (final_empty) list->field[ list->used++ ] = empty; if ( list->used == list->size ) { if ( list->size == 0 ) { if ( FT_NEW_ARRAY( list->field , 5 ) ) return error; } else { if ( FT_NEW_ARRAY( list->field, list->size, list->size + 5 ) ) return error; } list->size += 5; } list->field[ list->used ] = 0; return FT_Err_Ok; } static void _bdf_shift( unsigned long n, _bdf_list_t* list) { unsigned long i, u; if ( list == 0 || list->used == 0 || n == 0 ) return; if ( n >= list->used ) { list->used = 0; return; } for ( u = n, i = 0; u < list->used; i++, u++ ) list->field[i] = list->field[u]; list->used -= n; } static char* _bdf_join( int c, unsigned long* len, _bdf_list_t* list) { unsigned long i, j; char *fp, *dp; if ( list == 0 || list->used == 0 ) return 0; *len = 0; dp = list->field[0]; for ( i = j = 0; i < list->used; i++ ) { fp = list->field[i]; while (*fp) dp[j++] = *fp++; if (i + 1 < list->used) dp[j++] = c; } dp[j] = 0; *len = j; return dp; } /* * High speed file reader that passes each line to a callback. */ int ftreadstream( FT_Stream stream, char* buffer, int count ) { int read_bytes; int pos = stream->pos; if ( pos >= stream->size ) { FT_ERROR(( "FT_Read_Stream_At:" )); FT_ERROR(( " invalid i/o; pos = 0x%lx, size = 0x%lx\n", pos, stream->size )); return 0; } if ( stream->read ) read_bytes = stream->read( stream, pos, buffer, count ); else { read_bytes = stream->size - pos; if ( read_bytes > count ) read_bytes = count; ft_memcpy( buffer, stream->base + pos, read_bytes ); } stream->pos = pos + read_bytes; return read_bytes; } static int _bdf_readstream( FT_Stream stream, _bdf_line_func_t callback, void* client_data, unsigned long* lno) { _bdf_line_func_t cb; unsigned long lineno; int n, res, done, refill, bytes, hold; char *ls, *le, *pp, *pe, *hp; char buf[65536]; if (callback == 0) return -1; cb = callback; lineno = 1; buf[0] = 0; res = done = 0; pp = ls = le = buf; bytes = 65536; while ( !done && (n = ftreadstream(stream, pp, bytes)) > 0 ) { /* * Determine the new end of the buffer pages. */ pe = pp + n; for (refill = 0; done == 0 && refill == 0; ) { while (le < pe && *le != '\n' && *le != '\r') le++; if (le == pe) { /* * Hit the end of the last page in the buffer. Need to find * out how many pages to shift and how many pages need to be * read in. Adjust the line start and end pointers down to * point to the right places in the pages. */ pp = buf + (((ls - buf) >> 13) << 13); n = pp - buf; ls -= n; le -= n; n = pe - pp; (void) ft_memcpy(buf, pp, n); pp = buf + n; bytes = 65536 - n; refill = 1; } else { /* * Temporarily NULL terminate the line. */ hp = le; hold = *le; *le = 0; if (callback && *ls != '#' && *ls != 0x1a && le > ls && (res = (*cb)(ls, le - ls, lineno, (void *) &cb, client_data)) != 0) done = 1; else { ls = ++le; /* * Handle the case of DOS crlf sequences. */ if (le < pe && hold == '\n' && *le =='\r') ls = ++le; } /* * Increment the line number. */ lineno++; /* * Restore the character at the end of the line. */ *hp = hold; } } } *lno = lineno; return res; } FT_LOCAL_DEF( void ) _bdf_memmove(char *dest, char *src, unsigned long bytes) { long i, j; i = (long) bytes; j = i & 7; i = (i + 7) >> 3; /* * Do a memmove using Ye Olde Duff's Device for efficiency. */ if (src < dest) { src += bytes; dest += bytes; switch (j) { case 0: do { *--dest = *--src; case 7: *--dest = *--src; case 6: *--dest = *--src; case 5: *--dest = *--src; case 4: *--dest = *--src; case 3: *--dest = *--src; case 2: *--dest = *--src; case 1: *--dest = *--src; } while (--i > 0); } } else if (src > dest) { switch (j) { case 0: do { *dest++ = *src++; case 7: *dest++ = *src++; case 6: *dest++ = *src++; case 5: *dest++ = *src++; case 4: *dest++ = *src++; case 3: *dest++ = *src++; case 2: *dest++ = *src++; case 1: *dest++ = *src++; } while (--i > 0); } } } static const unsigned char a2i[128] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char odigits[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const unsigned char ddigits[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const unsigned char hdigits[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; #define isdigok(m, d) (m[(d) >> 3] & (1 << ((d) & 7))) /* * Routine to convert an ASCII string into an unsigned long integer. */ static unsigned long _bdf_atoul(char *s, char **end, int base) { unsigned long v; unsigned char *dmap; if (s == 0 || *s == 0) return 0; /* * Make sure the radix is something recognizable. Default to 10. */ switch (base) { case 8: dmap = odigits; break; case 16: dmap = hdigits; break; default: base = 10; dmap = ddigits; break; } /* * Check for the special hex prefix. */ if ( s[0] == '0' && ( s[1] == 'x' || s[1] == 'X')) { base = 16; dmap = hdigits; s += 2; } for ( v = 0; isdigok(dmap, *s); s++ ) v = (v * base) + a2i[(int) *s]; if (end != 0) *end = s; return v; } /* * Routine to convert an ASCII string into an signed long integer. */ static long _bdf_atol(char *s, char **end, int base) { long v, neg; unsigned char *dmap; if (s == 0 || *s == 0) return 0; /* * Make sure the radix is something recognizable. Default to 10. */ switch (base) { case 8: dmap = odigits; break; case 16: dmap = hdigits; break; default: base = 10; dmap = ddigits; break; } /* * Check for a minus sign. */ neg = 0; if (*s == '-') { s++; neg = 1; } /* * Check for the special hex prefix. */ if (*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X')) { base = 16; dmap = hdigits; s += 2; } for (v = 0; isdigok(dmap, *s); s++) v = (v * base) + a2i[(int) *s]; if (end != 0) *end = s; return (!neg) ? v : -v; } /* * Routine to convert an ASCII string into an signed short integer. */ static short _bdf_atos(char *s, char **end, int base) { short v, neg; unsigned char *dmap; if (s == 0 || *s == 0) return 0; /* * Make sure the radix is something recognizable. Default to 10. */ switch (base) { case 8: dmap = odigits; break; case 16: dmap = hdigits; break; default: base = 10; dmap = ddigits; break; } /* * Check for a minus. */ neg = 0; if (*s == '-') { s++; neg = 1; } /* * Check for the special hex prefix. */ if (*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X')) { base = 16; dmap = hdigits; s += 2; } for (v = 0; isdigok(dmap, *s); s++) v = (v * base) + a2i[(int) *s]; if (end != 0) *end = s; return (!neg) ? v : -v; } /* * Routine to compare two glyphs by encoding so they can be sorted. */ static int by_encoding(const void *a, const void *b) { bdf_glyph_t *c1, *c2; c1 = (bdf_glyph_t *) a; c2 = (bdf_glyph_t *) b; if (c1->encoding < c2->encoding) return -1; else if (c1->encoding > c2->encoding) return 1; return 0; } static FT_Error bdf_create_property( char* name, int format, bdf_font_t* font ) { unsigned long n; bdf_property_t* p; FT_Memory memory = font->memory; FT_Error error; /* * First check to see if the property has * already been added or not. If it has, then * simply ignore it. */ if ( hash_lookup( name, &(font->proptbl)) ) return FT_Err_Ok; if (font->nuser_props == 0) { if ( FT_NEW( font->user_props ) ) return error; } else { if ( FT_RENEW_ARRAY( font->user_props, font->nuser_props, (font->nuser_props + 1) ) ) return error; } p = font->user_props + font->nuser_props; FT_ZERO( p ); n = (unsigned long) (ft_strlen(name) + 1); if ( FT_ALLOC ( p->name , n) ) return error; FT_MEM_COPY(p->name, name, n); p->format = format; p->builtin = 0; n = _num_bdf_properties + font->nuser_props; error = hash_insert(p->name, (void *) n, &(font->proptbl) , memory); if (error) return error; font->nuser_props++; return FT_Err_Ok; } FT_LOCAL_DEF( bdf_property_t* ) bdf_get_property(char *name, bdf_font_t *font) { hashnode hn; unsigned long propid; if (name == 0 || *name == 0) return 0; if ((hn = hash_lookup(name, &(font->proptbl))) == 0) return 0; propid = (unsigned long) hn->data; if (propid >= _num_bdf_properties) return font->user_props + (propid - _num_bdf_properties); return _bdf_properties + propid; } /************************************************************************** * * BDF font file parsing flags and functions. * **************************************************************************/ /* * Parse flags. */ #define _BDF_START 0x0001 #define _BDF_FONT_NAME 0x0002 #define _BDF_SIZE 0x0004 #define _BDF_FONT_BBX 0x0008 #define _BDF_PROPS 0x0010 #define _BDF_GLYPHS 0x0020 #define _BDF_GLYPH 0x0040 #define _BDF_ENCODING 0x0080 #define _BDF_SWIDTH 0x0100 #define _BDF_DWIDTH 0x0200 #define _BDF_BBX 0x0400 #define _BDF_BITMAP 0x0800 #define _BDF_SWIDTH_ADJ 0x1000 #define _BDF_GLYPH_BITS (_BDF_GLYPH|_BDF_ENCODING|_BDF_SWIDTH|\ _BDF_DWIDTH|_BDF_BBX|_BDF_BITMAP) #define _BDF_GLYPH_WIDTH_CHECK 0x40000000 #define _BDF_GLYPH_HEIGHT_CHECK 0x80000000 /* * Auto correction messages. */ #define ACMSG1 "FONT_ASCENT property missing. Added \"FONT_ASCENT %hd\"." #define ACMSG2 "FONT_DESCENT property missing. Added \"FONT_DESCENT %hd\"." #define ACMSG3 "Font width != actual width. Old: %hd New: %hd." #define ACMSG4 "Font left bearing != actual left bearing. Old: %hd New: %hd." #define ACMSG5 "Font ascent != actual ascent. Old: %hd New: %hd." #define ACMSG6 "Font descent != actual descent. Old: %hd New: %hd." #define ACMSG7 "Font height != actual height. Old: %hd New: %hd." #define ACMSG8 "Glyph scalable width (SWIDTH) adjustments made." #define ACMSG9 "SWIDTH field missing at line %ld. Set automatically." #define ACMSG10 "DWIDTH field missing at line %ld. Set to glyph width." #define ACMSG11 "SIZE bits per pixel field adjusted to %hd." #define ACMSG12 "Duplicate encoding %ld (%s) changed to unencoded." #define ACMSG13 "Glyph %ld extra rows removed." #define ACMSG14 "Glyph %ld extra columns removed." #define ACMSG15 "Incorrect glyph count: %ld indicated but %ld found." /* * Error messages. */ #define ERRMSG1 "[line %ld] Missing \"%s\" line." #define ERRMSG2 "[line %ld] Font header corrupted or missing fields." #define ERRMSG3 "[line %ld] Font glyphs corrupted or missing fields." static FT_Error _bdf_add_acmsg ( bdf_font_t* font, char* msg, unsigned long len ) { char* cp; FT_Memory memory = font->memory; FT_Error error; if ( font->acmsgs_len == 0 ) { if ( FT_ALLOC ( font->acmsgs , len + 1 ) ) return error; } else { if ( FT_REALLOC ( font->acmsgs , font->acmsgs_len , font->acmsgs_len + len + 1 ) ) return error; } cp = font->acmsgs + font->acmsgs_len; FT_MEM_COPY(cp, msg, len); cp += len; *cp++ = '\n'; font->acmsgs_len += len + 1; return FT_Err_Ok; } static FT_Error _bdf_add_comment ( bdf_font_t* font, char* comment, unsigned long len ) { char *cp; FT_Memory memory = font->memory; FT_Error error; if (font->comments_len == 0) { if ( FT_ALLOC ( font->comments , len + 1 ) ) return error; } else { if ( FT_REALLOC ( font->comments , font->comments_len, font->comments_len + len + 1) ) return error; } cp = font->comments + font->comments_len; FT_MEM_COPY(cp, comment, len); cp += len; *cp++ = '\n'; font->comments_len += len + 1; return FT_Err_Ok; } /* * Set the spacing from the font name if it exists, or set it to the default * specified in the options. */ static void _bdf_set_default_spacing( bdf_font_t* font, bdf_options_t* opts) { unsigned long len; char name[128]; _bdf_list_t list; FT_Memory memory; /* FT_Error error; */ if ( font == 0 || font->name == 0 || font->name[0] == 0 ) return; memory = font->memory; font->spacing = opts->font_spacing; len = (unsigned long) ( ft_strlen(font->name) + 1 ); (void) ft_memcpy(name, font->name, len); list.size = list.used = 0; _bdf_split("-", name, len, &list, memory); if (list.used == 15) { switch (list.field[11][0]) { case 'C': case 'c': font->spacing = BDF_CHARCELL; break; case 'M': case 'm': font->spacing = BDF_MONOWIDTH; break; case 'P': case 'p': font->spacing = BDF_PROPORTIONAL; break; } } if (list.size > 0) FT_FREE(list.field); } /* * Determine if the property is an atom or not. If it is, then clean it up so * the double quotes are removed if they exist. */ static int _bdf_is_atom( char* line, unsigned long linelen, char* *name, char* *value, bdf_font_t* font) { int hold; char *sp, *ep; bdf_property_t *p; *name = sp = ep = line; while (*ep && *ep != ' ' && *ep != '\t') ep++; hold = -1; if (*ep) { hold = *ep; *ep = 0; } p = bdf_get_property(sp, font); /* * Restore the character that was saved before any return can happen. */ if (hold != -1) *ep = hold; /* * If the propert exists and is not an atom, just return here. */ if (p && p->format != BDF_ATOM) return 0; /* * The property is an atom. Trim all leading and trailing whitespace and * double quotes for the atom value. */ sp = ep; ep = line + linelen; /* * Trim the leading whitespace if it exists. */ *sp++ = 0; while (*sp && (*sp == ' ' || *sp == '\t')) sp++; /* * Trim the leading double quote if it exists. */ if (*sp == '"') sp++; *value = sp; /* * Trim the trailing whitespace if it exists. */ while (ep > sp && (*(ep - 1) == ' ' || *(ep - 1) == '\t')) *--ep = 0; /* * Trim the trailing double quote if it exists. */ if (ep > sp && *(ep - 1) == '"') *--ep = 0; return 1; } static FT_Error _bdf_add_property ( bdf_font_t* font, char* name, char* value) { unsigned long propid; hashnode hn; int len; bdf_property_t *prop, *fp; FT_Memory memory = font->memory; FT_Error error; /* hashtable proptbl = font->proptbl; bdf_property_t *user_props = font->user_props; unsigned long nuser_props = font->nuser_props; */ /* * First, check to see if the property already exists in the font. */ if ((hn = hash_lookup(name, (hashtable *) font->internal)) != 0) { /* * The property already exists in the font, so simply replace * the value of the property with the current value. */ fp = font->props + (unsigned long) hn->data; switch (prop->format) { case BDF_ATOM: { /* * Delete the current atom if it exists. */ FT_FREE ( fp->value.atom ); if (value == 0) len = 1; else len = ft_strlen(value) + 1; if (len > 1) { if ( FT_ALLOC ( fp->value.atom , len ) ) return error; FT_MEM_COPY(fp->value.atom, value, len); } else fp->value.atom = 0; } break; case BDF_INTEGER: fp->value.int32 = _bdf_atol(value, 0, 10); break; case BDF_CARDINAL: fp->value.card32 = _bdf_atoul(value, 0, 10); break; default: ; } return FT_Err_Ok; } /* * See if this property type exists yet or not. If not, create it. */ hn = hash_lookup(name, &(font->proptbl)); if (hn == 0) { bdf_create_property(name, BDF_ATOM, font); hn = hash_lookup(name, &(font->proptbl)); } /* * Allocate another property if this is overflow. */ if (font->props_used == font->props_size) { if (font->props_size == 0) { if ( FT_NEW( font->props ) ) return error; } else { if ( FT_RENEW_ARRAY( font->props, font->props_size, (font->props_size + 1) ) ) return error; } fp = font->props + font->props_size; FT_ZERO( fp ); font->props_size++; } propid = (unsigned long) hn->data; if (propid >= _num_bdf_properties) prop = font->user_props + (propid - _num_bdf_properties); else prop = _bdf_properties + propid; fp = font->props + font->props_used; fp->name = prop->name; fp->format = prop->format; fp->builtin = prop->builtin; switch (prop->format) { case BDF_ATOM: { fp->value.atom = NULL; if ( value && value[0] != 0 ) { len = ft_strlen(value) + 1; if ( FT_ALLOC ( fp->value.atom , len ) ) return error; FT_MEM_COPY (fp->value.atom, value, len); } } break; case BDF_INTEGER: fp->value.int32 = _bdf_atol(value, 0, 10); break; case BDF_CARDINAL: fp->value.card32 = _bdf_atoul(value, 0, 10); break; default: ; } /* * If the property happens to be a comment, then it doesn't need * to be added to the internal hash table. */ if ( ft_memcmp(name, "COMMENT", 7) != 0 ) /* * Add the property to the font property table. */ hash_insert( fp->name, (void *) font->props_used, (hashtable *) font->internal, memory); font->props_used++; /* * Some special cases need to be handled here. The DEFAULT_CHAR property * needs to be located if it exists in the property list, the FONT_ASCENT * and FONT_DESCENT need to be assigned if they are present, and the * SPACING property should override the default spacing. */ if ( ft_memcmp(name, "DEFAULT_CHAR", 12) == 0 ) font->default_glyph = fp->value.int32; else if ( ft_memcmp(name, "FONT_ASCENT", 11) == 0 ) font->font_ascent = fp->value.int32; else if ( ft_memcmp(name, "FONT_DESCENT", 12) == 0 ) font->font_descent = fp->value.int32; else if ( ft_memcmp(name, "SPACING", 7) == 0 ) { if (fp->value.atom[0] == 'p' || fp->value.atom[0] == 'P') font->spacing = BDF_PROPORTIONAL; else if (fp->value.atom[0] == 'm' || fp->value.atom[0] == 'M') font->spacing = BDF_MONOWIDTH; else if (fp->value.atom[0] == 'c' || fp->value.atom[0] == 'C') font->spacing = BDF_CHARCELL; } return FT_Err_Ok; } /* * Actually parse the glyph info and bitmaps. */ static int _bdf_parse_glyphs( char* line, unsigned long linelen, unsigned long lineno, void* call_data, void* client_data) { int c; char *s; unsigned char *bp; unsigned long i, slen, nibbles; double ps, rx, dw, sw; _bdf_line_func_t *next; _bdf_parse_t *p; bdf_glyph_t *glyph; bdf_font_t *font; char nbuf[128]; FT_Memory memory; FT_Error error; next = (_bdf_line_func_t *) call_data; p = (_bdf_parse_t *) client_data; font = p->font; memory = font->memory; /* * Check for a comment. */ if (ft_memcmp(line, "COMMENT", 7) == 0) { linelen -= 7; s = line + 7; if (*s != 0) { s++; linelen--; } _bdf_add_comment(p->font, s, linelen); return 0; } /* * The very first thing expected is the number of glyphs. */ if (!(p->flags & _BDF_GLYPHS)) { if (ft_memcmp(line, "CHARS", 5) != 0) { sprintf(nbuf, ERRMSG1, lineno, "CHARS"); _bdf_add_acmsg(p->font, nbuf, ft_strlen(nbuf)); return BDF_MISSING_CHARS; } _bdf_split(" +", line, linelen, &p->list, memory); p->cnt = font->glyphs_size = _bdf_atoul(p->list.field[1], 0, 10); /* * Make sure the number of glyphs is non-zero. */ if (p->cnt == 0) font->glyphs_size = 64; if ( FT_ALLOC ( font->glyphs , sizeof(bdf_glyph_t) * font->glyphs_size ) ) return FT_Err_Out_Of_Memory; /* * Set up the callback to indicate the glyph loading is about to * begin. */ if (p->callback != 0) { p->cb.reason = BDF_LOAD_START; p->cb.total = p->cnt; p->cb.current = 0; (*p->callback)(&p->cb, p->client_data); } p->flags |= _BDF_GLYPHS; return 0; } /* * Check for the ENDFONT field. */ if (ft_memcmp(line, "ENDFONT", 7) == 0) { /* * Sort the glyphs by encoding. */ qsort((char *) font->glyphs, font->glyphs_used, sizeof(bdf_glyph_t), by_encoding); p->flags &= ~_BDF_START; return 0; } /* * Check for the ENDCHAR field. */ if (ft_memcmp(line, "ENDCHAR", 7) == 0) { /* * Set up and call the callback if it was passed. */ if (p->callback != 0) { p->cb.reason = BDF_LOADING; p->cb.total = font->glyphs_size; p->cb.current = font->glyphs_used; (*p->callback)(&p->cb, p->client_data); } p->glyph_enc = 0; p->flags &= ~_BDF_GLYPH_BITS; return 0; } /* * Check to see if a glyph is being scanned but should be ignored * because it is an unencoded glyph. */ if ((p->flags & _BDF_GLYPH) && p->glyph_enc == -1 && p->opts->keep_unencoded == 0) return 0; /* * Check for the STARTCHAR field. */ if (ft_memcmp(line, "STARTCHAR", 9) == 0) { /* * Set the character name in the parse info first until the * encoding can be checked for an unencoded character. */ if (p->glyph_name != 0) FT_FREE(p->glyph_name); _bdf_split(" +", line, linelen, &p->list,memory); _bdf_shift(1, &p->list); s = _bdf_join(' ', &slen, &p->list); if ( FT_ALLOC ( p->glyph_name , (slen + 1) ) ) return BDF_OUT_OF_MEMORY; FT_MEM_COPY(p->glyph_name, s, slen + 1); p->flags |= _BDF_GLYPH; return 0; } /* * Check for the ENCODING field. */ if (ft_memcmp(line, "ENCODING", 8) == 0) { if (!(p->flags & _BDF_GLYPH)) { /* * Missing STARTCHAR field. */ sprintf(nbuf, ERRMSG1, lineno, "STARTCHAR"); _bdf_add_acmsg(font, nbuf, ft_strlen(nbuf)); return BDF_MISSING_STARTCHAR; } _bdf_split(" +", line, linelen, &p->list, memory); p->glyph_enc = _bdf_atol(p->list.field[1], 0, 10); /* * Check to see if this encoding has already been encountered. If it * has then change it to unencoded so it gets added if indicated. */ if (p->glyph_enc >= 0) { if (_bdf_glyph_modified(p->have, p->glyph_enc)) { /* * Add a message saying a glyph has been moved to the * unencoded area. */ sprintf(nbuf, ACMSG12, p->glyph_enc, p->glyph_name); _bdf_add_acmsg(font, nbuf, ft_strlen(nbuf)); p->glyph_enc = -1; font->modified = 1; } else _bdf_set_glyph_modified(p->have, p->glyph_enc); } if (p->glyph_enc >= 0) { /* * Make sure there are enough glyphs allocated in case the * number of characters happen to be wrong. */ if (font->glyphs_used == font->glyphs_size) { if ( FT_REALLOC ( font->glyphs, sizeof(bdf_glyph_t) * font->glyphs_size, sizeof(bdf_glyph_t) * (font->glyphs_size + 64) ) ) return BDF_OUT_OF_MEMORY; FT_MEM_SET ((char *) (font->glyphs + font->glyphs_size), 0, sizeof(bdf_glyph_t) << 6); /* FZ inutile */ font->glyphs_size += 64; } glyph = font->glyphs + font->glyphs_used++; glyph->name = p->glyph_name; glyph->encoding = p->glyph_enc; /* * Reset the initial glyph info. */ p->glyph_name = 0; } else { /* * Unencoded glyph. Check to see if it should be added or not. */ if (p->opts->keep_unencoded != 0) { /* * Allocate the next unencoded glyph. */ if (font->unencoded_used == font->unencoded_size) { if (font->unencoded_size == 0) { if ( FT_ALLOC ( font->unencoded , sizeof(bdf_glyph_t) << 2 ) ) return BDF_OUT_OF_MEMORY; } else { if ( FT_REALLOC ( font->unencoded , sizeof(bdf_glyph_t) * font->unencoded_size, sizeof(bdf_glyph_t) * (font->unencoded_size + 4) ) ) return BDF_OUT_OF_MEMORY; } font->unencoded_size += 4; } glyph = font->unencoded + font->unencoded_used; glyph->name = p->glyph_name; glyph->encoding = font->unencoded_used++; } else /* * Free up the glyph name if the unencoded shouldn't be * kept. */ FT_FREE( p->glyph_name ); p->glyph_name = 0; } /* * Clear the flags that might be added when width and height are * checked for consistency. */ p->flags &= ~(_BDF_GLYPH_WIDTH_CHECK|_BDF_GLYPH_HEIGHT_CHECK); p->flags |= _BDF_ENCODING; return 0; } /* * Point at the glyph being constructed. */ if (p->glyph_enc == -1) glyph = font->unencoded + (font->unencoded_used - 1); else glyph = font->glyphs + (font->glyphs_used - 1); /* * Check to see if a bitmap is being constructed. */ if (p->flags & _BDF_BITMAP) { /* * If there are more rows than are specified in the glyph metrics, * ignore the remaining lines. */ if (p->row >= glyph->bbx.height) { if (!(p->flags & _BDF_GLYPH_HEIGHT_CHECK)) { sprintf(nbuf, ACMSG13, glyph->encoding); _bdf_add_acmsg(font, nbuf, ft_strlen(nbuf)); p->flags |= _BDF_GLYPH_HEIGHT_CHECK; font->modified = 1; } return 0; } /* * Only collect the number of nibbles indicated by the glyph metrics. * If there are more columns, they are simply ignored. */ nibbles = p->bpr << 1; bp = glyph->bitmap + (p->row * p->bpr); for (i = 0, *bp = 0; i < nibbles; i++) { c = line[i]; *bp = (*bp << 4) + a2i[c]; if (i + 1 < nibbles && (i & 1)) *++bp = 0; } /* * If any line has extra columns, indicate they have been removed. */ if ((line[nibbles] == '0' || a2i[(int) line[nibbles]] != 0) && !(p->flags & _BDF_GLYPH_WIDTH_CHECK)) { sprintf(nbuf, ACMSG14, glyph->encoding); _bdf_add_acmsg(font, nbuf, ft_strlen(nbuf)); p->flags |= _BDF_GLYPH_WIDTH_CHECK; font->modified = 1; } p->row++; return 0; } /* * Expect the SWIDTH (scalable width) field next. */ if (ft_memcmp(line, "SWIDTH", 6) == 0) { if (!(p->flags & _BDF_ENCODING)) { /* * Missing ENCODING field. */ sprintf(nbuf, ERRMSG1, lineno, "ENCODING"); _bdf_add_acmsg(font, nbuf, ft_strlen(nbuf)); return BDF_MISSING_ENCODING; } _bdf_split(" +", line, linelen, &p->list, memory); glyph->swidth = _bdf_atoul(p->list.field[1], 0, 10); p->flags |= _BDF_SWIDTH; return 0; } /* * Expect the DWIDTH (scalable width) field next. */ if (ft_memcmp(line, "DWIDTH", 6) == 0) { _bdf_split(" +", line, linelen, &p->list,memory); glyph->dwidth = _bdf_atoul(p->list.field[1], 0, 10); if (!(p->flags & _BDF_SWIDTH)) { /* * Missing SWIDTH field. Add an auto correction message and set * the scalable width from the device width. */ sprintf(nbuf, ACMSG9, lineno); _bdf_add_acmsg(font, nbuf, ft_strlen(nbuf)); ps = (double) font->point_size; rx = (double) font->resolution_x; dw = (double) glyph->dwidth; glyph->swidth = (unsigned short) ((dw * 72000.0) / (ps * rx)); } p->flags |= _BDF_DWIDTH; return 0; } /* * Expect the BBX field next. */ if (ft_memcmp(line, "BBX", 3) == 0) { _bdf_split(" +", line, linelen, &p->list, memory); glyph->bbx.width = _bdf_atos(p->list.field[1], 0, 10); glyph->bbx.height = _bdf_atos(p->list.field[2], 0, 10); glyph->bbx.x_offset = _bdf_atos(p->list.field[3], 0, 10); glyph->bbx.y_offset = _bdf_atos(p->list.field[4], 0, 10); /* * Generate the ascent and descent of the character. */ glyph->bbx.ascent = glyph->bbx.height + glyph->bbx.y_offset; glyph->bbx.descent = -glyph->bbx.y_offset; /* * Determine the overall font bounding box as the characters are * loaded so corrections can be done later if indicated. */ p->maxas = MAX(glyph->bbx.ascent, p->maxas); p->maxds = MAX(glyph->bbx.descent, p->maxds); p->rbearing = glyph->bbx.width + glyph->bbx.x_offset; p->maxrb = MAX(p->rbearing, p->maxrb); p->minlb = MIN(glyph->bbx.x_offset, p->minlb); p->maxlb = MAX(glyph->bbx.x_offset, p->maxlb); if (!(p->flags & _BDF_DWIDTH)) { /* * Missing DWIDTH field. Add an auto correction message and set * the device width to the glyph width. */ sprintf(nbuf, ACMSG10, lineno); _bdf_add_acmsg(font, nbuf, ft_strlen(nbuf)); glyph->dwidth = glyph->bbx.width; } /* * If the BDF_CORRECT_METRICS flag is set, then adjust the SWIDTH * value if necessary. */ if (p->opts->correct_metrics != 0) { /* * Determine the point size of the glyph. */ ps = (double) font->point_size; rx = (double) font->resolution_x; dw = (double) glyph->dwidth; sw = (unsigned short) ((dw * 72000.0) / (ps * rx)); if (sw != glyph->swidth) { glyph->swidth = sw; if (p->glyph_enc == -1) _bdf_set_glyph_modified(font->umod, font->unencoded_used - 1); else _bdf_set_glyph_modified(font->nmod, glyph->encoding); p->flags |= _BDF_SWIDTH_ADJ; font->modified = 1; } } p->flags |= _BDF_BBX; return 0; } /* * And finally, gather up the bitmap. */ if (ft_memcmp(line, "BITMAP", 6) == 0) { if (!(p->flags & _BDF_BBX)) { /* * Missing BBX field. */ sprintf(nbuf, ERRMSG1, lineno, "BBX"); _bdf_add_acmsg(font, nbuf, ft_strlen(nbuf)); return BDF_MISSING_BBX; } /* * Allocate enough space for the bitmap. */ p->bpr = ((glyph->bbx.width * p->font->bpp) + 7) >> 3; glyph->bytes = p->bpr * glyph->bbx.height; if ( FT_ALLOC ( glyph->bitmap , glyph->bytes ) ) return BDF_OUT_OF_MEMORY; p->row = 0; p->flags |= _BDF_BITMAP; return 0; } return BDF_INVALID_LINE; } /* * Load the font properties. */ static int _bdf_parse_properties(char *line, unsigned long linelen, unsigned long lineno, void *call_data, void *client_data) { unsigned long vlen; _bdf_line_func_t *next; _bdf_parse_t *p; char *name, *value, nbuf[128]; FT_Memory memory; next = (_bdf_line_func_t *) call_data; p = (_bdf_parse_t *) client_data; memory = p->font->memory; /* * Check for the end of the properties. */ if (ft_memcmp(line, "ENDPROPERTIES", 13) == 0) { /* * If the FONT_ASCENT or FONT_DESCENT properties have not been * encountered yet, then make sure they are added as properties and * make sure they are set from the font bounding box info. * * This is *always* done regardless of the options, because X11 * requires these two fields to compile fonts. */ if (bdf_get_font_property(p->font, "FONT_ASCENT") == 0) { p->font->font_ascent = p->font->bbx.ascent; sprintf(nbuf, "%hd", p->font->bbx.ascent); _bdf_add_property(p->font, "FONT_ASCENT", nbuf); sprintf(nbuf, ACMSG1, p->font->bbx.ascent); _bdf_add_acmsg(p->font, nbuf, ft_strlen(nbuf)); p->font->modified = 1; } if (bdf_get_font_property(p->font, "FONT_DESCENT") == 0) { p->font->font_descent = p->font->bbx.descent; sprintf(nbuf, "%hd", p->font->bbx.descent); _bdf_add_property(p->font, "FONT_DESCENT", nbuf); sprintf(nbuf, ACMSG2, p->font->bbx.descent); _bdf_add_acmsg(p->font, nbuf, ft_strlen(nbuf)); p->font->modified = 1; } p->flags &= ~_BDF_PROPS; *next = _bdf_parse_glyphs; return 0; } /* * Ignore the _XFREE86_GLYPH_RANGES properties. */ if (ft_memcmp(line, "_XFREE86_GLYPH_RANGES", 21) == 0) return 0; /* * Handle COMMENT fields and properties in a special way to preserve * the spacing. */ if (ft_memcmp(line, "COMMENT", 7) == 0) { name = value = line; value += 7; if (*value) *value++ = 0; _bdf_add_property(p->font, name, value); } else if (_bdf_is_atom(line, linelen, &name, &value, p->font)) _bdf_add_property(p->font, name, value); else { _bdf_split(" +", line, linelen, &p->list, memory); name = p->list.field[0]; _bdf_shift(1, &p->list); value = _bdf_join(' ', &vlen, &p->list); _bdf_add_property(p->font, name, value); } return 0; } /* * Load the font header. */ static int _bdf_parse_start(char *line, unsigned long linelen, unsigned long lineno, void *call_data, void *client_data) { unsigned long slen; _bdf_line_func_t *next; _bdf_parse_t *p; bdf_font_t *font; char *s, nbuf[128]; /* int test; */ FT_Memory memory; FT_Error error; next = (_bdf_line_func_t *) call_data; p = (_bdf_parse_t *) client_data; if (p->font) memory = p->font->memory; /* * Check for a comment. This is done to handle those fonts that have * comments before the STARTFONT line for some reason. */ if (ft_memcmp(line, "COMMENT", 7) == 0) { if (p->opts->keep_comments != 0 && p->font != 0) { linelen -= 7; s = line + 7; if (*s != 0) { s++; linelen--; } _bdf_add_comment(p->font, s, linelen); /* here font is not defined ! */ } return 0; } if (!(p->flags & _BDF_START)) { memory = p->memory; if (ft_memcmp(line, "STARTFONT", 9) != 0) /* * No STARTFONT field is a good indication of a problem. */ return BDF_MISSING_START; p->flags = _BDF_START; font = p->font = 0; if ( FT_ALLOC ( font, sizeof(bdf_font_t) ) ) return BDF_OUT_OF_MEMORY; p->font = font; font->memory = p->memory; p->memory = 0; /* if (font == 0) { fprintf(stderr,"failed font\n"); }*/ /* XXX */ { /* setup */ unsigned long i; bdf_property_t *prop; hash_init(&(font->proptbl), memory); for (i = 0, prop = _bdf_properties; i < _num_bdf_properties; i++, prop++) hash_insert(prop->name, (void *) i, &(font->proptbl) , memory); } if ( FT_ALLOC ( p->font->internal , sizeof(hashtable) ) ) return BDF_OUT_OF_MEMORY; hash_init((hashtable *) p->font->internal,memory); p->font->spacing = p->opts->font_spacing; p->font->default_glyph = -1; return 0; } /* * Check for the start of the properties. */ if (ft_memcmp(line, "STARTPROPERTIES", 15) == 0) { _bdf_split(" +", line, linelen, &p->list, memory); p->cnt = p->font->props_size = _bdf_atoul(p->list.field[1], 0, 10); if ( FT_ALLOC ( p->font->props , (sizeof(bdf_property_t) * p->cnt) ) ) return BDF_OUT_OF_MEMORY; p->flags |= _BDF_PROPS; *next = _bdf_parse_properties; return 0; } /* * Check for the FONTBOUNDINGBOX field. */ if (ft_memcmp(line, "FONTBOUNDINGBOX", 15) == 0) { if (!(p->flags & _BDF_SIZE)) { /* * Missing the SIZE field. */ sprintf(nbuf, ERRMSG1, lineno, "SIZE"); _bdf_add_acmsg(p->font, nbuf, ft_strlen(nbuf)); return BDF_MISSING_SIZE; } _bdf_split(" +", line, linelen, &p->list , memory); p->font->bbx.width = _bdf_atos(p->list.field[1], 0, 10); p->font->bbx.height = _bdf_atos(p->list.field[2], 0, 10); p->font->bbx.x_offset = _bdf_atos(p->list.field[3], 0, 10); p->font->bbx.y_offset = _bdf_atos(p->list.field[4], 0, 10); p->font->bbx.ascent = p->font->bbx.height + p->font->bbx.y_offset; p->font->bbx.descent = -p->font->bbx.y_offset; p->flags |= _BDF_FONT_BBX; return 0; } /* * The next thing to check for is the FONT field. */ if (ft_memcmp(line, "FONT", 4) == 0) { _bdf_split(" +", line, linelen, &p->list , memory); _bdf_shift(1, &p->list); s = _bdf_join(' ', &slen, &p->list); if ( FT_ALLOC ( p->font->name , slen + 1 ) ) return BDF_OUT_OF_MEMORY; (void) ft_memcpy(p->font->name, s, slen + 1); /* * If the font name is an XLFD name, set the spacing to the one in the * font name. If there is no spacing fall back on the default. */ _bdf_set_default_spacing(p->font, p->opts); p->flags |= _BDF_FONT_NAME; return 0; } /* * Check for the SIZE field. */ if (ft_memcmp(line, "SIZE", 4) == 0) { if (!(p->flags & _BDF_FONT_NAME)) { /* * Missing the FONT field. */ sprintf(nbuf, ERRMSG1, lineno, "FONT"); _bdf_add_acmsg(p->font, nbuf, ft_strlen(nbuf)); return BDF_MISSING_FONTNAME; } _bdf_split(" +", line, linelen, &p->list, memory); p->font->point_size = _bdf_atoul(p->list.field[1], 0, 10); p->font->resolution_x = _bdf_atoul(p->list.field[2], 0, 10); p->font->resolution_y = _bdf_atoul(p->list.field[3], 0, 10); /* * Check for the bits per pixel field. */ if (p->list.used == 5) { p->font->bpp = _bdf_atos(p->list.field[4], 0, 10); if (p->font->bpp > 1 && (p->font->bpp & 1)) { /* * Move up to the next bits per pixel value if an odd number * is encountered. */ p->font->bpp++; if (p->font->bpp <= 4) { sprintf(nbuf, ACMSG11, p->font->bpp); _bdf_add_acmsg(p->font, nbuf, ft_strlen(nbuf)); } } if (p->font->bpp > 4) { sprintf(nbuf, ACMSG11, p->font->bpp); _bdf_add_acmsg(p->font, nbuf, ft_strlen(nbuf)); p->font->bpp = 4; } } else p->font->bpp = 1; p->flags |= _BDF_SIZE; return 0; } return BDF_INVALID_LINE; } /************************************************************************** * * API. * **************************************************************************/ FT_LOCAL_DEF( bdf_font_t* ) bdf_load_font( FT_Stream stream, FT_Memory extmemory, bdf_options_t* opts, bdf_callback_t callback, void* data) { int n; unsigned long lineno; char msgbuf[128]; _bdf_parse_t p; FT_Memory memory; FT_Error error; (void) ft_memset((char *) &p, 0, sizeof(_bdf_parse_t)); p.opts = (opts != 0) ? opts : &_bdf_opts; p.minlb = 32767; p.callback = callback; p.client_data = data; p.memory = extmemory; /* only during font creation */ n = _bdf_readstream(stream, _bdf_parse_start, (void *) &p, &lineno); if (p.font != 0) { /* * If the font is not proportional, set the fonts monowidth * field to the width of the font bounding box. */ memory = p.font->memory; if (p.font->spacing != BDF_PROPORTIONAL) p.font->monowidth = p.font->bbx.width; /* * If the number of glyphs loaded is not that of the original count, * indicate the difference. */ if (p.cnt != p.font->glyphs_used + p.font->unencoded_used) { sprintf(msgbuf, ACMSG15, p.cnt, p.font->glyphs_used + p.font->unencoded_used); _bdf_add_acmsg(p.font, msgbuf, ft_strlen(msgbuf)); p.font->modified = 1; } /* * Once the font has been loaded, adjust the overall font metrics if * necessary. */ if (p.opts->correct_metrics != 0 && (p.font->glyphs_used > 0 || p.font->unencoded_used > 0)) { if (p.maxrb - p.minlb != p.font->bbx.width) { sprintf(msgbuf, ACMSG3, p.font->bbx.width, p.maxrb - p.minlb); _bdf_add_acmsg(p.font, msgbuf, ft_strlen(msgbuf)); p.font->bbx.width = p.maxrb - p.minlb; p.font->modified = 1; } if (p.font->bbx.x_offset != p.minlb) { sprintf(msgbuf, ACMSG4, p.font->bbx.x_offset, p.minlb); _bdf_add_acmsg(p.font, msgbuf, ft_strlen(msgbuf)); p.font->bbx.x_offset = p.minlb; p.font->modified = 1; } if (p.font->bbx.ascent != p.maxas) { sprintf(msgbuf, ACMSG5, p.font->bbx.ascent, p.maxas); _bdf_add_acmsg(p.font, msgbuf, ft_strlen(msgbuf)); p.font->bbx.ascent = p.maxas; p.font->modified = 1; } if (p.font->bbx.descent != p.maxds) { sprintf(msgbuf, ACMSG6, p.font->bbx.descent, p.maxds); _bdf_add_acmsg(p.font, msgbuf, ft_strlen(msgbuf)); p.font->bbx.descent = p.maxds; p.font->bbx.y_offset = -p.maxds; p.font->modified = 1; } if (p.maxas + p.maxds != p.font->bbx.height) { sprintf(msgbuf, ACMSG7, p.font->bbx.height, p.maxas + p.maxds); _bdf_add_acmsg(p.font, msgbuf, ft_strlen(msgbuf)); } p.font->bbx.height = p.maxas + p.maxds; if (p.flags & _BDF_SWIDTH_ADJ) _bdf_add_acmsg(p.font, ACMSG8, ft_strlen(ACMSG8)); } } /* * Last, if an error happened during loading, handle the messages. */ if (n < 0 && callback != 0) { /* * An error was returned. Alert the client. */ p.cb.reason = BDF_ERROR; p.cb.errlineno = lineno; (*callback)(&p.cb, data); } else if (p.flags & _BDF_START) { if (p.font != 0) { /* * The ENDFONT field was never reached or did not exist. */ if (!(p.flags & _BDF_GLYPHS)) /* * Error happened while parsing header. */ sprintf(msgbuf, ERRMSG2, lineno); else /* * Error happened when parsing glyphs. */ sprintf(msgbuf, ERRMSG3, lineno); _bdf_add_acmsg(p.font, msgbuf, ft_strlen(msgbuf)); } if (callback != 0) { p.cb.reason = BDF_ERROR; p.cb.errlineno = lineno; (*callback)(&p.cb, data); } } else if (callback != 0) { /* * This forces the progress bar to always finish. */ p.cb.current = p.cb.total; (*p.callback)(&p.cb, p.client_data); } /* * Free up the list used during the parsing. */ if (p.list.size > 0) FT_FREE( p.list.field ); if (p.font != 0) { /* * Make sure the comments are NULL terminated if they exist. */ memory = p.font->memory; if (p.font->comments_len > 0) { if ( FT_REALLOC ( p.font->comments , p.font->comments_len , p.font->comments_len + 1 ) ) return 0; p.font->comments[p.font->comments_len] = 0; } /* * Make sure the auto-correct messages are NULL terminated if they * exist. */ if (p.font->acmsgs_len > 0) { memory = p.font->memory; if ( FT_REALLOC ( p.font->acmsgs , p.font->acmsgs_len , p.font->acmsgs_len + 1 ) ) return 0; p.font->acmsgs[p.font->acmsgs_len] = 0; } } return p.font; } FT_LOCAL_DEF( void ) bdf_free_font( bdf_font_t *font ) { bdf_property_t *prop; unsigned long i; bdf_glyph_t *glyphs; FT_Memory memory; if (font == 0) return; memory = font->memory; if (font->name != 0) FT_FREE(font->name); /* * Free up the internal hash table of property names. */ if (font->internal) { hash_free((hashtable *) font->internal, memory); FT_FREE(font->internal); } /* * Free up the comment info. */ if (font->comments_len > 0) FT_FREE(font->comments); /* * Free up the auto-correction messages. */ if (font->acmsgs_len > 0) FT_FREE(font->acmsgs); /* * Free up the properties. */ for (i = 0; i < font->props_size; i++) { if (font->props[i].format == BDF_ATOM && font->props[i].value.atom) FT_FREE(font->props[i].value.atom); } if (font->props_size > 0 && font->props != 0) FT_FREE(font->props); /* * Free up the character info. */ for (i = 0, glyphs = font->glyphs; i < font->glyphs_used; i++, glyphs++) { if (glyphs->name) FT_FREE(glyphs->name); if (glyphs->bytes > 0 && glyphs->bitmap != 0) FT_FREE(glyphs->bitmap); } for (i = 0, glyphs = font->unencoded; i < font->unencoded_used; i++, glyphs++) { if (glyphs->name) FT_FREE(glyphs->name); if (glyphs->bytes > 0) FT_FREE(glyphs->bitmap); } if (font->glyphs_size > 0) FT_FREE( font->glyphs); if (font->unencoded_size > 0) FT_FREE( font->unencoded); /* * Free up the overflow storage if it was used. */ for (i = 0, glyphs = font->overflow.glyphs; i < font->overflow.glyphs_used; i++, glyphs++) { if (glyphs->name != 0) FT_FREE(glyphs->name); if (glyphs->bytes > 0) FT_FREE( glyphs->bitmap);; } if (font->overflow.glyphs_size > 0) FT_FREE(font->overflow.glyphs); /* bdf_cleanup */ hash_free(&(font->proptbl),memory); /* * Free up the user defined properties. */ for (prop = font->user_props, i = 0; i < font->nuser_props; i++, prop++) { FT_FREE(prop->name); if (prop->format == BDF_ATOM && prop->value.atom != 0) FT_FREE(prop->value.atom); } if (font->nuser_props > 0) FT_FREE(font->user_props); /*FREE( font);*/ /* XXX Fixme */ } FT_LOCAL_DEF( bdf_property_t* ) bdf_get_font_property( bdf_font_t* font, char* name) { hashnode hn; if (font == 0 || font->props_size == 0 || name == 0 || *name == 0) return 0; hn = hash_lookup(name, (hashtable *) font->internal); return (hn) ? (font->props + ((unsigned long) hn->data)) : 0; }