freetype-devel
[Top][All Lists]
Advanced

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

[ft-devel] Exposing tricky font list to FT2 client


From: mpsuzuki
Subject: [ft-devel] Exposing tricky font list to FT2 client
Date: Wed, 25 Aug 2010 13:59:06 +0900

Hi all,

By recent discussions in poppler mailing list (poppler
is an open source PDF rendering library forked from xpdf),
I found a typical scenario that the tricky font list
cannot force the hinting for tricky fonts like MingLiU.

  http://lists.freedesktop.org/archives/poppler/2010-August/006295.html

By my analysis(*1), it is hard for FT2 decide if hinting
should be forced for a font extracted from a PDF. The
information available at higher layer is important to
decide.

FT2 already provides an API to switch the hinting after
the creation of FT_Face object (FT_Face_SetUnpatentedHinting).
The FT2 clients with their own tricky font list can use
this API to force the hinting.

But, it seems that some developers don't want to maintain
their own external tricky font list.

Thus, I sketched an interface to configure the tricky font
list from FT2 client. The mechanism is quite simple:

1) Substitute the array of the string for tricky font family
name list (stored in TrueType driver) by FT_List object in
FT_Library (FT_Library->tricky_fonts).

  1-a) tricky_fonts is allocated when FT_Library is created.
  1-b) tricky_fonts is freed when FT_Library is destroyed.

2) Add 3 public functions to configure FT_Library->tricky_fonts 

FT_Library_TrickyFontList_Add( library, condition )
FT_Library_TrickyFontList_Remove( library, condition )
FT_Face_Get_TrickyLevel( face, tricky_level )

  2-a) at present, "condition" is a string "FAMILYNAME=XXX".
       other properties(*2) can be supported in future.

  2-b) the condition string is duplicated to the buffer
       allocated during the insertion, and the buffer
       is freed during the removal.

3) Substitute tt_check_trickyness() by public function
FT_Face_Get_TrickyLevel().

4) Register known tricky fonts to FT_Library when TrueType
driver is initialized.

The following patch is a rough sketch. I'm wondering if
I should provide a simple function to check the list by
passing the family name string. In current sketch, FT2
clients should pass FT_Face object - if they want to check
before FT_Face creation, they have to make a fake FT_Face
object including family_name. Having a fake FT_Face object
and a genuine FT_Face object in one program might make
the maintainer confused.
--------------------------------------------------------------
diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h
index 7799b70..846385c 100644
--- a/include/freetype/freetype.h
+++ b/include/freetype/freetype.h
@@ -1081,6 +1081,40 @@ FT_BEGIN_HEADER
 #define FT_FACE_FLAG_TRICKY            ( 1L << 13 )
 
 
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Enum>                                                                */
+  /*    FT_Face_Tricky_Level                                               */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    An enumeration type to indicate the tricky level of the face.      */
+  /*                                                                       */
+  /* <Values>                                                              */
+  /*    FT_FACE_TRICKY_NO: nothing in blacklist matches                    */
+  /*                                                                       */
+  /*    FT_FACE_TRICKY_EXACT: partial match in blacklisted fonts.          */
+  /*                                                                       */
+  /*    FT_FACE_TRICKY_PARTIAL: partial match in blacklisted fonts.        */
+  /*                                                                       */
+  typedef enum  FT_Face_Tricky_Level_
+  {
+    FT_FACE_TRICKY_NO,       /* match nothing in blacklist */
+    FT_FACE_TRICKY_EXACT,    /* exact match in blacklist */
+    FT_FACE_TRICKY_PARTIAL   /* partial match in blacklist */ 
+  } FT_Face_Tricky_Level;
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Library_TrickyFontList_Add( FT_Library  library,
+                                 FT_String*  condition );
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Library_TrickyFontList_Remove( FT_Library  library,
+                                    FT_String*  condition );
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Face_Get_TrickyLevel( FT_Face                face,
+                           FT_Face_Tricky_Level*  level );
+
   /*************************************************************************
    *
    * @macro:
diff --git a/include/freetype/internal/ftobjs.h 
b/include/freetype/internal/ftobjs.h
index 670eb78..c47ffe5 100644
--- a/include/freetype/internal/ftobjs.h
+++ b/include/freetype/internal/ftobjs.h
@@ -850,6 +850,7 @@ FT_BEGIN_HEADER
     FT_ListRec         renderers;        /* list of renderers        */
     FT_Renderer        cur_renderer;     /* current outline renderer */
     FT_Module          auto_hinter;
