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