freetype-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[ft-devel] plan to support sfnt-wrapped CID-keyed font


From: mpsuzuki
Subject: [ft-devel] plan to support sfnt-wrapped CID-keyed font
Date: Fri, 29 Aug 2008 20:23:55 +0900

Hi,

Recently, sfnt-wrapped CID-keyed font support is discussed in
fontforge-users list (yet sf.net has not updated archive -
please find posts titled "Mac CID Fonts"). It made me interested
in the support by FreeType2, and today I tried to add the feature.
Yet it's not finished (in fact, wrong buffer handling makes
ftdump crashed), but I want to ask a few questions to implement
it properly. In following, I attach my patch for experiment and
describe what the modification does.

As Adobe TechNote #5180 tells, sfnt-wrapped CID-keyed font is not
distributed in naked TTF file format. The file format is used
as a structure of "sfnt" resource of suitcase font format (the
legacy font format of Macintosh: its data fork is empty, and
resource fork includes FOND, NFNT, vers and sfnt resources).
Current FreeType2 can access sfnt resource of suitcase font format.
So, the required part is a bridge from sfnt resource parser to
(naked) CID-keyed font parser (*).

###################################################################

The 1st gap is in the function ttobjs.c::tt_face_init().
It checks the 32bit tag at the beginning of sfnt resource,
and expects the tag is 0x00010000, or 0x00020000, or "true".
But, in sfnt-wrapped CID-keyed font, the tag is "typ1"
(See Adobe TN#5180, Section 2). So, I added a precheck of
"typ1" signature.

Also, I introduced a boolean "is_sfnt_ps" to avoid the
installation of bytecode interpreter for sfnt-wrapped
CID-keyed fonts. Maybe it is NOT essential.

Index: src/truetype/ttobjs.c
===================================================================
RCS file: /sources/freetype/freetype2/src/truetype/ttobjs.c,v
retrieving revision 1.120
diff -u -r1.120 ttobjs.c
--- src/truetype/ttobjs.c       17 May 2008 10:01:45 -0000      1.120
+++ src/truetype/ttobjs.c       29 Aug 2008 09:51:28 -0000
@@ -178,6 +178,7 @@
     FT_Library    library;
     SFNT_Service  sfnt;
     TT_Face       face = (TT_Face)ttface;
+    FT_Bool       is_sfnt_ps = FALSE;
 
 
     library = face->root.driver->root.library;
@@ -197,6 +198,9 @@
     /* We must also be able to accept Mac/GX fonts, as well as OT ones. */
     /* The 0x00020000 tag is completely undocumented; some fonts from   */
     /* Arphic made for Chinese Windows 3.1 have this.                   */
+    if ( face->format_tag == FT_MAKE_TAG( 't', 'y', 'p', '1' ) )
+      is_sfnt_ps = TRUE;
+    else
     if ( face->format_tag != 0x00010000L &&    /* MS fonts  */
          face->format_tag != 0x00020000L &&    /* CJK fonts for Win 3.1 */
          face->format_tag != TTAG_true   )     /* Mac fonts */
@@ -206,6 +210,7 @@
     }
 
 #ifdef TT_USE_BYTECODE_INTERPRETER
+    if ( !is_sfnt_ps )
     face->root.face_flags |= FT_FACE_FLAG_HINTER;
 #endif
 

The 2nd gap is in ttload.c::check_table_dir(), a function to
assue that essential tables are included. It searches head
(standard TrueType), bhed (MacOS sfnt-bitmap), sing/meta
(SING glyphlet font) tables. I'm not sure what sfnt tables
are essential in sfnt-wrapped CID-keyed fonts. Adobe TN#5180
does not tell if "name" "post" etc are required /or not.
In fact, a few samples (e.g. Adobe ValuePack, manufactured
by Adobe) in my hands does not have "head" nor "bhed".

# Adobe TN#5180 tells "BBOX", "CID ", "HFMX", "VFMX" are
# required, but I don't want to check it because they
# (except of CID) are not parsed by FreeType2 at all.

Thus, I added 2 booleans to check if table directory has
"CID " or "typ1" table. If one of them is found,
check_table_dir() permits the font missing "head"/"bhed"
/"SING"/"META". 

