diff --git a/ChangeLog b/ChangeLog index cde05d7a6..5fa7f2ea6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2005-02-25 David Turner + + * many, many files: several memory optimizations were implemented to + drastically reduce the heap usage of FreeType, especially in the case + of memory-mapped files. The idea is to avoid loading and decoding tables + in the heap, and instead access the raw data whenever possible (i.e. + when it doesn't compromise performance). + + This had several impacts: first, opening vera.ttf uses a ridiculous amount + of memory (when the FT_Library footprint is accounted for), until you start + loading glyphs. Even then, you'll save at least 20 Kb compared to the non + optimized case. performance of various operations, including open/close + has also been dramatically improved. + + More optimisations to come. The auto-hinter eats memory like crazy? This + must be stopped... + 2005-02-22 David Turner * src/base/ftdbgmem.c: adding the ability to list all allocation sites diff --git a/include/freetype/internal/sfnt.h b/include/freetype/internal/sfnt.h index 9a23b1f82..bcab14516 100644 --- a/include/freetype/internal/sfnt.h +++ b/include/freetype/internal/sfnt.h @@ -477,6 +477,24 @@ FT_BEGIN_HEADER typedef void (*TT_Free_Table_Func)( TT_Face face ); + /** + * @functype: TT_Face_GetKerningFunc + * + * @description: + * return the horizontal kerning value between two glyphs + * + * @input: + * face :: handle to source face object + * left_glyph :: left glyph index + * right_glyph :: right glyph index + * + * @return: + * kerning value in font units. + */ + typedef FT_Int + (*TT_Face_GetKerningFunc)( TT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph ); /*************************************************************************/ /* */ @@ -534,6 +552,9 @@ FT_BEGIN_HEADER TT_Load_SBit_Image_Func load_sbit_image; TT_Free_Table_Func free_sbits; + /* sett `ttkern.h' */ + TT_Face_GetKerningFunc get_kerning; + /* see `ttpost.h' */ TT_Get_PS_Name_Func get_psname; TT_Free_Table_Func free_psnames; diff --git a/include/freetype/internal/tttypes.h b/include/freetype/internal/tttypes.h index 183beb8b9..a8adf705d 100644 --- a/include/freetype/internal/tttypes.h +++ b/include/freetype/internal/tttypes.h @@ -310,7 +310,7 @@ FT_BEGIN_HEADER } TT_GaspRec; - +#ifndef FT_OPTIMIZE_MEMORY /*************************************************************************/ /* */ /* */ @@ -360,8 +360,6 @@ FT_BEGIN_HEADER } TT_HdmxRec, *TT_Hdmx; - - /*************************************************************************/ /* */ /* */ @@ -387,6 +385,7 @@ FT_BEGIN_HEADER FT_FWord value; /* kerning value */ } TT_Kern0_PairRec, *TT_Kern0_Pair; +#endif /* !OPTIMIZE_MEMORY */ /*************************************************************************/ @@ -1199,12 +1198,20 @@ FT_BEGIN_HEADER TT_Header header; /* TrueType header table */ TT_HoriHeader horizontal; /* TrueType horizontal header */ +#ifdef FT_OPTIMIZE_MEMORY + FT_Byte* horz_metrics; + FT_ULong horz_metrics_size; +#endif TT_MaxProfile max_profile; FT_ULong max_components; FT_Bool vertical_info; TT_VertHeader vertical; /* TT Vertical header, if present */ +#ifdef FT_OPTIMIZE_MEMORY + FT_Byte* vert_metrics; + FT_ULong vert_metrics_size; +#endif FT_UShort num_names; /* number of name records */ TT_NameTableRec name_table; /* name table */ @@ -1239,7 +1246,15 @@ FT_BEGIN_HEADER /***********************************************************************/ /* horizontal device metrics */ +#ifdef FT_OPTIMIZE_MEMORY + FT_Byte* hdmx_table; + FT_ULong hdmx_table_size; + FT_UInt hdmx_record_count; + FT_ULong hdmx_record_size; + FT_Byte* hdmx_record_sizes; +#else TT_HdmxRec hdmx; +#endif /* grid-fitting and scaling table */ TT_GaspRec gasp; /* the `gasp' table */ @@ -1285,10 +1300,18 @@ FT_BEGIN_HEADER FT_ULong cvt_size; FT_Short* cvt; +#ifdef FT_OPTIMIZE_MEMORY + FT_Byte* kern_table; + FT_ULong kern_table_size; + FT_UInt num_kern_tables; + FT_UInt32 kern_avail_bits; + FT_UInt32 kern_order_bits; +#else /* the format 0 kerning table, if any */ FT_Int num_kern_pairs; FT_Int kern_table_index; TT_Kern0_Pair kern_pairs; +#endif /* A pointer to the bytecode interpreter to use. This is also */ /* used to hook the debugger for the `ttdebug' utility. */ diff --git a/src/base/ftdbgmem.c b/src/base/ftdbgmem.c index 97757851f..492da7e66 100644 --- a/src/base/ftdbgmem.c +++ b/src/base/ftdbgmem.c @@ -497,8 +497,6 @@ node = *pnode; if ( node ) { - FT_MemSource source; - if ( node->size < 0 ) { /* this block was already freed. This means that our memory is */ @@ -536,7 +534,7 @@ if ( source->cur_blocks > source->max_blocks ) source->max_blocks = source->cur_blocks; - if ( size > source->cur_max ) + if ( size > (FT_ULong)source->cur_max ) source->cur_max = size; source->all_size += size; @@ -937,10 +935,10 @@ printf( "FreeType Memory Dump: current=%ld max=%ld total=%ld count=%ld\n", table->alloc_current, table->alloc_max, table->alloc_total, table->alloc_count ); - printf( " block block sizes sizes sizes source\n" ); - printf( " count high sum highsum max location\n" ); + printf( " block block sizes sizes sizes source\n" ); + printf( " count high sum highsum max location\n" ); printf( "-------------------------------------------------\n" ); - fmt = "%6ld %6ld %10ld %10ld %10ld %s:%d\n"; + fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n"; for ( ; bucket < limit; bucket++ ) { diff --git a/src/cff/cffdrivr.c b/src/cff/cffdrivr.c index b84bfdbf9..bc365ddf5 100644 --- a/src/cff/cffdrivr.c +++ b/src/cff/cffdrivr.c @@ -67,7 +67,7 @@ /*************************************************************************/ /* */ /* */ - /* Get_Kerning */ + /* cff_get_kerning */ /* */ /* */ /* A driver method used to return the kerning vector between two */ @@ -97,56 +97,21 @@ /* They can be implemented by format-specific interfaces. */ /* */ FT_CALLBACK_DEF( FT_Error ) - Get_Kerning( FT_Face ttface, /* TT_Face */ - FT_UInt left_glyph, - FT_UInt right_glyph, - FT_Vector* kerning ) + cff_get_kerning( FT_Face ttface, /* TT_Face */ + FT_UInt left_glyph, + FT_UInt right_glyph, + FT_Vector* kerning ) { - TT_Face face = (TT_Face)ttface; - TT_Kern0_Pair pair; - - - if ( !face ) - return CFF_Err_Invalid_Face_Handle; + TT_Face face = (TT_Face)ttface; + SFNT_Service sfnt = face->sfnt; kerning->x = 0; kerning->y = 0; - if ( face->kern_pairs ) - { - /* there are some kerning pairs in this font file! */ - FT_ULong search_tag = PAIR_TAG( left_glyph, right_glyph ); - FT_Long left, right; + if ( sfnt ) + kerning->x = sfnt->get_kerning( face, left_glyph, right_glyph ); - - left = 0; - right = face->num_kern_pairs - 1; - - while ( left <= right ) - { - FT_Long middle = left + ( ( right - left ) >> 1 ); - FT_ULong cur_pair; - - - pair = face->kern_pairs + middle; - cur_pair = PAIR_TAG( pair->left, pair->right ); - - if ( cur_pair == search_tag ) - goto Found; - - if ( cur_pair < search_tag ) - left = middle + 1; - else - right = middle - 1; - } - } - - Exit: - return CFF_Err_Ok; - - Found: - kerning->x = pair->value; - goto Exit; + return 0; } @@ -472,7 +437,7 @@ Load_Glyph, - Get_Kerning, + cff_get_kerning, 0, /* FT_Face_AttachFunc */ 0 /* FT_Face_GetAdvancesFunc */ }; diff --git a/src/sfnt/Jamfile b/src/sfnt/Jamfile index ce61d0807..256b63afd 100644 --- a/src/sfnt/Jamfile +++ b/src/sfnt/Jamfile @@ -8,7 +8,7 @@ SubDir FT2_TOP $(FT2_SRC_DIR) sfnt ; if $(FT2_MULTI) { - _sources = sfobjs sfdriver ttcmap ttpost ttload ttsbit ; + _sources = sfobjs sfdriver ttcmap ttpost ttload ttsbit ttkern ; } else { diff --git a/src/sfnt/rules.mk b/src/sfnt/rules.mk index fab5492ea..865eac1a1 100644 --- a/src/sfnt/rules.mk +++ b/src/sfnt/rules.mk @@ -29,6 +29,7 @@ SFNT_DRV_SRC := $(SFNT_DIR)/ttload.c \ $(SFNT_DIR)/ttcmap.c \ $(SFNT_DIR)/ttsbit.c \ $(SFNT_DIR)/ttpost.c \ + $(SFNT_DIR)/ttkern.c \ $(SFNT_DIR)/sfobjs.c \ $(SFNT_DIR)/sfdriver.c diff --git a/src/sfnt/sfdriver.c b/src/sfnt/sfdriver.c index 627899cd5..5d945fbbd 100644 --- a/src/sfnt/sfdriver.c +++ b/src/sfnt/sfdriver.c @@ -385,6 +385,9 @@ #ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES + /* see `ttkern.h' */ + tt_face_get_kerning, + /* see `ttpost.h' */ tt_face_get_ps_name, tt_face_free_ps_names, diff --git a/src/sfnt/sfnt.c b/src/sfnt/sfnt.c index ec6dc466e..1102bcb5e 100644 --- a/src/sfnt/sfnt.c +++ b/src/sfnt/sfnt.c @@ -21,6 +21,7 @@ #include #include "ttload.c" #include "ttcmap.c" +#include "ttkern.c" #include "sfobjs.c" #include "sfdriver.c" diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c index 98bc67785..744aa846c 100644 --- a/src/sfnt/sfobjs.c +++ b/src/sfnt/sfobjs.c @@ -20,6 +20,7 @@ #include "sfobjs.h" #include "ttload.h" #include "ttcmap.h" +#include "ttkern.h" #include FT_INTERNAL_SFNT_H #include FT_TRUETYPE_IDS_H #include FT_TRUETYPE_TAGS_H @@ -503,11 +504,13 @@ #endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ if ( LOAD_( hdmx ) || - LOAD_( gasp ) || - LOAD_( kerning ) || LOAD_( pclt ) ) goto Exit; + /* consider the kerning and gasp tables as optional */ + (void)LOAD_( gasp ); + (void)LOAD_( kerning ); + face->root.family_name = tt_face_get_name( face, TT_NAME_ID_PREFERRED_FAMILY ); if ( !face->root.family_name ) @@ -553,9 +556,11 @@ if ( face->vertical_info ) flags |= FT_FACE_FLAG_VERTICAL; +#if 0 /* kerning available ? */ - if ( face->kern_pairs ) + if ( TT_FACE_HAS_KERNING(face) ) flags |= FT_FACE_FLAG_KERNING; +#endif #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT /* Don't bother to load the tables unless somebody asks for them. */ @@ -802,8 +807,7 @@ } /* freeing the kerning table */ - FT_FREE( face->kern_pairs ); - face->num_kern_pairs = 0; + tt_face_done_kern( face ); /* freeing the collection table */ FT_FREE( face->ttc_header.offsets ); @@ -823,8 +827,19 @@ } /* freeing the horizontal metrics */ +#ifdef FT_OPTIMIZE_MEMORY + { + FT_Stream stream = FT_FACE_STREAM( face ); + + FT_FRAME_RELEASE( face->horz_metrics ); + FT_FRAME_RELEASE( face->vert_metrics ); + face->horz_metrics_size = 0; + face->vert_metrics_size = 0; + } +#else FT_FREE( face->horizontal.long_metrics ); FT_FREE( face->horizontal.short_metrics ); +#endif /* freeing the vertical ones, if any */ if ( face->vertical_info ) diff --git a/src/sfnt/ttkern.c b/src/sfnt/ttkern.c new file mode 100644 index 000000000..a75d56620 --- /dev/null +++ b/src/sfnt/ttkern.c @@ -0,0 +1,479 @@ +/***************************************************************************/ +/* */ +/* ttkern.h */ +/* */ +/* Load the basic TrueType kerning table. This doesn't handle */ +/* kerning data within the GPOS table at the moment. */ +/* */ +/* Copyright 1996-2001, 2002, 2003, 2004 by */ +/* 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. */ +/* */ +/***************************************************************************/ + + +#include +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TAGS_H +#include "ttload.h" + +#include "sferrors.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttload + + +#undef TT_KERN_INDEX +#define TT_KERN_INDEX( g1, g2 ) ( ( (FT_ULong)(g1) << 16 ) | (g2) ) + + +#ifdef FT_OPTIMIZE_MEMORY + + FT_LOCAL_DEF( FT_Error ) + tt_face_load_kern( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + FT_Memory memory = stream->memory; + FT_ULong table_size; + FT_Byte* p; + FT_Byte* p_limit; + FT_UInt nn, num_tables; + FT_UInt32 avail = 0, ordered = 0; + + + /* the kern table is optional; exit silently if it is missing */ + error = face->goto_table( face, TTAG_kern, stream, &table_size ); + if ( error ) + goto Exit; + + if ( table_size < 4 ) /* the case of a malformed table */ + { + FT_ERROR(( "kerning table is too small - ignored\n" )); + error = SFNT_Err_Table_Missing; + goto Exit; + } + + if ( FT_FRAME_EXTRACT( table_size, face->kern_table ) ) + { + FT_ERROR(( "could not extract kerning table\n" )); + goto Exit; + } + + face->kern_table_size = table_size; + + p = face->kern_table; + p_limit = p + table_size; + + p += 2; /* skip version */ + num_tables = FT_NEXT_USHORT(p); + + if ( num_tables > 32 ) /* we only support up to 32 sub-tables */ + num_tables = 32; + + for ( nn = 0; nn < num_tables; nn++ ) + { + FT_UInt num_pairs, version, length, coverage; + FT_Byte* p_next; + FT_UInt32 mask = 1UL << nn; + + if ( p + 6 > p_limit ) + break; + + p_next = p; + + version = FT_NEXT_USHORT(p); + length = FT_NEXT_USHORT(p); + coverage = FT_NEXT_USHORT(p); + + if ( length <= 6 ) + break; + + p_next += length; + + if ( (coverage & ~8) != 0x0001 || /* only use horizontal kerning tables */ + p+8 > p_limit ) + goto NextTable; + + num_pairs = FT_NEXT_USHORT(p); + p += 6; + + if ( p + 6*num_pairs > p_limit ) + goto NextTable; + + avail |= mask; + + /* now, try to see if the pairs in this table are ordered. + * when they are, we'll be able to use binary search + */ + if ( num_pairs > 0 ) + { + FT_UInt count; + FT_UInt old_pair; + + old_pair = FT_NEXT_ULONG(p); + p += 2; + + for ( count = num_pairs-1; count > 0; count-- ) + { + FT_UInt32 cur_pair; + + cur_pair = FT_NEXT_ULONG(p); + if ( cur_pair <= old_pair ) + break; + + p += 2; + old_pair = cur_pair; + } + + if ( count == 0 ) + ordered |= mask; + } + + NextTable: + p = p_next; + } + + face->num_kern_tables = nn; + face->kern_avail_bits = avail; + face->kern_order_bits = ordered; + + Exit: + return error; + } + + + FT_LOCAL_DEF( void ) + tt_face_done_kern( TT_Face face ) + { + FT_Stream stream = face->root.stream; + + FT_FRAME_RELEASE( face->kern_table ); + face->kern_table_size = 0; + face->num_kern_tables = 0; + face->kern_avail_bits = 0; + face->kern_order_bits = 0; + } + + + FT_LOCAL_DEF( FT_Int ) + tt_face_get_kerning( TT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph ) + { + FT_Int result = 0; + FT_Int value; + FT_UInt count, mask = 1; + FT_Byte* p = face->kern_table; + FT_Byte* p_limit = p + face->kern_table_size; + + p += 4; + mask = 0x0001; + + for ( count = face->num_kern_tables; count > 0; count--, mask <<= 1 ) + { + FT_Byte* base = p; + FT_Byte* next = base; + FT_UInt version = FT_NEXT_USHORT(p); + FT_UInt length = FT_NEXT_USHORT(p); + FT_UInt coverage = FT_NEXT_USHORT(p); + + next = base + length; + + if ( (face->kern_avail_bits & mask) == 0 ) + goto NextTable; + + if ( p+8 > next ) + goto NextTable; + + switch ( coverage >> 8 ) + { + case 0: + { + FT_UInt num_pairs = FT_NEXT_USHORT(p); + FT_ULong key0 = TT_KERN_INDEX(left_glyph,right_glyph); + FT_Int value = 0; + + p += 6; + + if ( face->kern_order_bits & mask ) /* binary search */ + { + FT_UInt min = 0; + FT_UInt max = num_pairs; + FT_Byte* q; + + while ( min < max ) + { + FT_UInt mid = (min+max) >> 1; + FT_Byte* q = p + 6*mid; + FT_ULong key; + + key = FT_NEXT_ULONG(q); + + if ( key == key0 ) + { + value = FT_PEEK_SHORT(q); + break; + } + if ( key < key0 ) + min = mid+1; + else + max = mid; + } + } + else /* linear search */ + { + FT_UInt count = num_pairs; + + for ( ; count > 0; count-- ) + { + FT_ULong key = FT_NEXT_ULONG(p); + + if ( key == key0 ) + { + value = FT_PEEK_SHORT(p); + break; + } + p += 2; + } + } + } + break; + + /* we don't support format 2 because we've never seen a single font + * using it in real life... + */ + + default: + ; + } + + if ( coverage & 8 ) /* overide or addition */ + result = value; + else + result += value; + + NextTable: + p = next; + } + + Exit: + return result; + } + + +#else /* !OPTMIZE_MEMORY */ + + FT_CALLBACK_DEF( int ) + tt_kern_pair_compare( const void* a, + const void* b ); + + + FT_LOCAL_DEF( FT_Error ) + tt_face_load_kern( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + FT_Memory memory = stream->memory; + + FT_UInt n, num_tables; + + + /* the kern table is optional; exit silently if it is missing */ + error = face->goto_table( face, TTAG_kern, stream, 0 ); + if ( error ) + return SFNT_Err_Ok; + + if ( FT_FRAME_ENTER( 4L ) ) + goto Exit; + + (void)FT_GET_USHORT(); /* version */ + num_tables = FT_GET_USHORT(); + + FT_FRAME_EXIT(); + + for ( n = 0; n < num_tables; n++ ) + { + FT_UInt coverage; + FT_UInt length; + + + if ( FT_FRAME_ENTER( 6L ) ) + goto Exit; + + (void)FT_GET_USHORT(); /* version */ + length = FT_GET_USHORT() - 6; /* substract header length */ + coverage = FT_GET_USHORT(); + + FT_FRAME_EXIT(); + + if ( coverage == 0x0001 ) + { + FT_UInt num_pairs; + TT_Kern0_Pair pair; + TT_Kern0_Pair limit; + + + /* found a horizontal format 0 kerning table! */ + if ( FT_FRAME_ENTER( 8L ) ) + goto Exit; + + num_pairs = FT_GET_USHORT(); + + /* skip the rest */ + + FT_FRAME_EXIT(); + + /* allocate array of kerning pairs */ + if ( FT_QNEW_ARRAY( face->kern_pairs, num_pairs ) || + FT_FRAME_ENTER( 6L * num_pairs ) ) + goto Exit; + + pair = face->kern_pairs; + limit = pair + num_pairs; + for ( ; pair < limit; pair++ ) + { + pair->left = FT_GET_USHORT(); + pair->right = FT_GET_USHORT(); + pair->value = FT_GET_USHORT(); + } + + FT_FRAME_EXIT(); + + face->num_kern_pairs = num_pairs; + face->kern_table_index = n; + + /* ensure that the kerning pair table is sorted (yes, some */ + /* fonts have unsorted tables!) */ + + if ( num_pairs > 0 ) + { + TT_Kern0_Pair pair0 = face->kern_pairs; + FT_ULong prev = TT_KERN_INDEX( pair0->left, pair0->right ); + + + for ( pair0++; pair0 < limit; pair0++ ) + { + FT_ULong next = TT_KERN_INDEX( pair0->left, pair0->right ); + + + if ( next < prev ) + goto SortIt; + + prev = next; + } + goto Exit; + + SortIt: + ft_qsort( (void*)face->kern_pairs, (int)num_pairs, + sizeof ( TT_Kern0_PairRec ), tt_kern_pair_compare ); + } + + goto Exit; + } + + if ( FT_STREAM_SKIP( length ) ) + goto Exit; + } + + /* no kern table found -- doesn't matter */ + face->kern_table_index = -1; + face->num_kern_pairs = 0; + face->kern_pairs = NULL; + + Exit: + return error; + } + + FT_CALLBACK_DEF( int ) + tt_kern_pair_compare( const void* a, + const void* b ) + { + TT_Kern0_Pair pair1 = (TT_Kern0_Pair)a; + TT_Kern0_Pair pair2 = (TT_Kern0_Pair)b; + + FT_ULong index1 = TT_KERN_INDEX( pair1->left, pair1->right ); + FT_ULong index2 = TT_KERN_INDEX( pair2->left, pair2->right ); + + + return ( index1 < index2 ? -1 : + ( index1 > index2 ? 1 : 0 )); + } + + + FT_LOCAL_DEF( void ) + tt_face_done_kern( TT_Face face ) + { + FT_Memory memory = face->root.stream->memory; + + FT_FREE( face->kern_pairs ); + face->num_kern_pairs = 0; + } + + + + FT_LOCAL_DEF( FT_Int ) + tt_face_get_kerning( TT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph ) + { + FT_Int result = 0; + TT_Kern0_Pair pair; + + + if ( face && face->kern_pairs ) + { + /* there are some kerning pairs in this font file! */ + FT_ULong search_tag = TT_KERN_INDEX( left_glyph, right_glyph ); + FT_Long left, right; + + + left = 0; + right = face->num_kern_pairs - 1; + + while ( left <= right ) + { + FT_Long middle = left + ( ( right - left ) >> 1 ); + FT_ULong cur_pair; + + + pair = face->kern_pairs + middle; + cur_pair = TT_KERN_INDEX( pair->left, pair->right ); + + if ( cur_pair == search_tag ) + goto Found; + + if ( cur_pair < search_tag ) + left = middle + 1; + else + right = middle - 1; + } + } + + Exit: + return result; + + Found: + result = pair->value; + goto Exit; + } + +#endif /* !OPTIMIZE_MEMORY */ + + +#undef TT_KERN_INDEX + +/* END */ diff --git a/src/sfnt/ttkern.h b/src/sfnt/ttkern.h new file mode 100644 index 000000000..f71849b1f --- /dev/null +++ b/src/sfnt/ttkern.h @@ -0,0 +1,55 @@ +/***************************************************************************/ +/* */ +/* ttkern.h */ +/* */ +/* Load the basic TrueType kerning table. This doesn't handle */ +/* kerning data within the GPOS table at the moment. */ +/* */ +/* Copyright 1996-2001, 2002 by */ +/* 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. */ +/* */ +/***************************************************************************/ + + +#ifndef __TTKERN_H__ +#define __TTKERN_H__ + + +#include +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_TRUETYPE_TYPES_H + + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + tt_face_load_kern( TT_Face face, + FT_Stream stream ); + + FT_LOCAL( void ) + tt_face_done_kern( TT_Face face ); + + FT_LOCAL( FT_Int ) + tt_face_get_kerning( TT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph ); + +#ifdef FT_OPTIMIZE_MEMORY +# define TT_FACE_HAS_KERNING(face) ((face)->kern_avail_bits != 0) +#else +# define TT_FACE_HAS_KERNING(face) ((face)->kern_pairs != NULL) +#endif + +FT_END_HEADER + +#endif /* __TTKERN_H__ */ + + +/* END */ diff --git a/src/sfnt/ttload.c b/src/sfnt/ttload.c index 1a3dcb6d2..e7a45f7e0 100644 --- a/src/sfnt/ttload.c +++ b/src/sfnt/ttload.c @@ -800,6 +800,82 @@ /* */ /* FreeType error code. 0 means success. */ /* */ +#ifdef FT_OPTIMIZE_MEMORY + static FT_Error + tt_face_load_metrics( TT_Face face, + FT_Stream stream, + FT_Bool vertical ) + { + FT_Error error; + FT_Memory memory = stream->memory; + FT_ULong table_size; + FT_Byte** ptable; + FT_ULong* ptable_size; + + + FT_TRACE2(( "TT_Load_%s_Metrics: %08p\n", vertical ? "Vertical" + : "Horizontal", + face )); + + if ( vertical ) + { + ptable = &face->vert_metrics; + ptable_size = &face->vert_metrics_size; + + /* The table is optional, quit silently if it wasn't found */ + /* */ + /* XXX: Some fonts have a valid vertical header with a non-null */ + /* `number_of_VMetrics' fields, but no corresponding `vmtx' */ + /* table to get the metrics from (e.g. mingliu). */ + /* */ + /* For safety, we set the field to 0! */ + /* */ + error = face->goto_table( face, TTAG_vmtx, stream, &table_size ); + if ( error ) + { + /* Set number_Of_VMetrics to 0! */ + FT_TRACE2(( " no vertical header in file.\n" )); + error = SFNT_Err_Ok; + goto Exit; + } + } + else + { + ptable = &face->horz_metrics; + ptable_size = &face->horz_metrics_size; + + error = face->goto_table( face, TTAG_hmtx, stream, &table_size ); + if ( error ) + { +#ifdef FT_CONFIG_OPTION_INCREMENTAL + /* If this is an incrementally loaded font and there are */ + /* overriding metrics tolerate a missing 'hmtx' table. */ + if ( face->root.internal->incremental_interface && + face->root.internal->incremental_interface->funcs-> + get_glyph_metrics ) + { + face->horizontal.number_Of_HMetrics = 0; + error = SFNT_Err_Ok; + goto Exit; + } +#endif + + FT_ERROR(( " no horizontal metrics in file!\n" )); + error = SFNT_Err_Hmtx_Table_Missing; + goto Exit; + } + } + + if ( FT_FRAME_EXTRACT( table_size, *ptable ) ) + goto Exit; + + *ptable_size = table_size; + + Exit: + return error; + } + +#else /* !OPTIMIZE_MEMORY */ static FT_Error tt_face_load_metrics( TT_Face face, FT_Stream stream, @@ -938,7 +1014,7 @@ Exit: return error; } - +#endif /* !FT_OPTIMIZE_METRICS */ /*************************************************************************/ /* */ @@ -1611,190 +1687,6 @@ } - FT_CALLBACK_DEF( int ) - tt_kern_pair_compare( const void* a, - const void* b ); - - - /*************************************************************************/ - /* */ - /* */ - /* tt_face_load_kern */ - /* */ - /* */ - /* Loads the first kerning table with format 0 in the font. Only */ - /* accepts the first horizontal kerning table. Developers should use */ - /* the `ftxkern' extension to access other kerning tables in the font */ - /* file, if they really want to. */ - /* */ - /* */ - /* face :: A handle to the target face object. */ - /* */ - /* stream :: The input stream. */ - /* */ - /* */ - /* FreeType error code. 0 means success. */ - /* */ - -#undef TT_KERN_INDEX -#define TT_KERN_INDEX( g1, g2 ) ( ( (FT_ULong)g1 << 16 ) | g2 ) - - - FT_LOCAL_DEF( FT_Error ) - tt_face_load_kern( TT_Face face, - FT_Stream stream ) - { - FT_Error error; - FT_Memory memory = stream->memory; - - FT_UInt n, num_tables; - - - /* the kern table is optional; exit silently if it is missing */ - error = face->goto_table( face, TTAG_kern, stream, 0 ); - if ( error ) - return SFNT_Err_Ok; - - if ( FT_FRAME_ENTER( 4L ) ) - goto Exit; - - (void)FT_GET_USHORT(); /* version */ - num_tables = FT_GET_USHORT(); - - FT_FRAME_EXIT(); - - for ( n = 0; n < num_tables; n++ ) - { - FT_UInt coverage; - FT_UInt length; - - - if ( FT_FRAME_ENTER( 6L ) ) - goto Exit; - - (void)FT_GET_USHORT(); /* version */ - length = FT_GET_USHORT() - 6; /* substract header length */ - coverage = FT_GET_USHORT(); - - FT_FRAME_EXIT(); - - if ( coverage == 0x0001 ) - { - FT_UInt num_pairs; - TT_Kern0_Pair pair; - TT_Kern0_Pair limit; - - - /* found a horizontal format 0 kerning table! */ - if ( FT_FRAME_ENTER( 8L ) ) - goto Exit; - - num_pairs = FT_GET_USHORT(); - - /* skip the rest */ - - FT_FRAME_EXIT(); - - /* allocate array of kerning pairs */ - if ( FT_QNEW_ARRAY( face->kern_pairs, num_pairs ) || - FT_FRAME_ENTER( 6L * num_pairs ) ) - goto Exit; - - pair = face->kern_pairs; - limit = pair + num_pairs; - for ( ; pair < limit; pair++ ) - { - pair->left = FT_GET_USHORT(); - pair->right = FT_GET_USHORT(); - pair->value = FT_GET_USHORT(); - } - - FT_FRAME_EXIT(); - - face->num_kern_pairs = num_pairs; - face->kern_table_index = n; - - /* ensure that the kerning pair table is sorted (yes, some */ - /* fonts have unsorted tables!) */ - -#if 1 - if ( num_pairs > 0 ) - { - TT_Kern0_Pair pair0 = face->kern_pairs; - FT_ULong prev = TT_KERN_INDEX( pair0->left, pair0->right ); - - - for ( pair0++; pair0 < limit; pair0++ ) - { - FT_ULong next = TT_KERN_INDEX( pair0->left, pair0->right ); - - - if ( next < prev ) - goto SortIt; - - prev = next; - } - goto Exit; - - SortIt: - ft_qsort( (void*)face->kern_pairs, (int)num_pairs, - sizeof ( TT_Kern0_PairRec ), tt_kern_pair_compare ); - } -#else - { - TT_Kern0_Pair pair0 = face->kern_pairs; - FT_UInt i; - - - for ( i = 1; i < num_pairs; i++, pair0++ ) - { - if ( tt_kern_pair_compare( pair0, pair0 + 1 ) != -1 ) - { - ft_qsort( (void*)face->kern_pairs, (int)num_pairs, - sizeof ( TT_Kern0_PairRec ), tt_kern_pair_compare ); - break; - } - } - } -#endif - - goto Exit; - } - - if ( FT_STREAM_SKIP( length ) ) - goto Exit; - } - - /* no kern table found -- doesn't matter */ - face->kern_table_index = -1; - face->num_kern_pairs = 0; - face->kern_pairs = NULL; - - Exit: - return error; - } - - - FT_CALLBACK_DEF( int ) - tt_kern_pair_compare( const void* a, - const void* b ) - { - TT_Kern0_Pair pair1 = (TT_Kern0_Pair)a; - TT_Kern0_Pair pair2 = (TT_Kern0_Pair)b; - - FT_ULong index1 = TT_KERN_INDEX( pair1->left, pair1->right ); - FT_ULong index2 = TT_KERN_INDEX( pair2->left, pair2->right ); - - - return ( index1 < index2 ? -1 : - ( index1 > index2 ? 1 : 0 )); - } - - -#undef TT_KERN_INDEX - - - /*************************************************************************/ /* */ /* */ @@ -1811,6 +1703,75 @@ /* */ /* FreeType error code. 0 means success. */ /* */ +#ifdef FT_OPTIMIZE_MEMORY + FT_LOCAL_DEF( FT_Error ) + tt_face_load_hdmx( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + FT_Memory memory = stream->memory; + FT_UInt version, nn, num_records; + FT_ULong table_size, record_size; + FT_Byte* p; + FT_Byte* limit; + + /* this table is optional */ + error = face->goto_table( face, TTAG_hdmx, stream, &table_size ); + if ( error || table_size < 8 ) + return SFNT_Err_Ok; + + if ( FT_FRAME_EXTRACT( table_size, face->hdmx_table ) ) + goto Exit; + + p = face->hdmx_table; + limit = p + table_size; + + version = FT_NEXT_USHORT(p); + num_records = FT_NEXT_USHORT(p); + record_size = FT_NEXT_ULONG(p); + + if ( version != 0 || num_records > 255 || record_size > 0x40000 ) + { + error = SFNT_Err_Invalid_File_Format; + goto Fail; + } + + if ( FT_NEW_ARRAY( face->hdmx_record_sizes, num_records ) ) + goto Fail; + + for ( nn = 0; nn < num_records; nn++ ) + { + if ( p+record_size > limit ) + break; + + face->hdmx_record_sizes[nn] = p[0]; + p += record_size; + } + + face->hdmx_record_count = nn; + face->hdmx_table_size = table_size; + + Exit: + return error; + + Fail: + FT_FRAME_RELEASE( face->hdmx_table ); + face->hdmx_table_size = 0; + goto Exit; + } + + + FT_LOCAL_DEF( void ) + tt_face_free_hdmx( TT_Face face ) + { + FT_Stream stream = face->root.stream; + FT_Memory memory = stream->memory; + + FT_FREE( face->hdmx_record_sizes ); + FT_FRAME_RELEASE( face->hdmx_table ); + } + +#else /* !FT_OPTIMIZE_MEMORY */ FT_LOCAL_DEF( FT_Error ) tt_face_load_hdmx( TT_Face face, FT_Stream stream ) @@ -1885,17 +1846,6 @@ } - /*************************************************************************/ - /* */ - /* */ - /* tt_face_free_hdmx */ - /* */ - /* */ - /* Frees the horizontal device metrics table. */ - /* */ - /* */ - /* face :: A handle to the target face object. */ - /* */ FT_LOCAL_DEF( void ) tt_face_free_hdmx( TT_Face face ) { @@ -1913,5 +1863,7 @@ } } +#endif /* !OPTIMIZE_MEMORY */ + /* END */ diff --git a/src/sfnt/ttload.h b/src/sfnt/ttload.h index 27c41b50b..439b4df3e 100644 --- a/src/sfnt/ttload.h +++ b/src/sfnt/ttload.h @@ -111,11 +111,6 @@ FT_BEGIN_HEADER tt_face_free_hdmx ( TT_Face face ); - FT_LOCAL( FT_Error ) - tt_face_load_kern( TT_Face face, - FT_Stream stream ); - - FT_LOCAL( FT_Error ) tt_face_load_gasp( TT_Face face, FT_Stream stream ); diff --git a/src/sfnt/ttsbit.c b/src/sfnt/ttsbit.c index ea48bde51..65b3bad93 100644 --- a/src/sfnt/ttsbit.c +++ b/src/sfnt/ttsbit.c @@ -185,7 +185,7 @@ } - const FT_Frame_Field sbit_metrics_fields[] = + static const FT_Frame_Field sbit_metrics_fields[] = { #undef FT_STRUCTURE #define FT_STRUCTURE TT_SBit_MetricsRec diff --git a/src/truetype/ttdriver.c b/src/truetype/ttdriver.c index 25948839d..0e0396ddc 100644 --- a/src/truetype/ttdriver.c +++ b/src/truetype/ttdriver.c @@ -69,7 +69,7 @@ /*************************************************************************/ /* */ /* */ - /* Get_Kerning */ + /* tt_get_kerning */ /* */ /* */ /* A driver method used to return the kerning vector between two */ @@ -99,56 +99,21 @@ /* They can be implemented by format-specific interfaces. */ /* */ static FT_Error - Get_Kerning( FT_Face ttface, /* TT_Face */ + tt_get_kerning( FT_Face ttface, /* TT_Face */ FT_UInt left_glyph, FT_UInt right_glyph, FT_Vector* kerning ) { - TT_Face face = (TT_Face)ttface; - TT_Kern0_Pair pair; - - - if ( !face ) - return TT_Err_Invalid_Face_Handle; + TT_Face face = (TT_Face)ttface; + SFNT_Service sfnt = (SFNT_Service) face->sfnt; kerning->x = 0; kerning->y = 0; - if ( face->kern_pairs ) - { - /* there are some kerning pairs in this font file! */ - FT_ULong search_tag = PAIR_TAG( left_glyph, right_glyph ); - FT_Long left, right; - - - left = 0; - right = face->num_kern_pairs - 1; - - while ( left <= right ) - { - FT_Long middle = left + ( ( right - left ) >> 1 ); - FT_ULong cur_pair; - - - pair = face->kern_pairs + middle; - cur_pair = PAIR_TAG( pair->left, pair->right ); - - if ( cur_pair == search_tag ) - goto Found; - - if ( cur_pair < search_tag ) - left = middle + 1; - else - right = middle - 1; - } - } - - Exit: - return TT_Err_Ok; - - Found: - kerning->x = pair->value; - goto Exit; + if ( sfnt ) + kerning->x = sfnt->get_kerning( face, left_glyph, right_glyph ); + + return 0; } @@ -456,7 +421,7 @@ Set_Pixel_Sizes, Load_Glyph, - Get_Kerning, + tt_get_kerning, 0, /* FT_Face_AttachFunc */ 0 /* FT_Face_GetAdvancesFunc */ }; diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c index 036a7d2ed..ddf4c2afd 100644 --- a/src/truetype/ttgload.c +++ b/src/truetype/ttgload.c @@ -96,34 +96,99 @@ /* This function will much probably move to another component in the */ /* near future, but I haven't decided which yet. */ /* */ - FT_LOCAL_DEF( void ) - TT_Get_Metrics( TT_HoriHeader* header, - FT_UInt idx, - FT_Short* bearing, - FT_UShort* advance ) +#ifdef FT_OPTIMIZE_MEMORY + static void + tt_face_get_metrics( TT_Face face, + FT_Bool vertical, + FT_UInt idx, + FT_Short *abearing, + FT_UShort *aadvance ) { + TT_HoriHeader* header; + FT_Byte* p; + FT_Byte* limit; + FT_UShort k; + + if ( vertical ) + { + header = (TT_HoriHeader*)&face->vertical; + p = face->vert_metrics; + limit = p + face->vert_metrics_size; + } + else + { + header = &face->horizontal; + p = face->horz_metrics; + limit = p + face->horz_metrics_size; + } + + k = header->number_Of_HMetrics; + + if ( k > 0 ) + { + if ( idx < (FT_UInt)k ) + { + p += 4*idx; + if ( p+4 >= limit ) + goto NoData; + + *aadvance = FT_NEXT_USHORT(p); + *abearing = FT_NEXT_SHORT(p); + } + else + { + p += 4*(k-1); + if ( p+4 > limit ) + goto NoData; + + *aadvance = FT_NEXT_USHORT(p); + p += 2 + 2*(idx-k); + if ( p+2 > limit ) + *abearing = 0; + else + *abearing = FT_PEEK_SHORT(p); + } + } + else + { + NoData: + *abearing = 0; + *aadvance = 0; + } + } +#else + static void + tt_face_get_metrics( TT_Face face, + FT_Bool vertical, + FT_UInt idx, + FT_Short *abearing, + FT_UShort *aadvance ) + { + TT_HoriHeader* header = (vertical ? (TT_HoriHeader*)&face->vertical + : &face->horizontal); TT_LongMetrics longs_m; - FT_UShort k = header->number_Of_HMetrics; + FT_UShort k = header->number_Of_HMetrics; if ( k == 0 ) { - *bearing = *advance = 0; + *abearing = *aadvance = 0; return; } if ( idx < (FT_UInt)k ) { - longs_m = (TT_LongMetrics )header->long_metrics + idx; - *bearing = longs_m->bearing; - *advance = longs_m->advance; + longs_m = (TT_LongMetrics )header->long_metrics + idx; + *abearing = longs_m->bearing; + *aadvance = longs_m->advance; } else { - *bearing = ((TT_ShortMetrics*)header->short_metrics)[idx - k]; - *advance = ((TT_LongMetrics )header->long_metrics)[k - 1].advance; + *abearing = ((TT_ShortMetrics*)header->short_metrics)[idx - k]; + *aadvance = ((TT_LongMetrics )header->long_metrics)[k - 1].advance; } } +#endif /*************************************************************************/ @@ -139,7 +204,7 @@ FT_Short* lsb, FT_UShort* aw ) { - TT_Get_Metrics( &face->horizontal, idx, lsb, aw ); + tt_face_get_metrics( face, 0, idx, lsb, aw ); if ( check && face->postscript.isFixedPitch ) *aw = face->horizontal.advance_Width_Max; @@ -152,17 +217,36 @@ /* in the font's `hdmx' table (if any). */ /* */ static FT_Byte* - Get_Advance_Widths( TT_Face face, - FT_UShort ppem ) + Get_Advance_WidthPtr( TT_Face face, + FT_Int ppem, + FT_UInt gindex ) { +#ifdef FT_OPTIMIZE_MEMORY + FT_UInt nn; + FT_Byte* result = NULL; + FT_ULong record_size = face->hdmx_record_size; + FT_Byte* record = face->hdmx_table + 8; + + for ( nn = 0; nn < face->hdmx_record_count; nn++ ) + if ( face->hdmx_record_sizes[nn] == ppem ) + { + gindex += 2; + if ( gindex < record_size ) + result = record + gindex; + break; + } + + return result; +#else FT_UShort n; for ( n = 0; n < face->hdmx.num_records; n++ ) if ( face->hdmx.records[n].ppem == ppem ) - return face->hdmx.records[n].widths; + return &face->hdmx.records[n].widths[gindex]; return NULL; +#endif } @@ -189,7 +273,7 @@ FT_UNUSED( check ); if ( face->vertical_info ) - TT_Get_Metrics( (TT_HoriHeader *)&face->vertical, idx, tsb, ah ); + tt_face_get_metrics( face, 1, idx, tsb, ah ); #if 1 /* Emperically determined, at variance with what MS said */ @@ -1782,12 +1866,13 @@ if ( !face->postscript.isFixedPitch && size && IS_HINTED( loader->load_flags ) ) { - FT_Byte* widths = Get_Advance_Widths( face, - size->root.metrics.x_ppem ); + FT_Byte* widthp = Get_Advance_WidthPtr( face, + size->root.metrics.x_ppem, + glyph_index ); - if ( widths ) - glyph->metrics.horiAdvance = widths[glyph_index] << 6; + if ( widthp ) + glyph->metrics.horiAdvance = *widthp << 6; } /* set glyph dimensions */ diff --git a/src/truetype/ttgload.h b/src/truetype/ttgload.h index dfa2a60a5..27565ed63 100644 --- a/src/truetype/ttgload.h +++ b/src/truetype/ttgload.h @@ -31,12 +31,6 @@ FT_BEGIN_HEADER - FT_LOCAL( void ) - TT_Get_Metrics( TT_HoriHeader* header, - FT_UInt idx, - FT_Short* bearing, - FT_UShort* advance ); - FT_LOCAL( void ) TT_Init_Glyph_Loading( TT_Face face );