shithub: freetype+ttf2subf

Download patch

ref: 65a60dc12ea895cbcff06bbf5ef781526fa9492a
parent: 5ae477c4f2569e1136035605cac4a4aa48765806
author: Just van Rossum <[email protected]>
date: Fri Mar 17 06:53:17 EST 2000

Here's the Mac FOND driver! It seems to work well, but it turns out the Type 1 drivers (old as well as new) reject about half the fonts I have.

git/fs: mount .git/fs: mount/attach disallowed
--- /dev/null
+++ b/src/macfond/fonddrvr.c
@@ -1,0 +1,590 @@
+/***************************************************************************/
+/*                                                                         */
+/*  fonddrvr.c                                                             */
+/*                                                                         */
+/*    Mac FOND font driver. Written by [email protected].                 */
+/*                                                                         */
+/*  Copyright 1996-2000 by                                                 */
+/*  Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg.     */
+/*                                                                         */
+/*  This file is part of the FreeType project, and may only be used,       */
+/*  modified, and distributed under the terms of the FreeType project      */
+/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+
+/*
+    Notes
+
+    Mac suitcase files can (and often do!) contain multiple fonts. To
+    support this I use the face_index argument of FT_(Open|New)_Face()
+    functions, and pretend the suitcase file is a collection.
+    Warning: although the FOND driver sets face->num_faces field to the
+    number of available fonts, but the Type 1 driver sets it to 1 anyway.
+    So this field is currently not reliable, and I don't see a clean way
+    to  resolve that. The face_index argument translates to
+      Get1IndResource( 'FOND', face_index + 1 );
+    so clients should figure out the resource index of the FOND.
+    (I'll try to provide some example code for this at some point.)
+
+    The Mac FOND driver works roughly like this:
+
+    - Check whether the offered stream points to a Mac suitcase file.
+      This is done by checking the file type: it has to be 'FFIL' or 'tfil'.
+      The stream that gets passed to our init_face() routine is a stdio
+      stream, which isn't usable for us, since the FOND resources live
+      in the resource fork. So we just grab the stream->pathname field.
+
+    - Read the FOND resource into memory, then check whether there is
+      a TrueType font and/or (!) a Type 1 font available.
+
+    - If there is a Type 1 font available (as a separate 'LWFN' file),
+      read it's data into memory, massage it slightly so it becomes
+      PFB data, wrap it into a memory stream, load the Type 1 driver
+      and delegate the rest of the work to it, by calling
+        t1_driver->interface.init_face( ... )
+      (XXX TODO: after this has been done, the kerning data from the FOND
+      resource should be appended to the face: on the Mac there are usually
+      no AFM files available. However, this is tricky since we need to map
+      Mac char codes to ps glyph names to glyph ID's...)
+
+    - If there is a TrueType font (an 'sfnt' resource), read it into
+      memory, wrap it into a memory stream, load the TrueType driver
+      and delegate the rest of the work to it, by calling
+        tt_driver->interface.init_face( ... )
+
+    - In both cases, the original stream gets closed and *reinitialized*
+      to become a memory stream. Additionally, the face->driver field --
+      which is set to the FOND driver upon entering our init_face() --
+      gets *reset* to either the TT or the T1 driver. I had to make a minor
+      change to ftobjs.c to make this work.
+
+    - Small note about memory: I use malloc() to allocate the buffer
+      for the Type 1 data. Of course it would be better to do this
+      through the library->memory stuff, but I can't access that from
+      my stream->close() method! Maybe it would be better to use the
+      Mac native NewPtr() and DisposePtr() calls.
+*/
+
+#include <ttobjs.h>
+#include <t1objs.h>
+
+#include <Resources.h>
+#include <Fonts.h>
+#include <Errors.h>
+
+#include <ctype.h>  /* for isupper() and isalnum() */
+#include <stdlib.h> /* for malloc() and free() */
+
+
+/* set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over
+   TrueType in case *both* are available */
+#ifndef PREFER_LWFN
+#define PREFER_LWFN 1
+#endif
+
+
+  static
+  FT_Error init_driver( FT_Driver  driver )
+  {
+    /* we don't keep no stinkin' state ;-) */
+    return FT_Err_Ok;
+  }
+
+  static
+  FT_Error done_driver( FT_Driver  driver )
+  {
+    return FT_Err_Ok;
+  }
+
+
+  /* MacRoman glyph names, needed for FOND kerning support. */
+  /* XXX which is not implemented yet! */
+  static const char*  mac_roman_glyph_names[256] = {
+    ".null",
+  };
+
+
+  /* The FOND face object is just a union of TT and T1: both is possible,
+     and we don't need anything else. */
+  typedef union FOND_FaceRec_
+  {
+    TT_FaceRec     tt;
+    T1_FaceRec     t1;
+  } FOND_FaceRec, *FOND_Face;
+
+
+  /* given a pathname, fill in a File Spec */
+  static
+  int make_file_spec( char* pathname, FSSpec *spec )
+  {
+    Str255  p_path;
+    int     path_len;
+
+    /* convert path to a pascal string */
+    path_len = strlen( pathname );
+    if ( path_len > 255 )
+      return -1;
+    p_path[0] = path_len;
+    strncpy( (char*)p_path+1, pathname, path_len );
+
+    if ( FSMakeFSSpec( 0, 0, p_path, spec ) != noErr )
+      return -1;
+    else
+      return 0;
+  }
+
+
+  /* is_suitcase() returns true if the file specified by 'pathname'
+     is a Mac suitcase file, and false if it ain't. */
+  static
+  int is_suitcase( FSSpec  *spec )
+  {
+    FInfo   finfo;
+
+    if ( FSpGetFInfo( spec, &finfo ) != noErr )
+      return 0;
+    if ( finfo.fdType == 'FFIL' || finfo.fdType == 'tfil' )
+      return 1;
+    else
+      return 0;
+  }
+
+
+  /* Quick 'n' Dirty Pascal string to C string converter. */
+  static
+  char * p2c_str( unsigned char *pstr )
+  {
+    static char cstr[256];
+
+    strncpy( cstr, (char*)pstr+1, pstr[0] );
+    cstr[pstr[0]] = '\0';
+    return cstr;
+  }
+
+
+  /* Given a PostScript font name, create the Macintosh LWFN file name */
+  static
+  void create_lwfn_name( char* ps_name, Str255 lwfn_file_name )
+  {
+    int  max = 5, count = 0;
+    unsigned char* p = lwfn_file_name;
+    char* q = ps_name;
+
+    lwfn_file_name[0] = 0;
+
+    while ( *q )
+    {
+      if ( isupper(*q) )
+      {
+        if ( count )
+          max = 3;
+        count = 0;
+      }
+      if ( count < max && (isalnum(*q) || *q == '_' ) )
+      {
+        *++p = *q;
+        lwfn_file_name[0]++;
+        count++;
+      }
+      q++;
+    }
+  }
+
+
+  /* Suck the relevant info out of the FOND data */
+  static
+  FT_Error parse_fond( char*   fond_data,
+                       short   *have_sfnt,
+                       short   *sfnt_id,
+                       Str255  lwfn_file_name )
+  {
+    AsscEntry*  assoc;
+    FamRec*     fond;
+
+    *sfnt_id = *have_sfnt = 0;
+    lwfn_file_name[0] = 0;
+
+    fond = (FamRec*)fond_data;
+    assoc = (AsscEntry*)(fond_data + sizeof(FamRec) + 2);
+
+    if ( assoc->fontSize == 0 )
+    {
+      *have_sfnt = 1;
+      *sfnt_id = assoc->fontID;
+    }
+
+    if ( fond->ffStylOff )
+    {
+      unsigned char*  p = (unsigned char*)fond_data;
+      StyleTable*     style;
+      unsigned short  string_count;
+      unsigned char*  name_table = 0;
+      char            ps_name[256];
+      unsigned char*  names[64];
+      int             i;
+
+      p += fond->ffStylOff;
+      style = (StyleTable*)p;
+      p += sizeof(StyleTable);
+      string_count = *(unsigned short*)(p);
+      p += sizeof(short);
+
+      for ( i=0 ; i<string_count && i<64; i++ )
+      {
+        names[i] = p;
+        p += names[i][0];
+        p++;
+      }
+      strcpy(ps_name, p2c_str(names[0])); /* Family name */
+
+      if ( style->indexes[0] > 1 )
+      {
+        unsigned char* suffixes = names[style->indexes[0]-1];
+        for ( i=1; i<=suffixes[0]; i++ )
+          strcat( ps_name, p2c_str(names[suffixes[i]-1]) );
+      }
+      create_lwfn_name( ps_name, lwfn_file_name );
+    }
+    return FT_Err_Ok;
+  }
+
+
+  /* Read Type 1 data from the POST resources inside the LWFN file, return a
+     PFB buffer -- apparently FT doesn't like a pure binary T1 stream. */
+  static
+  char* read_type1_data( FSSpec* lwfn_spec, unsigned long *size )
+  {
+    short          res_ref, res_id;
+    unsigned char  *buffer, *p;
+    unsigned long  total_size = 0;
+    Handle         post_data;
+
+    res_ref = FSpOpenResFile( lwfn_spec, fsRdPerm );
+    if ( ResError() )
+      return NULL;
+    UseResFile( res_ref );
+
+    /* first pass: load all POST resources, and determine the size of
+       the output buffer */
+    res_id = 501;
+    for (;;)
+    {
+      post_data = Get1Resource( 'POST', res_id++ );
+      if ( post_data == NULL ) break;
+      if ( (*post_data)[0] != 5 )
+        total_size += GetHandleSize( post_data ) + 4;
+      else
+        total_size += 2;
+    }
+
+    buffer = malloc( total_size );
+    if ( !buffer )
+      goto error;
+
+    /* second pass: append all POST data to the buffer, add PFB fields */
+    p = buffer;
+    res_id = 501;
+    for (;;)
+    {
+      long  chunk_size;
+      char  code;
+
+      post_data = Get1Resource( 'POST', res_id++ );
+      if ( post_data == NULL ) break;
+      chunk_size = GetHandleSize( post_data ) - 2;
+
+      *p++ = 128;
+
+      code = (*post_data)[0];
+      if ( code == 5 )
+        *p++ = 3;
+      else if ( code == 2 )
+        *p++ = 2;
+      else
+        *p++ = 1;
+      if ( code != 5 )
+      {
+        *p++ = chunk_size & 0xFF;
+        *p++ = (chunk_size >> 8) & 0xFF;
+        *p++ = (chunk_size >> 16) & 0xFF;
+        *p++ = (chunk_size >> 24) & 0xFF;
+      }
+      memcpy( p, *post_data + 2, chunk_size );
+      p += chunk_size;
+    }
+
+    CloseResFile( res_ref );
+
+    *size = total_size;
+    return (char*)buffer;
+
+error:
+    CloseResFile( res_ref );
+    return NULL;
+  }
+
+
+  /* Finalizer for the sfnt stream */
+  static
+  void sfnt_stream_close( FT_Stream  stream )
+  {
+    Handle sfnt_data = stream->descriptor.pointer;
+    HUnlock( sfnt_data );
+    DisposeHandle( sfnt_data );
+
+    stream->descriptor.pointer = NULL;
+    stream->size               = 0;
+    stream->base               = 0;
+    stream->close              = 0;
+  }
+
+
+  /* Finalizer for the LWFN stream */
+  static
+  void lwfn_stream_close( FT_Stream  stream )
+  {
+    free( stream->base );
+    stream->descriptor.pointer = NULL;
+    stream->size               = 0;
+    stream->base               = 0;
+    stream->close              = 0;
+  }
+
+
+  /* Main entry point. Determine whether we're dealing with a Mac
+     suitcase or not; then determine if we're dealing with Type 1
+     or TrueType; delegate the work to the proper driver. */
+  static
+  FT_Error init_face( FT_Stream  stream,
+                      FT_Long    face_index,
+                      FT_Face    face )
+  {
+    FT_Error      err;
+    FSSpec        suit_spec, lwfn_spec;
+    short         res_ref;
+    Handle        fond_data, sfnt_data;
+    short         res_index, sfnt_id, have_sfnt;
+    Str255        lwfn_file_name;
+
+    if ( !stream->pathname.pointer )
+      return FT_Err_Invalid_Argument;
+
+    if ( make_file_spec( stream->pathname.pointer, &suit_spec ) )
+      return FT_Err_Invalid_Argument;
+
+    if ( !is_suitcase( &suit_spec ) )
+      return FT_Err_Invalid_File_Format;
+
+    res_ref = FSpOpenResFile( &suit_spec, fsRdPerm );
+    if ( ResError() )
+      return FT_Err_Invalid_File_Format;
+    UseResFile( res_ref );
+
+    /* face_index may be -1, in which case we
+       just need to do a sanity check */
+    if ( face_index < 0)
+      res_index = 1;
+    else
+      res_index = face_index + 1;
+    fond_data = Get1IndResource( 'FOND', res_index );
+    if ( ResError() )
+    {
+      CloseResFile( res_ref );
+      return FT_Err_Invalid_File_Format;
+    }
+    /* Set the number of faces. Not that it helps much: the t1 driver
+       just sets it to 1 anyway :-( */
+    face->num_faces = Count1Resources('FOND');
+
+    HLock( fond_data );
+    err = parse_fond( *fond_data, &have_sfnt, &sfnt_id, lwfn_file_name );
+    HUnlock( fond_data );
+    if ( err )
+    {
+      CloseResFile( res_ref );
+      return FT_Err_Invalid_Resource_Handle;
+    }
+
+    if ( lwfn_file_name[0] )
+    {
+      /* We look for the LWFN file in the same directory as the suitcase
+         file. ATM would look in other places, too, but this is the usual
+         situation. */
+      err = FSMakeFSSpec( suit_spec.vRefNum, suit_spec.parID, lwfn_file_name, &lwfn_spec );
+      if ( err != noErr )
+        lwfn_file_name[0] = 0;  /* no LWFN file found */
+    }
+
+    if ( lwfn_file_name[0] && ( !have_sfnt || PREFER_LWFN ) )
+    {
+      FT_Driver     t1_driver;
+      char*         type1_data;
+      unsigned long size;
+
+      CloseResFile( res_ref ); /* XXX still need to read kerning! */
+
+      type1_data = read_type1_data( &lwfn_spec, &size );
+      if ( !type1_data )
+      {
+        return FT_Err_Out_Of_Memory;
+      }
+
+ #if 1
+      {
+        FILE* f;
+
+        f = fopen("Test.PFB", "wb");
+        if ( f )
+        {
+          fwrite( type1_data, 1, size, f );
+          fclose( f );
+        }
+      }
+#endif
+
+      /* reinitialize the stream */
+      if ( stream->close )
+        stream->close( stream );
+      stream->close = lwfn_stream_close;
+      stream->read = 0; /* it's now memory based */
+      stream->base = type1_data;
+      stream->size = size;
+      stream->pos = 0; /* just in case */
+
+      /* delegate the work to the Type 1 driver */
+      t1_driver = FT_Get_Driver( face->driver->library, "type1" );
+      if ( t1_driver )
+      {
+        face->driver = t1_driver;
+        return t1_driver->interface.init_face( stream, 0, face );
+      }
+      else
+        return FT_Err_Invalid_Driver_Handle;
+    }
+    else if ( have_sfnt )
+    {
+      FT_Driver     tt_driver;
+
+      sfnt_data = Get1Resource( 'sfnt', sfnt_id );
+      if ( ResError() )
+      {
+        CloseResFile( res_ref );
+        return FT_Err_Invalid_Resource_Handle;
+      }
+      DetachResource( sfnt_data );
+      CloseResFile( res_ref );
+      HLockHi( sfnt_data );
+
+      /* reinitialize the stream */
+      if ( stream->close )
+        stream->close( stream );
+      stream->close = sfnt_stream_close;
+      stream->descriptor.pointer = sfnt_data;
+      stream->read = 0; /* it's now memory based */
+      stream->base = *sfnt_data;
+      stream->size = GetHandleSize( sfnt_data );
+      stream->pos = 0; /* just in case */
+
+      /* delegate the work to the TrueType driver */
+      tt_driver = FT_Get_Driver( face->driver->library, "truetype" );
+      if ( tt_driver )
+      {
+        face->driver = tt_driver;
+        return tt_driver->interface.init_face( stream, 0, face );
+      }
+      else
+        return FT_Err_Invalid_Driver_Handle;
+    }
+    else
+    {
+      CloseResFile( res_ref );
+    }
+    return FT_Err_Invalid_File_Format;
+  }
+
+
+  static
+  void done_face( FOND_Face  face )
+  {
+    /*
+       We'll *only* get here if init_face() doesn't succeed,
+       since if it *does* succeed, it has set the face->driver
+       to either the TrueType driver or the Type 1 driver.
+       And since we promise not leave any garbage if init_face()
+       fails, there's nothing left to do.
+    */
+  }
+
+
+  /* The FT_DriverInterface structure is defined in ftdriver.h. */
+
+  const FT_DriverInterface  fond_driver_interface =
+  {
+    sizeof ( FT_DriverRec ),
+    sizeof ( FOND_FaceRec ),
+    0,
+    0,
+
+    "fond",          /* driver name                           */
+    100,             /* driver version == 1.0                 */
+    200,             /* driver requires FreeType 2.0 or above */
+
+    (void*)0,
+
+    (FTDriver_initDriver)        init_driver,
+    (FTDriver_doneDriver)        done_driver,
+    (FTDriver_getInterface)      0,
+
+    (FTDriver_initFace)          init_face,
+    (FTDriver_doneFace)          done_face,
+    (FTDriver_getKerning)        0,
+
+    (FTDriver_initSize)          0,
+    (FTDriver_doneSize)          0,
+    (FTDriver_setCharSizes)      0,
+    (FTDriver_setPixelSizes)     0,
+
+    (FTDriver_initGlyphSlot)     0,
+    (FTDriver_doneGlyphSlot)     0,
+    (FTDriver_loadGlyph)         0,
+
+    (FTDriver_getCharIndex)      0,
+  };
+
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Function>                                                            */
+  /*    getDriverInterface                                                 */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    This function is used when compiling the FOND driver as a          */
+  /*    shared library (`.DLL' or `.so').  It will be used by the          */
+  /*    high-level library of FreeType to retrieve the address of the      */
+  /*    driver's generic interface.                                        */
+  /*                                                                       */
+  /*    It shouldn't be implemented in a static build, as each driver must */
+  /*    have the same function as an exported entry point.                 */
+  /*                                                                       */
+  /* <Return>                                                              */
+  /*    The address of the TrueType's driver generic interface.  The       */
+  /*    format-specific interface can then be retrieved through the method */
+  /*    interface->get_format_interface.                                   */
+  /*                                                                       */
+#ifdef FT_CONFIG_OPTION_DYNAMIC_DRIVERS
+
+  EXPORT_FUNC
+  FT_DriverInterface*  getDriverInterface( void )
+  {
+    return &fond_driver_interface;
+  }
+
+#endif /* CONFIG_OPTION_DYNAMIC_DRIVERS */
+
+
+/* END */