Index: src/sfnt/ttload.c
===================================================================
RCS file: /sources/freetype/freetype2/src/sfnt/ttload.c,v
retrieving revision 1.132
diff -u -r1.132 ttload.c
--- src/sfnt/ttload.c   4 Aug 2008 05:45:41 -0000       1.132
+++ src/sfnt/ttload.c   29 Aug 2008 09:51:28 -0000
@@ -149,6 +149,7 @@
     FT_UInt   nn, valid_entries = 0;
     FT_UInt   has_head = 0, has_sing = 0, has_meta = 0;
     FT_ULong  offset = sfnt->offset + 12;
+    FT_Bool   has_cid = FALSE, has_typ1 = FALSE;
 
     static const FT_Frame_Field  table_dir_entry_fields[] =
     {
@@ -236,6 +237,10 @@
         has_sing = 1;
       else if ( table.Tag == TTAG_META )
         has_meta = 1;
+      else if ( table.Tag == FT_MAKE_TAG( 'T', 'Y', 'P', '1' ) )
+        has_typ1 = TRUE;
+      else if ( table.Tag == FT_MAKE_TAG( 'C', 'I', 'D', ' ' ) )
+        has_cid = TRUE;
     }
 
     sfnt->num_tables = valid_entries;
@@ -248,7 +253,7 @@
     }
 
     /* if `sing' and `meta' tables are present, there is no `head' table */
