:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / subsys / win32k / freetype / src / macfond / fonddrvr.c
1 /***************************************************************************/
2 /*                                                                         */
3 /*  fonddrvr.c                                                             */
4 /*                                                                         */
5 /*    Mac FOND font driver. Written by just@letterror.com.                 */
6 /*                                                                         */
7 /*  Copyright 1996-2000 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     The Mac FOND driver works roughly like this:
34
35     - Check whether the offered stream points to a Mac suitcase file.
36       This is done by checking the file type: it has to be 'FFIL' or 'tfil'.
37       The stream that gets passed to our init_face() routine is a stdio
38       stream, which isn't usable for us, since the FOND resources live
39       in the resource fork. So we just grab the stream->pathname field.
40
41     - Read the FOND resource into memory, then check whether there is
42       a TrueType font and/or (!) a Type 1 font available.
43
44     - If there is a Type 1 font available (as a separate 'LWFN' file),
45       read its data into memory, massage it slightly so it becomes
46       PFB data, wrap it into a memory stream, load the Type 1 driver
47       and delegate the rest of the work to it, by calling the init_face()
48       method of the Type 1 driver.
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 the init_face()
57       method if the TrueType driver.
58
59     - In both cases, the original stream gets closed and *reinitialized*
60       to become a memory stream. Additionally, the face->driver field --
61       which is set to the FOND driver upon entering our init_face() --
62       gets *reset* to either the TT or the T1 driver. I had to make a minor
63       change to ftobjs.c to make this work.
64
65     - We might consider creating an FT_New_Face_Mac() API call, as this
66       would avoid some of the mess described above.
67 */
68
69 #include <truetype/ttobjs.h>
70 #include <type1z/z1objs.h>
71
72 #include <Resources.h>
73 #include <Fonts.h>
74 #include <Errors.h>
75
76 #include <ctype.h>  /* for isupper() and isalnum() */
77 #include <stdlib.h> /* for malloc() and free() */
78
79
80 /* set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over
81    TrueType in case *both* are available */
82 #ifndef PREFER_LWFN
83 #define PREFER_LWFN 1
84 #endif
85
86
87   static
88   FT_Error init_driver( FT_Driver  driver )
89   {
90     /* we don't keep no stinkin' state ;-) */
91     return FT_Err_Ok;
92   }
93
94   static
95   FT_Error done_driver( FT_Driver  driver )
96   {
97     return FT_Err_Ok;
98   }
99
100
101   /* MacRoman glyph names, needed for FOND kerning support. */
102   /* XXX which is not implemented yet! */
103   static const char*  mac_roman_glyph_names[256] = {
104     ".null",
105   };
106
107
108   /* The FOND face object is just a union of TT and T1: both is possible,
109      and we don't need anything else. We still need to be able to hold
110      either, as the face object is not allocated by us. Again, creating
111      an FT_New_Face_Mac() would avoid this kludge. */
112   typedef union FOND_FaceRec_
113   {
114     TT_FaceRec     tt;
115     T1_FaceRec     t1;
116   } FOND_FaceRec, *FOND_Face;
117
118
119   /* given a pathname, fill in a File Spec */
120   static
121   int make_file_spec( char* pathname, FSSpec *spec )
122   {
123     Str255  p_path;
124     int     path_len;
125
126     /* convert path to a pascal string */
127     path_len = strlen( pathname );
128     if ( path_len > 255 )
129       return -1;
130     p_path[0] = path_len;
131     strncpy( (char*)p_path+1, pathname, path_len );
132
133     if ( FSMakeFSSpec( 0, 0, p_path, spec ) != noErr )
134       return -1;
135     else
136       return 0;
137   }
138
139
140   /* is_suitcase() returns true if the file specified by 'pathname'
141      is a Mac suitcase file, and false if it ain't. */
142   static
143   int is_suitcase( FSSpec  *spec )
144   {
145     FInfo   finfo;
146
147     if ( FSpGetFInfo( spec, &finfo ) != noErr )
148       return 0;
149     if ( finfo.fdType == 'FFIL' || finfo.fdType == 'tfil' )
150       return 1;
151     else
152       return 0;
153   }
154
155
156   /* Quick 'n' Dirty Pascal string to C string converter.
157      Warning: this call is not thread safe! Use with caution. */
158   static
159   char * p2c_str( unsigned char *pstr )
160   {
161     static char cstr[256];
162
163     strncpy( cstr, (char*)pstr+1, pstr[0] );
164     cstr[pstr[0]] = '\0';
165     return cstr;
166   }
167
168
169   /* Given a PostScript font name, create the Macintosh LWFN file name */
170   static
171   void create_lwfn_name( char* ps_name, Str255 lwfn_file_name )
172   {
173     int  max = 5, count = 0;
174     unsigned char* p = lwfn_file_name;
175     char* q = ps_name;
176
177     lwfn_file_name[0] = 0;
178
179     while ( *q )
180     {
181       if ( isupper(*q) )
182       {
183         if ( count )
184           max = 3;
185         count = 0;
186       }
187       if ( count < max && (isalnum(*q) || *q == '_' ) )
188       {
189         *++p = *q;
190         lwfn_file_name[0]++;
191         count++;
192       }
193       q++;
194     }
195   }
196
197
198   /* Suck the relevant info out of the FOND data */
199   static
200   FT_Error parse_fond( char*   fond_data,
201                        short   *have_sfnt,
202                        short   *sfnt_id,
203                        Str255  lwfn_file_name )
204   {
205     AsscEntry*  assoc;
206     FamRec*     fond;
207
208     *sfnt_id = *have_sfnt = 0;
209     lwfn_file_name[0] = 0;
210
211     fond = (FamRec*)fond_data;
212     assoc = (AsscEntry*)(fond_data + sizeof(FamRec) + 2);
213
214     if ( assoc->fontSize == 0 )
215     {
216       *have_sfnt = 1;
217       *sfnt_id = assoc->fontID;
218     }
219
220     if ( fond->ffStylOff )
221     {
222       unsigned char*  p = (unsigned char*)fond_data;
223       StyleTable*     style;
224       unsigned short  string_count;
225       unsigned char*  name_table = 0;
226       char            ps_name[256];
227       unsigned char*  names[64];
228       int             i;
229
230       p += fond->ffStylOff;
231       style = (StyleTable*)p;
232       p += sizeof(StyleTable);
233       string_count = *(unsigned short*)(p);
234       p += sizeof(short);
235
236       for ( i=0 ; i<string_count && i<64; i++ )
237       {
238         names[i] = p;
239         p += names[i][0];
240         p++;
241       }
242       strcpy(ps_name, p2c_str(names[0])); /* Family name */
243
244       if ( style->indexes[0] > 1 )
245       {
246         unsigned char* suffixes = names[style->indexes[0]-1];
247         for ( i=1; i<=suffixes[0]; i++ )
248           strcat( ps_name, p2c_str(names[suffixes[i]-1]) );
249       }
250       create_lwfn_name( ps_name, lwfn_file_name );
251     }
252     return FT_Err_Ok;
253   }
254
255
256   /* Read Type 1 data from the POST resources inside the LWFN file, return a
257      PFB buffer -- apparently FT doesn't like a pure binary T1 stream. */
258   static
259   unsigned char* read_type1_data( FT_Memory memory, FSSpec* lwfn_spec, unsigned long *size )
260   {
261     short          res_ref, res_id;
262     unsigned char  *buffer, *p, *size_p;
263     unsigned long  total_size = 0;
264     unsigned long  post_size, pfb_chunk_size;
265     Handle         post_data;
266     char           code, last_code;
267
268     res_ref = FSpOpenResFile( lwfn_spec, fsRdPerm );
269     if ( ResError() )
270       return NULL;
271     UseResFile( res_ref );
272
273     /* first pass: load all POST resources, and determine the size of
274        the output buffer */
275     res_id = 501;
276     last_code = -1;
277     for (;;)
278     {
279       post_data = Get1Resource( 'POST', res_id++ );
280       if ( post_data == NULL )
281         break;
282       code = (*post_data)[0];
283       if ( code != last_code )
284       {
285         if ( code == 5 )
286           total_size += 2; /* just the end code */
287         else
288           total_size += 6; /* code + 4 bytes chunk length */
289       }
290       total_size += GetHandleSize( post_data ) - 2;
291       last_code = code;
292     }
293
294     buffer = memory->alloc( memory, total_size );
295     if ( !buffer )
296       goto error;
297
298     /* second pass: append all POST data to the buffer, add PFB fields */
299     p = buffer;
300     res_id = 501;
301     last_code = -1;
302     pfb_chunk_size = 0;
303     for (;;)
304     {
305       post_data = Get1Resource( 'POST', res_id++ );
306       if ( post_data == NULL )
307         break;
308       post_size = GetHandleSize( post_data ) - 2;
309       code = (*post_data)[0];
310       if ( code != last_code )
311       {
312         if ( last_code != -1 )
313         {
314           /* we're done adding a chunk, fill in the size field */
315           *size_p++ = pfb_chunk_size & 0xFF;
316           *size_p++ = (pfb_chunk_size >> 8) & 0xFF;
317           *size_p++ = (pfb_chunk_size >> 16) & 0xFF;
318           *size_p++ = (pfb_chunk_size >> 24) & 0xFF;
319           pfb_chunk_size = 0;
320         }
321         *p++ = 0x80;
322         if ( code == 5 )
323           *p++ = 0x03;  /* the end */
324         else if ( code == 2 )
325           *p++ = 0x02;  /* binary segment */
326         else
327           *p++ = 0x01;  /* ASCII segment */
328         if ( code != 5 )
329         {
330           size_p = p;   /* save for later */
331           p += 4;       /* make space for size field */
332         }
333       }
334       memcpy( p, *post_data + 2, post_size );
335       pfb_chunk_size += post_size;
336       p += post_size;
337       last_code = code;
338     }
339
340     CloseResFile( res_ref );
341
342     *size = total_size;
343 /*    printf( "XXX %d %d\n", p - buffer, total_size ); */
344     return buffer;
345
346 error:
347     CloseResFile( res_ref );
348     return NULL;
349   }
350
351
352   /* Finalizer for the sfnt stream */
353   static
354   void sfnt_stream_close( FT_Stream  stream )
355   {
356     Handle sfnt_data = stream->descriptor.pointer;
357     HUnlock( sfnt_data );
358     DisposeHandle( sfnt_data );
359
360     stream->descriptor.pointer = NULL;
361     stream->size               = 0;
362     stream->base               = 0;
363     stream->close              = 0;
364   }
365
366
367   /* Finalizer for the LWFN stream */
368   static
369   void lwfn_stream_close( FT_Stream  stream )
370   {
371     stream->memory->free( stream->memory, stream->base );
372     stream->descriptor.pointer = NULL;
373     stream->size               = 0;
374     stream->base               = 0;
375     stream->close              = 0;
376   }
377
378
379   /* Main entry point. Determine whether we're dealing with a Mac
380      suitcase or not; then determine if we're dealing with Type 1
381      or TrueType; delegate the work to the proper driver. */
382   static
383   FT_Error init_face( FT_Stream      stream,
384                       FT_Face        face,
385                       FT_Int         face_index,
386                       FT_Int         num_params,
387                       FT_Parameter*  parameters )
388   {
389     FT_Error      err;
390     FSSpec        suit_spec, lwfn_spec;
391     short         res_ref;
392     Handle        fond_data, sfnt_data;
393     short         res_index, sfnt_id, have_sfnt;
394     Str255        lwfn_file_name;
395
396     if ( !stream->pathname.pointer )
397       return FT_Err_Unknown_File_Format;
398
399     if ( make_file_spec( stream->pathname.pointer, &suit_spec ) )
400       return FT_Err_Invalid_Argument;
401
402     if ( !is_suitcase( &suit_spec ) )
403       return FT_Err_Unknown_File_Format;
404
405     res_ref = FSpOpenResFile( &suit_spec, fsRdPerm );
406     if ( ResError() )
407       return FT_Err_Invalid_File_Format;
408     UseResFile( res_ref );
409
410     /* face_index may be -1, in which case we
411        just need to do a sanity check */
412     if ( face_index < 0)
413       res_index = 1;
414     else
415     {
416       res_index = face_index + 1;
417       face_index = 0;
418     }
419     fond_data = Get1IndResource( 'FOND', res_index );
420     if ( ResError() )
421     {
422       CloseResFile( res_ref );
423       return FT_Err_Invalid_File_Format;
424     }
425     /* Set the number of faces. Not that it helps much: the t1 driver
426        just sets it to 1 anyway :-( */
427     face->num_faces = Count1Resources('FOND');
428
429     HLock( fond_data );
430     err = parse_fond( *fond_data, &have_sfnt, &sfnt_id, lwfn_file_name );
431     HUnlock( fond_data );
432     if ( err )
433     {
434       CloseResFile( res_ref );
435       return FT_Err_Invalid_Handle;
436     }
437
438     if ( lwfn_file_name[0] )
439     {
440       /* We look for the LWFN file in the same directory as the suitcase
441          file. ATM would look in other places, too, but this is the usual
442          situation. */
443       err = FSMakeFSSpec( suit_spec.vRefNum, suit_spec.parID, lwfn_file_name, &lwfn_spec );
444       if ( err != noErr )
445         lwfn_file_name[0] = 0;  /* no LWFN file found */
446     }
447
448     if ( lwfn_file_name[0] && ( !have_sfnt || PREFER_LWFN ) )
449     {
450       FT_Driver       t1_driver;
451       unsigned char*  type1_data;
452       unsigned long   size;
453
454       CloseResFile( res_ref ); /* XXX still need to read kerning! */
455
456       type1_data = read_type1_data( stream->memory, &lwfn_spec, &size );
457       if ( !type1_data )
458       {
459         return FT_Err_Out_Of_Memory;
460       }
461
462 #if 0
463       {
464         FILE* f;
465         char * path;
466
467         path = p2c_str( lwfn_file_name );
468         strcat( path, ".PFB" );
469         f = fopen(path, "wb");
470         if ( f )
471         {
472           fwrite( type1_data, 1, size, f );
473           fclose( f );
474         }
475       }
476 #endif
477
478       /* reinitialize the stream */
479       if ( stream->close )
480         stream->close( stream );
481       stream->close = lwfn_stream_close;
482       stream->read = 0; /* it's now memory based */
483       stream->base = type1_data;
484       stream->size = size;
485       stream->pos = 0; /* just in case */
486
487       /* delegate the work to the Type 1 module */
488       t1_driver = (FT_Driver)FT_Get_Module( face->driver->root.library, "type1z" );
489       if ( t1_driver )
490       {
491         face->driver = t1_driver;
492         return t1_driver->clazz->init_face( stream, face, face_index, 0, NULL );
493       }
494       else
495         return FT_Err_Invalid_Driver_Handle;
496     }
497     else if ( have_sfnt )
498     {
499       FT_Driver     tt_driver;
500
501       sfnt_data = Get1Resource( 'sfnt', sfnt_id );
502       if ( ResError() )
503       {
504         CloseResFile( res_ref );
505         return FT_Err_Invalid_Handle;
506       }
507       DetachResource( sfnt_data );
508       CloseResFile( res_ref );
509       HLockHi( sfnt_data );
510
511       /* reinitialize the stream */
512       if ( stream->close )
513         stream->close( stream );
514       stream->close = sfnt_stream_close;
515       stream->descriptor.pointer = sfnt_data;
516       stream->read = 0; /* it's now memory based */
517       stream->base = (unsigned char *)*sfnt_data;
518       stream->size = GetHandleSize( sfnt_data );
519       stream->pos = 0; /* just in case */
520
521       /* delegate the work to the TrueType driver */
522       tt_driver = (FT_Driver)FT_Get_Module( face->driver->root.library, "truetype" );
523       if ( tt_driver )
524       {
525         face->driver = tt_driver;
526         return tt_driver->clazz->init_face( stream, face, face_index, 0, NULL );
527       }
528       else
529         return FT_Err_Invalid_Driver_Handle;
530     }
531     else
532     {
533       CloseResFile( res_ref );
534     }
535     return FT_Err_Invalid_File_Format;
536   }
537
538
539   static
540   void  done_face( FT_Face  face )
541   {
542     /* nothing to do */
543   }
544
545   /* The FT_DriverInterface structure is defined in ftdriver.h. */
546
547   const FT_Driver_Class  fond_driver_class =
548   {
549     {
550       ft_module_font_driver | ft_module_driver_scalable,
551       sizeof ( FT_DriverRec ),
552
553       "fond",          /* driver name                           */
554       0x10000L,        /* driver version == 1.0                 */
555       0x20000L,        /* driver requires FreeType 2.0 or above */
556
557       (void*)0,
558
559       (FT_Module_Constructor)     init_driver,
560       (FT_Module_Destructor)      done_driver,
561       (FT_Module_Requester)       0
562     },
563
564     sizeof ( FOND_FaceRec ),
565     0,
566     0,
567
568     (FTDriver_initFace)          init_face,
569     (FTDriver_doneFace)          done_face,
570     (FTDriver_initSize)          0,
571     (FTDriver_doneSize)          0,
572     (FTDriver_initGlyphSlot)     0,
573     (FTDriver_doneGlyphSlot)     0,
574
575     (FTDriver_setCharSizes)      0,
576     (FTDriver_setPixelSizes)     0,
577     (FTDriver_loadGlyph)         0,
578     (FTDriver_getCharIndex)      0,
579
580     (FTDriver_getKerning)        0,
581     (FTDriver_attachFile)        0,
582     (FTDriver_getAdvances)       0
583   };
584
585
586
587   /*************************************************************************/
588   /*                                                                       */
589   /* <Function>                                                            */
590   /*    getDriverInterface                                                 */
591   /*                                                                       */
592   /* <Description>                                                         */
593   /*    This function is used when compiling the FOND driver as a          */
594   /*    shared library (`.DLL' or `.so').  It will be used by the          */
595   /*    high-level library of FreeType to retrieve the address of the      */
596   /*    driver's generic interface.                                        */
597   /*                                                                       */
598   /*    It shouldn't be implemented in a static build, as each driver must */
599   /*    have the same function as an exported entry point.                 */
600   /*                                                                       */
601   /* <Return>                                                              */
602   /*    The address of the TrueType's driver generic interface.  The       */
603   /*    format-specific interface can then be retrieved through the method */
604   /*    interface->get_format_interface.                                   */
605   /*                                                                       */
606 #ifdef FT_CONFIG_OPTION_DYNAMIC_DRIVERS
607
608   FT_EXPORT_FUNC(const FT_Driver_Class*)  getDriverClass( void )
609   {
610     return &fond_driver_class;
611   }
612
613 #endif /* CONFIG_OPTION_DYNAMIC_DRIVERS */
614
615
616 /* END */