• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*  pcfread.c
2 
3     FreeType font driver for pcf fonts
4 
5   Copyright 2000-2010, 2012-2014 by
6   Francesco Zappa Nardelli
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice shall be included in
16 all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 THE SOFTWARE.
25 */
26 
27 
28 #include <ft2build.h>
29 
30 #include FT_INTERNAL_DEBUG_H
31 #include FT_INTERNAL_STREAM_H
32 #include FT_INTERNAL_OBJECTS_H
33 
34 #include "pcf.h"
35 #include "pcfread.h"
36 
37 #include "pcferror.h"
38 
39 
40   /*************************************************************************/
41   /*                                                                       */
42   /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
43   /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
44   /* messages during execution.                                            */
45   /*                                                                       */
46 #undef  FT_COMPONENT
47 #define FT_COMPONENT  trace_pcfread
48 
49 
50 #ifdef FT_DEBUG_LEVEL_TRACE
51   static const char* const  tableNames[] =
52   {
53     "properties",
54     "accelerators",
55     "metrics",
56     "bitmaps",
57     "ink metrics",
58     "encodings",
59     "swidths",
60     "glyph names",
61     "BDF accelerators"
62   };
63 #endif
64 
65 
66   static
67   const FT_Frame_Field  pcf_toc_header[] =
68   {
69 #undef  FT_STRUCTURE
70 #define FT_STRUCTURE  PCF_TocRec
71 
72     FT_FRAME_START( 8 ),
73       FT_FRAME_ULONG_LE( version ),
74       FT_FRAME_ULONG_LE( count ),
75     FT_FRAME_END
76   };
77 
78 
79   static
80   const FT_Frame_Field  pcf_table_header[] =
81   {
82 #undef  FT_STRUCTURE
83 #define FT_STRUCTURE  PCF_TableRec
84 
85     FT_FRAME_START( 16  ),
86       FT_FRAME_ULONG_LE( type ),
87       FT_FRAME_ULONG_LE( format ),
88       FT_FRAME_ULONG_LE( size ),   /* rounded up to a multiple of 4 */
89       FT_FRAME_ULONG_LE( offset ),
90     FT_FRAME_END
91   };
92 
93 
94   static FT_Error
pcf_read_TOC(FT_Stream stream,PCF_Face face)95   pcf_read_TOC( FT_Stream  stream,
96                 PCF_Face   face )
97   {
98     FT_Error   error;
99     PCF_Toc    toc = &face->toc;
100     PCF_Table  tables;
101 
102     FT_Memory  memory = FT_FACE( face )->memory;
103     FT_UInt    n;
104 
105     FT_ULong   size;
106 
107 
108     if ( FT_STREAM_SEEK( 0 )                          ||
109          FT_STREAM_READ_FIELDS( pcf_toc_header, toc ) )
110       return FT_THROW( Cannot_Open_Resource );
111 
112     if ( toc->version != PCF_FILE_VERSION ||
113          toc->count   == 0                )
114       return FT_THROW( Invalid_File_Format );
115 
116     if ( stream->size < 16 )
117       return FT_THROW( Invalid_File_Format );
118 
119     /* we need 16 bytes per TOC entry, */
120     /* and there can be most 9 tables  */
121     if ( toc->count > ( stream->size >> 4 ) ||
122          toc->count > 9                     )
123     {
124       FT_TRACE0(( "pcf_read_TOC: adjusting number of tables"
125                   " (from %d to %d)\n",
126                   toc->count,
127                   FT_MIN( stream->size >> 4, 9 ) ));
128       toc->count = FT_MIN( stream->size >> 4, 9 );
129     }
130 
131     if ( FT_NEW_ARRAY( face->toc.tables, toc->count ) )
132       return error;
133 
134     tables = face->toc.tables;
135     for ( n = 0; n < toc->count; n++ )
136     {
137       if ( FT_STREAM_READ_FIELDS( pcf_table_header, tables ) )
138         goto Exit;
139       tables++;
140     }
141 
142     /* Sort tables and check for overlaps.  Because they are almost      */
143     /* always ordered already, an in-place bubble sort with simultaneous */
144     /* boundary checking seems appropriate.                              */
145     tables = face->toc.tables;
146 
147     for ( n = 0; n < toc->count - 1; n++ )
148     {
149       FT_UInt  i, have_change;
150 
151 
152       have_change = 0;
153 
154       for ( i = 0; i < toc->count - 1 - n; i++ )
155       {
156         PCF_TableRec  tmp;
157 
158 
159         if ( tables[i].offset > tables[i + 1].offset )
160         {
161           tmp           = tables[i];
162           tables[i]     = tables[i + 1];
163           tables[i + 1] = tmp;
164 
165           have_change = 1;
166         }
167 
168         if ( ( tables[i].size   > tables[i + 1].offset )                  ||
169              ( tables[i].offset > tables[i + 1].offset - tables[i].size ) )
170         {
171           error = FT_THROW( Invalid_Offset );
172           goto Exit;
173         }
174       }
175 
176       if ( !have_change )
177         break;
178     }
179 
180     /*
181      *  We now check whether the `size' and `offset' values are reasonable:
182      *  `offset' + `size' must not exceed the stream size.
183      *
184      *  Note, however, that X11's `pcfWriteFont' routine (used by the
185      *  `bdftopcf' program to create PDF font files) has two special
186      *  features.
187      *
188      *  - It always assigns the accelerator table a size of 100 bytes in the
189      *    TOC, regardless of its real size, which can vary between 34 and 72
190      *    bytes.
191      *
192      *  - Due to the way the routine is designed, it ships out the last font
193      *    table with its real size, ignoring the TOC's size value.  Since
194      *    the TOC size values are always rounded up to a multiple of 4, the
195      *    difference can be up to three bytes for all tables except the
196      *    accelerator table, for which the difference can be as large as 66
197      *    bytes.
198      *
199      */
200 
201     tables = face->toc.tables;
202     size   = stream->size;
203 
204     for ( n = 0; n < toc->count - 1; n++ )
205     {
206       /* we need two checks to avoid overflow */
207       if ( ( tables->size   > size                ) ||
208            ( tables->offset > size - tables->size ) )
209       {
210         error = FT_THROW( Invalid_Table );
211         goto Exit;
212       }
213       tables++;
214     }
215 
216     /* only check `tables->offset' for last table element ... */
217     if ( ( tables->offset > size ) )
218     {
219       error = FT_THROW( Invalid_Table );
220       goto Exit;
221     }
222     /* ... and adjust `tables->size' to the real value if necessary */
223     if ( tables->size > size - tables->offset )
224       tables->size = size - tables->offset;
225 
226 #ifdef FT_DEBUG_LEVEL_TRACE
227 
228     {
229       FT_UInt      i, j;
230       const char*  name = "?";
231 
232 
233       FT_TRACE4(( "pcf_read_TOC:\n" ));
234 
235       FT_TRACE4(( "  number of tables: %ld\n", face->toc.count ));
236 
237       tables = face->toc.tables;
238       for ( i = 0; i < toc->count; i++ )
239       {
240         for ( j = 0; j < sizeof ( tableNames ) / sizeof ( tableNames[0] );
241               j++ )
242           if ( tables[i].type == (FT_UInt)( 1 << j ) )
243             name = tableNames[j];
244 
245         FT_TRACE4(( "  %d: type=%s, format=0x%X,"
246                     " size=%ld (0x%lX), offset=%ld (0x%lX)\n",
247                     i, name,
248                     tables[i].format,
249                     tables[i].size, tables[i].size,
250                     tables[i].offset, tables[i].offset ));
251       }
252     }
253 
254 #endif
255 
256     return FT_Err_Ok;
257 
258   Exit:
259     FT_FREE( face->toc.tables );
260     return error;
261   }
262 
263 
264 #define PCF_METRIC_SIZE  12
265 
266   static
267   const FT_Frame_Field  pcf_metric_header[] =
268   {
269 #undef  FT_STRUCTURE
270 #define FT_STRUCTURE  PCF_MetricRec
271 
272     FT_FRAME_START( PCF_METRIC_SIZE ),
273       FT_FRAME_SHORT_LE( leftSideBearing ),
274       FT_FRAME_SHORT_LE( rightSideBearing ),
275       FT_FRAME_SHORT_LE( characterWidth ),
276       FT_FRAME_SHORT_LE( ascent ),
277       FT_FRAME_SHORT_LE( descent ),
278       FT_FRAME_SHORT_LE( attributes ),
279     FT_FRAME_END
280   };
281 
282 
283   static
284   const FT_Frame_Field  pcf_metric_msb_header[] =
285   {
286 #undef  FT_STRUCTURE
287 #define FT_STRUCTURE  PCF_MetricRec
288 
289     FT_FRAME_START( PCF_METRIC_SIZE ),
290       FT_FRAME_SHORT( leftSideBearing ),
291       FT_FRAME_SHORT( rightSideBearing ),
292       FT_FRAME_SHORT( characterWidth ),
293       FT_FRAME_SHORT( ascent ),
294       FT_FRAME_SHORT( descent ),
295       FT_FRAME_SHORT( attributes ),
296     FT_FRAME_END
297   };
298 
299 
300 #define PCF_COMPRESSED_METRIC_SIZE  5
301 
302   static
303   const FT_Frame_Field  pcf_compressed_metric_header[] =
304   {
305 #undef  FT_STRUCTURE
306 #define FT_STRUCTURE  PCF_Compressed_MetricRec
307 
308     FT_FRAME_START( PCF_COMPRESSED_METRIC_SIZE ),
309       FT_FRAME_BYTE( leftSideBearing ),
310       FT_FRAME_BYTE( rightSideBearing ),
311       FT_FRAME_BYTE( characterWidth ),
312       FT_FRAME_BYTE( ascent ),
313       FT_FRAME_BYTE( descent ),
314     FT_FRAME_END
315   };
316 
317 
318   static FT_Error
pcf_get_metric(FT_Stream stream,FT_ULong format,PCF_Metric metric)319   pcf_get_metric( FT_Stream   stream,
320                   FT_ULong    format,
321                   PCF_Metric  metric )
322   {
323     FT_Error  error = FT_Err_Ok;
324 
325 
326     if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
327     {
328       const FT_Frame_Field*  fields;
329 
330 
331       /* parsing normal metrics */
332       fields = ( PCF_BYTE_ORDER( format ) == MSBFirst )
333                ? pcf_metric_msb_header
334                : pcf_metric_header;
335 
336       /* the following sets `error' but doesn't return in case of failure */
337       (void)FT_STREAM_READ_FIELDS( fields, metric );
338     }
339     else
340     {
341       PCF_Compressed_MetricRec  compr;
342 
343 
344       /* parsing compressed metrics */
345       if ( FT_STREAM_READ_FIELDS( pcf_compressed_metric_header, &compr ) )
346         goto Exit;
347 
348       metric->leftSideBearing  = (FT_Short)( compr.leftSideBearing  - 0x80 );
349       metric->rightSideBearing = (FT_Short)( compr.rightSideBearing - 0x80 );
350       metric->characterWidth   = (FT_Short)( compr.characterWidth   - 0x80 );
351       metric->ascent           = (FT_Short)( compr.ascent           - 0x80 );
352       metric->descent          = (FT_Short)( compr.descent          - 0x80 );
353       metric->attributes       = 0;
354     }
355 
356     FT_TRACE5(( " width=%d,"
357                 " lsb=%d, rsb=%d,"
358                 " ascent=%d, descent=%d,"
359                 " attributes=%d\n",
360                 metric->characterWidth,
361                 metric->leftSideBearing,
362                 metric->rightSideBearing,
363                 metric->ascent,
364                 metric->descent,
365                 metric->attributes ));
366 
367   Exit:
368     return error;
369   }
370 
371 
372   static FT_Error
pcf_seek_to_table_type(FT_Stream stream,PCF_Table tables,FT_ULong ntables,FT_ULong type,FT_ULong * aformat,FT_ULong * asize)373   pcf_seek_to_table_type( FT_Stream  stream,
374                           PCF_Table  tables,
375                           FT_ULong   ntables, /* same as PCF_Toc->count */
376                           FT_ULong   type,
377                           FT_ULong  *aformat,
378                           FT_ULong  *asize )
379   {
380     FT_Error  error = FT_ERR( Invalid_File_Format );
381     FT_ULong  i;
382 
383 
384     for ( i = 0; i < ntables; i++ )
385       if ( tables[i].type == type )
386       {
387         if ( stream->pos > tables[i].offset )
388         {
389           error = FT_THROW( Invalid_Stream_Skip );
390           goto Fail;
391         }
392 
393         if ( FT_STREAM_SKIP( tables[i].offset - stream->pos ) )
394         {
395           error = FT_THROW( Invalid_Stream_Skip );
396           goto Fail;
397         }
398 
399         *asize   = tables[i].size;
400         *aformat = tables[i].format;
401 
402         return FT_Err_Ok;
403       }
404 
405   Fail:
406     *asize = 0;
407     return error;
408   }
409 
410 
411   static FT_Bool
pcf_has_table_type(PCF_Table tables,FT_ULong ntables,FT_ULong type)412   pcf_has_table_type( PCF_Table  tables,
413                       FT_ULong   ntables, /* same as PCF_Toc->count */
414                       FT_ULong   type )
415   {
416     FT_ULong  i;
417 
418 
419     for ( i = 0; i < ntables; i++ )
420       if ( tables[i].type == type )
421         return TRUE;
422 
423     return FALSE;
424   }
425 
426 
427 #define PCF_PROPERTY_SIZE  9
428 
429   static
430   const FT_Frame_Field  pcf_property_header[] =
431   {
432 #undef  FT_STRUCTURE
433 #define FT_STRUCTURE  PCF_ParsePropertyRec
434 
435     FT_FRAME_START( PCF_PROPERTY_SIZE ),
436       FT_FRAME_LONG_LE( name ),
437       FT_FRAME_BYTE   ( isString ),
438       FT_FRAME_LONG_LE( value ),
439     FT_FRAME_END
440   };
441 
442 
443   static
444   const FT_Frame_Field  pcf_property_msb_header[] =
445   {
446 #undef  FT_STRUCTURE
447 #define FT_STRUCTURE  PCF_ParsePropertyRec
448 
449     FT_FRAME_START( PCF_PROPERTY_SIZE ),
450       FT_FRAME_LONG( name ),
451       FT_FRAME_BYTE( isString ),
452       FT_FRAME_LONG( value ),
453     FT_FRAME_END
454   };
455 
456 
457   FT_LOCAL_DEF( PCF_Property )
pcf_find_property(PCF_Face face,const FT_String * prop)458   pcf_find_property( PCF_Face          face,
459                      const FT_String*  prop )
460   {
461     PCF_Property  properties = face->properties;
462     FT_Bool       found      = 0;
463     int           i;
464 
465 
466     for ( i = 0; i < face->nprops && !found; i++ )
467     {
468       if ( !ft_strcmp( properties[i].name, prop ) )
469         found = 1;
470     }
471 
472     if ( found )
473       return properties + i - 1;
474     else
475       return NULL;
476   }
477 
478 
479   static FT_Error
pcf_get_properties(FT_Stream stream,PCF_Face face)480   pcf_get_properties( FT_Stream  stream,
481                       PCF_Face   face )
482   {
483     PCF_ParseProperty  props      = NULL;
484     PCF_Property       properties = NULL;
485     FT_ULong           nprops, orig_nprops, i;
486     FT_ULong           format, size;
487     FT_Error           error;
488     FT_Memory          memory     = FT_FACE( face )->memory;
489     FT_ULong           string_size;
490     FT_String*         strings    = NULL;
491 
492 
493     error = pcf_seek_to_table_type( stream,
494                                     face->toc.tables,
495                                     face->toc.count,
496                                     PCF_PROPERTIES,
497                                     &format,
498                                     &size );
499     if ( error )
500       goto Bail;
501 
502     if ( FT_READ_ULONG_LE( format ) )
503       goto Bail;
504 
505     FT_TRACE4(( "pcf_get_properties:\n"
506                 "  format: 0x%lX (%s)\n",
507                 format,
508                 PCF_BYTE_ORDER( format ) == MSBFirst ? "MSB" : "LSB" ));
509 
510     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
511       goto Bail;
512 
513     if ( PCF_BYTE_ORDER( format ) == MSBFirst )
514       (void)FT_READ_ULONG( orig_nprops );
515     else
516       (void)FT_READ_ULONG_LE( orig_nprops );
517     if ( error )
518       goto Bail;
519 
520     FT_TRACE4(( "  number of properties: %ld\n", orig_nprops ));
521 
522     /* rough estimate */
523     if ( orig_nprops > size / PCF_PROPERTY_SIZE )
524     {
525       error = FT_THROW( Invalid_Table );
526       goto Bail;
527     }
528 
529     /* as a heuristic limit to avoid excessive allocation in */
530     /* gzip bombs (i.e., very small, invalid input data that */
531     /* pretends to expand to an insanely large file) we only */
532     /* load the first 256 properties                         */
533     if ( orig_nprops > 256 )
534     {
535       FT_TRACE0(( "pcf_get_properties:"
536                   " only loading first 256 properties\n" ));
537       nprops = 256;
538     }
539     else
540       nprops = orig_nprops;
541 
542     face->nprops = (int)nprops;
543 
544     if ( FT_NEW_ARRAY( props, nprops ) )
545       goto Bail;
546 
547     for ( i = 0; i < nprops; i++ )
548     {
549       if ( PCF_BYTE_ORDER( format ) == MSBFirst )
550       {
551         if ( FT_STREAM_READ_FIELDS( pcf_property_msb_header, props + i ) )
552           goto Bail;
553       }
554       else
555       {
556         if ( FT_STREAM_READ_FIELDS( pcf_property_header, props + i ) )
557           goto Bail;
558       }
559     }
560 
561     /* this skip will only work if we really have an extremely large */
562     /* number of properties; it will fail for fake data, avoiding an */
563     /* unnecessarily large allocation later on                       */
564     if ( FT_STREAM_SKIP( ( orig_nprops - nprops ) * PCF_PROPERTY_SIZE ) )
565     {
566       error = FT_THROW( Invalid_Stream_Skip );
567       goto Bail;
568     }
569 
570     /* pad the property array                                            */
571     /*                                                                   */
572     /* clever here - nprops is the same as the number of odd-units read, */
573     /* as only isStringProp are odd length   (Keith Packard)             */
574     /*                                                                   */
575     if ( orig_nprops & 3 )
576     {
577       i = 4 - ( orig_nprops & 3 );
578       if ( FT_STREAM_SKIP( i ) )
579       {
580         error = FT_THROW( Invalid_Stream_Skip );
581         goto Bail;
582       }
583     }
584 
585     if ( PCF_BYTE_ORDER( format ) == MSBFirst )
586       (void)FT_READ_ULONG( string_size );
587     else
588       (void)FT_READ_ULONG_LE( string_size );
589     if ( error )
590       goto Bail;
591 
592     FT_TRACE4(( "  string size: %ld\n", string_size ));
593 
594     /* rough estimate */
595     if ( string_size > size - orig_nprops * PCF_PROPERTY_SIZE )
596     {
597       error = FT_THROW( Invalid_Table );
598       goto Bail;
599     }
600 
601     /* the strings in the `strings' array are PostScript strings, */
602     /* which can have a maximum length of 65536 characters each   */
603     if ( string_size > 16777472 )   /* 256 * (65536 + 1) */
604     {
605       FT_TRACE0(( "pcf_get_properties:"
606                   " loading only 16777472 bytes of strings array\n" ));
607       string_size = 16777472;
608     }
609 
610     /* allocate one more byte so that we have a final null byte */
611     if ( FT_NEW_ARRAY( strings, string_size + 1 ) )
612       goto Bail;
613 
614     error = FT_Stream_Read( stream, (FT_Byte*)strings, string_size );
615     if ( error )
616       goto Bail;
617 
618     if ( FT_NEW_ARRAY( properties, nprops ) )
619       goto Bail;
620 
621     face->properties = properties;
622 
623     FT_TRACE4(( "\n" ));
624     for ( i = 0; i < nprops; i++ )
625     {
626       FT_Long  name_offset = props[i].name;
627 
628 
629       if ( ( name_offset < 0 )                     ||
630            ( (FT_ULong)name_offset > string_size ) )
631       {
632         error = FT_THROW( Invalid_Offset );
633         goto Bail;
634       }
635 
636       if ( FT_STRDUP( properties[i].name, strings + name_offset ) )
637         goto Bail;
638 
639       FT_TRACE4(( "  %s:", properties[i].name ));
640 
641       properties[i].isString = props[i].isString;
642 
643       if ( props[i].isString )
644       {
645         FT_Long  value_offset = props[i].value;
646 
647 
648         if ( ( value_offset < 0 )                     ||
649              ( (FT_ULong)value_offset > string_size ) )
650         {
651           error = FT_THROW( Invalid_Offset );
652           goto Bail;
653         }
654 
655         if ( FT_STRDUP( properties[i].value.atom, strings + value_offset ) )
656           goto Bail;
657 
658         FT_TRACE4(( " `%s'\n", properties[i].value.atom ));
659       }
660       else
661       {
662         properties[i].value.l = props[i].value;
663 
664         FT_TRACE4(( " %d\n", properties[i].value.l ));
665       }
666     }
667 
668     error = FT_Err_Ok;
669 
670   Bail:
671     FT_FREE( props );
672     FT_FREE( strings );
673 
674     return error;
675   }
676 
677 
678   static FT_Error
pcf_get_metrics(FT_Stream stream,PCF_Face face)679   pcf_get_metrics( FT_Stream  stream,
680                    PCF_Face   face )
681   {
682     FT_Error    error;
683     FT_Memory   memory  = FT_FACE( face )->memory;
684     FT_ULong    format, size;
685     PCF_Metric  metrics = NULL;
686     FT_ULong    nmetrics, orig_nmetrics, i;
687 
688 
689     error = pcf_seek_to_table_type( stream,
690                                     face->toc.tables,
691                                     face->toc.count,
692                                     PCF_METRICS,
693                                     &format,
694                                     &size );
695     if ( error )
696       return error;
697 
698     if ( FT_READ_ULONG_LE( format ) )
699       goto Bail;
700 
701     FT_TRACE4(( "pcf_get_metrics:\n"
702                 "  format: 0x%lX (%s, %s)\n",
703                 format,
704                 PCF_BYTE_ORDER( format ) == MSBFirst ? "MSB" : "LSB",
705                 PCF_FORMAT_MATCH( format, PCF_COMPRESSED_METRICS ) ?
706                   "compressed" : "uncompressed" ));
707 
708     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT )     &&
709          !PCF_FORMAT_MATCH( format, PCF_COMPRESSED_METRICS ) )
710       return FT_THROW( Invalid_File_Format );
711 
712     if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
713     {
714       if ( PCF_BYTE_ORDER( format ) == MSBFirst )
715         (void)FT_READ_ULONG( orig_nmetrics );
716       else
717         (void)FT_READ_ULONG_LE( orig_nmetrics );
718     }
719     else
720     {
721       if ( PCF_BYTE_ORDER( format ) == MSBFirst )
722         (void)FT_READ_USHORT( orig_nmetrics );
723       else
724         (void)FT_READ_USHORT_LE( orig_nmetrics );
725     }
726     if ( error )
727       return FT_THROW( Invalid_File_Format );
728 
729     FT_TRACE4(( "  number of metrics: %ld\n", orig_nmetrics ));
730 
731     /* rough estimate */
732     if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
733     {
734       if ( orig_nmetrics > size / PCF_METRIC_SIZE )
735         return FT_THROW( Invalid_Table );
736     }
737     else
738     {
739       if ( orig_nmetrics > size / PCF_COMPRESSED_METRIC_SIZE )
740         return FT_THROW( Invalid_Table );
741     }
742 
743     if ( !orig_nmetrics )
744       return FT_THROW( Invalid_Table );
745 
746     /* PCF is a format from ancient times; Unicode was in its       */
747     /* infancy, and widely used two-byte character sets for CJK     */
748     /* scripts (Big 5, GB 2312, JIS X 0208, etc.) did have at most  */
749     /* 15000 characters.  Even the more exotic CNS 11643 and CCCII  */
750     /* standards, which were essentially three-byte character sets, */
751     /* provided less then 65536 assigned characters.                */
752     /*                                                              */
753     /* While technically possible to have a larger number of glyphs */
754     /* in PCF files, we thus limit the number to 65536.             */
755     if ( orig_nmetrics > 65536 )
756     {
757       FT_TRACE0(( "pcf_get_metrics:"
758                   " only loading first 65536 metrics\n" ));
759       nmetrics = 65536;
760     }
761     else
762       nmetrics = orig_nmetrics;
763 
764     face->nmetrics = nmetrics;
765 
766     if ( FT_NEW_ARRAY( face->metrics, nmetrics ) )
767       return error;
768 
769     metrics = face->metrics;
770 
771     FT_TRACE4(( "\n" ));
772     for ( i = 0; i < nmetrics; i++, metrics++ )
773     {
774       FT_TRACE5(( "  idx %ld:", i ));
775       error = pcf_get_metric( stream, format, metrics );
776 
777       metrics->bits = 0;
778 
779       if ( error )
780         break;
781 
782       /* sanity checks -- those values are used in `PCF_Glyph_Load' to     */
783       /* compute a glyph's bitmap dimensions, thus setting them to zero in */
784       /* case of an error disables this particular glyph only              */
785       if ( metrics->rightSideBearing < metrics->leftSideBearing ||
786            metrics->ascent < -metrics->descent                  )
787       {
788         metrics->characterWidth   = 0;
789         metrics->leftSideBearing  = 0;
790         metrics->rightSideBearing = 0;
791         metrics->ascent           = 0;
792         metrics->descent          = 0;
793 
794         FT_TRACE0(( "pcf_get_metrics:"
795                     " invalid metrics for glyph %d\n", i ));
796       }
797     }
798 
799     if ( error )
800       FT_FREE( face->metrics );
801 
802   Bail:
803     return error;
804   }
805 
806 
807   static FT_Error
pcf_get_bitmaps(FT_Stream stream,PCF_Face face)808   pcf_get_bitmaps( FT_Stream  stream,
809                    PCF_Face   face )
810   {
811     FT_Error   error;
812     FT_Memory  memory  = FT_FACE( face )->memory;
813     FT_Long*   offsets = NULL;
814     FT_Long    bitmapSizes[GLYPHPADOPTIONS];
815     FT_ULong   format, size;
816     FT_ULong   nbitmaps, orig_nbitmaps, i, sizebitmaps = 0;
817 
818 
819     error = pcf_seek_to_table_type( stream,
820                                     face->toc.tables,
821                                     face->toc.count,
822                                     PCF_BITMAPS,
823                                     &format,
824                                     &size );
825     if ( error )
826       return error;
827 
828     error = FT_Stream_EnterFrame( stream, 8 );
829     if ( error )
830       return error;
831 
832     format = FT_GET_ULONG_LE();
833     if ( PCF_BYTE_ORDER( format ) == MSBFirst )
834       orig_nbitmaps = FT_GET_ULONG();
835     else
836       orig_nbitmaps = FT_GET_ULONG_LE();
837 
838     FT_Stream_ExitFrame( stream );
839 
840     FT_TRACE4(( "pcf_get_bitmaps:\n"
841                 "  format: 0x%lX\n"
842                 "          (%s, %s,\n"
843                 "           padding=%d bit%s, scanning=%d bit%s)\n",
844                 format,
845                 PCF_BYTE_ORDER( format ) == MSBFirst
846                   ? "most significant byte first"
847                   : "least significant byte first",
848                 PCF_BIT_ORDER( format ) == MSBFirst
849                   ? "most significant bit first"
850                   : "least significant bit first",
851                 8 << PCF_GLYPH_PAD_INDEX( format ),
852                 ( 8 << PCF_GLYPH_PAD_INDEX( format ) ) == 1 ? "" : "s",
853                 8 << PCF_SCAN_UNIT_INDEX( format ),
854                 ( 8 << PCF_SCAN_UNIT_INDEX( format ) ) == 1 ? "" : "s" ));
855 
856     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
857       return FT_THROW( Invalid_File_Format );
858 
859     FT_TRACE4(( "  number of bitmaps: %ld\n", orig_nbitmaps ));
860 
861     /* see comment in `pcf_get_metrics' */
862     if ( orig_nbitmaps > 65536 )
863     {
864       FT_TRACE0(( "pcf_get_bitmaps:"
865                   " only loading first 65536 bitmaps\n" ));
866       nbitmaps = 65536;
867     }
868     else
869       nbitmaps = orig_nbitmaps;
870 
871     if ( nbitmaps != face->nmetrics )
872       return FT_THROW( Invalid_File_Format );
873 
874     if ( FT_NEW_ARRAY( offsets, nbitmaps ) )
875       return error;
876 
877     FT_TRACE5(( "\n" ));
878     for ( i = 0; i < nbitmaps; i++ )
879     {
880       if ( PCF_BYTE_ORDER( format ) == MSBFirst )
881         (void)FT_READ_LONG( offsets[i] );
882       else
883         (void)FT_READ_LONG_LE( offsets[i] );
884 
885       FT_TRACE5(( "  bitmap %ld: offset %ld (0x%lX)\n",
886                   i, offsets[i], offsets[i] ));
887     }
888     if ( error )
889       goto Bail;
890 
891     for ( i = 0; i < GLYPHPADOPTIONS; i++ )
892     {
893       if ( PCF_BYTE_ORDER( format ) == MSBFirst )
894         (void)FT_READ_LONG( bitmapSizes[i] );
895       else
896         (void)FT_READ_LONG_LE( bitmapSizes[i] );
897       if ( error )
898         goto Bail;
899 
900       sizebitmaps = (FT_ULong)bitmapSizes[PCF_GLYPH_PAD_INDEX( format )];
901 
902       FT_TRACE4(( "  %ld-bit padding implies a size of %ld\n",
903                   8 << i, bitmapSizes[i] ));
904     }
905 
906     FT_TRACE4(( "  %ld bitmaps, using %ld-bit padding\n",
907                 nbitmaps,
908                 8 << PCF_GLYPH_PAD_INDEX( format ) ));
909     FT_TRACE4(( "  bitmap size: %ld\n", sizebitmaps ));
910 
911     FT_UNUSED( sizebitmaps );       /* only used for debugging */
912 
913     /* right now, we only check the bitmap offsets; */
914     /* actual bitmaps are only loaded on demand     */
915     for ( i = 0; i < nbitmaps; i++ )
916     {
917       /* rough estimate */
918       if ( ( offsets[i] < 0 )              ||
919            ( (FT_ULong)offsets[i] > size ) )
920       {
921         FT_TRACE0(( "pcf_get_bitmaps:"
922                     " invalid offset to bitmap data of glyph %ld\n", i ));
923       }
924       else
925         face->metrics[i].bits = stream->pos + (FT_ULong)offsets[i];
926     }
927 
928     face->bitmapsFormat = format;
929 
930   Bail:
931     FT_FREE( offsets );
932     return error;
933   }
934 
935 
936   static FT_Error
pcf_get_encodings(FT_Stream stream,PCF_Face face)937   pcf_get_encodings( FT_Stream  stream,
938                      PCF_Face   face )
939   {
940     FT_Error      error;
941     FT_Memory     memory = FT_FACE( face )->memory;
942     FT_ULong      format, size;
943     int           firstCol, lastCol;
944     int           firstRow, lastRow;
945     FT_ULong      nencoding;
946     FT_UShort     encodingOffset;
947     int           i, j;
948     FT_ULong      k;
949     PCF_Encoding  encoding = NULL;
950 
951 
952     error = pcf_seek_to_table_type( stream,
953                                     face->toc.tables,
954                                     face->toc.count,
955                                     PCF_BDF_ENCODINGS,
956                                     &format,
957                                     &size );
958     if ( error )
959       return error;
960 
961     error = FT_Stream_EnterFrame( stream, 14 );
962     if ( error )
963       return error;
964 
965     format = FT_GET_ULONG_LE();
966 
967     if ( PCF_BYTE_ORDER( format ) == MSBFirst )
968     {
969       firstCol          = FT_GET_SHORT();
970       lastCol           = FT_GET_SHORT();
971       firstRow          = FT_GET_SHORT();
972       lastRow           = FT_GET_SHORT();
973       face->defaultChar = FT_GET_SHORT();
974     }
975     else
976     {
977       firstCol          = FT_GET_SHORT_LE();
978       lastCol           = FT_GET_SHORT_LE();
979       firstRow          = FT_GET_SHORT_LE();
980       lastRow           = FT_GET_SHORT_LE();
981       face->defaultChar = FT_GET_SHORT_LE();
982     }
983 
984     FT_Stream_ExitFrame( stream );
985 
986     FT_TRACE4(( "pcf_get_encodings:\n"
987                 "  format: 0x%lX (%s)\n",
988                 format,
989                 PCF_BYTE_ORDER( format ) == MSBFirst ? "MSB" : "LSB" ));
990 
991     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
992       return FT_THROW( Invalid_File_Format );
993 
994     FT_TRACE4(( "  firstCol 0x%X, lastCol 0x%X\n"
995                 "  firstRow 0x%X, lastRow 0x%X\n",
996                 firstCol, lastCol,
997                 firstRow, lastRow ));
998 
999     /* sanity checks; we limit numbers of rows and columns to 256 */
1000     if ( firstCol < 0       ||
1001          firstCol > lastCol ||
1002          lastCol  > 0xFF    ||
1003          firstRow < 0       ||
1004          firstRow > lastRow ||
1005          lastRow  > 0xFF    )
1006       return FT_THROW( Invalid_Table );
1007 
1008     nencoding = (FT_ULong)( lastCol - firstCol + 1 ) *
1009                 (FT_ULong)( lastRow - firstRow + 1 );
1010 
1011     if ( FT_NEW_ARRAY( encoding, nencoding ) )
1012       return error;
1013 
1014     error = FT_Stream_EnterFrame( stream, 2 * nencoding );
1015     if ( error )
1016       goto Bail;
1017 
1018     FT_TRACE5(( "\n" ));
1019 
1020     k = 0;
1021     for ( i = firstRow; i <= lastRow; i++ )
1022     {
1023       for ( j = firstCol; j <= lastCol; j++ )
1024       {
1025         /* X11's reference implementation uses the equivalent to  */
1026         /* `FT_GET_SHORT', however PCF fonts with more than 32768 */
1027         /* characters (e.g. `unifont.pcf') clearly show that an   */
1028         /* unsigned value is needed.                              */
1029         if ( PCF_BYTE_ORDER( format ) == MSBFirst )
1030           encodingOffset = FT_GET_USHORT();
1031         else
1032           encodingOffset = FT_GET_USHORT_LE();
1033 
1034         if ( encodingOffset != 0xFFFFU )
1035         {
1036           encoding[k].enc   = i * 256 + j;
1037           encoding[k].glyph = encodingOffset;
1038 
1039           FT_TRACE5(( "  code %d (0x%04X): idx %d\n",
1040                       encoding[k].enc, encoding[k].enc, encoding[k].glyph ));
1041 
1042           k++;
1043         }
1044       }
1045     }
1046     FT_Stream_ExitFrame( stream );
1047 
1048     if ( FT_RENEW_ARRAY( encoding, nencoding, k ) )
1049       goto Bail;
1050 
1051     face->nencodings = k;
1052     face->encodings  = encoding;
1053 
1054     return error;
1055 
1056   Bail:
1057     FT_FREE( encoding );
1058     return error;
1059   }
1060 
1061 
1062   static
1063   const FT_Frame_Field  pcf_accel_header[] =
1064   {
1065 #undef  FT_STRUCTURE
1066 #define FT_STRUCTURE  PCF_AccelRec
1067 
1068     FT_FRAME_START( 20 ),
1069       FT_FRAME_BYTE      ( noOverlap ),
1070       FT_FRAME_BYTE      ( constantMetrics ),
1071       FT_FRAME_BYTE      ( terminalFont ),
1072       FT_FRAME_BYTE      ( constantWidth ),
1073       FT_FRAME_BYTE      ( inkInside ),
1074       FT_FRAME_BYTE      ( inkMetrics ),
1075       FT_FRAME_BYTE      ( drawDirection ),
1076       FT_FRAME_SKIP_BYTES( 1 ),
1077       FT_FRAME_LONG_LE   ( fontAscent ),
1078       FT_FRAME_LONG_LE   ( fontDescent ),
1079       FT_FRAME_LONG_LE   ( maxOverlap ),
1080     FT_FRAME_END
1081   };
1082 
1083 
1084   static
1085   const FT_Frame_Field  pcf_accel_msb_header[] =
1086   {
1087 #undef  FT_STRUCTURE
1088 #define FT_STRUCTURE  PCF_AccelRec
1089 
1090     FT_FRAME_START( 20 ),
1091       FT_FRAME_BYTE      ( noOverlap ),
1092       FT_FRAME_BYTE      ( constantMetrics ),
1093       FT_FRAME_BYTE      ( terminalFont ),
1094       FT_FRAME_BYTE      ( constantWidth ),
1095       FT_FRAME_BYTE      ( inkInside ),
1096       FT_FRAME_BYTE      ( inkMetrics ),
1097       FT_FRAME_BYTE      ( drawDirection ),
1098       FT_FRAME_SKIP_BYTES( 1 ),
1099       FT_FRAME_LONG      ( fontAscent ),
1100       FT_FRAME_LONG      ( fontDescent ),
1101       FT_FRAME_LONG      ( maxOverlap ),
1102     FT_FRAME_END
1103   };
1104 
1105 
1106   static FT_Error
pcf_get_accel(FT_Stream stream,PCF_Face face,FT_ULong type)1107   pcf_get_accel( FT_Stream  stream,
1108                  PCF_Face   face,
1109                  FT_ULong   type )
1110   {
1111     FT_ULong   format, size;
1112     FT_Error   error;
1113     PCF_Accel  accel = &face->accel;
1114 
1115 
1116     error = pcf_seek_to_table_type( stream,
1117                                     face->toc.tables,
1118                                     face->toc.count,
1119                                     type,
1120                                     &format,
1121                                     &size );
1122     if ( error )
1123       goto Bail;
1124 
1125     if ( FT_READ_ULONG_LE( format ) )
1126       goto Bail;
1127 
1128     FT_TRACE4(( "pcf_get_accel%s:\n"
1129                 "  format: 0x%lX (%s, %s)\n",
1130                 type == PCF_BDF_ACCELERATORS ? " (getting BDF accelerators)"
1131                                              : "",
1132                 format,
1133                 PCF_BYTE_ORDER( format ) == MSBFirst ? "MSB" : "LSB",
1134                 PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) ?
1135                   "accelerated" : "not accelerated" ));
1136 
1137     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT )    &&
1138          !PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) )
1139       goto Bail;
1140 
1141     if ( PCF_BYTE_ORDER( format ) == MSBFirst )
1142     {
1143       if ( FT_STREAM_READ_FIELDS( pcf_accel_msb_header, accel ) )
1144         goto Bail;
1145     }
1146     else
1147     {
1148       if ( FT_STREAM_READ_FIELDS( pcf_accel_header, accel ) )
1149         goto Bail;
1150     }
1151 
1152     FT_TRACE5(( "  noOverlap=%s, constantMetrics=%s,"
1153                 " terminalFont=%s, constantWidth=%s\n"
1154                 "  inkInside=%s, inkMetrics=%s, drawDirection=%s\n"
1155                 "  fontAscent=%ld, fontDescent=%ld, maxOverlap=%ld\n",
1156                 accel->noOverlap ? "yes" : "no",
1157                 accel->constantMetrics ? "yes" : "no",
1158                 accel->terminalFont ? "yes" : "no",
1159                 accel->constantWidth ? "yes" : "no",
1160                 accel->inkInside ? "yes" : "no",
1161                 accel->inkMetrics ? "yes" : "no",
1162                 accel->drawDirection ? "RTL" : "LTR",
1163                 accel->fontAscent,
1164                 accel->fontDescent,
1165                 accel->maxOverlap ));
1166 
1167     /* sanity checks */
1168     if ( FT_ABS( accel->fontAscent ) > 0x7FFF )
1169     {
1170       accel->fontAscent = accel->fontAscent < 0 ? -0x7FFF : 0x7FFF;
1171       FT_TRACE0(( "pfc_get_accel: clamping font ascent to value %d\n",
1172                   accel->fontAscent ));
1173     }
1174     if ( FT_ABS( accel->fontDescent ) > 0x7FFF )
1175     {
1176       accel->fontDescent = accel->fontDescent < 0 ? -0x7FFF : 0x7FFF;
1177       FT_TRACE0(( "pfc_get_accel: clamping font descent to value %d\n",
1178                   accel->fontDescent ));
1179     }
1180 
1181     FT_TRACE5(( "  minbounds:" ));
1182     error = pcf_get_metric( stream,
1183                             format & ( ~PCF_FORMAT_MASK ),
1184                             &(accel->minbounds) );
1185     if ( error )
1186       goto Bail;
1187 
1188     FT_TRACE5(( "  maxbounds:" ));
1189     error = pcf_get_metric( stream,
1190                             format & ( ~PCF_FORMAT_MASK ),
1191                             &(accel->maxbounds) );
1192     if ( error )
1193       goto Bail;
1194 
1195     if ( PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) )
1196     {
1197       FT_TRACE5(( "  ink minbounds:" ));
1198       error = pcf_get_metric( stream,
1199                               format & ( ~PCF_FORMAT_MASK ),
1200                               &(accel->ink_minbounds) );
1201       if ( error )
1202         goto Bail;
1203 
1204       FT_TRACE5(( "  ink maxbounds:" ));
1205       error = pcf_get_metric( stream,
1206                               format & ( ~PCF_FORMAT_MASK ),
1207                               &(accel->ink_maxbounds) );
1208       if ( error )
1209         goto Bail;
1210     }
1211     else
1212     {
1213       accel->ink_minbounds = accel->minbounds;
1214       accel->ink_maxbounds = accel->maxbounds;
1215     }
1216 
1217   Bail:
1218     return error;
1219   }
1220 
1221 
1222   static FT_Error
pcf_interpret_style(PCF_Face pcf)1223   pcf_interpret_style( PCF_Face  pcf )
1224   {
1225     FT_Error   error  = FT_Err_Ok;
1226     FT_Face    face   = FT_FACE( pcf );
1227     FT_Memory  memory = face->memory;
1228 
1229     PCF_Property  prop;
1230 
1231     size_t  nn, len;
1232     char*   strings[4] = { NULL, NULL, NULL, NULL };
1233     size_t  lengths[4];
1234 
1235 
1236     face->style_flags = 0;
1237 
1238     prop = pcf_find_property( pcf, "SLANT" );
1239     if ( prop && prop->isString                                       &&
1240          ( *(prop->value.atom) == 'O' || *(prop->value.atom) == 'o' ||
1241            *(prop->value.atom) == 'I' || *(prop->value.atom) == 'i' ) )
1242     {
1243       face->style_flags |= FT_STYLE_FLAG_ITALIC;
1244       strings[2] = ( *(prop->value.atom) == 'O' ||
1245                      *(prop->value.atom) == 'o' ) ? (char *)"Oblique"
1246                                                   : (char *)"Italic";
1247     }
1248 
1249     prop = pcf_find_property( pcf, "WEIGHT_NAME" );
1250     if ( prop && prop->isString                                       &&
1251          ( *(prop->value.atom) == 'B' || *(prop->value.atom) == 'b' ) )
1252     {
1253       face->style_flags |= FT_STYLE_FLAG_BOLD;
1254       strings[1] = (char*)"Bold";
1255     }
1256 
1257     prop = pcf_find_property( pcf, "SETWIDTH_NAME" );
1258     if ( prop && prop->isString                                        &&
1259          *(prop->value.atom)                                           &&
1260          !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) )
1261       strings[3] = (char*)( prop->value.atom );
1262 
1263     prop = pcf_find_property( pcf, "ADD_STYLE_NAME" );
1264     if ( prop && prop->isString                                        &&
1265          *(prop->value.atom)                                           &&
1266          !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) )
1267       strings[0] = (char*)( prop->value.atom );
1268 
1269     for ( len = 0, nn = 0; nn < 4; nn++ )
1270     {
1271       lengths[nn] = 0;
1272       if ( strings[nn] )
1273       {
1274         lengths[nn] = ft_strlen( strings[nn] );
1275         len        += lengths[nn] + 1;
1276       }
1277     }
1278 
1279     if ( len == 0 )
1280     {
1281       strings[0] = (char*)"Regular";
1282       lengths[0] = ft_strlen( strings[0] );
1283       len        = lengths[0] + 1;
1284     }
1285 
1286     {
1287       char*  s;
1288 
1289 
1290       if ( FT_ALLOC( face->style_name, len ) )
1291         return error;
1292 
1293       s = face->style_name;
1294 
1295       for ( nn = 0; nn < 4; nn++ )
1296       {
1297         char*  src = strings[nn];
1298 
1299 
1300         len = lengths[nn];
1301 
1302         if ( !src )
1303           continue;
1304 
1305         /* separate elements with a space */
1306         if ( s != face->style_name )
1307           *s++ = ' ';
1308 
1309         ft_memcpy( s, src, len );
1310 
1311         /* need to convert spaces to dashes for */
1312         /* add_style_name and setwidth_name     */
1313         if ( nn == 0 || nn == 3 )
1314         {
1315           size_t  mm;
1316 
1317 
1318           for ( mm = 0; mm < len; mm++ )
1319             if ( s[mm] == ' ' )
1320               s[mm] = '-';
1321         }
1322 
1323         s += len;
1324       }
1325       *s = 0;
1326     }
1327 
1328     return error;
1329   }
1330 
1331 
1332   FT_LOCAL_DEF( FT_Error )
pcf_load_font(FT_Stream stream,PCF_Face face,FT_Long face_index)1333   pcf_load_font( FT_Stream  stream,
1334                  PCF_Face   face,
1335                  FT_Long    face_index )
1336   {
1337     FT_Face    root   = FT_FACE( face );
1338     FT_Error   error;
1339     FT_Memory  memory = FT_FACE( face )->memory;
1340     FT_Bool    hasBDFAccelerators;
1341 
1342 
1343     error = pcf_read_TOC( stream, face );
1344     if ( error )
1345       goto Exit;
1346 
1347     root->num_faces  = 1;
1348     root->face_index = 0;
1349 
1350     /* If we are performing a simple font format check, exit immediately. */
1351     if ( face_index < 0 )
1352       return FT_Err_Ok;
1353 
1354     error = pcf_get_properties( stream, face );
1355     if ( error )
1356       goto Exit;
1357 
1358     /* Use the old accelerators if no BDF accelerators are in the file. */
1359     hasBDFAccelerators = pcf_has_table_type( face->toc.tables,
1360                                              face->toc.count,
1361                                              PCF_BDF_ACCELERATORS );
1362     if ( !hasBDFAccelerators )
1363     {
1364       error = pcf_get_accel( stream, face, PCF_ACCELERATORS );
1365       if ( error )
1366         goto Exit;
1367     }
1368 
1369     /* metrics */
1370     error = pcf_get_metrics( stream, face );
1371     if ( error )
1372       goto Exit;
1373 
1374     /* bitmaps */
1375     error = pcf_get_bitmaps( stream, face );
1376     if ( error )
1377       goto Exit;
1378 
1379     /* encodings */
1380     error = pcf_get_encodings( stream, face );
1381     if ( error )
1382       goto Exit;
1383 
1384     /* BDF style accelerators (i.e. bounds based on encoded glyphs) */
1385     if ( hasBDFAccelerators )
1386     {
1387       error = pcf_get_accel( stream, face, PCF_BDF_ACCELERATORS );
1388       if ( error )
1389         goto Exit;
1390     }
1391 
1392     /* XXX: TO DO: inkmetrics and glyph_names are missing */
1393 
1394     /* now construct the face object */
1395     {
1396       PCF_Property  prop;
1397 
1398 
1399       root->face_flags |= FT_FACE_FLAG_FIXED_SIZES |
1400                           FT_FACE_FLAG_HORIZONTAL  |
1401                           FT_FACE_FLAG_FAST_GLYPHS;
1402 
1403       if ( face->accel.constantWidth )
1404         root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
1405 
1406       if ( FT_SET_ERROR( pcf_interpret_style( face ) ) )
1407         goto Exit;
1408 
1409       prop = pcf_find_property( face, "FAMILY_NAME" );
1410       if ( prop && prop->isString )
1411       {
1412 
1413 #ifdef PCF_CONFIG_OPTION_LONG_FAMILY_NAMES
1414 
1415         PCF_Driver  driver = (PCF_Driver)FT_FACE_DRIVER( face );
1416 
1417 
1418         if ( !driver->no_long_family_names )
1419         {
1420           /* Prepend the foundry name plus a space to the family name.     */
1421           /* There are many fonts just called `Fixed' which look           */
1422           /* completely different, and which have nothing to do with each  */
1423           /* other.  When selecting `Fixed' in KDE or Gnome one gets       */
1424           /* results that appear rather random, the style changes often if */
1425           /* one changes the size and one cannot select some fonts at all. */
1426           /*                                                               */
1427           /* We also check whether we have `wide' characters; all put      */
1428           /* together, we get family names like `Sony Fixed' or `Misc      */
1429           /* Fixed Wide'.                                                  */
1430 
1431           PCF_Property  foundry_prop, point_size_prop, average_width_prop;
1432 
1433           int  l    = ft_strlen( prop->value.atom ) + 1;
1434           int  wide = 0;
1435 
1436 
1437           foundry_prop       = pcf_find_property( face, "FOUNDRY" );
1438           point_size_prop    = pcf_find_property( face, "POINT_SIZE" );
1439           average_width_prop = pcf_find_property( face, "AVERAGE_WIDTH" );
1440 
1441           if ( point_size_prop && average_width_prop )
1442           {
1443             if ( average_width_prop->value.l >= point_size_prop->value.l )
1444             {
1445               /* This font is at least square shaped or even wider */
1446               wide = 1;
1447               l   += ft_strlen( " Wide" );
1448             }
1449           }
1450 
1451           if ( foundry_prop && foundry_prop->isString )
1452           {
1453             l += ft_strlen( foundry_prop->value.atom ) + 1;
1454 
1455             if ( FT_NEW_ARRAY( root->family_name, l ) )
1456               goto Exit;
1457 
1458             ft_strcpy( root->family_name, foundry_prop->value.atom );
1459             ft_strcat( root->family_name, " " );
1460             ft_strcat( root->family_name, prop->value.atom );
1461           }
1462           else
1463           {
1464             if ( FT_NEW_ARRAY( root->family_name, l ) )
1465               goto Exit;
1466 
1467             ft_strcpy( root->family_name, prop->value.atom );
1468           }
1469 
1470           if ( wide )
1471             ft_strcat( root->family_name, " Wide" );
1472         }
1473         else
1474 
1475 #endif /* PCF_CONFIG_OPTION_LONG_FAMILY_NAMES */
1476 
1477         {
1478           if ( FT_STRDUP( root->family_name, prop->value.atom ) )
1479             goto Exit;
1480         }
1481       }
1482       else
1483         root->family_name = NULL;
1484 
1485       /*
1486        * Note: We shift all glyph indices by +1 since we must
1487        * respect the convention that glyph 0 always corresponds
1488        * to the `missing glyph'.
1489        *
1490        * This implies bumping the number of `available' glyphs by 1.
1491        */
1492       root->num_glyphs = (FT_Long)( face->nmetrics + 1 );
1493 
1494       root->num_fixed_sizes = 1;
1495       if ( FT_NEW_ARRAY( root->available_sizes, 1 ) )
1496         goto Exit;
1497 
1498       {
1499         FT_Bitmap_Size*  bsize = root->available_sizes;
1500         FT_Short         resolution_x = 0, resolution_y = 0;
1501 
1502 
1503         FT_ZERO( bsize );
1504 
1505         /* for simplicity, we take absolute values of integer properties */
1506 
1507 #if 0
1508         bsize->height = face->accel.maxbounds.ascent << 6;
1509 #endif
1510 
1511 #ifdef FT_DEBUG_LEVEL_TRACE
1512         if ( face->accel.fontAscent + face->accel.fontDescent < 0 )
1513           FT_TRACE0(( "pcf_load_font: negative height\n" ));
1514 #endif
1515         if ( FT_ABS( face->accel.fontAscent +
1516                      face->accel.fontDescent ) > 0x7FFF )
1517         {
1518           bsize->height = 0x7FFF;
1519           FT_TRACE0(( "pcf_load_font: clamping height to value %d\n",
1520                       bsize->height ));
1521         }
1522         else
1523           bsize->height = FT_ABS( (FT_Short)( face->accel.fontAscent +
1524                                               face->accel.fontDescent ) );
1525 
1526         prop = pcf_find_property( face, "AVERAGE_WIDTH" );
1527         if ( prop )
1528         {
1529 #ifdef FT_DEBUG_LEVEL_TRACE
1530           if ( prop->value.l < 0 )
1531             FT_TRACE0(( "pcf_load_font: negative average width\n" ));
1532 #endif
1533           if ( ( FT_ABS( prop->value.l ) > 0x7FFFL * 10 - 5 ) )
1534           {
1535             bsize->width = 0x7FFF;
1536             FT_TRACE0(( "pcf_load_font: clamping average width to value %d\n",
1537                         bsize->width ));
1538           }
1539           else
1540             bsize->width = FT_ABS( (FT_Short)( ( prop->value.l + 5 ) / 10 ) );
1541         }
1542         else
1543         {
1544           /* this is a heuristical value */
1545           bsize->width = (FT_Short)FT_MulDiv( bsize->height, 2, 3 );
1546         }
1547 
1548         prop = pcf_find_property( face, "POINT_SIZE" );
1549         if ( prop )
1550         {
1551 #ifdef FT_DEBUG_LEVEL_TRACE
1552           if ( prop->value.l < 0 )
1553             FT_TRACE0(( "pcf_load_font: negative point size\n" ));
1554 #endif
1555           /* convert from 722.7 decipoints to 72 points per inch */
1556           if ( FT_ABS( prop->value.l ) > 0x504C2L ) /* 0x7FFF * 72270/7200 */
1557           {
1558             bsize->size = 0x7FFF;
1559             FT_TRACE0(( "pcf_load_font: clamping point size to value %d\n",
1560                         bsize->size ));
1561           }
1562           else
1563             bsize->size = FT_MulDiv( FT_ABS( prop->value.l ),
1564                                      64 * 7200,
1565                                      72270L );
1566         }
1567 
1568         prop = pcf_find_property( face, "PIXEL_SIZE" );
1569         if ( prop )
1570         {
1571 #ifdef FT_DEBUG_LEVEL_TRACE
1572           if ( prop->value.l < 0 )
1573             FT_TRACE0(( "pcf_load_font: negative pixel size\n" ));
1574 #endif
1575           if ( FT_ABS( prop->value.l ) > 0x7FFF )
1576           {
1577             bsize->y_ppem = 0x7FFF << 6;
1578             FT_TRACE0(( "pcf_load_font: clamping pixel size to value %d\n",
1579                         bsize->y_ppem ));
1580           }
1581           else
1582             bsize->y_ppem = FT_ABS( (FT_Short)prop->value.l ) << 6;
1583         }
1584 
1585         prop = pcf_find_property( face, "RESOLUTION_X" );
1586         if ( prop )
1587         {
1588 #ifdef FT_DEBUG_LEVEL_TRACE
1589           if ( prop->value.l < 0 )
1590             FT_TRACE0(( "pcf_load_font: negative X resolution\n" ));
1591 #endif
1592           if ( FT_ABS( prop->value.l ) > 0x7FFF )
1593           {
1594             resolution_x = 0x7FFF;
1595             FT_TRACE0(( "pcf_load_font: clamping X resolution to value %d\n",
1596                         resolution_x ));
1597           }
1598           else
1599             resolution_x = FT_ABS( (FT_Short)prop->value.l );
1600         }
1601 
1602         prop = pcf_find_property( face, "RESOLUTION_Y" );
1603         if ( prop )
1604         {
1605 #ifdef FT_DEBUG_LEVEL_TRACE
1606           if ( prop->value.l < 0 )
1607             FT_TRACE0(( "pcf_load_font: negative Y resolution\n" ));
1608 #endif
1609           if ( FT_ABS( prop->value.l ) > 0x7FFF )
1610           {
1611             resolution_y = 0x7FFF;
1612             FT_TRACE0(( "pcf_load_font: clamping Y resolution to value %d\n",
1613                         resolution_y ));
1614           }
1615           else
1616             resolution_y = FT_ABS( (FT_Short)prop->value.l );
1617         }
1618 
1619         if ( bsize->y_ppem == 0 )
1620         {
1621           bsize->y_ppem = bsize->size;
1622           if ( resolution_y )
1623             bsize->y_ppem = FT_MulDiv( bsize->y_ppem, resolution_y, 72 );
1624         }
1625         if ( resolution_x && resolution_y )
1626           bsize->x_ppem = FT_MulDiv( bsize->y_ppem,
1627                                      resolution_x,
1628                                      resolution_y );
1629         else
1630           bsize->x_ppem = bsize->y_ppem;
1631       }
1632 
1633       /* set up charset */
1634       {
1635         PCF_Property  charset_registry, charset_encoding;
1636 
1637 
1638         charset_registry = pcf_find_property( face, "CHARSET_REGISTRY" );
1639         charset_encoding = pcf_find_property( face, "CHARSET_ENCODING" );
1640 
1641         if ( charset_registry && charset_registry->isString &&
1642              charset_encoding && charset_encoding->isString )
1643         {
1644           if ( FT_STRDUP( face->charset_encoding,
1645                           charset_encoding->value.atom ) ||
1646                FT_STRDUP( face->charset_registry,
1647                           charset_registry->value.atom ) )
1648             goto Exit;
1649         }
1650       }
1651     }
1652 
1653   Exit:
1654     if ( error )
1655     {
1656       /* This is done to respect the behaviour of the original */
1657       /* PCF font driver.                                      */
1658       error = FT_THROW( Invalid_File_Format );
1659     }
1660 
1661     return error;
1662   }
1663 
1664 
1665 /* END */
1666