-    if ( has_head || ( has_sing && has_meta ) )
+    if ( has_head || ( has_sing && has_meta ) || ( has_typ1 || has_cid ) )
     {
       error = SFNT_Err_Ok;
       goto Exit;


The 3rd gap is in sfobjs.c::sfnt_open_font(). Although
ttobjs.c::tt_face_init() has already checked the tag,
this function checks it again. So, again I added a precheck
to permit "typ1"-tagged resource. As the 1st gap, I added
a boolean "is_sfnt_ps", but it is not essential at present.


Index: src/sfnt/sfobjs.c
===================================================================
RCS file: /sources/freetype/freetype2/src/sfnt/sfobjs.c,v
retrieving revision 1.133
diff -u -r1.133 sfobjs.c
--- src/sfnt/sfobjs.c   29 Aug 2008 06:09:03 -0000      1.133
+++ src/sfnt/sfobjs.c   29 Aug 2008 09:51:28 -0000
@@ -336,6 +336,7 @@
   /* synthesized into a TTC with one offset table.              */
   static FT_Error
   sfnt_open_font( FT_Stream  stream,
+                  FT_Bool*   is_sfnt_ps,   
                   TT_Face    face )
   {
     FT_Memory  memory = stream->memory;
@@ -357,12 +358,16 @@
     face->ttc_header.tag     = 0;
     face->ttc_header.version = 0;
     face->ttc_header.count   = 0;
+    *is_sfnt_ps = FALSE;
 
     offset = FT_STREAM_POS();
 
     if ( FT_READ_ULONG( tag ) )
       return error;
 
+    if ( tag == FT_MAKE_TAG( 't', 'y', 'p', '1' ) )
+      *is_sfnt_ps = TRUE;
+    else
     if ( tag != 0x00010000UL                      &&
          tag != TTAG_ttcf                         &&
          tag != TTAG_OTTO                         &&
@@ -421,6 +426,7 @@
     FT_Error        error;
     FT_Library      library = face->root.driver->root.library;
     SFNT_Service    sfnt;
+    FT_Bool         is_sfnt_ps;
 
 
     /* for now, parameters are unused */
@@ -441,7 +447,7 @@
 
     FT_FACE_FIND_GLOBAL_SERVICE( face, face->psnames, POSTSCRIPT_CMAPS );
 
-    error = sfnt_open_font( stream, face );
+    error = sfnt_open_font( stream, &is_sfnt_ps, face );
     if ( error )
       return error;




The 4th gap is in sfobjs.c::sfnt_load_face(). It searches "glyf"
or "CFF " tables, the outline glyph tables of standard OpenType
font. If both tables are not found, the function proceeds to
load bitmap tables. Thus, I inserted a fallback to search "CID "
or "TYP1" tables.

"CID " table consists from 2 parts: binary header and standard
CID-keyed font data. FreeType2 CID-keyed font parser can parse
latter.

If "CID " or "TYP1" is found, the stream is seeked to the table
and binary header is parsed (sorry, at present, sfnt-wrapped
Type1 is not supported because I don't have the documents nor
sample fonts). After the binary header is fed, the rest content
of the table is duplicated to the buffer. The buffer is passed
to open_face_from_buffer(), as MacOS LaserWriter PS fonts.
open_face_from_buffer() and its component functions are duplicated
from ftobjs.c. The duplication of "CID " table content is just
for the first stage experiment, reusing of existing stream is
expected in proper implementation.

Index: src/sfnt/sfobjs.c
===================================================================
RCS file: /sources/freetype/freetype2/src/sfnt/sfobjs.c,v
retrieving revision 1.133
diff -u -r1.133 sfobjs.c
--- src/sfnt/sfobjs.c   29 Aug 2008 06:09:03 -0000      1.133
+++ src/sfnt/sfobjs.c   29 Aug 2008 09:51:28 -0000
@@ -546,6 +654,82 @@
                              tt_face_lookup_table( face, TTAG_CFF )  != 0 );
 #endif
 
+    /* Fallback for sfnt-wrapped CID or Type1 fonts for Macintosh.
+     * In comparison from CFF OpenType, the essential sfnt tables in
+     * sfnt-wrapped are not documented. For minimum support, we parse
+     * TYP1 or CID table only, other contents are ignored. 
+     */
+    FT_TRACE2(( "has_outline=%d\n", has_outline ));
+    if ( !has_outline )
+    {
+      FT_Library  library = face->root.driver->root.library;
+      FT_Memory   memory = library->memory;
+      FT_Byte*    pstable_data   = NULL;
+      FT_ULong    pstable_length = 0;
+      FT_ULong    ttag_TYP1 = FT_MAKE_TAG( 'T', 'Y', 'P', '1' );
+      FT_ULong    ttag_CID  = FT_MAKE_TAG( 'C', 'I', 'D', ' ' );
+      FT_Bool     is_sfnt_cid = FALSE;
+
+
+      FT_TRACE2(( "check sfnt-PS font.\n" ));
+
+      if ( 0 != tt_face_lookup_table( face, ttag_TYP1 ) )
+      {
+        FT_TRACE2(( "TYP1 table is found in sfnt resource.\n" ));
+        if ( tt_face_goto_table( face, ttag_TYP1, stream, &pstable_length ) )
+          goto Abort_to_load_sfnt_PS;
+      }
+      else if ( 0 != tt_face_lookup_table( face, ttag_CID ) )
+      {
+        FT_TRACE2(( "CID table is found in sfnt resource.\n" ));
+        if ( tt_face_goto_table( face, ttag_CID, stream, &pstable_length ) )
+          goto Abort_to_load_sfnt_PS;
+
+        is_sfnt_cid = TRUE;
+      }
+      else
+        goto Abort_to_load_sfnt_PS;
+
+      FT_TRACE2(( "try to allocate buffer to copy PS table content.\n" ));
+      if ( FT_ALLOC( pstable_data, (FT_Long)pstable_length ) )
+        goto Abort_to_load_sfnt_PS;
+
+      FT_TRACE2(( "copy PS table content to buffer.\n" ));
+      error = FT_Stream_Read( stream, (FT_Byte *)pstable_data, pstable_length 
);
+      if ( !error )
+      {
+        FT_Byte*  p = pstable_data;
+
+
+        FT_TRACE2(( "try to parse PS table in buffer.\n" ));
+        if ( is_sfnt_cid )
+       {
+         FT_Fixed   sfnt_cid_version       = FT_NEXT_ULONG( p );
+         FT_UShort  sfnt_cid_flags         = FT_NEXT_USHORT( p );
+         FT_UShort  sfnt_cid_CIDCount      = FT_NEXT_USHORT( p );
+         FT_ULong   sfnt_cid_CIDFontLength = FT_NEXT_ULONG( p );
+         FT_ULong   sfnt_cid_asciiLength   = FT_NEXT_ULONG( p );
+         FT_ULong   sfnt_cid_binaryLength  = FT_NEXT_ULONG( p );
+         FT_UShort  sfnt_cid_FDCount       = FT_NEXT_USHORT( p );
+       }
+
+        error = open_face_from_buffer( library,
+                                      p,
+                                      pstable_length,
+                                       face_index,
+                                       is_sfnt_cid ? "t1cid" : "type1",
+                                       &face );
+      }
+
+      if ( !error )
+        return error;
+      else
+        FT_FREE( pstable_data );
+
+Abort_to_load_sfnt_PS:
+      ;
+    }
+
     is_apple_sbit = 0;
 
     /* if this font doesn't contain outlines, we try to load */
@@ -499,6 +505,108 @@
   } while ( 0 )
 
 
+  /* Finalizer for a memory stream; gets called by FT_Done_Face().
+ *      It frees the memory it uses. */
+  /* from ftmac.c */
+  static void
+  memory_stream_close( FT_Stream  stream )
+  {
+    FT_Memory  memory = stream->memory;
+
+
+    FT_FREE( stream->base );
+
+    stream->size  = 0;
+    stream->base  = 0;
+    stream->close = 0;
+  }
+
+
+  /* Create a new memory stream from a buffer and a size. */
+  /* from ftmac.c */
+  static FT_Error
+  new_memory_stream( FT_Library           library,
+                     FT_Byte*             base,
+                     FT_ULong             size,
+                     FT_Stream_CloseFunc  close,
+                     FT_Stream           *astream )
+  {
+    FT_Error   error;
+    FT_Memory  memory;
+    FT_Stream  stream;
+
+
+    if ( !library )
+      return FT_Err_Invalid_Library_Handle;
+
+    if ( !base )
+      return FT_Err_Invalid_Argument;
+
+    *astream = 0;
+    memory = library->memory;
+    if ( FT_NEW( stream ) )
+      goto Exit;
+
+    FT_Stream_OpenMemory( stream, base, size );
+
+    stream->close = close;
+
+    *astream = stream;
+
+  Exit:
+    return error;
+  }
+
+
+  /* Create a new FT_Face given a buffer and a driver name. */
+  /* from ftmac.c */
+  static FT_Error
+  open_face_from_buffer( FT_Library   library,
+                         FT_Byte*     base,
+                         FT_ULong     size,
+                         FT_Long      face_index,
+                         const char*  driver_name,
+                         FT_Face     *aface )
+  {
+    FT_Open_Args  args;
+    FT_Error      error;
+    FT_Stream     stream = NULL;
+    FT_Memory     memory = library->memory;
+
+
+    error = new_memory_stream( library,
+                               base,
+                               size,
+                               memory_stream_close,
+                               &stream );
+    if ( error )
+    {
+      FT_FREE( base );
+      return error;
+    }
+
+    args.flags = FT_OPEN_STREAM;
+    args.stream = stream;
+    if ( driver_name )
+    {
+      args.flags = args.flags | FT_OPEN_DRIVER;
+      args.driver = FT_Get_Module( library, driver_name );
+    }
+
+    error = FT_Open_Face( library, &args, face_index, aface );
+
+    if ( error == FT_Err_Ok )
+      (*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
+    else
+    {
+      FT_Stream_Close( stream );
+      FT_FREE( stream );
+    }
+
+    return error;
+  }
+
+
   FT_LOCAL_DEF( FT_Error )
   sfnt_load_face( FT_Stream      stream,
                   TT_Face        face,


##########################################################################

While I was writing the patch in above, I had a few questions:

Q1) There are 2 tag checks in tt_face_init() and sfnt_open_font().
    The purposes would be slightly different, they should not be
    unified?

Q2) For the earliest phase, it would be reasonable to parse
    "CID " or "TYP1" table only and ignore other contents for
    simplicity. Considering such strategy, ftobjs.c would be
    the right place to implement the functionalities to skip
    unrequired parts (as Yamato-san implemented resource fork
    accessing routines). It is not essential to down to TrueType
    driver and jump to CID-keyed font driver.

    But, in next phase, possibly the support of "cmap" table
    would be important (I'm not sure if "ALMX", "ROTA", "HFMX",
    "VFMX" tables are still important when the content of
    "CID " is already parsed). In such case, utilization of
    TrueType font parser would be important.

    I wish I can implement the first phase within 2008, but
    I think I cannot finish the second phase within 2008.
    Can I leave unused "trampolines" in TrueType drivers until
    the second phase? Or, should I restrict the modifications
    in ftobjs.c, until when I start the work for the second
    phase?

Q3) Does anybody have a documentation on "Apple's GX Type1 font"?
    Adobe TN#5180 tells that it was included in Apple Developers
    CD-ROM.


Regards,
mpsuzuki




reply via email to

[Prev in Thread] Current Thread [Next in Thread]