+    FT_List            tricky_fonts;     /* font list to force hinting */
 
     FT_Byte*           raster_pool;      /* scan-line conversion */
                                          /* render pool          */
diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c
index 7f38df0..87a5b8b 100644
--- a/src/base/ftobjs.c
+++ b/src/base/ftobjs.c
@@ -3960,6 +3960,127 @@
   /*************************************************************************/
   /****                                                                 ****/
   /****                                                                 ****/
+  /****     T R I C K Y  F O N T  L I S T  M A N I P U L A T I O N      ****/
+  /****                                                                 ****/
+  /****                                                                 ****/
+  /*************************************************************************/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  static void
+  ft_tricky_font_destroy_entry( FT_Memory   memory,
+                                void*       entry,
+                                void*       extra )
+  {
+    FT_FREE( entry );
+  }
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Library_TrickyFontList_Add( FT_Library  library,
+                                 FT_String*  condition )
+  {
+    FT_Memory    memory = library->memory;
+    FT_Error     error;
+    FT_ListNode  node;
+
+
+    if ( !library || !library->memory )
+      return FT_Err_Invalid_Argument;
+
+    if ( FT_NEW( node ) )
+      return FT_Err_Out_Of_Memory;
+
+    if ( FT_ALLOC( node->data, ft_strlen( condition ) ) )
+      return FT_Err_Out_Of_Memory;
+
+    ft_memcpy( node->data, condition, ft_strlen( condition ) );
+
+    FT_List_Add( library->tricky_fonts, node );
+
+    return FT_Err_Ok;
+  }
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Library_TrickyFontList_Remove( FT_Library  library,
+                                    FT_String*  condition )
+  {
+    FT_Memory    memory = library->memory;
+    FT_ListNode  found_node = FT_List_Find( library->tricky_fonts,
+                                            condition );
+
+
+    if ( !found_node )
+      return FT_Err_Invalid_Argument;
+
+    FT_List_Remove( library->tricky_fonts, found_node ); 
+    FT_FREE( found_node->data );
+    return FT_Err_Ok;
+  }
+
+  typedef struct FT_FaceAndTrickyLevel_
+  {
+    FT_Face               face;
+    FT_Face_Tricky_Level  level;
+  } FT_FaceAndTrickyLevel;
+
+
+  static FT_Error
+  ft_face_eval_tricky_level( FT_ListNode             node,
+                             FT_FaceAndTrickyLevel*  face_level )
+  {
+    FT_PtrDist  key_length = ft_strstr( node->data, "=" ) - (FT_String 
*)node->data;
+    if ( 0 == ft_strncmp( node->data, "FAMILYNAME", key_length ) &&
+         face_level->face->family_name )
+    {
+      FT_String  *c, *value = (FT_String*)node->data + key_length + 1;
+      
+      c = ft_strstr( face_level->face->family_name, value );
+
+      if ( NULL == c )
+        ;
+      else if ( c != face_level->face->family_name ||
+                '\0' != c[ft_strlen( value )]      )
+        face_level->level = FT_FACE_TRICKY_PARTIAL;
+      else
+      {
+        face_level->level = FT_FACE_TRICKY_EXACT;
+        return FT_Err_Too_Many_Hints; /* abort iteration */
+      }
+    }
+    return FT_Err_Ok;
+  }
+
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Face_Get_TrickyLevel( FT_Face                face,
+                           FT_Face_Tricky_Level*  level )
+  {
+    FT_Error               err;
+    FT_FaceAndTrickyLevel  face_level;
+
+
+    *level = FT_FACE_TRICKY_NO;
+    if ( !face )
+      return FT_Err_Ok;
+
+    face_level.face  = face;
+    face_level.level = FT_FACE_TRICKY_NO;
+    err = FT_List_Iterate( face->driver->root.library->tricky_fonts,
+                           (FT_List_Iterator)ft_face_eval_tricky_level,
+                           &face_level );
+    if ( err && err != FT_Err_Too_Many_Hints )
+      return err;
+
+    *level = face_level.level;
+    return FT_Err_Ok;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*************************************************************************/
+  /****                                                                 ****/
+  /****                                                                 ****/
   /****                         M O D U L E S                           ****/
   /****                                                                 ****/
   /****                                                                 ****/
@@ -4336,6 +4457,10 @@
       goto Fail;
 #endif
 
+    /* allocate the pool to known tricky fonts */
+    if ( FT_ALLOC( library->tricky_fonts, sizeof ( FT_ListRec ) ) )
+      goto Fail;
+
     library->version_major = FREETYPE_MAJOR;
     library->version_minor = FREETYPE_MINOR;
     library->version_patch = FREETYPE_PATCH;
@@ -4491,6 +4616,12 @@
     FT_FREE( library->raster_pool );
     library->raster_pool_size = 0;
 
+    /* Delete tricky font list */
+    FT_List_Finalize( library->tricky_fonts,
+                      (FT_List_Destructor)ft_tricky_font_destroy_entry,
+                      library->memory, NULL );
+    FT_FREE( library->tricky_fonts );
+
 #ifdef FT_CONFIG_OPTION_PIC
     /* Destroy pic container contents */
     ft_pic_container_destroy( library );
diff --git a/src/truetype/ttobjs.c b/src/truetype/ttobjs.c
index 11d662d..73d2e9e 100644
--- a/src/truetype/ttobjs.c
+++ b/src/truetype/ttobjs.c
@@ -142,40 +142,6 @@
 #endif /* TT_USE_BYTECODE_INTERPRETER */
 
 
-  /* Compare the face with a list of well-known `tricky' fonts. */
-  /* This list shall be expanded as we find more of them.       */
-
-  static FT_Bool
-  tt_check_trickyness( FT_String*  name )
-  {
-#define TRICK_NAMES_MAX_CHARACTERS  16
-#define TRICK_NAMES_COUNT 7
-    static const char 
trick_names[TRICK_NAMES_COUNT][TRICK_NAMES_MAX_CHARACTERS+1] =
-    {
-      "DFKaiSho-SB",     /* dfkaisb.ttf */
-      "DFKaiShu",
-      "DFKai-SB",        /* kaiu.ttf */
-      "HuaTianSongTi?",  /* htst3.ttf */
-      "MingLiU",         /* mingliu.ttf & mingliu.ttc */
-      "PMingLiU",        /* mingliu.ttc */
-      "MingLi43",        /* mingli.ttf */
-    };
-    int  nn;
-
-
-    if ( !name )
-      return FALSE;
-
-    /* Note that we only check the face name at the moment; it might */
-    /* be worth to do more checks for a few special cases.           */
-    for ( nn = 0; nn < TRICK_NAMES_COUNT; nn++ )
-      if ( ft_strstr( name, trick_names[nn] ) )
-        return TRUE;
-
-    return FALSE;
-  }
-
-
   /*************************************************************************/
   /*                                                                       */
   /* <Function>                                                            */
@@ -250,8 +216,12 @@
     if ( error )
       goto Exit;
 
-    if ( tt_check_trickyness( ttface->family_name ) )
-      ttface->face_flags |= FT_FACE_FLAG_TRICKY;
+    {
+      FT_Face_Tricky_Level  level;
+      if ( !FT_Face_Get_TrickyLevel( ttface, &level ) )
+        if ( level > FT_FACE_TRICKY_NO )
+          ttface->face_flags |= FT_FACE_FLAG_TRICKY;
+    }
 
     error = tt_face_load_hdmx( face, stream );
     if ( error )
@@ -877,6 +847,7 @@
   FT_LOCAL_DEF( FT_Error )
   tt_driver_init( FT_Module  ttdriver )     /* TT_Driver */
   {
+    FT_Error  error;
 
 #ifdef TT_USE_BYTECODE_INTERPRETER
 
@@ -892,7 +863,20 @@
 
 #endif
 
-    return TT_Err_Ok;
+    {
+      FT_Library  library = ttdriver->library;
+
+      /* if add a condition returns an error, don't add anymore */
+      if ( ( error = FT_Library_TrickyFontList_Add( library, 
"FAMILYNAME=FDFKaiSho-SB" ) )  ||
+           ( error = FT_Library_TrickyFontList_Add( library, 
"FAMILYNAME=DFKaiShu" ) )      ||
+           ( error = FT_Library_TrickyFontList_Add( library, 
"FAMILYNAME=DFKai-SB" ) )      ||
+           ( error = FT_Library_TrickyFontList_Add( library, 
"FAMILYNAME=HuaTianSongTi?" ) ) ||
+           ( error = FT_Library_TrickyFontList_Add( library, 
"FAMILYNAME=MingLiU" ) )       ||
+           ( error = FT_Library_TrickyFontList_Add( library, 
"FAMILYNAME=PMingLiU" ) )      ||
+           ( error = FT_Library_TrickyFontList_Add( library, 
"FAMILYNAME=MingLi143" ) ) )
+        return error;
+    }
+    return FT_Err_Ok;
   }
 
 
--------------------------------------------------------------
(*1) Why FT2 cannot detect the tricky font in embedded font in PDF?

In PDF, embedding of TrueType font follows the manner of
Type42 font format. The Type42 font consists from PostScript
like properties described by PS/PDF-like syntax and the body
of (subsetted) TrueType font data stream in "sfnt" string array.

According to Type42 font spec, "sfnt" string array may lack
"name" table. This is because the required informations in
"name" are copied to PostScript like properites, out of "sfnt".

When such "sfnt" data is passed to FT_New_Face(), FT2 cannot
lookup the tricky font list, because it has no family name.

Careful person will think, "it's because sfnt in Type42 is
not complete font object, the Type42 font name written out
of sfnt should be checked". It's good idea for PS renderer
which pass Type42 font described by PostScript language to
FT2. But it does not work well for PDF.

In PDF, the format how Type42 font is described is slightly
different from genuine Type42 in PostScript. It is stored
by 2 objects: a font descriptor object, and a byte stream
including TrueType data.

The font descriptor in PDF is like following:

<<
        /FontName       /LNQGRD+#b7s#b2#d3#a9#fa#c5#e9
        /StemV          150
        /FontFile2      37 0 R
        /Ascent         800
        /Flags          5
        /AvgWidth       1000
        /Descent        -194
        /ItalicAngle    0
        /MaxWidth       1000
        /MissingWidth   1000
        /CIDSet         38 0 R
        /FontBBox       [0 -194 1000 800]
        /Type           /FontDescriptor
        /CapHeight      800
>>

"/FontFile2 37 0 R" refers another object number 37
revision 0 as a bytestream including TrueType data
(it's sfnt string array of Type42).

Because 2 objects are required to complete a font object,
it is not so easy for PDF renderers to pass them to FT2's
Type42 driver. In fact, current Type42 driver in FT2 accepts
only PS syntax. Thus, most PDF renderers using FT2 will
pass FontFile2 bytestream to FT2's TrueType driver.

As a result, the embedded font in PDF can look as if it
is unlisted in the tricky font list.

(*2) Why FT_Library_TrickyFontList_{Add|Remove} accepts "condition"
     instead of the family name string?

At present, we expect a scenario that the PDF renderer
gives the Type42 font name to FT2 and let FT2 switch
the autohinter.

But the Type42 font name in PDF can be different from
the original font names of the TrueType (I'm afraid
about the braindead PDF generators that substitute the
fonts and erase original font names). So, if it is possible,
checking without family name might be expected in future.

One of my idea is the detection by TrueType tables
preserved in Type42 sfnt array, like, "cvt ", "fpgm"
and "prep". These tables are deeply related with the
glyph description progam in "glyf" table. Without
parsing and regenerating TrueType glyph program,
it is difficult to change these table consistently.
Therefore I guess there is a possibility that the checksum
(or we can calculate our own hash) of these tables
are not changed during font subsetting & embedding.
To use such detection, I want to add new condition
without familyname, like "cvt_CHECKSUM=0x05bcf058".



reply via email to

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