private sync
[framework/graphics/freetype.git] / src / bdf / bdflib.c
1 /*
2  * Copyright 2000 Computing Research Labs, New Mexico State University
3  * Copyright 2001-2012
4  *   Francesco Zappa Nardelli
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
21  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24
25   /*************************************************************************/
26   /*                                                                       */
27   /*  This file is based on bdf.c,v 1.22 2000/03/16 20:08:50               */
28   /*                                                                       */
29   /*  taken from Mark Leisher's xmbdfed package                            */
30   /*                                                                       */
31   /*************************************************************************/
32
33
34 #include <ft2build.h>
35
36 #include FT_FREETYPE_H
37 #include FT_INTERNAL_DEBUG_H
38 #include FT_INTERNAL_STREAM_H
39 #include FT_INTERNAL_OBJECTS_H
40
41 #include "bdf.h"
42 #include "bdferror.h"
43
44
45   /*************************************************************************/
46   /*                                                                       */
47   /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
48   /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
49   /* messages during execution.                                            */
50   /*                                                                       */
51 #undef  FT_COMPONENT
52 #define FT_COMPONENT  trace_bdflib
53
54
55   /*************************************************************************/
56   /*                                                                       */
57   /* Default BDF font options.                                             */
58   /*                                                                       */
59   /*************************************************************************/
60
61
62   static const bdf_options_t  _bdf_opts =
63   {
64     1,                /* Correct metrics.               */
65     1,                /* Preserve unencoded glyphs.     */
66     0,                /* Preserve comments.             */
67     BDF_PROPORTIONAL  /* Default spacing.               */
68   };
69
70
71   /*************************************************************************/
72   /*                                                                       */
73   /* Builtin BDF font properties.                                          */
74   /*                                                                       */
75   /*************************************************************************/
76
77   /* List of most properties that might appear in a font.  Doesn't include */
78   /* the RAW_* and AXIS_* properties in X11R6 polymorphic fonts.           */
79
80   static const bdf_property_t  _bdf_properties[] =
81   {
82     { (char *)"ADD_STYLE_NAME",          BDF_ATOM,     1, { 0 } },
83     { (char *)"AVERAGE_WIDTH",           BDF_INTEGER,  1, { 0 } },
84     { (char *)"AVG_CAPITAL_WIDTH",       BDF_INTEGER,  1, { 0 } },
85     { (char *)"AVG_LOWERCASE_WIDTH",     BDF_INTEGER,  1, { 0 } },
86     { (char *)"CAP_HEIGHT",              BDF_INTEGER,  1, { 0 } },
87     { (char *)"CHARSET_COLLECTIONS",     BDF_ATOM,     1, { 0 } },
88     { (char *)"CHARSET_ENCODING",        BDF_ATOM,     1, { 0 } },
89     { (char *)"CHARSET_REGISTRY",        BDF_ATOM,     1, { 0 } },
90     { (char *)"COMMENT",                 BDF_ATOM,     1, { 0 } },
91     { (char *)"COPYRIGHT",               BDF_ATOM,     1, { 0 } },
92     { (char *)"DEFAULT_CHAR",            BDF_CARDINAL, 1, { 0 } },
93     { (char *)"DESTINATION",             BDF_CARDINAL, 1, { 0 } },
94     { (char *)"DEVICE_FONT_NAME",        BDF_ATOM,     1, { 0 } },
95     { (char *)"END_SPACE",               BDF_INTEGER,  1, { 0 } },
96     { (char *)"FACE_NAME",               BDF_ATOM,     1, { 0 } },
97     { (char *)"FAMILY_NAME",             BDF_ATOM,     1, { 0 } },
98     { (char *)"FIGURE_WIDTH",            BDF_INTEGER,  1, { 0 } },
99     { (char *)"FONT",                    BDF_ATOM,     1, { 0 } },
100     { (char *)"FONTNAME_REGISTRY",       BDF_ATOM,     1, { 0 } },
101     { (char *)"FONT_ASCENT",             BDF_INTEGER,  1, { 0 } },
102     { (char *)"FONT_DESCENT",            BDF_INTEGER,  1, { 0 } },
103     { (char *)"FOUNDRY",                 BDF_ATOM,     1, { 0 } },
104     { (char *)"FULL_NAME",               BDF_ATOM,     1, { 0 } },
105     { (char *)"ITALIC_ANGLE",            BDF_INTEGER,  1, { 0 } },
106     { (char *)"MAX_SPACE",               BDF_INTEGER,  1, { 0 } },
107     { (char *)"MIN_SPACE",               BDF_INTEGER,  1, { 0 } },
108     { (char *)"NORM_SPACE",              BDF_INTEGER,  1, { 0 } },
109     { (char *)"NOTICE",                  BDF_ATOM,     1, { 0 } },
110     { (char *)"PIXEL_SIZE",              BDF_INTEGER,  1, { 0 } },
111     { (char *)"POINT_SIZE",              BDF_INTEGER,  1, { 0 } },
112     { (char *)"QUAD_WIDTH",              BDF_INTEGER,  1, { 0 } },
113     { (char *)"RAW_ASCENT",              BDF_INTEGER,  1, { 0 } },
114     { (char *)"RAW_AVERAGE_WIDTH",       BDF_INTEGER,  1, { 0 } },
115     { (char *)"RAW_AVG_CAPITAL_WIDTH",   BDF_INTEGER,  1, { 0 } },
116     { (char *)"RAW_AVG_LOWERCASE_WIDTH", BDF_INTEGER,  1, { 0 } },
117     { (char *)"RAW_CAP_HEIGHT",          BDF_INTEGER,  1, { 0 } },
118     { (char *)"RAW_DESCENT",             BDF_INTEGER,  1, { 0 } },
119     { (char *)"RAW_END_SPACE",           BDF_INTEGER,  1, { 0 } },
120     { (char *)"RAW_FIGURE_WIDTH",        BDF_INTEGER,  1, { 0 } },
121     { (char *)"RAW_MAX_SPACE",           BDF_INTEGER,  1, { 0 } },
122     { (char *)"RAW_MIN_SPACE",           BDF_INTEGER,  1, { 0 } },
123     { (char *)"RAW_NORM_SPACE",          BDF_INTEGER,  1, { 0 } },
124     { (char *)"RAW_PIXEL_SIZE",          BDF_INTEGER,  1, { 0 } },
125     { (char *)"RAW_POINT_SIZE",          BDF_INTEGER,  1, { 0 } },
126     { (char *)"RAW_PIXELSIZE",           BDF_INTEGER,  1, { 0 } },
127     { (char *)"RAW_POINTSIZE",           BDF_INTEGER,  1, { 0 } },
128     { (char *)"RAW_QUAD_WIDTH",          BDF_INTEGER,  1, { 0 } },
129     { (char *)"RAW_SMALL_CAP_SIZE",      BDF_INTEGER,  1, { 0 } },
130     { (char *)"RAW_STRIKEOUT_ASCENT",    BDF_INTEGER,  1, { 0 } },
131     { (char *)"RAW_STRIKEOUT_DESCENT",   BDF_INTEGER,  1, { 0 } },
132     { (char *)"RAW_SUBSCRIPT_SIZE",      BDF_INTEGER,  1, { 0 } },
133     { (char *)"RAW_SUBSCRIPT_X",         BDF_INTEGER,  1, { 0 } },
134     { (char *)"RAW_SUBSCRIPT_Y",         BDF_INTEGER,  1, { 0 } },
135     { (char *)"RAW_SUPERSCRIPT_SIZE",    BDF_INTEGER,  1, { 0 } },
136     { (char *)"RAW_SUPERSCRIPT_X",       BDF_INTEGER,  1, { 0 } },
137     { (char *)"RAW_SUPERSCRIPT_Y",       BDF_INTEGER,  1, { 0 } },
138     { (char *)"RAW_UNDERLINE_POSITION",  BDF_INTEGER,  1, { 0 } },
139     { (char *)"RAW_UNDERLINE_THICKNESS", BDF_INTEGER,  1, { 0 } },
140     { (char *)"RAW_X_HEIGHT",            BDF_INTEGER,  1, { 0 } },
141     { (char *)"RELATIVE_SETWIDTH",       BDF_CARDINAL, 1, { 0 } },
142     { (char *)"RELATIVE_WEIGHT",         BDF_CARDINAL, 1, { 0 } },
143     { (char *)"RESOLUTION",              BDF_INTEGER,  1, { 0 } },
144     { (char *)"RESOLUTION_X",            BDF_CARDINAL, 1, { 0 } },
145     { (char *)"RESOLUTION_Y",            BDF_CARDINAL, 1, { 0 } },
146     { (char *)"SETWIDTH_NAME",           BDF_ATOM,     1, { 0 } },
147     { (char *)"SLANT",                   BDF_ATOM,     1, { 0 } },
148     { (char *)"SMALL_CAP_SIZE",          BDF_INTEGER,  1, { 0 } },
149     { (char *)"SPACING",                 BDF_ATOM,     1, { 0 } },
150     { (char *)"STRIKEOUT_ASCENT",        BDF_INTEGER,  1, { 0 } },
151     { (char *)"STRIKEOUT_DESCENT",       BDF_INTEGER,  1, { 0 } },
152     { (char *)"SUBSCRIPT_SIZE",          BDF_INTEGER,  1, { 0 } },
153     { (char *)"SUBSCRIPT_X",             BDF_INTEGER,  1, { 0 } },
154     { (char *)"SUBSCRIPT_Y",             BDF_INTEGER,  1, { 0 } },
155     { (char *)"SUPERSCRIPT_SIZE",        BDF_INTEGER,  1, { 0 } },
156     { (char *)"SUPERSCRIPT_X",           BDF_INTEGER,  1, { 0 } },
157     { (char *)"SUPERSCRIPT_Y",           BDF_INTEGER,  1, { 0 } },
158     { (char *)"UNDERLINE_POSITION",      BDF_INTEGER,  1, { 0 } },
159     { (char *)"UNDERLINE_THICKNESS",     BDF_INTEGER,  1, { 0 } },
160     { (char *)"WEIGHT",                  BDF_CARDINAL, 1, { 0 } },
161     { (char *)"WEIGHT_NAME",             BDF_ATOM,     1, { 0 } },
162     { (char *)"X_HEIGHT",                BDF_INTEGER,  1, { 0 } },
163     { (char *)"_MULE_BASELINE_OFFSET",   BDF_INTEGER,  1, { 0 } },
164     { (char *)"_MULE_RELATIVE_COMPOSE",  BDF_INTEGER,  1, { 0 } },
165   };
166
167   static const unsigned long
168   _num_bdf_properties = sizeof ( _bdf_properties ) /
169                         sizeof ( _bdf_properties[0] );
170
171
172   /* Auto correction messages. */
173 #define ACMSG1   "FONT_ASCENT property missing.  " \
174                  "Added `FONT_ASCENT %hd'.\n"
175 #define ACMSG2   "FONT_DESCENT property missing.  " \
176                  "Added `FONT_DESCENT %hd'.\n"
177 #define ACMSG3   "Font width != actual width.  Old: %hd New: %hd.\n"
178 #define ACMSG4   "Font left bearing != actual left bearing.  " \
179                  "Old: %hd New: %hd.\n"
180 #define ACMSG5   "Font ascent != actual ascent.  Old: %hd New: %hd.\n"
181 #define ACMSG6   "Font descent != actual descent.  Old: %hd New: %hd.\n"
182 #define ACMSG7   "Font height != actual height. Old: %hd New: %hd.\n"
183 #define ACMSG8   "Glyph scalable width (SWIDTH) adjustments made.\n"
184 #define ACMSG9   "SWIDTH field missing at line %ld.  Set automatically.\n"
185 #define ACMSG10  "DWIDTH field missing at line %ld.  Set to glyph width.\n"
186 #define ACMSG11  "SIZE bits per pixel field adjusted to %hd.\n"
187 #define ACMSG12  "Duplicate encoding %ld (%s) changed to unencoded.\n"
188 #define ACMSG13  "Glyph %ld extra rows removed.\n"
189 #define ACMSG14  "Glyph %ld extra columns removed.\n"
190 #define ACMSG15  "Incorrect glyph count: %ld indicated but %ld found.\n"
191 #define ACMSG16  "Glyph %ld missing columns padded with zero bits.\n"
192
193   /* Error messages. */
194 #define ERRMSG1  "[line %ld] Missing `%s' line.\n"
195 #define ERRMSG2  "[line %ld] Font header corrupted or missing fields.\n"
196 #define ERRMSG3  "[line %ld] Font glyphs corrupted or missing fields.\n"
197 #define ERRMSG4  "[line %ld] BBX too big.\n"
198 #define ERRMSG5  "[line %ld] `%s' value too big.\n"
199 #define ERRMSG6  "[line %ld] Input line too long.\n"
200 #define ERRMSG7  "[line %ld] Font name too long.\n"
201 #define ERRMSG8  "[line %ld] Invalid `%s' value.\n"
202 #define ERRMSG9  "[line %ld] Invalid keyword.\n"
203
204   /* Debug messages. */
205 #define DBGMSG1  "  [%6ld] %s" /* no \n */
206 #define DBGMSG2  " (0x%lX)\n"
207
208
209   /*************************************************************************/
210   /*                                                                       */
211   /* Hash table utilities for the properties.                              */
212   /*                                                                       */
213   /*************************************************************************/
214
215   /* XXX: Replace this with FreeType's hash functions */
216
217
218 #define INITIAL_HT_SIZE  241
219
220   typedef void
221   (*hash_free_func)( hashnode  node );
222
223   static hashnode*
224   hash_bucket( const char*  key,
225                hashtable*   ht )
226   {
227     const char*    kp  = key;
228     unsigned long  res = 0;
229     hashnode*      bp  = ht->table, *ndp;
230
231
232     /* Mocklisp hash function. */
233     while ( *kp )
234       res = ( res << 5 ) - res + *kp++;
235
236     ndp = bp + ( res % ht->size );
237     while ( *ndp )
238     {
239       kp = (*ndp)->key;
240       if ( kp[0] == key[0] && ft_strcmp( kp, key ) == 0 )
241         break;
242       ndp--;
243       if ( ndp < bp )
244         ndp = bp + ( ht->size - 1 );
245     }
246
247     return ndp;
248   }
249
250
251   static FT_Error
252   hash_rehash( hashtable*  ht,
253                FT_Memory   memory )
254   {
255     hashnode*  obp = ht->table, *bp, *nbp;
256     int        i, sz = ht->size;
257     FT_Error   error = BDF_Err_Ok;
258
259
260     ht->size <<= 1;
261     ht->limit  = ht->size / 3;
262
263     if ( FT_NEW_ARRAY( ht->table, ht->size ) )
264       goto Exit;
265
266     for ( i = 0, bp = obp; i < sz; i++, bp++ )
267     {
268       if ( *bp )
269       {
270         nbp = hash_bucket( (*bp)->key, ht );
271         *nbp = *bp;
272       }
273     }
274     FT_FREE( obp );
275
276   Exit:
277     return error;
278   }
279
280
281   static FT_Error
282   hash_init( hashtable*  ht,
283              FT_Memory   memory )
284   {
285     int       sz = INITIAL_HT_SIZE;
286     FT_Error  error = BDF_Err_Ok;
287
288
289     ht->size  = sz;
290     ht->limit = sz / 3;
291     ht->used  = 0;
292
293     if ( FT_NEW_ARRAY( ht->table, sz ) )
294       goto Exit;
295
296   Exit:
297     return error;
298   }
299
300
301   static void
302   hash_free( hashtable*  ht,
303              FT_Memory   memory )
304   {
305     if ( ht != 0 )
306     {
307       int        i, sz = ht->size;
308       hashnode*  bp = ht->table;
309
310
311       for ( i = 0; i < sz; i++, bp++ )
312         FT_FREE( *bp );
313
314       FT_FREE( ht->table );
315     }
316   }
317
318
319   static FT_Error
320   hash_insert( char*       key,
321                size_t      data,
322                hashtable*  ht,
323                FT_Memory   memory )
324   {
325     hashnode  nn, *bp = hash_bucket( key, ht );
326     FT_Error  error = BDF_Err_Ok;
327
328
329     nn = *bp;
330     if ( !nn )
331     {
332       if ( FT_NEW( nn ) )
333         goto Exit;
334       *bp = nn;
335
336       nn->key  = key;
337       nn->data = data;
338
339       if ( ht->used >= ht->limit )
340       {
341         error = hash_rehash( ht, memory );
342         if ( error )
343           goto Exit;
344       }
345       ht->used++;
346     }
347     else
348       nn->data = data;
349
350   Exit:
351     return error;
352   }
353
354
355   static hashnode
356   hash_lookup( const char* key,
357                hashtable*  ht )
358   {
359     hashnode *np = hash_bucket( key, ht );
360
361
362     return *np;
363   }
364
365
366   /*************************************************************************/
367   /*                                                                       */
368   /* Utility types and functions.                                          */
369   /*                                                                       */
370   /*************************************************************************/
371
372
373   /* Function type for parsing lines of a BDF font. */
374
375   typedef FT_Error
376   (*_bdf_line_func_t)( char*          line,
377                        unsigned long  linelen,
378                        unsigned long  lineno,
379                        void*          call_data,
380                        void*          client_data );
381
382
383   /* List structure for splitting lines into fields. */
384
385   typedef struct  _bdf_list_t_
386   {
387     char**         field;
388     unsigned long  size;
389     unsigned long  used;
390     FT_Memory      memory;
391
392   } _bdf_list_t;
393
394
395   /* Structure used while loading BDF fonts. */
396
397   typedef struct  _bdf_parse_t_
398   {
399     unsigned long   flags;
400     unsigned long   cnt;
401     unsigned long   row;
402
403     short           minlb;
404     short           maxlb;
405     short           maxrb;
406     short           maxas;
407     short           maxds;
408
409     short           rbearing;
410
411     char*           glyph_name;
412     long            glyph_enc;
413
414     bdf_font_t*     font;
415     bdf_options_t*  opts;
416
417     unsigned long   have[34816]; /* must be in sync with `nmod' and `umod' */
418                                  /* arrays from `bdf_font_t' structure     */
419     _bdf_list_t     list;
420
421     FT_Memory       memory;
422
423   } _bdf_parse_t;
424
425
426 #define setsbit( m, cc ) \
427           ( m[(FT_Byte)(cc) >> 3] |= (FT_Byte)( 1 << ( (cc) & 7 ) ) )
428 #define sbitset( m, cc ) \
429           ( m[(FT_Byte)(cc) >> 3]  & ( 1 << ( (cc) & 7 ) ) )
430
431
432   static void
433   _bdf_list_init( _bdf_list_t*  list,
434                   FT_Memory     memory )
435   {
436     FT_ZERO( list );
437     list->memory = memory;
438   }
439
440
441   static void
442   _bdf_list_done( _bdf_list_t*  list )
443   {
444     FT_Memory  memory = list->memory;
445
446
447     if ( memory )
448     {
449       FT_FREE( list->field );
450       FT_ZERO( list );
451     }
452   }
453
454
455   static FT_Error
456   _bdf_list_ensure( _bdf_list_t*   list,
457                     unsigned long  num_items ) /* same as _bdf_list_t.used */
458   {
459     FT_Error  error = BDF_Err_Ok;
460
461
462     if ( num_items > list->size )
463     {
464       unsigned long  oldsize = list->size; /* same as _bdf_list_t.size */
465       unsigned long  newsize = oldsize + ( oldsize >> 1 ) + 5;
466       unsigned long  bigsize = (unsigned long)( FT_INT_MAX / sizeof ( char* ) );
467       FT_Memory      memory  = list->memory;
468
469
470       if ( oldsize == bigsize )
471       {
472         error = BDF_Err_Out_Of_Memory;
473         goto Exit;
474       }
475       else if ( newsize < oldsize || newsize > bigsize )
476         newsize = bigsize;
477
478       if ( FT_RENEW_ARRAY( list->field, oldsize, newsize ) )
479         goto Exit;
480
481       list->size = newsize;
482     }
483
484   Exit:
485     return error;
486   }
487
488
489   static void
490   _bdf_list_shift( _bdf_list_t*   list,
491                    unsigned long  n )
492   {
493     unsigned long  i, u;
494
495
496     if ( list == 0 || list->used == 0 || n == 0 )
497       return;
498
499     if ( n >= list->used )
500     {
501       list->used = 0;
502       return;
503     }
504
505     for ( u = n, i = 0; u < list->used; i++, u++ )
506       list->field[i] = list->field[u];
507     list->used -= n;
508   }
509
510
511   /* An empty string for empty fields. */
512
513   static const char  empty[1] = { 0 };      /* XXX eliminate this */
514
515
516   static char *
517   _bdf_list_join( _bdf_list_t*    list,
518                   int             c,
519                   unsigned long  *alen )
520   {
521     unsigned long  i, j;
522     char           *fp, *dp;
523
524
525     *alen = 0;
526
527     if ( list == 0 || list->used == 0 )
528       return 0;
529
530     dp = list->field[0];
531     for ( i = j = 0; i < list->used; i++ )
532     {
533       fp = list->field[i];
534       while ( *fp )
535         dp[j++] = *fp++;
536
537       if ( i + 1 < list->used )
538         dp[j++] = (char)c;
539     }
540     if ( dp != empty )
541       dp[j] = 0;
542
543     *alen = j;
544     return dp;
545   }
546
547
548   /* The code below ensures that we have at least 4 + 1 `field' */
549   /* elements in `list' (which are possibly NULL) so that we    */
550   /* don't have to check the number of fields in most cases.    */
551
552   static FT_Error
553   _bdf_list_split( _bdf_list_t*   list,
554                    char*          separators,
555                    char*          line,
556                    unsigned long  linelen )
557   {
558     int       mult, final_empty;
559     char      *sp, *ep, *end;
560     char      seps[32];
561     FT_Error  error = BDF_Err_Ok;
562
563
564     /* Initialize the list. */
565     list->used = 0;
566     if ( list->size )
567     {
568       list->field[0] = (char*)empty;
569       list->field[1] = (char*)empty;
570       list->field[2] = (char*)empty;
571       list->field[3] = (char*)empty;
572     }
573
574     /* If the line is empty, then simply return. */
575     if ( linelen == 0 || line[0] == 0 )
576       goto Exit;
577
578     /* In the original code, if the `separators' parameter is NULL or */
579     /* empty, the list is split into individual bytes.  We don't need */
580     /* this, so an error is signaled.                                 */
581     if ( separators == 0 || *separators == 0 )
582     {
583       error = BDF_Err_Invalid_Argument;
584       goto Exit;
585     }
586
587     /* Prepare the separator bitmap. */
588     FT_MEM_ZERO( seps, 32 );
589
590     /* If the very last character of the separator string is a plus, then */
591     /* set the `mult' flag to indicate that multiple separators should be */
592     /* collapsed into one.                                                */
593     for ( mult = 0, sp = separators; sp && *sp; sp++ )
594     {
595       if ( *sp == '+' && *( sp + 1 ) == 0 )
596         mult = 1;
597       else
598         setsbit( seps, *sp );
599     }
600
601     /* Break the line up into fields. */
602     for ( final_empty = 0, sp = ep = line, end = sp + linelen;
603           sp < end && *sp; )
604     {
605       /* Collect everything that is not a separator. */
606       for ( ; *ep && !sbitset( seps, *ep ); ep++ )
607         ;
608
609       /* Resize the list if necessary. */
610       if ( list->used == list->size )
611       {
612         error = _bdf_list_ensure( list, list->used + 1 );
613         if ( error )
614           goto Exit;
615       }
616
617       /* Assign the field appropriately. */
618       list->field[list->used++] = ( ep > sp ) ? sp : (char*)empty;
619
620       sp = ep;
621
622       if ( mult )
623       {
624         /* If multiple separators should be collapsed, do it now by */
625         /* setting all the separator characters to 0.               */
626         for ( ; *ep && sbitset( seps, *ep ); ep++ )
627           *ep = 0;
628       }
629       else if ( *ep != 0 )
630         /* Don't collapse multiple separators by making them 0, so just */
631         /* make the one encountered 0.                                  */
632         *ep++ = 0;
633
634       final_empty = ( ep > sp && *ep == 0 );
635       sp = ep;
636     }
637
638     /* Finally, NULL-terminate the list. */
639     if ( list->used + final_empty >= list->size )
640     {
641       error = _bdf_list_ensure( list, list->used + final_empty + 1 );
642       if ( error )
643         goto Exit;
644     }
645
646     if ( final_empty )
647       list->field[list->used++] = (char*)empty;
648
649     list->field[list->used] = 0;
650
651   Exit:
652     return error;
653   }
654
655
656 #define NO_SKIP  256  /* this value cannot be stored in a 'char' */
657
658
659   static FT_Error
660   _bdf_readstream( FT_Stream         stream,
661                    _bdf_line_func_t  callback,
662                    void*             client_data,
663                    unsigned long    *lno )
664   {
665     _bdf_line_func_t  cb;
666     unsigned long     lineno, buf_size;
667     int               refill, hold, to_skip;
668     ptrdiff_t         bytes, start, end, cursor, avail;
669     char*             buf = 0;
670     FT_Memory         memory = stream->memory;
671     FT_Error          error = BDF_Err_Ok;
672
673
674     if ( callback == 0 )
675     {
676       error = BDF_Err_Invalid_Argument;
677       goto Exit;
678     }
679
680     /* initial size and allocation of the input buffer */
681     buf_size = 1024;
682
683     if ( FT_NEW_ARRAY( buf, buf_size ) )
684       goto Exit;
685
686     cb      = callback;
687     lineno  = 1;
688     buf[0]  = 0;
689     start   = 0;
690     end     = 0;
691     avail   = 0;
692     cursor  = 0;
693     refill  = 1;
694     to_skip = NO_SKIP;
695     bytes   = 0;        /* make compiler happy */
696
697     for (;;)
698     {
699       if ( refill )
700       {
701         bytes  = (ptrdiff_t)FT_Stream_TryRead(
702                    stream, (FT_Byte*)buf + cursor,
703                    (FT_ULong)( buf_size - cursor ) );
704         avail  = cursor + bytes;
705         cursor = 0;
706         refill = 0;
707       }
708
709       end = start;
710
711       /* should we skip an optional character like \n or \r? */
712       if ( start < avail && buf[start] == to_skip )
713       {
714         start  += 1;
715         to_skip = NO_SKIP;
716         continue;
717       }
718
719       /* try to find the end of the line */
720       while ( end < avail && buf[end] != '\n' && buf[end] != '\r' )
721         end++;
722
723       /* if we hit the end of the buffer, try shifting its content */
724       /* or even resizing it                                       */
725       if ( end >= avail )
726       {
727         if ( bytes == 0 )  /* last line in file doesn't end in \r or \n */
728           break;           /* ignore it then exit                       */
729
730         if ( start == 0 )
731         {
732           /* this line is definitely too long; try resizing the input */
733           /* buffer a bit to handle it.                               */
734           FT_ULong  new_size;
735
736
737           if ( buf_size >= 65536UL )  /* limit ourselves to 64KByte */
738           {
739             FT_ERROR(( "_bdf_readstream: " ERRMSG6, lineno ));
740             error = BDF_Err_Invalid_Argument;
741             goto Exit;
742           }
743
744           new_size = buf_size * 2;
745           if ( FT_RENEW_ARRAY( buf, buf_size, new_size ) )
746             goto Exit;
747
748           cursor   = buf_size;
749           buf_size = new_size;
750         }
751         else
752         {
753           bytes = avail - start;
754
755           FT_MEM_COPY( buf, buf + start, bytes );
756
757           cursor = bytes;
758           avail -= bytes;
759           start  = 0;
760         }
761         refill = 1;
762         continue;
763       }
764
765       /* Temporarily NUL-terminate the line. */
766       hold     = buf[end];
767       buf[end] = 0;
768
769       /* XXX: Use encoding independent value for 0x1a */
770       if ( buf[start] != '#' && buf[start] != 0x1a && end > start )
771       {
772         error = (*cb)( buf + start, end - start, lineno,
773                        (void*)&cb, client_data );
774         /* Redo if we have encountered CHARS without properties. */
775         if ( error == -1 )
776           error = (*cb)( buf + start, end - start, lineno,
777                          (void*)&cb, client_data );
778         if ( error )
779           break;
780       }
781
782       lineno  += 1;
783       buf[end] = (char)hold;
784       start    = end + 1;
785
786       if ( hold == '\n' )
787         to_skip = '\r';
788       else if ( hold == '\r' )
789         to_skip = '\n';
790       else
791         to_skip = NO_SKIP;
792     }
793
794     *lno = lineno;
795
796   Exit:
797     FT_FREE( buf );
798     return error;
799   }
800
801
802   /* XXX: make this work with EBCDIC also */
803
804   static const unsigned char  a2i[128] =
805   {
806     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
807     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
808     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
809     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
810     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00,
811     0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
812     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
813     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
814     0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
815     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
816     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
817   };
818
819   static const unsigned char  odigits[32] =
820   {
821     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
822     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
823     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
824     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
825   };
826
827   static const unsigned char  ddigits[32] =
828   {
829     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03,
830     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
831     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
832     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
833   };
834
835   static const unsigned char  hdigits[32] =
836   {
837     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03,
838     0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
839     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
840     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
841   };
842
843
844 #define isdigok( m, d )  (m[(d) >> 3] & ( 1 << ( (d) & 7 ) ) )
845
846
847   /* Routine to convert an ASCII string into an unsigned long integer. */
848   static unsigned long
849   _bdf_atoul( char*   s,
850               char**  end,
851               int     base )
852   {
853     unsigned long         v;
854     const unsigned char*  dmap;
855
856
857     if ( s == 0 || *s == 0 )
858       return 0;
859
860     /* Make sure the radix is something recognizable.  Default to 10. */
861     switch ( base )
862     {
863     case 8:
864       dmap = odigits;
865       break;
866     case 16:
867       dmap = hdigits;
868       break;
869     default:
870       base = 10;
871       dmap = ddigits;
872       break;
873     }
874
875     /* Check for the special hex prefix. */
876     if ( *s == '0'                                  &&
877          ( *( s + 1 ) == 'x' || *( s + 1 ) == 'X' ) )
878     {
879       base = 16;
880       dmap = hdigits;
881       s   += 2;
882     }
883
884     for ( v = 0; isdigok( dmap, *s ); s++ )
885       v = v * base + a2i[(int)*s];
886
887     if ( end != 0 )
888       *end = s;
889
890     return v;
891   }
892
893
894   /* Routine to convert an ASCII string into an signed long integer. */
895   static long
896   _bdf_atol( char*   s,
897              char**  end,
898              int     base )
899   {
900     long                  v, neg;
901     const unsigned char*  dmap;
902
903
904     if ( s == 0 || *s == 0 )
905       return 0;
906
907     /* Make sure the radix is something recognizable.  Default to 10. */
908     switch ( base )
909     {
910     case 8:
911       dmap = odigits;
912       break;
913     case 16:
914       dmap = hdigits;
915       break;
916     default:
917       base = 10;
918       dmap = ddigits;
919       break;
920     }
921
922     /* Check for a minus sign. */
923     neg = 0;
924     if ( *s == '-' )
925     {
926       s++;
927       neg = 1;
928     }
929
930     /* Check for the special hex prefix. */
931     if ( *s == '0'                                  &&
932          ( *( s + 1 ) == 'x' || *( s + 1 ) == 'X' ) )
933     {
934       base = 16;
935       dmap = hdigits;
936       s   += 2;
937     }
938
939     for ( v = 0; isdigok( dmap, *s ); s++ )
940       v = v * base + a2i[(int)*s];
941
942     if ( end != 0 )
943       *end = s;
944
945     return ( !neg ) ? v : -v;
946   }
947
948
949   /* Routine to convert an ASCII string into an signed short integer. */
950   static short
951   _bdf_atos( char*   s,
952              char**  end,
953              int     base )
954   {
955     short                 v, neg;
956     const unsigned char*  dmap;
957
958
959     if ( s == 0 || *s == 0 )
960       return 0;
961
962     /* Make sure the radix is something recognizable.  Default to 10. */
963     switch ( base )
964     {
965     case 8:
966       dmap = odigits;
967       break;
968     case 16:
969       dmap = hdigits;
970       break;
971     default:
972       base = 10;
973       dmap = ddigits;
974       break;
975     }
976
977     /* Check for a minus. */
978     neg = 0;
979     if ( *s == '-' )
980     {
981       s++;
982       neg = 1;
983     }
984
985     /* Check for the special hex prefix. */
986     if ( *s == '0'                                  &&
987          ( *( s + 1 ) == 'x' || *( s + 1 ) == 'X' ) )
988     {
989       base = 16;
990       dmap = hdigits;
991       s   += 2;
992     }
993
994     for ( v = 0; isdigok( dmap, *s ); s++ )
995       v = (short)( v * base + a2i[(int)*s] );
996
997     if ( end != 0 )
998       *end = s;
999
1000     return (short)( ( !neg ) ? v : -v );
1001   }
1002
1003
1004   /* Routine to compare two glyphs by encoding so they can be sorted. */
1005   static int
1006   by_encoding( const void*  a,
1007                const void*  b )
1008   {
1009     bdf_glyph_t  *c1, *c2;
1010
1011
1012     c1 = (bdf_glyph_t *)a;
1013     c2 = (bdf_glyph_t *)b;
1014
1015     if ( c1->encoding < c2->encoding )
1016       return -1;
1017
1018     if ( c1->encoding > c2->encoding )
1019       return 1;
1020
1021     return 0;
1022   }
1023
1024
1025   static FT_Error
1026   bdf_create_property( char*        name,
1027                        int          format,
1028                        bdf_font_t*  font )
1029   {
1030     size_t           n;
1031     bdf_property_t*  p;
1032     FT_Memory        memory = font->memory;
1033     FT_Error         error = BDF_Err_Ok;
1034
1035
1036     /* First check whether the property has        */
1037     /* already been added or not.  If it has, then */
1038     /* simply ignore it.                           */
1039     if ( hash_lookup( name, &(font->proptbl) ) )
1040       goto Exit;
1041
1042     if ( FT_RENEW_ARRAY( font->user_props,
1043                          font->nuser_props,
1044                          font->nuser_props + 1 ) )
1045       goto Exit;
1046
1047     p = font->user_props + font->nuser_props;
1048     FT_ZERO( p );
1049
1050     n = ft_strlen( name ) + 1;
1051     if ( n > FT_ULONG_MAX )
1052       return BDF_Err_Invalid_Argument;
1053
1054     if ( FT_NEW_ARRAY( p->name, n ) )
1055       goto Exit;
1056
1057     FT_MEM_COPY( (char *)p->name, name, n );
1058
1059     p->format  = format;
1060     p->builtin = 0;
1061
1062     n = _num_bdf_properties + font->nuser_props;
1063
1064     error = hash_insert( p->name, n, &(font->proptbl), memory );
1065     if ( error )
1066       goto Exit;
1067
1068     font->nuser_props++;
1069
1070   Exit:
1071     return error;
1072   }
1073
1074
1075   FT_LOCAL_DEF( bdf_property_t * )
1076   bdf_get_property( char*        name,
1077                     bdf_font_t*  font )
1078   {
1079     hashnode  hn;
1080     size_t    propid;
1081
1082
1083     if ( name == 0 || *name == 0 )
1084       return 0;
1085
1086     if ( ( hn = hash_lookup( name, &(font->proptbl) ) ) == 0 )
1087       return 0;
1088
1089     propid = hn->data;
1090     if ( propid >= _num_bdf_properties )
1091       return font->user_props + ( propid - _num_bdf_properties );
1092
1093     return (bdf_property_t*)_bdf_properties + propid;
1094   }
1095
1096
1097   /*************************************************************************/
1098   /*                                                                       */
1099   /* BDF font file parsing flags and functions.                            */
1100   /*                                                                       */
1101   /*************************************************************************/
1102
1103
1104   /* Parse flags. */
1105
1106 #define _BDF_START      0x0001
1107 #define _BDF_FONT_NAME  0x0002
1108 #define _BDF_SIZE       0x0004
1109 #define _BDF_FONT_BBX   0x0008
1110 #define _BDF_PROPS      0x0010
1111 #define _BDF_GLYPHS     0x0020
1112 #define _BDF_GLYPH      0x0040
1113 #define _BDF_ENCODING   0x0080
1114 #define _BDF_SWIDTH     0x0100
1115 #define _BDF_DWIDTH     0x0200
1116 #define _BDF_BBX        0x0400
1117 #define _BDF_BITMAP     0x0800
1118
1119 #define _BDF_SWIDTH_ADJ  0x1000
1120
1121 #define _BDF_GLYPH_BITS ( _BDF_GLYPH    | \
1122                           _BDF_ENCODING | \
1123                           _BDF_SWIDTH   | \
1124                           _BDF_DWIDTH   | \
1125                           _BDF_BBX      | \
1126                           _BDF_BITMAP   )
1127
1128 #define _BDF_GLYPH_WIDTH_CHECK   0x40000000UL
1129 #define _BDF_GLYPH_HEIGHT_CHECK  0x80000000UL
1130
1131
1132   static FT_Error
1133   _bdf_add_comment( bdf_font_t*    font,
1134                     char*          comment,
1135                     unsigned long  len )
1136   {
1137     char*      cp;
1138     FT_Memory  memory = font->memory;
1139     FT_Error   error = BDF_Err_Ok;
1140
1141
1142     if ( FT_RENEW_ARRAY( font->comments,
1143                          font->comments_len,
1144                          font->comments_len + len + 1 ) )
1145       goto Exit;
1146
1147     cp = font->comments + font->comments_len;
1148
1149     FT_MEM_COPY( cp, comment, len );
1150     cp[len] = '\n';
1151
1152     font->comments_len += len + 1;
1153
1154   Exit:
1155     return error;
1156   }
1157
1158
1159   /* Set the spacing from the font name if it exists, or set it to the */
1160   /* default specified in the options.                                 */
1161   static FT_Error
1162   _bdf_set_default_spacing( bdf_font_t*     font,
1163                             bdf_options_t*  opts,
1164                             unsigned long   lineno )
1165   {
1166     size_t       len;
1167     char         name[256];
1168     _bdf_list_t  list;
1169     FT_Memory    memory;
1170     FT_Error     error = BDF_Err_Ok;
1171
1172
1173     if ( font == 0 || font->name == 0 || font->name[0] == 0 )
1174     {
1175       error = BDF_Err_Invalid_Argument;
1176       goto Exit;
1177     }
1178
1179     memory = font->memory;
1180
1181     _bdf_list_init( &list, memory );
1182
1183     font->spacing = opts->font_spacing;
1184
1185     len = ft_strlen( font->name ) + 1;
1186     /* Limit ourselves to 256 characters in the font name. */
1187     if ( len >= 256 )
1188     {
1189       FT_ERROR(( "_bdf_set_default_spacing: " ERRMSG7, lineno ));
1190       error = BDF_Err_Invalid_Argument;
1191       goto Exit;
1192     }
1193
1194     FT_MEM_COPY( name, font->name, len );
1195
1196     error = _bdf_list_split( &list, (char *)"-", name, len );
1197     if ( error )
1198       goto Fail;
1199
1200     if ( list.used == 15 )
1201     {
1202       switch ( list.field[11][0] )
1203       {
1204       case 'C':
1205       case 'c':
1206         font->spacing = BDF_CHARCELL;
1207         break;
1208       case 'M':
1209       case 'm':
1210         font->spacing = BDF_MONOWIDTH;
1211         break;
1212       case 'P':
1213       case 'p':
1214         font->spacing = BDF_PROPORTIONAL;
1215         break;
1216       }
1217     }
1218
1219   Fail:
1220     _bdf_list_done( &list );
1221
1222   Exit:
1223     return error;
1224   }
1225
1226
1227   /* Determine whether the property is an atom or not.  If it is, then */
1228   /* clean it up so the double quotes are removed if they exist.       */
1229   static int
1230   _bdf_is_atom( char*          line,
1231                 unsigned long  linelen,
1232                 char**         name,
1233                 char**         value,
1234                 bdf_font_t*    font )
1235   {
1236     int              hold;
1237     char             *sp, *ep;
1238     bdf_property_t*  p;
1239
1240
1241     *name = sp = ep = line;
1242
1243     while ( *ep && *ep != ' ' && *ep != '\t' )
1244       ep++;
1245
1246     hold = -1;
1247     if ( *ep )
1248     {
1249       hold = *ep;
1250       *ep  = 0;
1251     }
1252
1253     p = bdf_get_property( sp, font );
1254
1255     /* Restore the character that was saved before any return can happen. */
1256     if ( hold != -1 )
1257       *ep = (char)hold;
1258
1259     /* If the property exists and is not an atom, just return here. */
1260     if ( p && p->format != BDF_ATOM )
1261       return 0;
1262
1263     /* The property is an atom.  Trim all leading and trailing whitespace */
1264     /* and double quotes for the atom value.                              */
1265     sp = ep;
1266     ep = line + linelen;
1267
1268     /* Trim the leading whitespace if it exists. */
1269     if ( *sp )
1270       *sp++ = 0;
1271     while ( *sp                           &&
1272             ( *sp == ' ' || *sp == '\t' ) )
1273       sp++;
1274
1275     /* Trim the leading double quote if it exists. */
1276     if ( *sp == '"' )
1277       sp++;
1278     *value = sp;
1279
1280     /* Trim the trailing whitespace if it exists. */
1281     while ( ep > sp                                       &&
1282             ( *( ep - 1 ) == ' ' || *( ep - 1 ) == '\t' ) )
1283       *--ep = 0;
1284
1285     /* Trim the trailing double quote if it exists. */
1286     if ( ep > sp && *( ep - 1 ) == '"' )
1287       *--ep = 0;
1288
1289     return 1;
1290   }
1291
1292
1293   static FT_Error
1294   _bdf_add_property( bdf_font_t*    font,
1295                      char*          name,
1296                      char*          value,
1297                      unsigned long  lineno )
1298   {
1299     size_t          propid;
1300     hashnode        hn;
1301     bdf_property_t  *prop, *fp;
1302     FT_Memory       memory = font->memory;
1303     FT_Error        error = BDF_Err_Ok;
1304
1305
1306     /* First, check whether the property already exists in the font. */
1307     if ( ( hn = hash_lookup( name, (hashtable *)font->internal ) ) != 0 )
1308     {
1309       /* The property already exists in the font, so simply replace */
1310       /* the value of the property with the current value.          */
1311       fp = font->props + hn->data;
1312
1313       switch ( fp->format )
1314       {
1315       case BDF_ATOM:
1316         /* Delete the current atom if it exists. */
1317         FT_FREE( fp->value.atom );
1318
1319         if ( value && value[0] != 0 )
1320         {
1321           if ( FT_STRDUP( fp->value.atom, value ) )
1322             goto Exit;
1323         }
1324         break;
1325
1326       case BDF_INTEGER:
1327         fp->value.l = _bdf_atol( value, 0, 10 );
1328         break;
1329
1330       case BDF_CARDINAL:
1331         fp->value.ul = _bdf_atoul( value, 0, 10 );
1332         break;
1333
1334       default:
1335         ;
1336       }
1337
1338       goto Exit;
1339     }
1340
1341     /* See whether this property type exists yet or not. */
1342     /* If not, create it.                                */
1343     hn = hash_lookup( name, &(font->proptbl) );
1344     if ( hn == 0 )
1345     {
1346       error = bdf_create_property( name, BDF_ATOM, font );
1347       if ( error )
1348         goto Exit;
1349       hn = hash_lookup( name, &(font->proptbl) );
1350     }
1351
1352     /* Allocate another property if this is overflow. */
1353     if ( font->props_used == font->props_size )
1354     {
1355       if ( font->props_size == 0 )
1356       {
1357         if ( FT_NEW_ARRAY( font->props, 1 ) )
1358           goto Exit;
1359       }
1360       else
1361       {
1362         if ( FT_RENEW_ARRAY( font->props,
1363                              font->props_size,
1364                              font->props_size + 1 ) )
1365           goto Exit;
1366       }
1367
1368       fp = font->props + font->props_size;
1369       FT_MEM_ZERO( fp, sizeof ( bdf_property_t ) );
1370       font->props_size++;
1371     }
1372
1373     propid = hn->data;
1374     if ( propid >= _num_bdf_properties )
1375       prop = font->user_props + ( propid - _num_bdf_properties );
1376     else
1377       prop = (bdf_property_t*)_bdf_properties + propid;
1378
1379     fp = font->props + font->props_used;
1380
1381     fp->name    = prop->name;
1382     fp->format  = prop->format;
1383     fp->builtin = prop->builtin;
1384
1385     switch ( prop->format )
1386     {
1387     case BDF_ATOM:
1388       fp->value.atom = 0;
1389       if ( value != 0 && value[0] )
1390       {
1391         if ( FT_STRDUP( fp->value.atom, value ) )
1392           goto Exit;
1393       }
1394       break;
1395
1396     case BDF_INTEGER:
1397       fp->value.l = _bdf_atol( value, 0, 10 );
1398       break;
1399
1400     case BDF_CARDINAL:
1401       fp->value.ul = _bdf_atoul( value, 0, 10 );
1402       break;
1403     }
1404
1405     /* If the property happens to be a comment, then it doesn't need */
1406     /* to be added to the internal hash table.                       */
1407     if ( ft_memcmp( name, "COMMENT", 7 ) != 0 )
1408     {
1409       /* Add the property to the font property table. */
1410       error = hash_insert( fp->name,
1411                            font->props_used,
1412                            (hashtable *)font->internal,
1413                            memory );
1414       if ( error )
1415         goto Exit;
1416     }
1417
1418     font->props_used++;
1419
1420     /* Some special cases need to be handled here.  The DEFAULT_CHAR       */
1421     /* property needs to be located if it exists in the property list, the */
1422     /* FONT_ASCENT and FONT_DESCENT need to be assigned if they are        */
1423     /* present, and the SPACING property should override the default       */
1424     /* spacing.                                                            */
1425     if ( ft_memcmp( name, "DEFAULT_CHAR", 12 ) == 0 )
1426       font->default_char = fp->value.l;
1427     else if ( ft_memcmp( name, "FONT_ASCENT", 11 ) == 0 )
1428       font->font_ascent = fp->value.l;
1429     else if ( ft_memcmp( name, "FONT_DESCENT", 12 ) == 0 )
1430       font->font_descent = fp->value.l;
1431     else if ( ft_memcmp( name, "SPACING", 7 ) == 0 )
1432     {
1433       if ( !fp->value.atom )
1434       {
1435         FT_ERROR(( "_bdf_add_property: " ERRMSG8, lineno, "SPACING" ));
1436         error = BDF_Err_Invalid_File_Format;
1437         goto Exit;
1438       }
1439
1440       if ( fp->value.atom[0] == 'p' || fp->value.atom[0] == 'P' )
1441         font->spacing = BDF_PROPORTIONAL;
1442       else if ( fp->value.atom[0] == 'm' || fp->value.atom[0] == 'M' )
1443         font->spacing = BDF_MONOWIDTH;
1444       else if ( fp->value.atom[0] == 'c' || fp->value.atom[0] == 'C' )
1445         font->spacing = BDF_CHARCELL;
1446     }
1447
1448   Exit:
1449     return error;
1450   }
1451
1452
1453   static const unsigned char nibble_mask[8] =
1454   {
1455     0xFF, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE
1456   };
1457
1458
1459   /* Actually parse the glyph info and bitmaps. */
1460   static FT_Error
1461   _bdf_parse_glyphs( char*          line,
1462                      unsigned long  linelen,
1463                      unsigned long  lineno,
1464                      void*          call_data,
1465                      void*          client_data )
1466   {
1467     int                c, mask_index;
1468     char*              s;
1469     unsigned char*     bp;
1470     unsigned long      i, slen, nibbles;
1471
1472     _bdf_parse_t*      p;
1473     bdf_glyph_t*       glyph;
1474     bdf_font_t*        font;
1475
1476     FT_Memory          memory;
1477     FT_Error           error = BDF_Err_Ok;
1478
1479     FT_UNUSED( call_data );
1480     FT_UNUSED( lineno );        /* only used in debug mode */
1481
1482
1483     p = (_bdf_parse_t *)client_data;
1484
1485     font   = p->font;
1486     memory = font->memory;
1487
1488     /* Check for a comment. */
1489     if ( ft_memcmp( line, "COMMENT", 7 ) == 0 )
1490     {
1491       linelen -= 7;
1492
1493       s = line + 7;
1494       if ( *s != 0 )
1495       {
1496         s++;
1497         linelen--;
1498       }
1499       error = _bdf_add_comment( p->font, s, linelen );
1500       goto Exit;
1501     }
1502
1503     /* The very first thing expected is the number of glyphs. */
1504     if ( !( p->flags & _BDF_GLYPHS ) )
1505     {
1506       if ( ft_memcmp( line, "CHARS", 5 ) != 0 )
1507       {
1508         FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG1, lineno, "CHARS" ));
1509         error = BDF_Err_Missing_Chars_Field;
1510         goto Exit;
1511       }
1512
1513       error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
1514       if ( error )
1515         goto Exit;
1516       p->cnt = font->glyphs_size = _bdf_atoul( p->list.field[1], 0, 10 );
1517
1518       /* Make sure the number of glyphs is non-zero. */
1519       if ( p->cnt == 0 )
1520         font->glyphs_size = 64;
1521
1522       /* Limit ourselves to 1,114,112 glyphs in the font (this is the */
1523       /* number of code points available in Unicode).                 */
1524       if ( p->cnt >= 0x110000UL )
1525       {
1526         FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG5, lineno, "CHARS" ));
1527         error = BDF_Err_Invalid_Argument;
1528         goto Exit;
1529       }
1530
1531       if ( FT_NEW_ARRAY( font->glyphs, font->glyphs_size ) )
1532         goto Exit;
1533
1534       p->flags |= _BDF_GLYPHS;
1535
1536       goto Exit;
1537     }
1538
1539     /* Check for the ENDFONT field. */
1540     if ( ft_memcmp( line, "ENDFONT", 7 ) == 0 )
1541     {
1542       /* Sort the glyphs by encoding. */
1543       ft_qsort( (char *)font->glyphs,
1544                 font->glyphs_used,
1545                 sizeof ( bdf_glyph_t ),
1546                 by_encoding );
1547
1548       p->flags &= ~_BDF_START;
1549
1550       goto Exit;
1551     }
1552
1553     /* Check for the ENDCHAR field. */
1554     if ( ft_memcmp( line, "ENDCHAR", 7 ) == 0 )
1555     {
1556       p->glyph_enc = 0;
1557       p->flags    &= ~_BDF_GLYPH_BITS;
1558
1559       goto Exit;
1560     }
1561
1562     /* Check whether a glyph is being scanned but should be */
1563     /* ignored because it is an unencoded glyph.            */
1564     if ( ( p->flags & _BDF_GLYPH )     &&
1565          p->glyph_enc            == -1 &&
1566          p->opts->keep_unencoded == 0  )
1567       goto Exit;
1568
1569     /* Check for the STARTCHAR field. */
1570     if ( ft_memcmp( line, "STARTCHAR", 9 ) == 0 )
1571     {
1572       /* Set the character name in the parse info first until the */
1573       /* encoding can be checked for an unencoded character.      */
1574       FT_FREE( p->glyph_name );
1575
1576       error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
1577       if ( error )
1578         goto Exit;
1579
1580       _bdf_list_shift( &p->list, 1 );
1581
1582       s = _bdf_list_join( &p->list, ' ', &slen );
1583
1584       if ( !s )
1585       {
1586         FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG8, lineno, "STARTCHAR" ));
1587         error = BDF_Err_Invalid_File_Format;
1588         goto Exit;
1589       }
1590
1591       if ( FT_NEW_ARRAY( p->glyph_name, slen + 1 ) )
1592         goto Exit;
1593
1594       FT_MEM_COPY( p->glyph_name, s, slen + 1 );
1595
1596       p->flags |= _BDF_GLYPH;
1597
1598       FT_TRACE4(( DBGMSG1, lineno, s ));
1599
1600       goto Exit;
1601     }
1602
1603     /* Check for the ENCODING field. */
1604     if ( ft_memcmp( line, "ENCODING", 8 ) == 0 )
1605     {
1606       if ( !( p->flags & _BDF_GLYPH ) )
1607       {
1608         /* Missing STARTCHAR field. */
1609         FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG1, lineno, "STARTCHAR" ));
1610         error = BDF_Err_Missing_Startchar_Field;
1611         goto Exit;
1612       }
1613
1614       error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
1615       if ( error )
1616         goto Exit;
1617
1618       p->glyph_enc = _bdf_atol( p->list.field[1], 0, 10 );
1619
1620       /* Normalize negative encoding values.  The specification only */
1621       /* allows -1, but we can be more generous here.                */
1622       if ( p->glyph_enc < -1 )
1623         p->glyph_enc = -1;
1624
1625       /* Check for alternative encoding format. */
1626       if ( p->glyph_enc == -1 && p->list.used > 2 )
1627         p->glyph_enc = _bdf_atol( p->list.field[2], 0, 10 );
1628
1629         if ( p->glyph_enc < -1 )
1630                 p->glyph_enc = -1;
1631
1632       FT_TRACE4(( DBGMSG2, p->glyph_enc ));
1633
1634       /* Check that the encoding is in the Unicode range because  */
1635       /* otherwise p->have (a bitmap with static size) overflows. */
1636       if ( p->glyph_enc > 0                               &&
1637            (size_t)p->glyph_enc >= sizeof ( p->have ) /
1638                                                         sizeof ( unsigned long ) * 32)
1639       {
1640         FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG5, lineno, "ENCODING" ));
1641         error = BDF_Err_Invalid_File_Format;
1642         goto Exit;
1643       }
1644
1645       /* Check whether this encoding has already been encountered. */
1646       /* If it has then change it to unencoded so it gets added if */
1647       /* indicated.                                                */
1648       if ( p->glyph_enc >= 0 )
1649       {
1650         if ( _bdf_glyph_modified( p->have, p->glyph_enc ) )
1651         {
1652           /* Emit a message saying a glyph has been moved to the */
1653           /* unencoded area.                                     */
1654           FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG12,
1655                       p->glyph_enc, p->glyph_name ));
1656           p->glyph_enc = -1;
1657           font->modified = 1;
1658         }
1659         else
1660           _bdf_set_glyph_modified( p->have, p->glyph_enc );
1661       }
1662
1663       if ( p->glyph_enc >= 0 )
1664       {
1665         /* Make sure there are enough glyphs allocated in case the */
1666         /* number of characters happen to be wrong.                */
1667         if ( font->glyphs_used == font->glyphs_size )
1668         {
1669           if ( FT_RENEW_ARRAY( font->glyphs,
1670                                font->glyphs_size,
1671                                font->glyphs_size + 64 ) )
1672             goto Exit;
1673
1674           font->glyphs_size += 64;
1675         }
1676
1677         glyph           = font->glyphs + font->glyphs_used++;
1678         glyph->name     = p->glyph_name;
1679         glyph->encoding = p->glyph_enc;
1680
1681         /* Reset the initial glyph info. */
1682         p->glyph_name = 0;
1683       }
1684       else
1685       {
1686         /* Unencoded glyph.  Check whether it should */
1687         /* be added or not.                          */
1688         if ( p->opts->keep_unencoded != 0 )
1689         {
1690           /* Allocate the next unencoded glyph. */
1691           if ( font->unencoded_used == font->unencoded_size )
1692           {
1693             if ( FT_RENEW_ARRAY( font->unencoded ,
1694                                  font->unencoded_size,
1695                                  font->unencoded_size + 4 ) )
1696               goto Exit;
1697
1698             font->unencoded_size += 4;
1699           }
1700
1701           glyph           = font->unencoded + font->unencoded_used;
1702           glyph->name     = p->glyph_name;
1703           glyph->encoding = font->unencoded_used++;
1704         }
1705         else
1706           /* Free up the glyph name if the unencoded shouldn't be */
1707           /* kept.                                                */
1708           FT_FREE( p->glyph_name );
1709
1710         p->glyph_name = 0;
1711       }
1712
1713       /* Clear the flags that might be added when width and height are */
1714       /* checked for consistency.                                      */
1715       p->flags &= ~( _BDF_GLYPH_WIDTH_CHECK | _BDF_GLYPH_HEIGHT_CHECK );
1716
1717       p->flags |= _BDF_ENCODING;
1718
1719       goto Exit;
1720     }
1721
1722     /* Point at the glyph being constructed. */
1723     if ( p->glyph_enc == -1 )
1724       glyph = font->unencoded + ( font->unencoded_used - 1 );
1725     else
1726       glyph = font->glyphs + ( font->glyphs_used - 1 );
1727
1728     /* Check whether a bitmap is being constructed. */
1729     if ( p->flags & _BDF_BITMAP )
1730     {
1731       /* If there are more rows than are specified in the glyph metrics, */
1732       /* ignore the remaining lines.                                     */
1733       if ( p->row >= (unsigned long)glyph->bbx.height )
1734       {
1735         if ( !( p->flags & _BDF_GLYPH_HEIGHT_CHECK ) )
1736         {
1737           FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG13, glyph->encoding ));
1738           p->flags |= _BDF_GLYPH_HEIGHT_CHECK;
1739           font->modified = 1;
1740         }
1741
1742         goto Exit;
1743       }
1744
1745       /* Only collect the number of nibbles indicated by the glyph     */
1746       /* metrics.  If there are more columns, they are simply ignored. */
1747       nibbles = glyph->bpr << 1;
1748       bp      = glyph->bitmap + p->row * glyph->bpr;
1749
1750       for ( i = 0; i < nibbles; i++ )
1751       {
1752         c = line[i];
1753         if ( !isdigok( hdigits, c ) )
1754           break;
1755         *bp = (FT_Byte)( ( *bp << 4 ) + a2i[c] );
1756         if ( i + 1 < nibbles && ( i & 1 ) )
1757           *++bp = 0;
1758       }
1759
1760       /* If any line has not enough columns,            */
1761       /* indicate they have been padded with zero bits. */
1762       if ( i < nibbles                            &&
1763            !( p->flags & _BDF_GLYPH_WIDTH_CHECK ) )
1764       {
1765         FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG16, glyph->encoding ));
1766         p->flags       |= _BDF_GLYPH_WIDTH_CHECK;
1767         font->modified  = 1;
1768       }
1769
1770       /* Remove possible garbage at the right. */
1771       mask_index = ( glyph->bbx.width * p->font->bpp ) & 7;
1772       if ( glyph->bbx.width )
1773         *bp &= nibble_mask[mask_index];
1774
1775       /* If any line has extra columns, indicate they have been removed. */
1776       if ( i == nibbles                           &&
1777            isdigok( hdigits, line[nibbles] )      &&
1778            !( p->flags & _BDF_GLYPH_WIDTH_CHECK ) )
1779       {
1780         FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG14, glyph->encoding ));
1781         p->flags       |= _BDF_GLYPH_WIDTH_CHECK;
1782         font->modified  = 1;
1783       }
1784
1785       p->row++;
1786       goto Exit;
1787     }
1788
1789     /* Expect the SWIDTH (scalable width) field next. */
1790     if ( ft_memcmp( line, "SWIDTH", 6 ) == 0 )
1791     {
1792       if ( !( p->flags & _BDF_ENCODING ) )
1793         goto Missing_Encoding;
1794
1795       error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
1796       if ( error )
1797         goto Exit;
1798
1799       glyph->swidth = (unsigned short)_bdf_atoul( p->list.field[1], 0, 10 );
1800       p->flags |= _BDF_SWIDTH;
1801
1802       goto Exit;
1803     }
1804
1805     /* Expect the DWIDTH (scalable width) field next. */
1806     if ( ft_memcmp( line, "DWIDTH", 6 ) == 0 )
1807     {
1808       if ( !( p->flags & _BDF_ENCODING ) )
1809         goto Missing_Encoding;
1810
1811       error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
1812       if ( error )
1813         goto Exit;
1814
1815       glyph->dwidth = (unsigned short)_bdf_atoul( p->list.field[1], 0, 10 );
1816
1817       if ( !( p->flags & _BDF_SWIDTH ) )
1818       {
1819         /* Missing SWIDTH field.  Emit an auto correction message and set */
1820         /* the scalable width from the device width.                      */
1821         FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG9, lineno ));
1822
1823         glyph->swidth = (unsigned short)FT_MulDiv(
1824                           glyph->dwidth, 72000L,
1825                           (FT_Long)( font->point_size *
1826                                      font->resolution_x ) );
1827       }
1828
1829       p->flags |= _BDF_DWIDTH;
1830       goto Exit;
1831     }
1832
1833     /* Expect the BBX field next. */
1834     if ( ft_memcmp( line, "BBX", 3 ) == 0 )
1835     {
1836       if ( !( p->flags & _BDF_ENCODING ) )
1837         goto Missing_Encoding;
1838
1839       error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
1840       if ( error )
1841         goto Exit;
1842
1843       glyph->bbx.width    = _bdf_atos( p->list.field[1], 0, 10 );
1844       glyph->bbx.height   = _bdf_atos( p->list.field[2], 0, 10 );
1845       glyph->bbx.x_offset = _bdf_atos( p->list.field[3], 0, 10 );
1846       glyph->bbx.y_offset = _bdf_atos( p->list.field[4], 0, 10 );
1847
1848       /* Generate the ascent and descent of the character. */
1849       glyph->bbx.ascent  = (short)( glyph->bbx.height + glyph->bbx.y_offset );
1850       glyph->bbx.descent = (short)( -glyph->bbx.y_offset );
1851
1852       /* Determine the overall font bounding box as the characters are */
1853       /* loaded so corrections can be done later if indicated.         */
1854       p->maxas    = (short)FT_MAX( glyph->bbx.ascent, p->maxas );
1855       p->maxds    = (short)FT_MAX( glyph->bbx.descent, p->maxds );
1856
1857       p->rbearing = (short)( glyph->bbx.width + glyph->bbx.x_offset );
1858
1859       p->maxrb    = (short)FT_MAX( p->rbearing, p->maxrb );
1860       p->minlb    = (short)FT_MIN( glyph->bbx.x_offset, p->minlb );
1861       p->maxlb    = (short)FT_MAX( glyph->bbx.x_offset, p->maxlb );
1862
1863       if ( !( p->flags & _BDF_DWIDTH ) )
1864       {
1865         /* Missing DWIDTH field.  Emit an auto correction message and set */
1866         /* the device width to the glyph width.                           */
1867         FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG10, lineno ));
1868         glyph->dwidth = glyph->bbx.width;
1869       }
1870
1871       /* If the BDF_CORRECT_METRICS flag is set, then adjust the SWIDTH */
1872       /* value if necessary.                                            */
1873       if ( p->opts->correct_metrics != 0 )
1874       {
1875         /* Determine the point size of the glyph. */
1876         unsigned short  sw = (unsigned short)FT_MulDiv(
1877                                glyph->dwidth, 72000L,
1878                                (FT_Long)( font->point_size *
1879                                           font->resolution_x ) );
1880
1881
1882         if ( sw != glyph->swidth )
1883         {
1884           glyph->swidth = sw;
1885
1886           if ( p->glyph_enc == -1 )
1887             _bdf_set_glyph_modified( font->umod,
1888                                      font->unencoded_used - 1 );
1889           else
1890             _bdf_set_glyph_modified( font->nmod, glyph->encoding );
1891
1892           p->flags       |= _BDF_SWIDTH_ADJ;
1893           font->modified  = 1;
1894         }
1895       }
1896
1897       p->flags |= _BDF_BBX;
1898       goto Exit;
1899     }
1900
1901     /* And finally, gather up the bitmap. */
1902     if ( ft_memcmp( line, "BITMAP", 6 ) == 0 )
1903     {
1904       unsigned long  bitmap_size;
1905
1906
1907       if ( !( p->flags & _BDF_BBX ) )
1908       {
1909         /* Missing BBX field. */
1910         FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG1, lineno, "BBX" ));
1911         error = BDF_Err_Missing_Bbx_Field;
1912         goto Exit;
1913       }
1914
1915       /* Allocate enough space for the bitmap. */
1916       glyph->bpr = ( glyph->bbx.width * p->font->bpp + 7 ) >> 3;
1917
1918       bitmap_size = glyph->bpr * glyph->bbx.height;
1919       if ( glyph->bpr > 0xFFFFU || bitmap_size > 0xFFFFU )
1920       {
1921         FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG4, lineno ));
1922         error = BDF_Err_Bbx_Too_Big;
1923         goto Exit;
1924       }
1925       else
1926         glyph->bytes = (unsigned short)bitmap_size;
1927
1928       if ( FT_NEW_ARRAY( glyph->bitmap, glyph->bytes ) )
1929         goto Exit;
1930
1931       p->row    = 0;
1932       p->flags |= _BDF_BITMAP;
1933
1934       goto Exit;
1935     }
1936
1937     FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG9, lineno ));
1938     error = BDF_Err_Invalid_File_Format;
1939     goto Exit;
1940
1941   Missing_Encoding:
1942     /* Missing ENCODING field. */
1943     FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG1, lineno, "ENCODING" ));
1944     error = BDF_Err_Missing_Encoding_Field;
1945
1946   Exit:
1947     if ( error && ( p->flags & _BDF_GLYPH ) )
1948       FT_FREE( p->glyph_name );
1949
1950     return error;
1951   }
1952
1953
1954   /* Load the font properties. */
1955   static FT_Error
1956   _bdf_parse_properties( char*          line,
1957                          unsigned long  linelen,
1958                          unsigned long  lineno,
1959                          void*          call_data,
1960                          void*          client_data )
1961   {
1962     unsigned long      vlen;
1963     _bdf_line_func_t*  next;
1964     _bdf_parse_t*      p;
1965     char*              name;
1966     char*              value;
1967     char               nbuf[128];
1968     FT_Error           error = BDF_Err_Ok;
1969
1970     FT_UNUSED( lineno );
1971
1972
1973     next = (_bdf_line_func_t *)call_data;
1974     p    = (_bdf_parse_t *)    client_data;
1975
1976     /* Check for the end of the properties. */
1977     if ( ft_memcmp( line, "ENDPROPERTIES", 13 ) == 0 )
1978     {
1979       /* If the FONT_ASCENT or FONT_DESCENT properties have not been      */
1980       /* encountered yet, then make sure they are added as properties and */
1981       /* make sure they are set from the font bounding box info.          */
1982       /*                                                                  */
1983       /* This is *always* done regardless of the options, because X11     */
1984       /* requires these two fields to compile fonts.                      */
1985       if ( bdf_get_font_property( p->font, "FONT_ASCENT" ) == 0 )
1986       {
1987         p->font->font_ascent = p->font->bbx.ascent;
1988         ft_sprintf( nbuf, "%hd", p->font->bbx.ascent );
1989         error = _bdf_add_property( p->font, (char *)"FONT_ASCENT",
1990                                    nbuf, lineno );
1991         if ( error )
1992           goto Exit;
1993
1994         FT_TRACE2(( "_bdf_parse_properties: " ACMSG1, p->font->bbx.ascent ));
1995         p->font->modified = 1;
1996       }
1997
1998       if ( bdf_get_font_property( p->font, "FONT_DESCENT" ) == 0 )
1999       {
2000         p->font->font_descent = p->font->bbx.descent;
2001         ft_sprintf( nbuf, "%hd", p->font->bbx.descent );
2002         error = _bdf_add_property( p->font, (char *)"FONT_DESCENT",
2003                                    nbuf, lineno );
2004         if ( error )
2005           goto Exit;
2006
2007         FT_TRACE2(( "_bdf_parse_properties: " ACMSG2, p->font->bbx.descent ));
2008         p->font->modified = 1;
2009       }
2010
2011       p->flags &= ~_BDF_PROPS;
2012       *next     = _bdf_parse_glyphs;
2013
2014       goto Exit;
2015     }
2016
2017     /* Ignore the _XFREE86_GLYPH_RANGES properties. */
2018     if ( ft_memcmp( line, "_XFREE86_GLYPH_RANGES", 21 ) == 0 )
2019       goto Exit;
2020
2021     /* Handle COMMENT fields and properties in a special way to preserve */
2022     /* the spacing.                                                      */
2023     if ( ft_memcmp( line, "COMMENT", 7 ) == 0 )
2024     {
2025       name = value = line;
2026       value += 7;
2027       if ( *value )
2028         *value++ = 0;
2029       error = _bdf_add_property( p->font, name, value, lineno );
2030       if ( error )
2031         goto Exit;
2032     }
2033     else if ( _bdf_is_atom( line, linelen, &name, &value, p->font ) )
2034     {
2035       error = _bdf_add_property( p->font, name, value, lineno );
2036       if ( error )
2037         goto Exit;
2038     }
2039     else
2040     {
2041       error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
2042       if ( error )
2043         goto Exit;
2044       name = p->list.field[0];
2045
2046       _bdf_list_shift( &p->list, 1 );
2047       value = _bdf_list_join( &p->list, ' ', &vlen );
2048
2049       error = _bdf_add_property( p->font, name, value, lineno );
2050       if ( error )
2051         goto Exit;
2052     }
2053
2054   Exit:
2055     return error;
2056   }
2057
2058
2059   /* Load the font header. */
2060   static FT_Error
2061   _bdf_parse_start( char*          line,
2062                     unsigned long  linelen,
2063                     unsigned long  lineno,
2064                     void*          call_data,
2065                     void*          client_data )
2066   {
2067     unsigned long      slen;
2068     _bdf_line_func_t*  next;
2069     _bdf_parse_t*      p;
2070     bdf_font_t*        font;
2071     char               *s;
2072
2073     FT_Memory          memory = NULL;
2074     FT_Error           error  = BDF_Err_Ok;
2075
2076     FT_UNUSED( lineno );            /* only used in debug mode */
2077
2078
2079     next = (_bdf_line_func_t *)call_data;
2080     p    = (_bdf_parse_t *)    client_data;
2081
2082     if ( p->font )
2083       memory = p->font->memory;
2084
2085     /* Check for a comment.  This is done to handle those fonts that have */
2086     /* comments before the STARTFONT line for some reason.                */
2087     if ( ft_memcmp( line, "COMMENT", 7 ) == 0 )
2088     {
2089       if ( p->opts->keep_comments != 0 && p->font != 0 )
2090       {
2091         linelen -= 7;
2092
2093         s = line + 7;
2094         if ( *s != 0 )
2095         {
2096           s++;
2097           linelen--;
2098         }
2099
2100         error = _bdf_add_comment( p->font, s, linelen );
2101         if ( error )
2102           goto Exit;
2103         /* here font is not defined! */
2104       }
2105
2106       goto Exit;
2107     }
2108
2109     if ( !( p->flags & _BDF_START ) )
2110     {
2111       memory = p->memory;
2112
2113       if ( ft_memcmp( line, "STARTFONT", 9 ) != 0 )
2114       {
2115         /* we don't emit an error message since this code gets */
2116         /* explicitly caught one level higher                  */
2117         error = BDF_Err_Missing_Startfont_Field;
2118         goto Exit;
2119       }
2120
2121       p->flags = _BDF_START;
2122       font = p->font = 0;
2123
2124       if ( FT_NEW( font ) )
2125         goto Exit;
2126       p->font = font;
2127
2128       font->memory = p->memory;
2129       p->memory    = 0;
2130
2131       { /* setup */
2132         size_t           i;
2133         bdf_property_t*  prop;
2134
2135
2136         error = hash_init( &(font->proptbl), memory );
2137         if ( error )
2138           goto Exit;
2139         for ( i = 0, prop = (bdf_property_t*)_bdf_properties;
2140               i < _num_bdf_properties; i++, prop++ )
2141         {
2142           error = hash_insert( prop->name, i,
2143                                &(font->proptbl), memory );
2144           if ( error )
2145             goto Exit;
2146         }
2147       }
2148
2149       if ( FT_ALLOC( p->font->internal, sizeof ( hashtable ) ) )
2150         goto Exit;
2151       error = hash_init( (hashtable *)p->font->internal,memory );
2152       if ( error )
2153         goto Exit;
2154       p->font->spacing      = p->opts->font_spacing;
2155       p->font->default_char = -1;
2156
2157       goto Exit;
2158     }
2159
2160     /* Check for the start of the properties. */
2161     if ( ft_memcmp( line, "STARTPROPERTIES", 15 ) == 0 )
2162     {
2163       if ( !( p->flags & _BDF_FONT_BBX ) )
2164       {
2165         /* Missing the FONTBOUNDINGBOX field. */
2166         FT_ERROR(( "_bdf_parse_start: " ERRMSG1, lineno, "FONTBOUNDINGBOX" ));
2167         error = BDF_Err_Missing_Fontboundingbox_Field;
2168         goto Exit;
2169       }
2170
2171       error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
2172       if ( error )
2173         goto Exit;
2174       /* at this point, `p->font' can't be NULL */
2175       p->cnt = p->font->props_size = _bdf_atoul( p->list.field[1], 0, 10 );
2176
2177       if ( FT_NEW_ARRAY( p->font->props, p->cnt ) )
2178           {
2179                   p->font->props_size = 0;
2180                   goto Exit;
2181           }
2182
2183       p->flags |= _BDF_PROPS;
2184       *next     = _bdf_parse_properties;
2185
2186       goto Exit;
2187     }
2188
2189     /* Check for the FONTBOUNDINGBOX field. */
2190     if ( ft_memcmp( line, "FONTBOUNDINGBOX", 15 ) == 0 )
2191     {
2192       if ( !( p->flags & _BDF_SIZE ) )
2193       {
2194         /* Missing the SIZE field. */
2195         FT_ERROR(( "_bdf_parse_start: " ERRMSG1, lineno, "SIZE" ));
2196         error = BDF_Err_Missing_Size_Field;
2197         goto Exit;
2198       }
2199
2200       error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
2201       if ( error )
2202         goto Exit;
2203
2204       p->font->bbx.width  = _bdf_atos( p->list.field[1], 0, 10 );
2205       p->font->bbx.height = _bdf_atos( p->list.field[2], 0, 10 );
2206
2207       p->font->bbx.x_offset = _bdf_atos( p->list.field[3], 0, 10 );
2208       p->font->bbx.y_offset = _bdf_atos( p->list.field[4], 0, 10 );
2209
2210       p->font->bbx.ascent  = (short)( p->font->bbx.height +
2211                                       p->font->bbx.y_offset );
2212
2213       p->font->bbx.descent = (short)( -p->font->bbx.y_offset );
2214
2215       p->flags |= _BDF_FONT_BBX;
2216
2217       goto Exit;
2218     }
2219
2220     /* The next thing to check for is the FONT field. */
2221     if ( ft_memcmp( line, "FONT", 4 ) == 0 )
2222     {
2223       error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
2224       if ( error )
2225         goto Exit;
2226       _bdf_list_shift( &p->list, 1 );
2227
2228       s = _bdf_list_join( &p->list, ' ', &slen );
2229
2230       if ( !s )
2231       {
2232         FT_ERROR(( "_bdf_parse_start: " ERRMSG8, lineno, "FONT" ));
2233         error = BDF_Err_Invalid_File_Format;
2234         goto Exit;
2235       }
2236
2237       /* Allowing multiple `FONT' lines (which is invalid) doesn't hurt... */
2238       FT_FREE( p->font->name );
2239
2240       if ( FT_NEW_ARRAY( p->font->name, slen + 1 ) )
2241         goto Exit;
2242       FT_MEM_COPY( p->font->name, s, slen + 1 );
2243
2244       /* If the font name is an XLFD name, set the spacing to the one in  */
2245       /* the font name.  If there is no spacing fall back on the default. */
2246       error = _bdf_set_default_spacing( p->font, p->opts, lineno );
2247       if ( error )
2248         goto Exit;
2249
2250       p->flags |= _BDF_FONT_NAME;
2251
2252       goto Exit;
2253     }
2254
2255     /* Check for the SIZE field. */
2256     if ( ft_memcmp( line, "SIZE", 4 ) == 0 )
2257     {
2258       if ( !( p->flags & _BDF_FONT_NAME ) )
2259       {
2260         /* Missing the FONT field. */
2261         FT_ERROR(( "_bdf_parse_start: " ERRMSG1, lineno, "FONT" ));
2262         error = BDF_Err_Missing_Font_Field;
2263         goto Exit;
2264       }
2265
2266       error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
2267       if ( error )
2268         goto Exit;
2269
2270       p->font->point_size   = _bdf_atoul( p->list.field[1], 0, 10 );
2271       p->font->resolution_x = _bdf_atoul( p->list.field[2], 0, 10 );
2272       p->font->resolution_y = _bdf_atoul( p->list.field[3], 0, 10 );
2273
2274       /* Check for the bits per pixel field. */
2275       if ( p->list.used == 5 )
2276       {
2277         unsigned short bitcount, i, shift;
2278
2279
2280         p->font->bpp = (unsigned short)_bdf_atos( p->list.field[4], 0, 10 );
2281
2282         /* Only values 1, 2, 4, 8 are allowed. */
2283         shift = p->font->bpp;
2284         bitcount = 0;
2285         for ( i = 0; shift > 0; i++ )
2286         {
2287           if ( shift & 1 )
2288             bitcount = i;
2289           shift >>= 1;
2290         }
2291
2292         shift = (short)( ( bitcount > 3 ) ? 8 : ( 1 << bitcount ) );
2293
2294         if ( p->font->bpp > shift || p->font->bpp != shift )
2295         {
2296           /* select next higher value */
2297           p->font->bpp = (unsigned short)( shift << 1 );
2298           FT_TRACE2(( "_bdf_parse_start: " ACMSG11, p->font->bpp ));
2299         }
2300       }
2301       else
2302         p->font->bpp = 1;
2303
2304       p->flags |= _BDF_SIZE;
2305
2306       goto Exit;
2307     }
2308
2309     /* Check for the CHARS field -- font properties are optional */
2310     if ( ft_memcmp( line, "CHARS", 5 ) == 0 )
2311     {
2312       char  nbuf[128];
2313
2314
2315       if ( !( p->flags & _BDF_FONT_BBX ) )
2316       {
2317         /* Missing the FONTBOUNDINGBOX field. */
2318         FT_ERROR(( "_bdf_parse_start: " ERRMSG1, lineno, "FONTBOUNDINGBOX" ));
2319         error = BDF_Err_Missing_Fontboundingbox_Field;
2320         goto Exit;
2321       }
2322
2323       /* Add the two standard X11 properties which are required */
2324       /* for compiling fonts.                                   */
2325       p->font->font_ascent = p->font->bbx.ascent;
2326       ft_sprintf( nbuf, "%hd", p->font->bbx.ascent );
2327       error = _bdf_add_property( p->font, (char *)"FONT_ASCENT",
2328                                  nbuf, lineno );
2329       if ( error )
2330         goto Exit;
2331       FT_TRACE2(( "_bdf_parse_properties: " ACMSG1, p->font->bbx.ascent ));
2332
2333       p->font->font_descent = p->font->bbx.descent;
2334       ft_sprintf( nbuf, "%hd", p->font->bbx.descent );
2335       error = _bdf_add_property( p->font, (char *)"FONT_DESCENT",
2336                                  nbuf, lineno );
2337       if ( error )
2338         goto Exit;
2339       FT_TRACE2(( "_bdf_parse_properties: " ACMSG2, p->font->bbx.descent ));
2340
2341       p->font->modified = 1;
2342
2343       *next = _bdf_parse_glyphs;
2344
2345       /* A special return value. */
2346       error = -1;
2347       goto Exit;
2348     }
2349
2350     FT_ERROR(( "_bdf_parse_start: " ERRMSG9, lineno ));
2351     error = BDF_Err_Invalid_File_Format;
2352
2353   Exit:
2354     return error;
2355   }
2356
2357
2358   /*************************************************************************/
2359   /*                                                                       */
2360   /* API.                                                                  */
2361   /*                                                                       */
2362   /*************************************************************************/
2363
2364
2365   FT_LOCAL_DEF( FT_Error )
2366   bdf_load_font( FT_Stream       stream,
2367                  FT_Memory       extmemory,
2368                  bdf_options_t*  opts,
2369                  bdf_font_t*    *font )
2370   {
2371     unsigned long  lineno = 0; /* make compiler happy */
2372     _bdf_parse_t   *p     = NULL;
2373
2374     FT_Memory      memory = extmemory;
2375     FT_Error       error  = BDF_Err_Ok;
2376
2377
2378     if ( FT_NEW( p ) )
2379       goto Exit;
2380
2381     memory    = NULL;
2382     p->opts   = (bdf_options_t*)( ( opts != 0 ) ? opts : &_bdf_opts );
2383     p->minlb  = 32767;
2384     p->memory = extmemory;  /* only during font creation */
2385
2386     _bdf_list_init( &p->list, extmemory );
2387
2388     error = _bdf_readstream( stream, _bdf_parse_start,
2389                              (void *)p, &lineno );
2390     if ( error )
2391       goto Fail;
2392
2393     if ( p->font != 0 )
2394     {
2395       /* If the font is not proportional, set the font's monowidth */
2396       /* field to the width of the font bounding box.              */
2397       memory = p->font->memory;
2398
2399       if ( p->font->spacing != BDF_PROPORTIONAL )
2400         p->font->monowidth = p->font->bbx.width;
2401
2402       /* If the number of glyphs loaded is not that of the original count, */
2403       /* indicate the difference.                                          */
2404       if ( p->cnt != p->font->glyphs_used + p->font->unencoded_used )
2405       {
2406         FT_TRACE2(( "bdf_load_font: " ACMSG15, p->cnt,
2407                     p->font->glyphs_used + p->font->unencoded_used ));
2408         p->font->modified = 1;
2409       }
2410
2411       /* Once the font has been loaded, adjust the overall font metrics if */
2412       /* necessary.                                                        */
2413       if ( p->opts->correct_metrics != 0 &&
2414            ( p->font->glyphs_used > 0 || p->font->unencoded_used > 0 ) )
2415       {
2416         if ( p->maxrb - p->minlb != p->font->bbx.width )
2417         {
2418           FT_TRACE2(( "bdf_load_font: " ACMSG3,
2419                       p->font->bbx.width, p->maxrb - p->minlb ));
2420           p->font->bbx.width = (unsigned short)( p->maxrb - p->minlb );
2421           p->font->modified  = 1;
2422         }
2423
2424         if ( p->font->bbx.x_offset != p->minlb )
2425         {
2426           FT_TRACE2(( "bdf_load_font: " ACMSG4,
2427                       p->font->bbx.x_offset, p->minlb ));
2428           p->font->bbx.x_offset = p->minlb;
2429           p->font->modified     = 1;
2430         }
2431
2432         if ( p->font->bbx.ascent != p->maxas )
2433         {
2434           FT_TRACE2(( "bdf_load_font: " ACMSG5,
2435                       p->font->bbx.ascent, p->maxas ));
2436           p->font->bbx.ascent = p->maxas;
2437           p->font->modified   = 1;
2438         }
2439
2440         if ( p->font->bbx.descent != p->maxds )
2441         {
2442           FT_TRACE2(( "bdf_load_font: " ACMSG6,
2443                       p->font->bbx.descent, p->maxds ));
2444           p->font->bbx.descent  = p->maxds;
2445           p->font->bbx.y_offset = (short)( -p->maxds );
2446           p->font->modified     = 1;
2447         }
2448
2449         if ( p->maxas + p->maxds != p->font->bbx.height )
2450         {
2451           FT_TRACE2(( "bdf_load_font: " ACMSG7,
2452                       p->font->bbx.height, p->maxas + p->maxds ));
2453           p->font->bbx.height = (unsigned short)( p->maxas + p->maxds );
2454         }
2455
2456         if ( p->flags & _BDF_SWIDTH_ADJ )
2457           FT_TRACE2(( "bdf_load_font: " ACMSG8 ));
2458       }
2459     }
2460
2461     if ( p->flags & _BDF_START )
2462     {
2463       /* The ENDFONT field was never reached or did not exist. */
2464       if ( !( p->flags & _BDF_GLYPHS ) )
2465       {
2466         /* Error happened while parsing header. */
2467         FT_ERROR(( "bdf_load_font: " ERRMSG2, lineno ));
2468         error = BDF_Err_Corrupted_Font_Header;
2469         goto Exit;
2470       }
2471       else
2472       {
2473         /* Error happened when parsing glyphs. */
2474         FT_ERROR(( "bdf_load_font: " ERRMSG3, lineno ));
2475         error = BDF_Err_Corrupted_Font_Glyphs;
2476         goto Exit;
2477       }
2478     }
2479
2480     if ( p->font != 0 )
2481     {
2482       /* Make sure the comments are NULL terminated if they exist. */
2483       memory = p->font->memory;
2484
2485       if ( p->font->comments_len > 0 )
2486       {
2487         if ( FT_RENEW_ARRAY( p->font->comments,
2488                              p->font->comments_len,
2489                              p->font->comments_len + 1 ) )
2490           goto Fail;
2491
2492         p->font->comments[p->font->comments_len] = 0;
2493       }
2494     }
2495     else if ( error == BDF_Err_Ok )
2496       error = BDF_Err_Invalid_File_Format;
2497
2498     *font = p->font;
2499
2500   Exit:
2501     if ( p )
2502     {
2503       _bdf_list_done( &p->list );
2504
2505       memory = extmemory;
2506
2507       FT_FREE( p );
2508     }
2509
2510     return error;
2511
2512   Fail:
2513     bdf_free_font( p->font );
2514
2515     memory = extmemory;
2516
2517     FT_FREE( p->font );
2518
2519     goto Exit;
2520   }
2521
2522
2523   FT_LOCAL_DEF( void )
2524   bdf_free_font( bdf_font_t*  font )
2525   {
2526     bdf_property_t*  prop;
2527     unsigned long    i;
2528     bdf_glyph_t*     glyphs;
2529     FT_Memory        memory;
2530
2531
2532     if ( font == 0 )
2533       return;
2534
2535     memory = font->memory;
2536
2537     FT_FREE( font->name );
2538
2539     /* Free up the internal hash table of property names. */
2540     if ( font->internal )
2541     {
2542       hash_free( (hashtable *)font->internal, memory );
2543       FT_FREE( font->internal );
2544     }
2545
2546     /* Free up the comment info. */
2547     FT_FREE( font->comments );
2548
2549     /* Free up the properties. */
2550     for ( i = 0; i < font->props_size; i++ )
2551     {
2552       if ( font->props[i].format == BDF_ATOM )
2553         FT_FREE( font->props[i].value.atom );
2554     }
2555
2556     FT_FREE( font->props );
2557
2558     /* Free up the character info. */
2559     for ( i = 0, glyphs = font->glyphs;
2560           i < font->glyphs_used; i++, glyphs++ )
2561     {
2562       FT_FREE( glyphs->name );
2563       FT_FREE( glyphs->bitmap );
2564     }
2565
2566     for ( i = 0, glyphs = font->unencoded; i < font->unencoded_used;
2567           i++, glyphs++ )
2568     {
2569       FT_FREE( glyphs->name );
2570       FT_FREE( glyphs->bitmap );
2571     }
2572
2573     FT_FREE( font->glyphs );
2574     FT_FREE( font->unencoded );
2575
2576     /* Free up the overflow storage if it was used. */
2577     for ( i = 0, glyphs = font->overflow.glyphs;
2578           i < font->overflow.glyphs_used; i++, glyphs++ )
2579     {
2580       FT_FREE( glyphs->name );
2581       FT_FREE( glyphs->bitmap );
2582     }
2583
2584     FT_FREE( font->overflow.glyphs );
2585
2586     /* bdf_cleanup */
2587     hash_free( &(font->proptbl), memory );
2588
2589     /* Free up the user defined properties. */
2590     for ( prop = font->user_props, i = 0;
2591           i < font->nuser_props; i++, prop++ )
2592     {
2593       FT_FREE( prop->name );
2594       if ( prop->format == BDF_ATOM )
2595         FT_FREE( prop->value.atom );
2596     }
2597
2598     FT_FREE( font->user_props );
2599
2600     /* FREE( font ); */ /* XXX Fixme */
2601   }
2602
2603
2604   FT_LOCAL_DEF( bdf_property_t * )
2605   bdf_get_font_property( bdf_font_t*  font,
2606                          const char*  name )
2607   {
2608     hashnode  hn;
2609
2610
2611     if ( font == 0 || font->props_size == 0 || name == 0 || *name == 0 )
2612       return 0;
2613
2614     hn = hash_lookup( name, (hashtable *)font->internal );
2615
2616     return hn ? ( font->props + hn->data ) : 0;
2617   }
2618
2619
2620 /* END */