• 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 PCF 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_ULong*  offsets = NULL;
814     FT_ULong   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_ULONG( offsets[i] );
882       else
883         (void)FT_READ_ULONG_LE( offsets[i] );
884 
885       FT_TRACE5(( "  bitmap %lu: offset %lu (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_ULONG( bitmapSizes[i] );
895       else
896         (void)FT_READ_ULONG_LE( bitmapSizes[i] );
897       if ( error )
898         goto Bail;
899 
900       sizebitmaps = bitmapSizes[PCF_GLYPH_PAD_INDEX( format )];
901 
902       FT_TRACE4(( "  %ld-bit padding implies a size of %lu\n",
903                   8 << i, bitmapSizes[i] ));
904     }
905 
906     FT_TRACE4(( "  %lu bitmaps, using %ld-bit padding\n",
907                 nbitmaps,
908                 8 << PCF_GLYPH_PAD_INDEX( format ) ));
909     FT_TRACE4(( "  bitmap size: %lu\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] > size )
919       {
920         FT_TRACE0(( "pcf_get_bitmaps:"
921                     " invalid offset to bitmap data of glyph %lu\n", i ));
922       }
923       else
924         face->metrics[i].bits = stream->pos + offsets[i];
925     }
926 
927     face->bitmapsFormat = format;
928 
929   Bail:
930     FT_FREE( offsets );
931     return error;
932   }
933 
934 
935   /*
936    * This file uses X11 terminology for PCF data; an `encoding' in X11 speak
937    * is the same as a character code in FreeType speak.
938    */
939   static FT_Error
pcf_get_encodings(FT_Stream stream,PCF_Face face)940   pcf_get_encodings( FT_Stream  stream,
941                      PCF_Face   face )
942   {
943     FT_Error      error;
944     FT_Memory     memory = FT_FACE( face )->memory;
945     FT_ULong      format, size;
946     FT_UShort     firstCol, lastCol;
947     FT_UShort     firstRow, lastRow;
948     FT_ULong      nencoding;
949     FT_UShort     defaultCharRow, defaultCharCol;
950     FT_UShort     encodingOffset, defaultCharEncodingOffset;
951     FT_UShort     i, j;
952     FT_Byte*      pos;
953     FT_ULong      k;
954     PCF_Encoding  encoding = NULL;
955 
956 
957     error = pcf_seek_to_table_type( stream,
958                                     face->toc.tables,
959                                     face->toc.count,
960                                     PCF_BDF_ENCODINGS,
961                                     &format,
962                                     &size );
963     if ( error )
964       return error;
965 
966     error = FT_Stream_EnterFrame( stream, 14 );
967     if ( error )
968       return error;
969 
970     format = FT_GET_ULONG_LE();
971 
972     /* X11's reference implementation uses the equivalent to  */
973     /* `FT_GET_SHORT' for `defaultChar', however this doesn't */
974     /* make sense for most encodings.                         */
975     if ( PCF_BYTE_ORDER( format ) == MSBFirst )
976     {
977       firstCol          = FT_GET_USHORT();
978       lastCol           = FT_GET_USHORT();
979       firstRow          = FT_GET_USHORT();
980       lastRow           = FT_GET_USHORT();
981       face->defaultChar = FT_GET_USHORT();
982     }
983     else
984     {
985       firstCol          = FT_GET_USHORT_LE();
986       lastCol           = FT_GET_USHORT_LE();
987       firstRow          = FT_GET_USHORT_LE();
988       lastRow           = FT_GET_USHORT_LE();
989       face->defaultChar = FT_GET_USHORT_LE();
990     }
991 
992     FT_Stream_ExitFrame( stream );
993 
994     FT_TRACE4(( "pcf_get_encodings:\n"
995                 "  format: 0x%lX (%s)\n",
996                 format,
997                 PCF_BYTE_ORDER( format ) == MSBFirst ? "MSB" : "LSB" ));
998 
999     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
1000       return FT_THROW( Invalid_File_Format );
1001 
1002     FT_TRACE4(( "  firstCol 0x%X, lastCol 0x%X\n"
1003                 "  firstRow 0x%X, lastRow 0x%X\n"
1004                 "  defaultChar 0x%X\n",
1005                 firstCol, lastCol,
1006                 firstRow, lastRow,
1007                 face->defaultChar ));
1008 
1009     /* sanity checks; we limit numbers of rows and columns to 256 */
1010     if ( firstCol > lastCol ||
1011          lastCol  > 0xFF    ||
1012          firstRow > lastRow ||
1013          lastRow  > 0xFF    )
1014       return FT_THROW( Invalid_Table );
1015 
1016     nencoding = (FT_ULong)( lastCol - firstCol + 1 ) *
1017                 (FT_ULong)( lastRow - firstRow + 1 );
1018 
1019     if ( FT_NEW_ARRAY( encoding, nencoding ) )
1020       return error;
1021 
1022     error = FT_Stream_EnterFrame( stream, 2 * nencoding );
1023     if ( error )
1024       goto Bail;
1025 
1026     FT_TRACE5(( "\n" ));
1027 
1028     defaultCharRow = face->defaultChar >> 8;
1029     defaultCharCol = face->defaultChar & 0xFF;
1030 
1031     /* validate default character */
1032     if ( defaultCharRow < firstRow ||
1033          defaultCharRow > lastRow  ||
1034          defaultCharCol < firstCol ||
1035          defaultCharCol > lastCol  )
1036     {
1037       face->defaultChar = firstRow * 256U + firstCol;
1038       FT_TRACE0(( "pcf_get_encodings:"
1039                   " Invalid default character set to %u\n",
1040                   face->defaultChar ));
1041 
1042       defaultCharRow = face->defaultChar >> 8;
1043       defaultCharCol = face->defaultChar & 0xFF;
1044     }
1045 
1046     /* FreeType mandates that glyph index 0 is the `undefined glyph',  */
1047     /* which PCF calls the `default character'.  For this reason, we   */
1048     /* swap the positions of glyph index 0 and the index corresponding */
1049     /* to `defaultChar' in case they are different.                    */
1050 
1051     /* `stream->cursor' still points at the beginning of the frame; */
1052     /* we can thus easily get the offset to the default character   */
1053     pos = stream->cursor +
1054             2 * ( ( defaultCharRow - firstRow ) * ( lastCol - firstCol + 1 ) +
1055                   defaultCharCol - firstCol );
1056 
1057     if ( PCF_BYTE_ORDER( format ) == MSBFirst )
1058       defaultCharEncodingOffset = FT_PEEK_USHORT( pos );
1059     else
1060       defaultCharEncodingOffset = FT_PEEK_USHORT_LE( pos );
1061 
1062     if ( defaultCharEncodingOffset >= face->nmetrics )
1063     {
1064       FT_TRACE0(( "pcf_get_encodings:"
1065                   " Invalid glyph index for default character,"
1066                   " setting to zero\n" ));
1067       defaultCharEncodingOffset = 0;
1068     }
1069 
1070     if ( defaultCharEncodingOffset )
1071     {
1072       /* do the swapping */
1073       PCF_MetricRec  tmp = face->metrics[defaultCharEncodingOffset];
1074 
1075 
1076       face->metrics[defaultCharEncodingOffset] = face->metrics[0];
1077       face->metrics[0]                         = tmp;
1078     }
1079 
1080     k = 0;
1081     for ( i = firstRow; i <= lastRow; i++ )
1082     {
1083       for ( j = firstCol; j <= lastCol; j++ )
1084       {
1085         /* X11's reference implementation uses the equivalent to  */
1086         /* `FT_GET_SHORT', however PCF fonts with more than 32768 */
1087         /* characters (e.g., `unifont.pcf') clearly show that an  */
1088         /* unsigned value is needed.                              */
1089         if ( PCF_BYTE_ORDER( format ) == MSBFirst )
1090           encodingOffset = FT_GET_USHORT();
1091         else
1092           encodingOffset = FT_GET_USHORT_LE();
1093 
1094         if ( encodingOffset != 0xFFFFU )
1095         {
1096           if ( encodingOffset == defaultCharEncodingOffset )
1097             encodingOffset = 0;
1098           else if ( encodingOffset == 0 )
1099             encodingOffset = defaultCharEncodingOffset;
1100 
1101           encoding[k].enc   = i * 256U + j;
1102           encoding[k].glyph = encodingOffset;
1103 
1104           FT_TRACE5(( "  code %u (0x%04X): idx %u\n",
1105                       encoding[k].enc, encoding[k].enc, encoding[k].glyph ));
1106 
1107           k++;
1108         }
1109       }
1110     }
1111     FT_Stream_ExitFrame( stream );
1112 
1113     if ( FT_RENEW_ARRAY( encoding, nencoding, k ) )
1114       goto Bail;
1115 
1116     face->nencodings = k;
1117     face->encodings  = encoding;
1118 
1119     return error;
1120 
1121   Bail:
1122     FT_FREE( encoding );
1123     return error;
1124   }
1125 
1126 
1127   static
1128   const FT_Frame_Field  pcf_accel_header[] =
1129   {
1130 #undef  FT_STRUCTURE
1131 #define FT_STRUCTURE  PCF_AccelRec
1132 
1133     FT_FRAME_START( 20 ),
1134       FT_FRAME_BYTE      ( noOverlap ),
1135       FT_FRAME_BYTE      ( constantMetrics ),
1136       FT_FRAME_BYTE      ( terminalFont ),
1137       FT_FRAME_BYTE      ( constantWidth ),
1138       FT_FRAME_BYTE      ( inkInside ),
1139       FT_FRAME_BYTE      ( inkMetrics ),
1140       FT_FRAME_BYTE      ( drawDirection ),
1141       FT_FRAME_SKIP_BYTES( 1 ),
1142       FT_FRAME_LONG_LE   ( fontAscent ),
1143       FT_FRAME_LONG_LE   ( fontDescent ),
1144       FT_FRAME_LONG_LE   ( maxOverlap ),
1145     FT_FRAME_END
1146   };
1147 
1148 
1149   static
1150   const FT_Frame_Field  pcf_accel_msb_header[] =
1151   {
1152 #undef  FT_STRUCTURE
1153 #define FT_STRUCTURE  PCF_AccelRec
1154 
1155     FT_FRAME_START( 20 ),
1156       FT_FRAME_BYTE      ( noOverlap ),
1157       FT_FRAME_BYTE      ( constantMetrics ),
1158       FT_FRAME_BYTE      ( terminalFont ),
1159       FT_FRAME_BYTE      ( constantWidth ),
1160       FT_FRAME_BYTE      ( inkInside ),
1161       FT_FRAME_BYTE      ( inkMetrics ),
1162       FT_FRAME_BYTE      ( drawDirection ),
1163       FT_FRAME_SKIP_BYTES( 1 ),
1164       FT_FRAME_LONG      ( fontAscent ),
1165       FT_FRAME_LONG      ( fontDescent ),
1166       FT_FRAME_LONG      ( maxOverlap ),
1167     FT_FRAME_END
1168   };
1169 
1170 
1171   static FT_Error
pcf_get_accel(FT_Stream stream,PCF_Face face,FT_ULong type)1172   pcf_get_accel( FT_Stream  stream,
1173                  PCF_Face   face,
1174                  FT_ULong   type )
1175   {
1176     FT_ULong   format, size;
1177     FT_Error   error;
1178     PCF_Accel  accel = &face->accel;
1179 
1180 
1181     error = pcf_seek_to_table_type( stream,
1182                                     face->toc.tables,
1183                                     face->toc.count,
1184                                     type,
1185                                     &format,
1186                                     &size );
1187     if ( error )
1188       goto Bail;
1189 
1190     if ( FT_READ_ULONG_LE( format ) )
1191       goto Bail;
1192 
1193     FT_TRACE4(( "pcf_get_accel%s:\n"
1194                 "  format: 0x%lX (%s, %s)\n",
1195                 type == PCF_BDF_ACCELERATORS ? " (getting BDF accelerators)"
1196                                              : "",
1197                 format,
1198                 PCF_BYTE_ORDER( format ) == MSBFirst ? "MSB" : "LSB",
1199                 PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) ?
1200                   "accelerated" : "not accelerated" ));
1201 
1202     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT )    &&
1203          !PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) )
1204       goto Bail;
1205 
1206     if ( PCF_BYTE_ORDER( format ) == MSBFirst )
1207     {
1208       if ( FT_STREAM_READ_FIELDS( pcf_accel_msb_header, accel ) )
1209         goto Bail;
1210     }
1211     else
1212     {
1213       if ( FT_STREAM_READ_FIELDS( pcf_accel_header, accel ) )
1214         goto Bail;
1215     }
1216 
1217     FT_TRACE5(( "  noOverlap=%s, constantMetrics=%s,"
1218                 " terminalFont=%s, constantWidth=%s\n"
1219                 "  inkInside=%s, inkMetrics=%s, drawDirection=%s\n"
1220                 "  fontAscent=%ld, fontDescent=%ld, maxOverlap=%ld\n",
1221                 accel->noOverlap ? "yes" : "no",
1222                 accel->constantMetrics ? "yes" : "no",
1223                 accel->terminalFont ? "yes" : "no",
1224                 accel->constantWidth ? "yes" : "no",
1225                 accel->inkInside ? "yes" : "no",
1226                 accel->inkMetrics ? "yes" : "no",
1227                 accel->drawDirection ? "RTL" : "LTR",
1228                 accel->fontAscent,
1229                 accel->fontDescent,
1230                 accel->maxOverlap ));
1231 
1232     /* sanity checks */
1233     if ( FT_ABS( accel->fontAscent ) > 0x7FFF )
1234     {
1235       accel->fontAscent = accel->fontAscent < 0 ? -0x7FFF : 0x7FFF;
1236       FT_TRACE0(( "pfc_get_accel: clamping font ascent to value %d\n",
1237                   accel->fontAscent ));
1238     }
1239     if ( FT_ABS( accel->fontDescent ) > 0x7FFF )
1240     {
1241       accel->fontDescent = accel->fontDescent < 0 ? -0x7FFF : 0x7FFF;
1242       FT_TRACE0(( "pfc_get_accel: clamping font descent to value %d\n",
1243                   accel->fontDescent ));
1244     }
1245 
1246     FT_TRACE5(( "  minbounds:" ));
1247     error = pcf_get_metric( stream,
1248                             format & ( ~PCF_FORMAT_MASK ),
1249                             &(accel->minbounds) );
1250     if ( error )
1251       goto Bail;
1252 
1253     FT_TRACE5(( "  maxbounds:" ));
1254     error = pcf_get_metric( stream,
1255                             format & ( ~PCF_FORMAT_MASK ),
1256                             &(accel->maxbounds) );
1257     if ( error )
1258       goto Bail;
1259 
1260     if ( PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) )
1261     {
1262       FT_TRACE5(( "  ink minbounds:" ));
1263       error = pcf_get_metric( stream,
1264                               format & ( ~PCF_FORMAT_MASK ),
1265                               &(accel->ink_minbounds) );
1266       if ( error )
1267         goto Bail;
1268 
1269       FT_TRACE5(( "  ink maxbounds:" ));
1270       error = pcf_get_metric( stream,
1271                               format & ( ~PCF_FORMAT_MASK ),
1272                               &(accel->ink_maxbounds) );
1273       if ( error )
1274         goto Bail;
1275     }
1276     else
1277     {
1278       accel->ink_minbounds = accel->minbounds;
1279       accel->ink_maxbounds = accel->maxbounds;
1280     }
1281 
1282   Bail:
1283     return error;
1284   }
1285 
1286 
1287   static FT_Error
pcf_interpret_style(PCF_Face pcf)1288   pcf_interpret_style( PCF_Face  pcf )
1289   {
1290     FT_Error   error  = FT_Err_Ok;
1291     FT_Face    face   = FT_FACE( pcf );
1292     FT_Memory  memory = face->memory;
1293 
1294     PCF_Property  prop;
1295 
1296     size_t  nn, len;
1297     char*   strings[4] = { NULL, NULL, NULL, NULL };
1298     size_t  lengths[4];
1299 
1300 
1301     face->style_flags = 0;
1302 
1303     prop = pcf_find_property( pcf, "SLANT" );
1304     if ( prop && prop->isString                                       &&
1305          ( *(prop->value.atom) == 'O' || *(prop->value.atom) == 'o' ||
1306            *(prop->value.atom) == 'I' || *(prop->value.atom) == 'i' ) )
1307     {
1308       face->style_flags |= FT_STYLE_FLAG_ITALIC;
1309       strings[2] = ( *(prop->value.atom) == 'O' ||
1310                      *(prop->value.atom) == 'o' ) ? (char *)"Oblique"
1311                                                   : (char *)"Italic";
1312     }
1313 
1314     prop = pcf_find_property( pcf, "WEIGHT_NAME" );
1315     if ( prop && prop->isString                                       &&
1316          ( *(prop->value.atom) == 'B' || *(prop->value.atom) == 'b' ) )
1317     {
1318       face->style_flags |= FT_STYLE_FLAG_BOLD;
1319       strings[1] = (char*)"Bold";
1320     }
1321 
1322     prop = pcf_find_property( pcf, "SETWIDTH_NAME" );
1323     if ( prop && prop->isString                                        &&
1324          *(prop->value.atom)                                           &&
1325          !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) )
1326       strings[3] = (char*)( prop->value.atom );
1327 
1328     prop = pcf_find_property( pcf, "ADD_STYLE_NAME" );
1329     if ( prop && prop->isString                                        &&
1330          *(prop->value.atom)                                           &&
1331          !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) )
1332       strings[0] = (char*)( prop->value.atom );
1333 
1334     for ( len = 0, nn = 0; nn < 4; nn++ )
1335     {
1336       lengths[nn] = 0;
1337       if ( strings[nn] )
1338       {
1339         lengths[nn] = ft_strlen( strings[nn] );
1340         len        += lengths[nn] + 1;
1341       }
1342     }
1343 
1344     if ( len == 0 )
1345     {
1346       strings[0] = (char*)"Regular";
1347       lengths[0] = ft_strlen( strings[0] );
1348       len        = lengths[0] + 1;
1349     }
1350 
1351     {
1352       char*  s;
1353 
1354 
1355       if ( FT_ALLOC( face->style_name, len ) )
1356         return error;
1357 
1358       s = face->style_name;
1359 
1360       for ( nn = 0; nn < 4; nn++ )
1361       {
1362         char*  src = strings[nn];
1363 
1364 
1365         len = lengths[nn];
1366 
1367         if ( !src )
1368           continue;
1369 
1370         /* separate elements with a space */
1371         if ( s != face->style_name )
1372           *s++ = ' ';
1373 
1374         ft_memcpy( s, src, len );
1375 
1376         /* need to convert spaces to dashes for */
1377         /* add_style_name and setwidth_name     */
1378         if ( nn == 0 || nn == 3 )
1379         {
1380           size_t  mm;
1381 
1382 
1383           for ( mm = 0; mm < len; mm++ )
1384             if ( s[mm] == ' ' )
1385               s[mm] = '-';
1386         }
1387 
1388         s += len;
1389       }
1390       *s = 0;
1391     }
1392 
1393     return error;
1394   }
1395 
1396 
1397   FT_LOCAL_DEF( FT_Error )
pcf_load_font(FT_Stream stream,PCF_Face face,FT_Long face_index)1398   pcf_load_font( FT_Stream  stream,
1399                  PCF_Face   face,
1400                  FT_Long    face_index )
1401   {
1402     FT_Face    root   = FT_FACE( face );
1403     FT_Error   error;
1404     FT_Memory  memory = FT_FACE( face )->memory;
1405     FT_Bool    hasBDFAccelerators;
1406 
1407 
1408     error = pcf_read_TOC( stream, face );
1409     if ( error )
1410       goto Exit;
1411 
1412     root->num_faces  = 1;
1413     root->face_index = 0;
1414 
1415     /* If we are performing a simple font format check, exit immediately. */
1416     if ( face_index < 0 )
1417       return FT_Err_Ok;
1418 
1419     error = pcf_get_properties( stream, face );
1420     if ( error )
1421       goto Exit;
1422 
1423     /* Use the old accelerators if no BDF accelerators are in the file. */
1424     hasBDFAccelerators = pcf_has_table_type( face->toc.tables,
1425                                              face->toc.count,
1426                                              PCF_BDF_ACCELERATORS );
1427     if ( !hasBDFAccelerators )
1428     {
1429       error = pcf_get_accel( stream, face, PCF_ACCELERATORS );
1430       if ( error )
1431         goto Exit;
1432     }
1433 
1434     /* metrics */
1435     error = pcf_get_metrics( stream, face );
1436     if ( error )
1437       goto Exit;
1438 
1439     /* bitmaps */
1440     error = pcf_get_bitmaps( stream, face );
1441     if ( error )
1442       goto Exit;
1443 
1444     /* encodings */
1445     error = pcf_get_encodings( stream, face );
1446     if ( error )
1447       goto Exit;
1448 
1449     /* BDF style accelerators (i.e. bounds based on encoded glyphs) */
1450     if ( hasBDFAccelerators )
1451     {
1452       error = pcf_get_accel( stream, face, PCF_BDF_ACCELERATORS );
1453       if ( error )
1454         goto Exit;
1455     }
1456 
1457     /* XXX: TO DO: inkmetrics and glyph_names are missing */
1458 
1459     /* now construct the face object */
1460     {
1461       PCF_Property  prop;
1462 
1463 
1464       root->face_flags |= FT_FACE_FLAG_FIXED_SIZES |
1465                           FT_FACE_FLAG_HORIZONTAL;
1466 
1467       if ( face->accel.constantWidth )
1468         root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
1469 
1470       if ( FT_SET_ERROR( pcf_interpret_style( face ) ) )
1471         goto Exit;
1472 
1473       prop = pcf_find_property( face, "FAMILY_NAME" );
1474       if ( prop && prop->isString )
1475       {
1476 
1477 #ifdef PCF_CONFIG_OPTION_LONG_FAMILY_NAMES
1478 
1479         PCF_Driver  driver = (PCF_Driver)FT_FACE_DRIVER( face );
1480 
1481 
1482         if ( !driver->no_long_family_names )
1483         {
1484           /* Prepend the foundry name plus a space to the family name.     */
1485           /* There are many fonts just called `Fixed' which look           */
1486           /* completely different, and which have nothing to do with each  */
1487           /* other.  When selecting `Fixed' in KDE or Gnome one gets       */
1488           /* results that appear rather random, the style changes often if */
1489           /* one changes the size and one cannot select some fonts at all. */
1490           /*                                                               */
1491           /* We also check whether we have `wide' characters; all put      */
1492           /* together, we get family names like `Sony Fixed' or `Misc      */
1493           /* Fixed Wide'.                                                  */
1494 
1495           PCF_Property  foundry_prop, point_size_prop, average_width_prop;
1496 
1497           int  l    = ft_strlen( prop->value.atom ) + 1;
1498           int  wide = 0;
1499 
1500 
1501           foundry_prop       = pcf_find_property( face, "FOUNDRY" );
1502           point_size_prop    = pcf_find_property( face, "POINT_SIZE" );
1503           average_width_prop = pcf_find_property( face, "AVERAGE_WIDTH" );
1504 
1505           if ( point_size_prop && average_width_prop )
1506           {
1507             if ( average_width_prop->value.l >= point_size_prop->value.l )
1508             {
1509               /* This font is at least square shaped or even wider */
1510               wide = 1;
1511               l   += ft_strlen( " Wide" );
1512             }
1513           }
1514 
1515           if ( foundry_prop && foundry_prop->isString )
1516           {
1517             l += ft_strlen( foundry_prop->value.atom ) + 1;
1518 
1519             if ( FT_NEW_ARRAY( root->family_name, l ) )
1520               goto Exit;
1521 
1522             ft_strcpy( root->family_name, foundry_prop->value.atom );
1523             ft_strcat( root->family_name, " " );
1524             ft_strcat( root->family_name, prop->value.atom );
1525           }
1526           else
1527           {
1528             if ( FT_NEW_ARRAY( root->family_name, l ) )
1529               goto Exit;
1530 
1531             ft_strcpy( root->family_name, prop->value.atom );
1532           }
1533 
1534           if ( wide )
1535             ft_strcat( root->family_name, " Wide" );
1536         }
1537         else
1538 
1539 #endif /* PCF_CONFIG_OPTION_LONG_FAMILY_NAMES */
1540 
1541         {
1542           if ( FT_STRDUP( root->family_name, prop->value.atom ) )
1543             goto Exit;
1544         }
1545       }
1546       else
1547         root->family_name = NULL;
1548 
1549       root->num_glyphs = (FT_Long)face->nmetrics;
1550 
1551       root->num_fixed_sizes = 1;
1552       if ( FT_NEW_ARRAY( root->available_sizes, 1 ) )
1553         goto Exit;
1554 
1555       {
1556         FT_Bitmap_Size*  bsize = root->available_sizes;
1557         FT_Short         resolution_x = 0, resolution_y = 0;
1558 
1559 
1560         FT_ZERO( bsize );
1561 
1562         /* for simplicity, we take absolute values of integer properties */
1563 
1564 #if 0
1565         bsize->height = face->accel.maxbounds.ascent << 6;
1566 #endif
1567 
1568 #ifdef FT_DEBUG_LEVEL_TRACE
1569         if ( face->accel.fontAscent + face->accel.fontDescent < 0 )
1570           FT_TRACE0(( "pcf_load_font: negative height\n" ));
1571 #endif
1572         if ( FT_ABS( face->accel.fontAscent +
1573                      face->accel.fontDescent ) > 0x7FFF )
1574         {
1575           bsize->height = 0x7FFF;
1576           FT_TRACE0(( "pcf_load_font: clamping height to value %d\n",
1577                       bsize->height ));
1578         }
1579         else
1580           bsize->height = FT_ABS( (FT_Short)( face->accel.fontAscent +
1581                                               face->accel.fontDescent ) );
1582 
1583         prop = pcf_find_property( face, "AVERAGE_WIDTH" );
1584         if ( prop )
1585         {
1586 #ifdef FT_DEBUG_LEVEL_TRACE
1587           if ( prop->value.l < 0 )
1588             FT_TRACE0(( "pcf_load_font: negative average width\n" ));
1589 #endif
1590           if ( ( FT_ABS( prop->value.l ) > 0x7FFFL * 10 - 5 ) )
1591           {
1592             bsize->width = 0x7FFF;
1593             FT_TRACE0(( "pcf_load_font: clamping average width to value %d\n",
1594                         bsize->width ));
1595           }
1596           else
1597             bsize->width = FT_ABS( (FT_Short)( ( prop->value.l + 5 ) / 10 ) );
1598         }
1599         else
1600         {
1601           /* this is a heuristical value */
1602           bsize->width = (FT_Short)FT_MulDiv( bsize->height, 2, 3 );
1603         }
1604 
1605         prop = pcf_find_property( face, "POINT_SIZE" );
1606         if ( prop )
1607         {
1608 #ifdef FT_DEBUG_LEVEL_TRACE
1609           if ( prop->value.l < 0 )
1610             FT_TRACE0(( "pcf_load_font: negative point size\n" ));
1611 #endif
1612           /* convert from 722.7 decipoints to 72 points per inch */
1613           if ( FT_ABS( prop->value.l ) > 0x504C2L ) /* 0x7FFF * 72270/7200 */
1614           {
1615             bsize->size = 0x7FFF;
1616             FT_TRACE0(( "pcf_load_font: clamping point size to value %d\n",
1617                         bsize->size ));
1618           }
1619           else
1620             bsize->size = FT_MulDiv( FT_ABS( prop->value.l ),
1621                                      64 * 7200,
1622                                      72270L );
1623         }
1624 
1625         prop = pcf_find_property( face, "PIXEL_SIZE" );
1626         if ( prop )
1627         {
1628 #ifdef FT_DEBUG_LEVEL_TRACE
1629           if ( prop->value.l < 0 )
1630             FT_TRACE0(( "pcf_load_font: negative pixel size\n" ));
1631 #endif
1632           if ( FT_ABS( prop->value.l ) > 0x7FFF )
1633           {
1634             bsize->y_ppem = 0x7FFF << 6;
1635             FT_TRACE0(( "pcf_load_font: clamping pixel size to value %d\n",
1636                         bsize->y_ppem ));
1637           }
1638           else
1639             bsize->y_ppem = FT_ABS( (FT_Short)prop->value.l ) << 6;
1640         }
1641 
1642         prop = pcf_find_property( face, "RESOLUTION_X" );
1643         if ( prop )
1644         {
1645 #ifdef FT_DEBUG_LEVEL_TRACE
1646           if ( prop->value.l < 0 )
1647             FT_TRACE0(( "pcf_load_font: negative X resolution\n" ));
1648 #endif
1649           if ( FT_ABS( prop->value.l ) > 0x7FFF )
1650           {
1651             resolution_x = 0x7FFF;
1652             FT_TRACE0(( "pcf_load_font: clamping X resolution to value %d\n",
1653                         resolution_x ));
1654           }
1655           else
1656             resolution_x = FT_ABS( (FT_Short)prop->value.l );
1657         }
1658 
1659         prop = pcf_find_property( face, "RESOLUTION_Y" );
1660         if ( prop )
1661         {
1662 #ifdef FT_DEBUG_LEVEL_TRACE
1663           if ( prop->value.l < 0 )
1664             FT_TRACE0(( "pcf_load_font: negative Y resolution\n" ));
1665 #endif
1666           if ( FT_ABS( prop->value.l ) > 0x7FFF )
1667           {
1668             resolution_y = 0x7FFF;
1669             FT_TRACE0(( "pcf_load_font: clamping Y resolution to value %d\n",
1670                         resolution_y ));
1671           }
1672           else
1673             resolution_y = FT_ABS( (FT_Short)prop->value.l );
1674         }
1675 
1676         if ( bsize->y_ppem == 0 )
1677         {
1678           bsize->y_ppem = bsize->size;
1679           if ( resolution_y )
1680             bsize->y_ppem = FT_MulDiv( bsize->y_ppem, resolution_y, 72 );
1681         }
1682         if ( resolution_x && resolution_y )
1683           bsize->x_ppem = FT_MulDiv( bsize->y_ppem,
1684                                      resolution_x,
1685                                      resolution_y );
1686         else
1687           bsize->x_ppem = bsize->y_ppem;
1688       }
1689 
1690       /* set up charset */
1691       {
1692         PCF_Property  charset_registry, charset_encoding;
1693 
1694 
1695         charset_registry = pcf_find_property( face, "CHARSET_REGISTRY" );
1696         charset_encoding = pcf_find_property( face, "CHARSET_ENCODING" );
1697 
1698         if ( charset_registry && charset_registry->isString &&
1699              charset_encoding && charset_encoding->isString )
1700         {
1701           if ( FT_STRDUP( face->charset_encoding,
1702                           charset_encoding->value.atom ) ||
1703                FT_STRDUP( face->charset_registry,
1704                           charset_registry->value.atom ) )
1705             goto Exit;
1706         }
1707       }
1708     }
1709 
1710   Exit:
1711     if ( error )
1712     {
1713       /* This is done to respect the behaviour of the original */
1714       /* PCF font driver.                                      */
1715       error = FT_THROW( Invalid_File_Format );
1716     }
1717 
1718     return error;
1719   }
1720 
1721 
1722 /* END */
1723