[Top][All Lists]
[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".
- [ft-devel] Exposing tricky font list to FT2 client,
mpsuzuki <=