summary refs log tree commit diff
path: root/src/freetype/ftmac.c
diff options
context:
space:
mode:
authorWlodekM <[email protected]>2024-06-16 10:35:45 +0300
committerWlodekM <[email protected]>2024-06-16 10:35:45 +0300
commitabef6da56913f1c55528103e60a50451a39628b1 (patch)
treeb3c8092471ecbb73e568cd0d336efa0e7871ee8d /src/freetype/ftmac.c
initial commit
Diffstat (limited to 'src/freetype/ftmac.c')
-rw-r--r--src/freetype/ftmac.c426
1 files changed, 426 insertions, 0 deletions
diff --git a/src/freetype/ftmac.c b/src/freetype/ftmac.c
new file mode 100644
index 0000000..b674e53
--- /dev/null
+++ b/src/freetype/ftmac.c
@@ -0,0 +1,426 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ftmac.c                                                                */
+/*                                                                         */
+/*    Mac FOND support.  Written by [email protected].                    */
+/*  Heavily modified by mpsuzuki, George Williams, and Sean McBride.       */
+/*                                                                         */
+/*  This file is for Mac OS X only; see builds/mac/ftoldmac.c for          */
+/*  classic platforms built by MPW.                                        */
+/*                                                                         */
+/*  Copyright 1996-2018 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: fbit and NFNT bitmap resources are not supported yet.  In old
+    sfnt fonts, bitmap glyph data for each size is stored in each `NFNT'
+    resources instead of the `bdat' table in the sfnt resource.  Therefore,
+    face->num_fixed_sizes is set to 0, because bitmap data in `NFNT'
+    resource is unavailable at present.
+
+    The Mac FOND support 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
+      its 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 FT_Open_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 FT_Open_Face().
+
+    - Some suitcase fonts (notably Onyx) might point the `LWFN' file to
+      itself, even though it doesn't contains `POST' resources.  To handle
+      this special case without opening the file an extra time, we just
+      ignore errors from the `LWFN' and fallback to the `sfnt' if both are
+      available.
+  */
+
+
+#include "ft2build.h"
+#include FT_FREETYPE_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_INTERNAL_STREAM_H
+#include "ftbase.h"
+
+
+#ifdef FT_MACINTOSH
+
+  /* This is for Mac OS X.  Without redefinition, OS_INLINE */
+  /* expands to `static inline' which doesn't survive the   */
+  /* -ansi compilation flag of GCC.                         */
+#if !HAVE_ANSI_OS_INLINE
+#undef  OS_INLINE
+#define OS_INLINE  static __inline__
+#endif
+
+  /* `configure' checks the availability of `ResourceIndex' strictly */
+  /* and sets HAVE_TYPE_RESOURCE_INDEX 1 or 0 always.  If it is      */
+  /* not set (e.g., a build without `configure'), the availability   */
+  /* is guessed from the SDK version.                                */
+#ifndef HAVE_TYPE_RESOURCE_INDEX
+#if !defined( MAC_OS_X_VERSION_10_5 ) || \
+    ( MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 )
+#define HAVE_TYPE_RESOURCE_INDEX 0
+#else
+#define HAVE_TYPE_RESOURCE_INDEX 1
+#endif
+#endif /* !HAVE_TYPE_RESOURCE_INDEX */
+
+#if ( HAVE_TYPE_RESOURCE_INDEX == 0 )
+  typedef short  ResourceIndex;
+#endif
+
+#include <CoreServices/CoreServices.h>
+#include <ApplicationServices/ApplicationServices.h>
+
+  /* Don't want warnings about our own use of deprecated functions. */
+#define FT_DEPRECATED_ATTRIBUTE
+
+#include FT_MAC_H
+
+
+  static OSErr
+  FT_FSPathMakeRes( const UInt8*    pathname,
+                    ResFileRefNum*  res )
+  {
+    OSErr  err;
+    FSRef  ref;
+
+
+    if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
+      return FT_THROW( Cannot_Open_Resource );
+
+    /* at present, no support for dfont format */
+    err = FSOpenResourceFile( &ref, 0, NULL, fsRdPerm, res );
+    if ( noErr == err )
+      return err;
+
+    /* fallback to original resource-fork font */
+    *res = FSOpenResFile( &ref, fsRdPerm );
+    err  = ResError();
+
+    return err;
+  }
+
+
+  static short
+  count_faces_sfnt( char*  fond_data )
+  {
+    /* The count is 1 greater than the value in the FOND.  */
+    /* Isn't that cute? :-)                                */
+
+    return EndianS16_BtoN( *( (short*)( fond_data +
+                                        sizeof ( FamRec ) ) ) ) + 1;
+  }
+
+
+  static short
+  count_faces_scalable( char*  fond_data )
+  {
+    AsscEntry*  assoc;
+    short       i, face, face_all;
+
+
+    face_all = EndianS16_BtoN( *( (short *)( fond_data +
+                                             sizeof ( FamRec ) ) ) ) + 1;
+    assoc    = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
+    face     = 0;
+
+    for ( i = 0; i < face_all; i++ )
+    {
+      if ( 0 == EndianS16_BtoN( assoc[i].fontSize ) )
+        face++;
+    }
+    return face;
+  }
+
+
+  /* Look inside the FOND data, answer whether there should be an SFNT
+     resource, and answer the name of a possible LWFN Type 1 file.
+
+     Thanks to Paul Miller ([email protected]) for the fix
+     to load a face OTHER than the first one in the FOND!
+  */
+
+
+  static void
+  parse_fond( char*   fond_data,
+              short*  have_sfnt,
+              ResID*  sfnt_id,
+              Str255  lwfn_file_name,
+              short   face_index )
+  {
+    AsscEntry*  assoc;
+    AsscEntry*  base_assoc;
+    FamRec*     fond;
+
+
+    *sfnt_id          = 0;
+    *have_sfnt        = 0;
+    lwfn_file_name[0] = 0;
+
+    fond       = (FamRec*)fond_data;
+    assoc      = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
+    base_assoc = assoc;
+
+    /* the maximum faces in a FOND is 48, size of StyleTable.indexes[] */
+    if ( 47 < face_index )
+      return;
+
+    /* Let's do a little range checking before we get too excited here */
+    if ( face_index < count_faces_sfnt( fond_data ) )
+    {
+      assoc += face_index;        /* add on the face_index! */
+
+      /* if the face at this index is not scalable,
+         fall back to the first one (old behavior) */
+      if ( EndianS16_BtoN( assoc->fontSize ) == 0 )
+      {
+        *have_sfnt = 1;
+        *sfnt_id   = EndianS16_BtoN( assoc->fontID );
+      }
+      else if ( base_assoc->fontSize == 0 )
+      {
+        *have_sfnt = 1;
+        *sfnt_id   = EndianS16_BtoN( base_assoc->fontID );
+      }
+    }
+  }
+
+
+  static short
+  count_faces( Handle        fond,
+               const UInt8*  pathname )
+  {
+    ResID     sfnt_id;
+    short     have_sfnt;
+    Str255    lwfn_file_name;
+
+    have_sfnt = 0;
+    parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 );
+
+    return count_faces_scalable( *fond );
+  }
+
+
+  /* Create a new FT_Face from an SFNT resource, specified by res ID. */
+  static FT_Error
+  FT_New_Face_From_SFNT( FT_Library  library,
+                         ResID       sfnt_id,
+                         FT_Long     face_index,
+                         FT_Face*    aface )
+  {
+    Handle     sfnt = NULL;
+    FT_Byte*   sfnt_data;
+    size_t     sfnt_size;
+    FT_Error   error  = FT_Err_Ok;
+    FT_Memory  memory = library->memory;
+    int        is_cff, is_sfnt_ps;
+
+
+    sfnt = GetResource( TTAG_sfnt, sfnt_id );
+    if ( !sfnt )
+      return FT_THROW( Invalid_Handle );
+
+    sfnt_size = (FT_ULong)GetHandleSize( sfnt );
+
+    /* detect resource fork overflow */
+    if ( FT_MAC_RFORK_MAX_LEN < sfnt_size )
+      return FT_THROW( Array_Too_Large );
+
+    if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) )
+    {
+      ReleaseResource( sfnt );
+      return error;
+    }
+
+    ft_memcpy( sfnt_data, *sfnt, sfnt_size );
+    ReleaseResource( sfnt );
+
+    is_cff     = sfnt_size > 4 && !ft_memcmp( sfnt_data, "OTTO", 4 );
+    is_sfnt_ps = sfnt_size > 4 && !ft_memcmp( sfnt_data, "typ1", 4 );
+
+    if ( is_sfnt_ps )
+    {
+      FT_Stream  stream;
+
+
+      if ( FT_NEW( stream ) )
+        goto Try_OpenType;
+
+      FT_Stream_OpenMemory( stream, sfnt_data, sfnt_size );
+      if ( !open_face_PS_from_sfnt_stream( library,
+                                           stream,
+                                           face_index,
+                                           0, NULL,
+                                           aface ) )
+      {
+        FT_Stream_Close( stream );
+        FT_FREE( stream );
+        FT_FREE( sfnt_data );
+        goto Exit;
+      }
+
+      FT_FREE( stream );
+    }
+  Try_OpenType:
+    error = open_face_from_buffer( library,
+                                   sfnt_data,
+                                   sfnt_size,
+                                   face_index,
+                                   is_cff ? "cff" : "truetype",
+                                   aface );
+  Exit:
+    return error;
+  }
+
+
+  /* Create a new FT_Face from a file path to a suitcase file. */
+  static FT_Error
+  FT_New_Face_From_Suitcase( FT_Library    library,
+                             const UInt8*  pathname,
+                             FT_Long       face_index,
+                             FT_Face*      aface )
+  {
+    FT_Error       error = FT_ERR( Cannot_Open_Resource );
+    ResFileRefNum  res_ref;
+    ResourceIndex  res_index;
+    Handle         fond;
+    short          num_faces_in_res;
+
+
+    if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) )
+      return FT_THROW( Cannot_Open_Resource );
+
+    UseResFile( res_ref );
+    if ( ResError() )
+      return FT_THROW( Cannot_Open_Resource );
+
+    num_faces_in_res = 0;
+    for ( res_index = 1; ; res_index++ )
+    {
+      short  num_faces_in_fond;
+
+
+      fond = Get1IndResource( TTAG_FOND, res_index );
+      if ( ResError() )
+        break;
+
+      num_faces_in_fond  = count_faces( fond, pathname );
+      num_faces_in_res  += num_faces_in_fond;
+
+      if ( 0 <= face_index && face_index < num_faces_in_fond && error )
+        error = FT_New_Face_From_FOND( library, fond, face_index, aface );
+
+      face_index -= num_faces_in_fond;
+    }
+
+    CloseResFile( res_ref );
+    if ( !error && aface && *aface )
+      (*aface)->num_faces = num_faces_in_res;
+    return error;
+  }
+
+
+  /* documentation is in ftmac.h */
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_New_Face_From_FOND( FT_Library  library,
+                         Handle      fond,
+                         FT_Long     face_index,
+                         FT_Face*    aface )
+  {
+    short     have_sfnt = 0;
+    ResID     sfnt_id, fond_id;
+    OSType    fond_type;
+    Str255    fond_name;
+    Str255    lwfn_file_name;
+
+
+    /* check of `library' and `aface' delayed to `FT_New_Face_From_XXX' */
+
+    GetResInfo( fond, &fond_id, &fond_type, fond_name );
+    if ( ResError() != noErr || fond_type != TTAG_FOND )
+      return FT_THROW( Invalid_File_Format );
+
+    parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index );
+
+    return FT_New_Face_From_SFNT( library,
+                                     sfnt_id,
+                                     face_index,
+                                     aface );
+  }
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Function>                                                            */
+  /*    FT_New_Face                                                        */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    This is the Mac-specific implementation of FT_New_Face.  In        */
+  /*    addition to the standard FT_New_Face() functionality, it also      */
+  /*    accepts pathnames to Mac suitcase files.  For further              */
+  /*    documentation see the original FT_New_Face() in freetype.h.        */
+  /*                                                                       */
+  FT_EXPORT_DEF( FT_Error )
+  FT_New_Face( FT_Library   library,
+               const FT_Open_Args*  args,
+               FT_Long      face_index,
+               FT_Face*     aface )
+  {
+    FT_Error      error;
+
+
+    /* test for valid `library' and `aface' delayed to FT_Open_Face() */
+    if ( !args || !args->pathname )
+      return FT_THROW( Invalid_Argument );
+
+    *aface = NULL;
+
+    error = FT_New_Face_From_Suitcase( library, args->pathname, face_index, aface );
+    if ( *aface )
+      return FT_Err_Ok;
+
+    /* let it fall through to normal loader (.ttf, .otf, etc.) */
+    return FT_Open_Face( library, args, face_index, aface );
+  }
+
+#else /* !FT_MACINTOSH */
+
+  /* ANSI C doesn't like empty source files */
+  typedef int  _ft_mac_dummy;
+
+#endif /* !FT_MACINTOSH */
+
+
+/* END */