misc fixes to get rrdtool working without included libraries.
[rrdtool.git] / libraries / freetype-2.0.5 / ftmac.c
1 /***************************************************************************/
2 /*                                                                         */
3 /*  ftmac.c                                                                */
4 /*                                                                         */
5 /*    Mac FOND support.  Written by just@letterror.com.                    */
6 /*                                                                         */
7 /*  Copyright 1996-2001 by                                                 */
8 /*  Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg.     */
9 /*                                                                         */
10 /*  This file is part of the FreeType project, and may only be used,       */
11 /*  modified, and distributed under the terms of the FreeType project      */
12 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
13 /*  this file you indicate that you have read the license and              */
14 /*  understand and accept it fully.                                        */
15 /*                                                                         */
16 /***************************************************************************/
17
18
19   /*
20     Notes
21
22     Mac suitcase files can (and often do!) contain multiple fonts. To
23     support this I use the face_index argument of FT_(Open|New)_Face()
24     functions, and pretend the suitcase file is a collection.
25     Warning: although the FOND driver sets face->num_faces field to the
26     number of available fonts, but the Type 1 driver sets it to 1 anyway.
27     So this field is currently not reliable, and I don't see a clean way
28     to  resolve that. The face_index argument translates to
29       Get1IndResource( 'FOND', face_index + 1 );
30     so clients should figure out the resource index of the FOND.
31     (I'll try to provide some example code for this at some point.)
32
33
34     The Mac FOND support works roughly like this:
35
36     - Check whether the offered stream points to a Mac suitcase file.
37       This is done by checking the file type: it has to be 'FFIL' or 'tfil'.
38       The stream that gets passed to our init_face() routine is a stdio
39       stream, which isn't usable for us, since the FOND resources live
40       in the resource fork. So we just grab the stream->pathname field.
41
42     - Read the FOND resource into memory, then check whether there is
43       a TrueType font and/or (!) a Type 1 font available.
44
45     - If there is a Type 1 font available (as a separate 'LWFN' file),
46       read its data into memory, massage it slightly so it becomes
47       PFB data, wrap it into a memory stream, load the Type 1 driver
48       and delegate the rest of the work to it by calling FT_Open_Face().
49       (XXX TODO: after this has been done, the kerning data from the FOND
50       resource should be appended to the face: on the Mac there are usually
51       no AFM files available. However, this is tricky since we need to map
52       Mac char codes to ps glyph names to glyph ID's...)
53
54     - If there is a TrueType font (an 'sfnt' resource), read it into
55       memory, wrap it into a memory stream, load the TrueType driver
56       and delegate the rest of the work to it, by calling FT_Open_Face().
57   */
58
59
60 #include <ft2build.h>
61 #include FT_FREETYPE_H
62 #include FT_INTERNAL_STREAM_H
63 #include "truetype/ttobjs.h"
64 #include "type1/t1objs.h"
65
66 #include <Resources.h>
67 #include <Fonts.h>
68 #include <Errors.h>
69
70 #include <ctype.h>  /* for isupper() and isalnum() */
71
72 #include FT_MAC_H
73
74
75
76   /* Set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over
77      TrueType in case *both* are available (this is not common,
78      but it *is* possible). */
79 #ifndef PREFER_LWFN
80 #define PREFER_LWFN 1
81 #endif
82
83
84
85   /* Quick'n'dirty Pascal string to C string converter.
86      Warning: this call is not thread safe! Use with caution. */
87   static char*
88   p2c_str( unsigned char*  pstr )
89   {
90     static char  cstr[256];
91
92
93     strncpy( cstr, (char*)pstr + 1, pstr[0] );
94     cstr[pstr[0]] = '\0';
95     return cstr;
96   }
97
98
99   /* Given a pathname, fill in a file spec. */
100   static int
101   file_spec_from_path( const char*  pathname,
102                        FSSpec*      spec )
103   {
104     Str255    p_path;
105     FT_ULong  path_len;
106
107
108     /* convert path to a pascal string */
109     path_len = strlen( pathname );
110     if ( path_len > 255 )
111       return -1;
112     p_path[0] = (unsigned char)path_len;
113     strncpy( (char*)p_path + 1, pathname, path_len );
114
115     if ( FSMakeFSSpec( 0, 0, p_path, spec ) != noErr )
116       return -1;
117     else
118       return 0;
119   }
120
121
122   /* Return the file type of the file specified by spec. */
123   static OSType
124   get_file_type( FSSpec*  spec )
125   {
126     FInfo  finfo;
127
128
129     if ( FSpGetFInfo( spec, &finfo ) != noErr )
130       return 0;  /* file might not exist */
131
132     return finfo.fdType;
133   }
134
135
136   /* Given a PostScript font name, create the Macintosh LWFN file name. */
137   static void
138   create_lwfn_name( char*   ps_name,
139                     Str255  lwfn_file_name )
140   {
141     int       max = 5, count = 0;
142     FT_Byte*  p = lwfn_file_name;
143     FT_Byte*  q = (FT_Byte*)ps_name;
144
145
146     lwfn_file_name[0] = 0;
147
148     while ( *q )
149     {
150       if ( isupper( *q ) )
151       {
152         if ( count )
153           max = 3;
154         count = 0;
155       }
156       if ( count < max && ( isalnum( *q ) || *q == '_' ) )
157       {
158         *++p = *q;
159         lwfn_file_name[0]++;
160         count++;
161       }
162       q++;
163     }
164   }
165
166
167   /* Given a file reference, answer its location as a vRefNum
168      and a dirID. */
169   static FT_Error
170   get_file_location( short           ref_num,
171                      short*          v_ref_num,
172                      long*           dir_id,
173                      unsigned char*  file_name )
174   {
175     FCBPBRec  pb;
176     OSErr     error;
177
178     pb.ioNamePtr = file_name;
179     pb.ioVRefNum = 0;
180     pb.ioRefNum  = ref_num;
181     pb.ioFCBIndx = 0;
182
183
184     error = PBGetFCBInfoSync( &pb );
185     if ( error == noErr )
186     {
187       *v_ref_num = pb.ioFCBVRefNum;
188       *dir_id    = pb.ioFCBParID;
189     }
190     return error;
191   }
192
193
194   /* Make a file spec for an LWFN file from a FOND resource and
195      a file name. */
196   static FT_Error
197   make_lwfn_spec( Handle          fond,
198                   unsigned char*  file_name,
199                   FSSpec*         spec )
200   {
201     FT_Error  error;
202     short     ref_num, v_ref_num;
203     long      dir_id;
204     Str255    fond_file_name;
205
206
207     ref_num = HomeResFile( fond );
208
209     error = ResError();
210     if ( !error )
211       error = get_file_location( ref_num, &v_ref_num,
212                                  &dir_id, fond_file_name );
213     if ( !error )
214       error = FSMakeFSSpec( v_ref_num, dir_id, file_name, spec );
215
216     return error;
217   }
218
219
220   /* Look inside the FOND data, answer whether there should be an SFNT
221      resource, and answer the name of a possible LWFN Type 1 file. */
222   static void
223   parse_fond( char*   fond_data,
224               short*  have_sfnt,
225               short*  sfnt_id,
226               Str255  lwfn_file_name )
227   {
228     AsscEntry*  assoc;
229     FamRec*     fond;
230
231
232     *sfnt_id          = 0;
233     *have_sfnt        = 0;
234     lwfn_file_name[0] = 0;
235
236     fond = (FamRec*)fond_data;
237     assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
238
239     if ( assoc->fontSize == 0 )
240     {
241       *have_sfnt = 1;
242       *sfnt_id = assoc->fontID;
243     }
244
245     if ( fond->ffStylOff )
246     {
247       unsigned char*  p = (unsigned char*)fond_data;
248       StyleTable*     style;
249       unsigned short  string_count;
250       unsigned char*  name_table = 0;
251       char            ps_name[256];
252       unsigned char*  names[64];
253       int             i;
254
255
256       p += fond->ffStylOff;
257       style = (StyleTable*)p;
258       p += sizeof ( StyleTable );
259       string_count = *(unsigned short*)(p);
260       p += sizeof ( short );
261
262       for ( i = 0 ; i < string_count && i < 64; i++ )
263       {
264         names[i] = p;
265         p += names[i][0];
266         p++;
267       }
268       strcpy( ps_name, p2c_str( names[0] ) );  /* Family name */
269
270       if ( style->indexes[0] > 1 )
271       {
272         unsigned char*  suffixes = names[style->indexes[0] - 1];
273
274
275         for ( i=1; i<=suffixes[0]; i++ )
276           strcat( ps_name, p2c_str( names[suffixes[i] - 1 ] ) );
277       }
278       create_lwfn_name( ps_name, lwfn_file_name );
279     }
280   }
281
282
283   /* Read Type 1 data from the POST resources inside the LWFN file,
284      return a PFB buffer. This is somewhat convoluted because the FT2
285      PFB parser wants the ASCII header as one chunk, and the LWFN
286      chunks are often not organized that way, so we'll glue chunks
287      of the same type together. */
288   static FT_Error
289   read_lwfn( FT_Memory  memory,
290              FSSpec*    lwfn_spec,
291              FT_Byte**  pfb_data,
292              FT_ULong*  size )
293   {
294     FT_Error       error = FT_Err_Ok;
295     short          res_ref, res_id;
296     unsigned char  *buffer, *p, *size_p;
297     FT_ULong       total_size = 0;
298     FT_ULong       post_size, pfb_chunk_size;
299     Handle         post_data;
300     char           code, last_code;
301
302
303     res_ref = FSpOpenResFile( lwfn_spec, fsRdPerm );
304     if ( ResError() )
305       return FT_Err_Out_Of_Memory;
306     UseResFile( res_ref );
307
308     /* First pass: load all POST resources, and determine the size of
309        the output buffer. */
310     res_id = 501;
311     last_code = -1;
312
313     for (;;)
314     {
315       post_data = Get1Resource( 'POST', res_id++ );
316       if ( post_data == NULL )
317         break;  /* we're done */
318
319       code = (*post_data)[0];
320
321       if ( code != last_code )
322       {
323         if ( code == 5 )
324           total_size += 2; /* just the end code */
325         else
326           total_size += 6; /* code + 4 bytes chunk length */
327       }
328
329       total_size += GetHandleSize( post_data ) - 2;
330       last_code = code;
331     }
332
333     if ( ALLOC( buffer, (FT_Long)total_size ) )
334       goto Error;
335
336     /* Second pass: append all POST data to the buffer, add PFB fields.
337        Glue all consecutive chunks of the same type together. */
338     p = buffer;
339     res_id = 501;
340     last_code = -1;
341     pfb_chunk_size = 0;
342
343     for (;;)
344     {
345       post_data = Get1Resource( 'POST', res_id++ );
346       if ( post_data == NULL )
347         break;  /* we're done */
348
349       post_size = (FT_ULong)GetHandleSize( post_data ) - 2;
350       code = (*post_data)[0];
351
352       if ( code != last_code )
353       {
354
355         if ( last_code != -1 )
356         {
357           /* we're done adding a chunk, fill in the size field */
358           *size_p++ = (FT_Byte)(   pfb_chunk_size         & 0xFF );
359           *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8  ) & 0xFF );
360           *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF );
361           *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF );
362           pfb_chunk_size = 0;
363         }
364
365         *p++ = 0x80;
366         if ( code == 5 )
367           *p++ = 0x03;  /* the end */
368         else if ( code == 2 )
369           *p++ = 0x02;  /* binary segment */
370         else
371           *p++ = 0x01;  /* ASCII segment */
372
373         if ( code != 5 )
374         {
375           size_p = p;   /* save for later */
376           p += 4;       /* make space for size field */
377         }
378       }
379
380       memcpy( p, *post_data + 2, post_size );
381       pfb_chunk_size += post_size;
382       p += post_size;
383       last_code = code;
384     }
385
386     *pfb_data = buffer;
387     *size = total_size;
388
389   Error:
390     CloseResFile( res_ref );
391     return error;
392   }
393
394
395   /* Finalizer for a memory stream; gets called by FT_Done_Face().
396      It frees the memory it uses. */
397   static void
398   memory_stream_close( FT_Stream  stream )
399   {
400     FT_Memory  memory = stream->memory;
401
402
403     FREE( stream->base );
404
405     stream->size  = 0;
406     stream->base  = 0;
407     stream->close = 0;
408   }
409
410
411   /* Create a new memory stream from a buffer and a size. */
412   static FT_Error
413   new_memory_stream( FT_Library       library,
414                      FT_Byte*         base,
415                      FT_ULong         size,
416                      FT_Stream_Close  close,
417                      FT_Stream*       astream )
418   {
419       FT_Error   error;
420       FT_Memory  memory;
421       FT_Stream  stream;
422
423
424       if ( !library )
425         return FT_Err_Invalid_Library_Handle;
426
427       if ( !base )
428         return FT_Err_Invalid_Argument;
429
430       *astream = 0;
431       memory = library->memory;
432       if ( ALLOC( stream, sizeof ( *stream ) ) )
433         goto Exit;
434
435       FT_New_Memory_Stream( library,
436                             base,
437                             size,
438                             stream );
439
440       stream->close = close;
441
442       *astream = stream;
443
444     Exit:
445       return error;
446   }
447
448
449   /* Create a new FT_Face given a buffer and a driver name. */
450   static FT_Error
451   open_face_from_buffer( FT_Library  library,
452                          FT_Byte*    base,
453                          FT_ULong    size,
454                          FT_Long     face_index,
455                          char*       driver_name,
456                          FT_Face*    aface )
457   {
458     FT_Open_Args  args;
459     FT_Error      error;
460     FT_Stream     stream;
461     FT_Memory     memory = library->memory;
462
463
464     error = new_memory_stream( library,
465                                base,
466                                size,
467                                memory_stream_close,
468                                &stream );
469     if ( error )
470     {
471       FREE( base );
472       return error;
473     }
474
475     args.flags = ft_open_stream;
476     args.stream = stream;
477     if ( driver_name )
478     {
479       args.flags = args.flags | ft_open_driver;
480       args.driver = FT_Get_Module( library, driver_name );
481     }
482
483     error = FT_Open_Face( library, &args, face_index, aface );
484     if ( error == FT_Err_Ok )
485       (*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
486     else
487     {
488       FT_Done_Stream( stream );
489       FREE( stream );
490     }
491     return error;
492   }
493
494
495   /* Create a new FT_Face from a file spec to an LWFN file. */
496   static FT_Error
497   FT_New_Face_From_LWFN( FT_Library  library,
498                          FSSpec*     spec,
499                          FT_Long     face_index,
500                          FT_Face*    aface )
501   {
502     FT_Byte*   pfb_data;
503     FT_ULong   pfb_size;
504     FT_Error   error;
505     FT_Memory  memory = library->memory;
506
507
508     error = read_lwfn( library->memory, spec, &pfb_data, &pfb_size );
509     if ( error )
510       return error;
511
512 #if 0
513     {
514       FILE*  f;
515       char*  path;
516
517
518       path = p2c_str( spec->name );
519       strcat( path, ".PFB" );
520       f = fopen( path, "wb" );
521       if ( f )
522       {
523         fwrite( pfb_data, 1, pfb_size, f );
524         fclose( f );
525       }
526     }
527 #endif
528
529     return open_face_from_buffer( library,
530                                   pfb_data,
531                                   pfb_size,
532                                   face_index,
533                                   "type1",
534                                   aface );
535   }
536
537
538   /* Create a new FT_Face from an SFNT resource, specified by res ID. */
539   static FT_Error
540   FT_New_Face_From_SFNT( FT_Library  library,
541                          short       sfnt_id,
542                          FT_Long     face_index,
543                          FT_Face*    aface )
544   {
545     Handle     sfnt = NULL;
546     FT_Byte*   sfnt_data;
547     size_t     sfnt_size;
548     FT_Stream  stream = NULL;
549     FT_Error   error = 0;
550     FT_Memory  memory = library->memory;
551
552
553     sfnt = GetResource( 'sfnt', sfnt_id );
554     if ( ResError() )
555       return FT_Err_Invalid_Handle;
556
557     sfnt_size = (FT_ULong)GetHandleSize( sfnt );
558     if ( ALLOC( sfnt_data, (FT_Long)sfnt_size ) )
559     {
560       ReleaseResource( sfnt );
561       return error;
562     }
563
564     HLock( sfnt );
565     memcpy( sfnt_data, *sfnt, sfnt_size );
566     HUnlock( sfnt );
567     ReleaseResource( sfnt );
568
569     return open_face_from_buffer( library,
570                                   sfnt_data,
571                                   sfnt_size,
572                                   face_index,
573                                   "truetype",
574                                   aface );
575   }
576
577
578   /* Create a new FT_Face from a file spec to a suitcase file. */
579   static FT_Error
580   FT_New_Face_From_Suitcase( FT_Library  library,
581                              FSSpec*     spec,
582                              FT_Long     face_index,
583                              FT_Face*    aface )
584   {
585     FT_Error  error = FT_Err_Ok;
586     short     res_ref, res_index;
587     Handle    fond;
588
589
590     res_ref = FSpOpenResFile( spec, fsRdPerm );
591     if ( ResError() )
592       return FT_Err_Cannot_Open_Resource;
593     UseResFile( res_ref );
594
595     /* face_index may be -1, in which case we
596        just need to do a sanity check */
597     if ( face_index < 0 )
598       res_index = 1;
599     else
600     {
601       res_index = (short)( face_index + 1 );
602       face_index = 0;
603     }
604     fond = Get1IndResource( 'FOND', res_index );
605     if ( ResError() )
606     {
607       error = FT_Err_Cannot_Open_Resource;
608       goto Error;
609     }
610
611     error = FT_New_Face_From_FOND( library, fond, face_index, aface );
612
613   Error:
614     CloseResFile( res_ref );
615     return error;
616   }
617
618
619   /* documentation in ftmac.h */
620
621   FT_EXPORT_DEF( FT_Error )
622   FT_New_Face_From_FOND( FT_Library  library,
623                          Handle      fond,
624                          FT_Long     face_index,
625                          FT_Face    *aface )
626   {
627     short     sfnt_id, have_sfnt, have_lwfn = 0;
628     Str255    lwfn_file_name;
629     short     fond_id;
630     OSType    fond_type;
631     Str255    fond_name;
632     FSSpec    lwfn_spec;
633     FT_Error  error = FT_Err_Unknown_File_Format;
634
635
636     GetResInfo(fond, &fond_id, &fond_type, fond_name);
637     if ( ResError() != noErr || fond_type != 'FOND' )
638       return FT_Err_Invalid_File_Format;
639
640     HLock( fond );
641     parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name );
642     HUnlock( fond );
643
644     if ( lwfn_file_name[0] )
645     {
646       if ( make_lwfn_spec( fond, lwfn_file_name, &lwfn_spec ) == FT_Err_Ok )
647         have_lwfn = 1;  /* yeah, we got one! */
648       else
649         have_lwfn = 0;  /* no LWFN file found */
650     }
651
652     if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
653       return FT_New_Face_From_LWFN( library,
654                                     &lwfn_spec,
655                                     face_index,
656                                     aface );
657     else if ( have_sfnt )
658       return FT_New_Face_From_SFNT( library,
659                                     sfnt_id,
660                                     face_index,
661                                     aface );
662
663     return FT_Err_Unknown_File_Format;
664   }
665
666
667   /*************************************************************************/
668   /*                                                                       */
669   /* <Function>                                                            */
670   /*    FT_New_Face                                                        */
671   /*                                                                       */
672   /* <Description>                                                         */
673   /*    This is the Mac-specific implementation of FT_New_Face.  In        */
674   /*    addition to the standard FT_New_Face() functionality, it also      */
675   /*    accepts pathnames to Mac suitcase files.  For further              */
676   /*    documentation see the original FT_New_Face() in freetype.h.        */
677   /*                                                                       */
678   FT_EXPORT_DEF( FT_Error )
679   FT_New_Face( FT_Library   library,
680                const char*  pathname,
681                FT_Long      face_index,
682                FT_Face     *aface )
683   {
684     FT_Open_Args  args;
685     FSSpec        spec;
686     OSType        file_type;
687
688
689     /* test for valid `library' and `aface' delayed to FT_Open_Face() */
690     if ( !pathname )
691       return FT_Err_Invalid_Argument;
692
693     if ( file_spec_from_path( pathname, &spec ) )
694       return FT_Err_Invalid_Argument;
695
696     file_type = get_file_type( &spec );
697     if ( file_type == 'FFIL' || file_type == 'tfil' )
698       return FT_New_Face_From_Suitcase( library, &spec, face_index, aface );
699     else if ( file_type == 'LWFN' )
700       return FT_New_Face_From_LWFN( library, &spec, face_index, aface );
701     else
702     {
703       args.flags    = ft_open_pathname;
704       args.pathname = (char*)pathname;
705
706       return FT_Open_Face( library, &args, face_index, aface );
707     }
708   }
709
710
711 /* END */