1/*  pcfread.c
2
3    FreeType font driver for pcf fonts
4
5  Copyright 2000-2010, 2012-2014 by
6  Francesco Zappa Nardelli
7
8Permission is hereby granted, free of charge, to any person obtaining a copy
9of this software and associated documentation files (the "Software"), to deal
10in the Software without restriction, including without limitation the rights
11to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12copies of the Software, and to permit persons to whom the Software is
13furnished to do so, subject to the following conditions:
14
15The above copyright notice and this permission notice shall be included in
16all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
21AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24THE 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
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
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
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
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 )
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
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
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
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
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
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
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 )
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