Imported Upstream version 2.5.5
[platform/upstream/freetype2.git] / src / pcf / pcfread.c
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     "prop", "accl", "mtrcs", "bmps", "imtrcs",
54     "enc", "swidth", "names", "accel"
55   };
56 #endif
57
58
59   static
60   const FT_Frame_Field  pcf_toc_header[] =
61   {
62 #undef  FT_STRUCTURE
63 #define FT_STRUCTURE  PCF_TocRec
64
65     FT_FRAME_START( 8 ),
66       FT_FRAME_ULONG_LE( version ),
67       FT_FRAME_ULONG_LE( count ),
68     FT_FRAME_END
69   };
70
71
72   static
73   const FT_Frame_Field  pcf_table_header[] =
74   {
75 #undef  FT_STRUCTURE
76 #define FT_STRUCTURE  PCF_TableRec
77
78     FT_FRAME_START( 16  ),
79       FT_FRAME_ULONG_LE( type ),
80       FT_FRAME_ULONG_LE( format ),
81       FT_FRAME_ULONG_LE( size ),   /* rounded up to a multiple of 4 */
82       FT_FRAME_ULONG_LE( offset ),
83     FT_FRAME_END
84   };
85
86
87   static FT_Error
88   pcf_read_TOC( FT_Stream  stream,
89                 PCF_Face   face )
90   {
91     FT_Error   error;
92     PCF_Toc    toc = &face->toc;
93     PCF_Table  tables;
94
95     FT_Memory  memory = FT_FACE( face )->memory;
96     FT_UInt    n;
97
98     FT_ULong   size;
99
100
101     if ( FT_STREAM_SEEK( 0 )                          ||
102          FT_STREAM_READ_FIELDS( pcf_toc_header, toc ) )
103       return FT_THROW( Cannot_Open_Resource );
104
105     if ( toc->version != PCF_FILE_VERSION                 ||
106          toc->count   >  FT_ARRAY_MAX( face->toc.tables ) ||
107          toc->count   == 0                                )
108       return FT_THROW( Invalid_File_Format );
109
110     if ( FT_NEW_ARRAY( face->toc.tables, toc->count ) )
111       return FT_THROW( Out_Of_Memory );
112
113     tables = face->toc.tables;
114     for ( n = 0; n < toc->count; n++ )
115     {
116       if ( FT_STREAM_READ_FIELDS( pcf_table_header, tables ) )
117         goto Exit;
118       tables++;
119     }
120
121     /* Sort tables and check for overlaps.  Because they are almost      */
122     /* always ordered already, an in-place bubble sort with simultaneous */
123     /* boundary checking seems appropriate.                              */
124     tables = face->toc.tables;
125
126     for ( n = 0; n < toc->count - 1; n++ )
127     {
128       FT_UInt  i, have_change;
129
130
131       have_change = 0;
132
133       for ( i = 0; i < toc->count - 1 - n; i++ )
134       {
135         PCF_TableRec  tmp;
136
137
138         if ( tables[i].offset > tables[i + 1].offset )
139         {
140           tmp           = tables[i];
141           tables[i]     = tables[i + 1];
142           tables[i + 1] = tmp;
143
144           have_change = 1;
145         }
146
147         if ( ( tables[i].size   > tables[i + 1].offset )                  ||
148              ( tables[i].offset > tables[i + 1].offset - tables[i].size ) )
149         {
150           error = FT_THROW( Invalid_Offset );
151           goto Exit;
152         }
153       }
154
155       if ( !have_change )
156         break;
157     }
158
159     /*
160      *  We now check whether the `size' and `offset' values are reasonable:
161      *  `offset' + `size' must not exceed the stream size.
162      *
163      *  Note, however, that X11's `pcfWriteFont' routine (used by the
164      *  `bdftopcf' program to create PDF font files) has two special
165      *  features.
166      *
167      *  - It always assigns the accelerator table a size of 100 bytes in the
168      *    TOC, regardless of its real size, which can vary between 34 and 72
169      *    bytes.
170      *
171      *  - Due to the way the routine is designed, it ships out the last font
172      *    table with its real size, ignoring the TOC's size value.  Since
173      *    the TOC size values are always rounded up to a multiple of 4, the
174      *    difference can be up to three bytes for all tables except the
175      *    accelerator table, for which the difference can be as large as 66
176      *    bytes.
177      *
178      */
179
180     tables = face->toc.tables;
181     size   = stream->size;
182
183     for ( n = 0; n < toc->count - 1; n++ )
184     {
185       /* we need two checks to avoid overflow */
186       if ( ( tables->size   > size                ) ||
187            ( tables->offset > size - tables->size ) )
188       {
189         error = FT_THROW( Invalid_Table );
190         goto Exit;
191       }
192       tables++;
193     }
194
195     /* only check `tables->offset' for last table element ... */
196     if ( ( tables->offset > size ) )
197     {
198       error = FT_THROW( Invalid_Table );
199       goto Exit;
200     }
201     /* ... and adjust `tables->size' to the real value if necessary */
202     if ( tables->size > size - tables->offset )
203       tables->size = size - tables->offset;
204
205 #ifdef FT_DEBUG_LEVEL_TRACE
206
207     {
208       FT_UInt      i, j;
209       const char*  name = "?";
210
211
212       FT_TRACE4(( "pcf_read_TOC:\n" ));
213
214       FT_TRACE4(( "  number of tables: %ld\n", face->toc.count ));
215
216       tables = face->toc.tables;
217       for ( i = 0; i < toc->count; i++ )
218       {
219         for ( j = 0; j < sizeof ( tableNames ) / sizeof ( tableNames[0] );
220               j++ )
221           if ( tables[i].type == (FT_UInt)( 1 << j ) )
222             name = tableNames[j];
223
224         FT_TRACE4(( "  %d: type=%s, format=0x%X, "
225                     "size=%ld (0x%lX), offset=%ld (0x%lX)\n",
226                     i, name,
227                     tables[i].format,
228                     tables[i].size, tables[i].size,
229                     tables[i].offset, tables[i].offset ));
230       }
231     }
232
233 #endif
234
235     return FT_Err_Ok;
236
237   Exit:
238     FT_FREE( face->toc.tables );
239     return error;
240   }
241
242
243 #define PCF_METRIC_SIZE  12
244
245   static
246   const FT_Frame_Field  pcf_metric_header[] =
247   {
248 #undef  FT_STRUCTURE
249 #define FT_STRUCTURE  PCF_MetricRec
250
251     FT_FRAME_START( PCF_METRIC_SIZE ),
252       FT_FRAME_SHORT_LE( leftSideBearing ),
253       FT_FRAME_SHORT_LE( rightSideBearing ),
254       FT_FRAME_SHORT_LE( characterWidth ),
255       FT_FRAME_SHORT_LE( ascent ),
256       FT_FRAME_SHORT_LE( descent ),
257       FT_FRAME_SHORT_LE( attributes ),
258     FT_FRAME_END
259   };
260
261
262   static
263   const FT_Frame_Field  pcf_metric_msb_header[] =
264   {
265 #undef  FT_STRUCTURE
266 #define FT_STRUCTURE  PCF_MetricRec
267
268     FT_FRAME_START( PCF_METRIC_SIZE ),
269       FT_FRAME_SHORT( leftSideBearing ),
270       FT_FRAME_SHORT( rightSideBearing ),
271       FT_FRAME_SHORT( characterWidth ),
272       FT_FRAME_SHORT( ascent ),
273       FT_FRAME_SHORT( descent ),
274       FT_FRAME_SHORT( attributes ),
275     FT_FRAME_END
276   };
277
278
279 #define PCF_COMPRESSED_METRIC_SIZE  5
280
281   static
282   const FT_Frame_Field  pcf_compressed_metric_header[] =
283   {
284 #undef  FT_STRUCTURE
285 #define FT_STRUCTURE  PCF_Compressed_MetricRec
286
287     FT_FRAME_START( PCF_COMPRESSED_METRIC_SIZE ),
288       FT_FRAME_BYTE( leftSideBearing ),
289       FT_FRAME_BYTE( rightSideBearing ),
290       FT_FRAME_BYTE( characterWidth ),
291       FT_FRAME_BYTE( ascent ),
292       FT_FRAME_BYTE( descent ),
293     FT_FRAME_END
294   };
295
296
297   static FT_Error
298   pcf_get_metric( FT_Stream   stream,
299                   FT_ULong    format,
300                   PCF_Metric  metric )
301   {
302     FT_Error  error = FT_Err_Ok;
303
304
305     if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
306     {
307       const FT_Frame_Field*  fields;
308
309
310       /* parsing normal metrics */
311       fields = PCF_BYTE_ORDER( format ) == MSBFirst
312                ? pcf_metric_msb_header
313                : pcf_metric_header;
314
315       /* the following sets `error' but doesn't return in case of failure */
316       (void)FT_STREAM_READ_FIELDS( fields, metric );
317     }
318     else
319     {
320       PCF_Compressed_MetricRec  compr;
321
322
323       /* parsing compressed metrics */
324       if ( FT_STREAM_READ_FIELDS( pcf_compressed_metric_header, &compr ) )
325         goto Exit;
326
327       metric->leftSideBearing  = (FT_Short)( compr.leftSideBearing  - 0x80 );
328       metric->rightSideBearing = (FT_Short)( compr.rightSideBearing - 0x80 );
329       metric->characterWidth   = (FT_Short)( compr.characterWidth   - 0x80 );
330       metric->ascent           = (FT_Short)( compr.ascent           - 0x80 );
331       metric->descent          = (FT_Short)( compr.descent          - 0x80 );
332       metric->attributes       = 0;
333     }
334
335   Exit:
336     return error;
337   }
338
339
340   static FT_Error
341   pcf_seek_to_table_type( FT_Stream  stream,
342                           PCF_Table  tables,
343                           FT_ULong   ntables, /* same as PCF_Toc->count */
344                           FT_ULong   type,
345                           FT_ULong  *aformat,
346                           FT_ULong  *asize )
347   {
348     FT_Error  error = FT_ERR( Invalid_File_Format );
349     FT_ULong  i;
350
351
352     for ( i = 0; i < ntables; i++ )
353       if ( tables[i].type == type )
354       {
355         if ( stream->pos > tables[i].offset )
356         {
357           error = FT_THROW( Invalid_Stream_Skip );
358           goto Fail;
359         }
360
361         if ( FT_STREAM_SKIP( tables[i].offset - stream->pos ) )
362         {
363           error = FT_THROW( Invalid_Stream_Skip );
364           goto Fail;
365         }
366
367         *asize   = tables[i].size;
368         *aformat = tables[i].format;
369
370         return FT_Err_Ok;
371       }
372
373   Fail:
374     *asize = 0;
375     return error;
376   }
377
378
379   static FT_Bool
380   pcf_has_table_type( PCF_Table  tables,
381                       FT_ULong   ntables, /* same as PCF_Toc->count */
382                       FT_ULong   type )
383   {
384     FT_ULong  i;
385
386
387     for ( i = 0; i < ntables; i++ )
388       if ( tables[i].type == type )
389         return TRUE;
390
391     return FALSE;
392   }
393
394
395 #define PCF_PROPERTY_SIZE  9
396
397   static
398   const FT_Frame_Field  pcf_property_header[] =
399   {
400 #undef  FT_STRUCTURE
401 #define FT_STRUCTURE  PCF_ParsePropertyRec
402
403     FT_FRAME_START( PCF_PROPERTY_SIZE ),
404       FT_FRAME_LONG_LE( name ),
405       FT_FRAME_BYTE   ( isString ),
406       FT_FRAME_LONG_LE( value ),
407     FT_FRAME_END
408   };
409
410
411   static
412   const FT_Frame_Field  pcf_property_msb_header[] =
413   {
414 #undef  FT_STRUCTURE
415 #define FT_STRUCTURE  PCF_ParsePropertyRec
416
417     FT_FRAME_START( PCF_PROPERTY_SIZE ),
418       FT_FRAME_LONG( name ),
419       FT_FRAME_BYTE( isString ),
420       FT_FRAME_LONG( value ),
421     FT_FRAME_END
422   };
423
424
425   FT_LOCAL_DEF( PCF_Property )
426   pcf_find_property( PCF_Face          face,
427                      const FT_String*  prop )
428   {
429     PCF_Property  properties = face->properties;
430     FT_Bool       found      = 0;
431     int           i;
432
433
434     for ( i = 0 ; i < face->nprops && !found; i++ )
435     {
436       if ( !ft_strcmp( properties[i].name, prop ) )
437         found = 1;
438     }
439
440     if ( found )
441       return properties + i - 1;
442     else
443       return NULL;
444   }
445
446
447   static FT_Error
448   pcf_get_properties( FT_Stream  stream,
449                       PCF_Face   face )
450   {
451     PCF_ParseProperty  props      = 0;
452     PCF_Property       properties = NULL;
453     FT_ULong           nprops, i;
454     FT_ULong           format, size;
455     FT_Error           error;
456     FT_Memory          memory     = FT_FACE( face )->memory;
457     FT_ULong           string_size;
458     FT_String*         strings    = 0;
459
460
461     error = pcf_seek_to_table_type( stream,
462                                     face->toc.tables,
463                                     face->toc.count,
464                                     PCF_PROPERTIES,
465                                     &format,
466                                     &size );
467     if ( error )
468       goto Bail;
469
470     if ( FT_READ_ULONG_LE( format ) )
471       goto Bail;
472
473     FT_TRACE4(( "pcf_get_properties:\n" ));
474
475     FT_TRACE4(( "  format = %ld\n", format ));
476
477     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
478       goto Bail;
479
480     if ( PCF_BYTE_ORDER( format ) == MSBFirst )
481       (void)FT_READ_ULONG( nprops );
482     else
483       (void)FT_READ_ULONG_LE( nprops );
484     if ( error )
485       goto Bail;
486
487     FT_TRACE4(( "  nprop = %d (truncate %d props)\n",
488                 (int)nprops, nprops - (int)nprops ));
489
490     nprops = (int)nprops;
491
492     /* rough estimate */
493     if ( nprops > size / PCF_PROPERTY_SIZE )
494     {
495       error = FT_THROW( Invalid_Table );
496       goto Bail;
497     }
498
499     face->nprops = (int)nprops;
500
501     if ( FT_NEW_ARRAY( props, nprops ) )
502       goto Bail;
503
504     for ( i = 0; i < nprops; i++ )
505     {
506       if ( PCF_BYTE_ORDER( format ) == MSBFirst )
507       {
508         if ( FT_STREAM_READ_FIELDS( pcf_property_msb_header, props + i ) )
509           goto Bail;
510       }
511       else
512       {
513         if ( FT_STREAM_READ_FIELDS( pcf_property_header, props + i ) )
514           goto Bail;
515       }
516     }
517
518     /* pad the property array                                            */
519     /*                                                                   */
520     /* clever here - nprops is the same as the number of odd-units read, */
521     /* as only isStringProp are odd length   (Keith Packard)             */
522     /*                                                                   */
523     if ( nprops & 3 )
524     {
525       i = 4 - ( nprops & 3 );
526       if ( FT_STREAM_SKIP( i ) )
527       {
528         error = FT_THROW( Invalid_Stream_Skip );
529         goto Bail;
530       }
531     }
532
533     if ( PCF_BYTE_ORDER( format ) == MSBFirst )
534       (void)FT_READ_ULONG( string_size );
535     else
536       (void)FT_READ_ULONG_LE( string_size );
537     if ( error )
538       goto Bail;
539
540     FT_TRACE4(( "  string_size = %ld\n", string_size ));
541
542     /* rough estimate */
543     if ( string_size > size - nprops * PCF_PROPERTY_SIZE )
544     {
545       error = FT_THROW( Invalid_Table );
546       goto Bail;
547     }
548
549     /* allocate one more byte so that we have a final null byte */
550     if ( FT_NEW_ARRAY( strings, string_size + 1 ) )
551       goto Bail;
552
553     error = FT_Stream_Read( stream, (FT_Byte*)strings, string_size );
554     if ( error )
555       goto Bail;
556
557     if ( FT_NEW_ARRAY( properties, nprops ) )
558       goto Bail;
559
560     face->properties = properties;
561
562     for ( i = 0; i < nprops; i++ )
563     {
564       FT_Long  name_offset = props[i].name;
565
566
567       if ( ( name_offset < 0 )                     ||
568            ( (FT_ULong)name_offset > string_size ) )
569       {
570         error = FT_THROW( Invalid_Offset );
571         goto Bail;
572       }
573
574       if ( FT_STRDUP( properties[i].name, strings + name_offset ) )
575         goto Bail;
576
577       FT_TRACE4(( "  %s:", properties[i].name ));
578
579       properties[i].isString = props[i].isString;
580
581       if ( props[i].isString )
582       {
583         FT_Long  value_offset = props[i].value;
584
585
586         if ( ( value_offset < 0 )                     ||
587              ( (FT_ULong)value_offset > string_size ) )
588         {
589           error = FT_THROW( Invalid_Offset );
590           goto Bail;
591         }
592
593         if ( FT_STRDUP( properties[i].value.atom, strings + value_offset ) )
594           goto Bail;
595
596         FT_TRACE4(( " `%s'\n", properties[i].value.atom ));
597       }
598       else
599       {
600         properties[i].value.l = props[i].value;
601
602         FT_TRACE4(( " %d\n", properties[i].value.l ));
603       }
604     }
605
606     error = FT_Err_Ok;
607
608   Bail:
609     FT_FREE( props );
610     FT_FREE( strings );
611
612     return error;
613   }
614
615
616   static FT_Error
617   pcf_get_metrics( FT_Stream  stream,
618                    PCF_Face   face )
619   {
620     FT_Error    error;
621     FT_Memory   memory  = FT_FACE( face )->memory;
622     FT_ULong    format, size;
623     PCF_Metric  metrics = 0;
624     FT_ULong    nmetrics, i;
625
626
627     error = pcf_seek_to_table_type( stream,
628                                     face->toc.tables,
629                                     face->toc.count,
630                                     PCF_METRICS,
631                                     &format,
632                                     &size );
633     if ( error )
634       return error;
635
636     if ( FT_READ_ULONG_LE( format ) )
637       goto Bail;
638
639     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT )     &&
640          !PCF_FORMAT_MATCH( format, PCF_COMPRESSED_METRICS ) )
641       return FT_THROW( Invalid_File_Format );
642
643     if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
644     {
645       if ( PCF_BYTE_ORDER( format ) == MSBFirst )
646         (void)FT_READ_ULONG( nmetrics );
647       else
648         (void)FT_READ_ULONG_LE( nmetrics );
649     }
650     else
651     {
652       if ( PCF_BYTE_ORDER( format ) == MSBFirst )
653         (void)FT_READ_USHORT( nmetrics );
654       else
655         (void)FT_READ_USHORT_LE( nmetrics );
656     }
657     if ( error )
658       return FT_THROW( Invalid_File_Format );
659
660     face->nmetrics = nmetrics;
661
662     if ( !nmetrics )
663       return FT_THROW( Invalid_Table );
664
665     FT_TRACE4(( "pcf_get_metrics:\n" ));
666
667     FT_TRACE4(( "  number of metrics: %d\n", nmetrics ));
668
669     /* rough estimate */
670     if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
671     {
672       if ( nmetrics > size / PCF_METRIC_SIZE )
673         return FT_THROW( Invalid_Table );
674     }
675     else
676     {
677       if ( nmetrics > size / PCF_COMPRESSED_METRIC_SIZE )
678         return FT_THROW( Invalid_Table );
679     }
680
681     if ( FT_NEW_ARRAY( face->metrics, nmetrics ) )
682       return FT_THROW( Out_Of_Memory );
683
684     metrics = face->metrics;
685     for ( i = 0; i < nmetrics; i++, metrics++ )
686     {
687       error = pcf_get_metric( stream, format, metrics );
688
689       metrics->bits = 0;
690
691       FT_TRACE5(( "  idx %d: width=%d, "
692                   "lsb=%d, rsb=%d, ascent=%d, descent=%d, swidth=%d\n",
693                   i,
694                   metrics->characterWidth,
695                   metrics->leftSideBearing,
696                   metrics->rightSideBearing,
697                   metrics->ascent,
698                   metrics->descent,
699                   metrics->attributes ));
700
701       if ( error )
702         break;
703
704       /* sanity checks -- those values are used in `PCF_Glyph_Load' to     */
705       /* compute a glyph's bitmap dimensions, thus setting them to zero in */
706       /* case of an error disables this particular glyph only              */
707       if ( metrics->rightSideBearing < metrics->leftSideBearing ||
708            metrics->ascent + metrics->descent < 0               )
709       {
710         metrics->characterWidth   = 0;
711         metrics->leftSideBearing  = 0;
712         metrics->rightSideBearing = 0;
713         metrics->ascent           = 0;
714         metrics->descent          = 0;
715
716         FT_TRACE0(( "pcf_get_metrics:"
717                     " invalid metrics for glyph %d\n", i ));
718       }
719     }
720
721     if ( error )
722       FT_FREE( face->metrics );
723
724   Bail:
725     return error;
726   }
727
728
729   static FT_Error
730   pcf_get_bitmaps( FT_Stream  stream,
731                    PCF_Face   face )
732   {
733     FT_Error   error;
734     FT_Memory  memory  = FT_FACE( face )->memory;
735     FT_Long*   offsets = NULL;
736     FT_Long    bitmapSizes[GLYPHPADOPTIONS];
737     FT_ULong   format, size;
738     FT_ULong   nbitmaps, i, sizebitmaps = 0;
739
740
741     error = pcf_seek_to_table_type( stream,
742                                     face->toc.tables,
743                                     face->toc.count,
744                                     PCF_BITMAPS,
745                                     &format,
746                                     &size );
747     if ( error )
748       return error;
749
750     error = FT_Stream_EnterFrame( stream, 8 );
751     if ( error )
752       return error;
753
754     format = FT_GET_ULONG_LE();
755     if ( PCF_BYTE_ORDER( format ) == MSBFirst )
756       nbitmaps  = FT_GET_ULONG();
757     else
758       nbitmaps  = FT_GET_ULONG_LE();
759
760     FT_Stream_ExitFrame( stream );
761
762     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
763       return FT_THROW( Invalid_File_Format );
764
765     FT_TRACE4(( "pcf_get_bitmaps:\n" ));
766
767     FT_TRACE4(( "  number of bitmaps: %d\n", nbitmaps ));
768
769     /* XXX: PCF_Face->nmetrics is signed FT_Long, see pcf.h */
770     if ( face->nmetrics < 0 || nbitmaps != (FT_ULong)face->nmetrics )
771       return FT_THROW( Invalid_File_Format );
772
773     if ( FT_NEW_ARRAY( offsets, nbitmaps ) )
774       return error;
775
776     for ( i = 0; i < nbitmaps; i++ )
777     {
778       if ( PCF_BYTE_ORDER( format ) == MSBFirst )
779         (void)FT_READ_LONG( offsets[i] );
780       else
781         (void)FT_READ_LONG_LE( offsets[i] );
782
783       FT_TRACE5(( "  bitmap %d: offset %ld (0x%lX)\n",
784                   i, offsets[i], offsets[i] ));
785     }
786     if ( error )
787       goto Bail;
788
789     for ( i = 0; i < GLYPHPADOPTIONS; i++ )
790     {
791       if ( PCF_BYTE_ORDER( format ) == MSBFirst )
792         (void)FT_READ_LONG( bitmapSizes[i] );
793       else
794         (void)FT_READ_LONG_LE( bitmapSizes[i] );
795       if ( error )
796         goto Bail;
797
798       sizebitmaps = bitmapSizes[PCF_GLYPH_PAD_INDEX( format )];
799
800       FT_TRACE4(( "  padding %d implies a size of %ld\n", i, bitmapSizes[i] ));
801     }
802
803     FT_TRACE4(( "  %d bitmaps, padding index %ld\n",
804                 nbitmaps,
805                 PCF_GLYPH_PAD_INDEX( format ) ));
806     FT_TRACE4(( "  bitmap size = %d\n", sizebitmaps ));
807
808     FT_UNUSED( sizebitmaps );       /* only used for debugging */
809
810     for ( i = 0; i < nbitmaps; i++ )
811     {
812       /* rough estimate */
813       if ( ( offsets[i] < 0 )              ||
814            ( (FT_ULong)offsets[i] > size ) )
815       {
816         FT_TRACE0(( "pcf_get_bitmaps:"
817                     " invalid offset to bitmap data of glyph %d\n", i ));
818       }
819       else
820         face->metrics[i].bits = stream->pos + offsets[i];
821     }
822
823     face->bitmapsFormat = format;
824
825   Bail:
826     FT_FREE( offsets );
827     return error;
828   }
829
830
831   static FT_Error
832   pcf_get_encodings( FT_Stream  stream,
833                      PCF_Face   face )
834   {
835     FT_Error      error;
836     FT_Memory     memory = FT_FACE( face )->memory;
837     FT_ULong      format, size;
838     int           firstCol, lastCol;
839     int           firstRow, lastRow;
840     int           nencoding, encodingOffset;
841     int           i, j, k;
842     PCF_Encoding  encoding = NULL;
843
844
845     error = pcf_seek_to_table_type( stream,
846                                     face->toc.tables,
847                                     face->toc.count,
848                                     PCF_BDF_ENCODINGS,
849                                     &format,
850                                     &size );
851     if ( error )
852       return error;
853
854     error = FT_Stream_EnterFrame( stream, 14 );
855     if ( error )
856       return error;
857
858     format = FT_GET_ULONG_LE();
859
860     if ( PCF_BYTE_ORDER( format ) == MSBFirst )
861     {
862       firstCol          = FT_GET_SHORT();
863       lastCol           = FT_GET_SHORT();
864       firstRow          = FT_GET_SHORT();
865       lastRow           = FT_GET_SHORT();
866       face->defaultChar = FT_GET_SHORT();
867     }
868     else
869     {
870       firstCol          = FT_GET_SHORT_LE();
871       lastCol           = FT_GET_SHORT_LE();
872       firstRow          = FT_GET_SHORT_LE();
873       lastRow           = FT_GET_SHORT_LE();
874       face->defaultChar = FT_GET_SHORT_LE();
875     }
876
877     FT_Stream_ExitFrame( stream );
878
879     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
880       return FT_THROW( Invalid_File_Format );
881
882     /* sanity checks */
883     if ( firstCol < 0       ||
884          firstCol > lastCol ||
885          lastCol  > 0xFF    ||
886          firstRow < 0       ||
887          firstRow > lastRow ||
888          lastRow  > 0xFF    )
889       return FT_THROW( Invalid_Table );
890
891     FT_TRACE4(( "pdf_get_encodings:\n" ));
892
893     FT_TRACE4(( "  firstCol %d, lastCol %d, firstRow %d, lastRow %d\n",
894                 firstCol, lastCol, firstRow, lastRow ));
895
896     nencoding = ( lastCol - firstCol + 1 ) * ( lastRow - firstRow + 1 );
897
898     if ( FT_NEW_ARRAY( encoding, nencoding ) )
899       return FT_THROW( Out_Of_Memory );
900
901     error = FT_Stream_EnterFrame( stream, 2 * nencoding );
902     if ( error )
903       goto Bail;
904
905     k = 0;
906     for ( i = firstRow; i <= lastRow; i++ )
907     {
908       for ( j = firstCol; j <= lastCol; j++ )
909       {
910         if ( PCF_BYTE_ORDER( format ) == MSBFirst )
911           encodingOffset = FT_GET_SHORT();
912         else
913           encodingOffset = FT_GET_SHORT_LE();
914
915         if ( encodingOffset != -1 )
916         {
917           encoding[k].enc   = i * 256 + j;
918           encoding[k].glyph = (FT_Short)encodingOffset;
919
920           FT_TRACE5(( "  code %d (0x%04X): idx %d\n",
921                       encoding[k].enc, encoding[k].enc, encoding[k].glyph ));
922
923           k++;
924         }
925       }
926     }
927     FT_Stream_ExitFrame( stream );
928
929     if ( FT_RENEW_ARRAY( encoding, nencoding, k ) )
930       goto Bail;
931
932     face->nencodings = k;
933     face->encodings  = encoding;
934
935     return error;
936
937   Bail:
938     FT_FREE( encoding );
939     return error;
940   }
941
942
943   static
944   const FT_Frame_Field  pcf_accel_header[] =
945   {
946 #undef  FT_STRUCTURE
947 #define FT_STRUCTURE  PCF_AccelRec
948
949     FT_FRAME_START( 20 ),
950       FT_FRAME_BYTE      ( noOverlap ),
951       FT_FRAME_BYTE      ( constantMetrics ),
952       FT_FRAME_BYTE      ( terminalFont ),
953       FT_FRAME_BYTE      ( constantWidth ),
954       FT_FRAME_BYTE      ( inkInside ),
955       FT_FRAME_BYTE      ( inkMetrics ),
956       FT_FRAME_BYTE      ( drawDirection ),
957       FT_FRAME_SKIP_BYTES( 1 ),
958       FT_FRAME_LONG_LE   ( fontAscent ),
959       FT_FRAME_LONG_LE   ( fontDescent ),
960       FT_FRAME_LONG_LE   ( maxOverlap ),
961     FT_FRAME_END
962   };
963
964
965   static
966   const FT_Frame_Field  pcf_accel_msb_header[] =
967   {
968 #undef  FT_STRUCTURE
969 #define FT_STRUCTURE  PCF_AccelRec
970
971     FT_FRAME_START( 20 ),
972       FT_FRAME_BYTE      ( noOverlap ),
973       FT_FRAME_BYTE      ( constantMetrics ),
974       FT_FRAME_BYTE      ( terminalFont ),
975       FT_FRAME_BYTE      ( constantWidth ),
976       FT_FRAME_BYTE      ( inkInside ),
977       FT_FRAME_BYTE      ( inkMetrics ),
978       FT_FRAME_BYTE      ( drawDirection ),
979       FT_FRAME_SKIP_BYTES( 1 ),
980       FT_FRAME_LONG      ( fontAscent ),
981       FT_FRAME_LONG      ( fontDescent ),
982       FT_FRAME_LONG      ( maxOverlap ),
983     FT_FRAME_END
984   };
985
986
987   static FT_Error
988   pcf_get_accel( FT_Stream  stream,
989                  PCF_Face   face,
990                  FT_ULong   type )
991   {
992     FT_ULong   format, size;
993     FT_Error   error;
994     PCF_Accel  accel = &face->accel;
995
996
997     error = pcf_seek_to_table_type( stream,
998                                     face->toc.tables,
999                                     face->toc.count,
1000                                     type,
1001                                     &format,
1002                                     &size );
1003     if ( error )
1004       goto Bail;
1005
1006     if ( FT_READ_ULONG_LE( format ) )
1007       goto Bail;
1008
1009     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT )    &&
1010          !PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) )
1011       goto Bail;
1012
1013     if ( PCF_BYTE_ORDER( format ) == MSBFirst )
1014     {
1015       if ( FT_STREAM_READ_FIELDS( pcf_accel_msb_header, accel ) )
1016         goto Bail;
1017     }
1018     else
1019     {
1020       if ( FT_STREAM_READ_FIELDS( pcf_accel_header, accel ) )
1021         goto Bail;
1022     }
1023
1024     error = pcf_get_metric( stream,
1025                             format & ( ~PCF_FORMAT_MASK ),
1026                             &(accel->minbounds) );
1027     if ( error )
1028       goto Bail;
1029
1030     error = pcf_get_metric( stream,
1031                             format & ( ~PCF_FORMAT_MASK ),
1032                             &(accel->maxbounds) );
1033     if ( error )
1034       goto Bail;
1035
1036     if ( PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) )
1037     {
1038       error = pcf_get_metric( stream,
1039                               format & ( ~PCF_FORMAT_MASK ),
1040                               &(accel->ink_minbounds) );
1041       if ( error )
1042         goto Bail;
1043
1044       error = pcf_get_metric( stream,
1045                               format & ( ~PCF_FORMAT_MASK ),
1046                               &(accel->ink_maxbounds) );
1047       if ( error )
1048         goto Bail;
1049     }
1050     else
1051     {
1052       accel->ink_minbounds = accel->minbounds; /* I'm not sure about this */
1053       accel->ink_maxbounds = accel->maxbounds;
1054     }
1055
1056   Bail:
1057     return error;
1058   }
1059
1060
1061   static FT_Error
1062   pcf_interpret_style( PCF_Face  pcf )
1063   {
1064     FT_Error   error  = FT_Err_Ok;
1065     FT_Face    face   = FT_FACE( pcf );
1066     FT_Memory  memory = face->memory;
1067
1068     PCF_Property  prop;
1069
1070     size_t  nn, len;
1071     char*   strings[4] = { NULL, NULL, NULL, NULL };
1072     size_t  lengths[4];
1073
1074
1075     face->style_flags = 0;
1076
1077     prop = pcf_find_property( pcf, "SLANT" );
1078     if ( prop && prop->isString                                       &&
1079          ( *(prop->value.atom) == 'O' || *(prop->value.atom) == 'o' ||
1080            *(prop->value.atom) == 'I' || *(prop->value.atom) == 'i' ) )
1081     {
1082       face->style_flags |= FT_STYLE_FLAG_ITALIC;
1083       strings[2] = ( *(prop->value.atom) == 'O' ||
1084                      *(prop->value.atom) == 'o' ) ? (char *)"Oblique"
1085                                                   : (char *)"Italic";
1086     }
1087
1088     prop = pcf_find_property( pcf, "WEIGHT_NAME" );
1089     if ( prop && prop->isString                                       &&
1090          ( *(prop->value.atom) == 'B' || *(prop->value.atom) == 'b' ) )
1091     {
1092       face->style_flags |= FT_STYLE_FLAG_BOLD;
1093       strings[1] = (char*)"Bold";
1094     }
1095
1096     prop = pcf_find_property( pcf, "SETWIDTH_NAME" );
1097     if ( prop && prop->isString                                        &&
1098          *(prop->value.atom)                                           &&
1099          !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) )
1100       strings[3] = (char*)( prop->value.atom );
1101
1102     prop = pcf_find_property( pcf, "ADD_STYLE_NAME" );
1103     if ( prop && prop->isString                                        &&
1104          *(prop->value.atom)                                           &&
1105          !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) )
1106       strings[0] = (char*)( prop->value.atom );
1107
1108     for ( len = 0, nn = 0; nn < 4; nn++ )
1109     {
1110       lengths[nn] = 0;
1111       if ( strings[nn] )
1112       {
1113         lengths[nn] = ft_strlen( strings[nn] );
1114         len        += lengths[nn] + 1;
1115       }
1116     }
1117
1118     if ( len == 0 )
1119     {
1120       strings[0] = (char*)"Regular";
1121       lengths[0] = ft_strlen( strings[0] );
1122       len        = lengths[0] + 1;
1123     }
1124
1125     {
1126       char*  s;
1127
1128
1129       if ( FT_ALLOC( face->style_name, len ) )
1130         return error;
1131
1132       s = face->style_name;
1133
1134       for ( nn = 0; nn < 4; nn++ )
1135       {
1136         char*  src = strings[nn];
1137
1138
1139         len = lengths[nn];
1140
1141         if ( src == NULL )
1142           continue;
1143
1144         /* separate elements with a space */
1145         if ( s != face->style_name )
1146           *s++ = ' ';
1147
1148         ft_memcpy( s, src, len );
1149
1150         /* need to convert spaces to dashes for */
1151         /* add_style_name and setwidth_name     */
1152         if ( nn == 0 || nn == 3 )
1153         {
1154           size_t  mm;
1155
1156
1157           for ( mm = 0; mm < len; mm++ )
1158             if ( s[mm] == ' ' )
1159               s[mm] = '-';
1160         }
1161
1162         s += len;
1163       }
1164       *s = 0;
1165     }
1166
1167     return error;
1168   }
1169
1170
1171   FT_LOCAL_DEF( FT_Error )
1172   pcf_load_font( FT_Stream  stream,
1173                  PCF_Face   face )
1174   {
1175     FT_Error   error;
1176     FT_Memory  memory = FT_FACE( face )->memory;
1177     FT_Bool    hasBDFAccelerators;
1178
1179
1180     error = pcf_read_TOC( stream, face );
1181     if ( error )
1182       goto Exit;
1183
1184     error = pcf_get_properties( stream, face );
1185     if ( error )
1186       goto Exit;
1187
1188     /* Use the old accelerators if no BDF accelerators are in the file. */
1189     hasBDFAccelerators = pcf_has_table_type( face->toc.tables,
1190                                              face->toc.count,
1191                                              PCF_BDF_ACCELERATORS );
1192     if ( !hasBDFAccelerators )
1193     {
1194       error = pcf_get_accel( stream, face, PCF_ACCELERATORS );
1195       if ( error )
1196         goto Exit;
1197     }
1198
1199     /* metrics */
1200     error = pcf_get_metrics( stream, face );
1201     if ( error )
1202       goto Exit;
1203
1204     /* bitmaps */
1205     error = pcf_get_bitmaps( stream, face );
1206     if ( error )
1207       goto Exit;
1208
1209     /* encodings */
1210     error = pcf_get_encodings( stream, face );
1211     if ( error )
1212       goto Exit;
1213
1214     /* BDF style accelerators (i.e. bounds based on encoded glyphs) */
1215     if ( hasBDFAccelerators )
1216     {
1217       error = pcf_get_accel( stream, face, PCF_BDF_ACCELERATORS );
1218       if ( error )
1219         goto Exit;
1220     }
1221
1222     /* XXX: TO DO: inkmetrics and glyph_names are missing */
1223
1224     /* now construct the face object */
1225     {
1226       FT_Face       root = FT_FACE( face );
1227       PCF_Property  prop;
1228
1229
1230       root->num_faces  = 1;
1231       root->face_index = 0;
1232
1233       root->face_flags |= FT_FACE_FLAG_FIXED_SIZES |
1234                           FT_FACE_FLAG_HORIZONTAL  |
1235                           FT_FACE_FLAG_FAST_GLYPHS;
1236
1237       if ( face->accel.constantWidth )
1238         root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
1239
1240       if ( ( error = pcf_interpret_style( face ) ) != 0 )
1241          goto Exit;
1242
1243       prop = pcf_find_property( face, "FAMILY_NAME" );
1244       if ( prop && prop->isString )
1245       {
1246         if ( FT_STRDUP( root->family_name, prop->value.atom ) )
1247           goto Exit;
1248       }
1249       else
1250         root->family_name = NULL;
1251
1252       /*
1253        * Note: We shift all glyph indices by +1 since we must
1254        * respect the convention that glyph 0 always corresponds
1255        * to the `missing glyph'.
1256        *
1257        * This implies bumping the number of `available' glyphs by 1.
1258        */
1259       root->num_glyphs = face->nmetrics + 1;
1260
1261       root->num_fixed_sizes = 1;
1262       if ( FT_NEW_ARRAY( root->available_sizes, 1 ) )
1263         goto Exit;
1264
1265       {
1266         FT_Bitmap_Size*  bsize = root->available_sizes;
1267         FT_Short         resolution_x = 0, resolution_y = 0;
1268
1269
1270         FT_MEM_ZERO( bsize, sizeof ( FT_Bitmap_Size ) );
1271
1272 #if 0
1273         bsize->height = face->accel.maxbounds.ascent << 6;
1274 #endif
1275         bsize->height = (FT_Short)( face->accel.fontAscent +
1276                                     face->accel.fontDescent );
1277
1278         prop = pcf_find_property( face, "AVERAGE_WIDTH" );
1279         if ( prop )
1280           bsize->width = (FT_Short)( ( prop->value.l + 5 ) / 10 );
1281         else
1282           bsize->width = (FT_Short)( bsize->height * 2/3 );
1283
1284         prop = pcf_find_property( face, "POINT_SIZE" );
1285         if ( prop )
1286           /* convert from 722.7 decipoints to 72 points per inch */
1287           bsize->size =
1288             (FT_Pos)( ( prop->value.l * 64 * 7200 + 36135L ) / 72270L );
1289
1290         prop = pcf_find_property( face, "PIXEL_SIZE" );
1291         if ( prop )
1292           bsize->y_ppem = (FT_Short)prop->value.l << 6;
1293
1294         prop = pcf_find_property( face, "RESOLUTION_X" );
1295         if ( prop )
1296           resolution_x = (FT_Short)prop->value.l;
1297
1298         prop = pcf_find_property( face, "RESOLUTION_Y" );
1299         if ( prop )
1300           resolution_y = (FT_Short)prop->value.l;
1301
1302         if ( bsize->y_ppem == 0 )
1303         {
1304           bsize->y_ppem = bsize->size;
1305           if ( resolution_y )
1306             bsize->y_ppem = bsize->y_ppem * resolution_y / 72;
1307         }
1308         if ( resolution_x && resolution_y )
1309           bsize->x_ppem = bsize->y_ppem * resolution_x / resolution_y;
1310         else
1311           bsize->x_ppem = bsize->y_ppem;
1312       }
1313
1314       /* set up charset */
1315       {
1316         PCF_Property  charset_registry = 0, charset_encoding = 0;
1317
1318
1319         charset_registry = pcf_find_property( face, "CHARSET_REGISTRY" );
1320         charset_encoding = pcf_find_property( face, "CHARSET_ENCODING" );
1321
1322         if ( charset_registry && charset_registry->isString &&
1323              charset_encoding && charset_encoding->isString )
1324         {
1325           if ( FT_STRDUP( face->charset_encoding,
1326                           charset_encoding->value.atom ) ||
1327                FT_STRDUP( face->charset_registry,
1328                           charset_registry->value.atom ) )
1329             goto Exit;
1330         }
1331       }
1332     }
1333
1334   Exit:
1335     if ( error )
1336     {
1337       /* This is done to respect the behaviour of the original */
1338       /* PCF font driver.                                      */
1339       error = FT_THROW( Invalid_File_Format );
1340     }
1341
1342     return error;
1343   }
1344
1345
1346 /* END */