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