* 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...
This commit is contained in:
parent
d7fe752715
commit
a511bc8b9b
17
ChangeLog
17
ChangeLog
@ -1,3 +1,20 @@
|
||||
2005-02-25 David Turner <david@freetype.org>
|
||||
|
||||
* 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 <david@freetype.org>
|
||||
|
||||
* src/base/ftdbgmem.c: adding the ability to list all allocation sites
|
||||
|
@ -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;
|
||||
|
@ -310,7 +310,7 @@ FT_BEGIN_HEADER
|
||||
|
||||
} TT_GaspRec;
|
||||
|
||||
|
||||
#ifndef FT_OPTIMIZE_MEMORY
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Struct> */
|
||||
@ -360,8 +360,6 @@ FT_BEGIN_HEADER
|
||||
|
||||
} TT_HdmxRec, *TT_Hdmx;
|
||||
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Struct> */
|
||||
@ -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. */
|
||||
|
@ -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++ )
|
||||
{
|
||||
|
@ -67,7 +67,7 @@
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Function> */
|
||||
/* Get_Kerning */
|
||||
/* cff_get_kerning */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* 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 */
|
||||
};
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <ft2build.h>
|
||||
#include "ttload.c"
|
||||
#include "ttcmap.c"
|
||||
#include "ttkern.c"
|
||||
#include "sfobjs.c"
|
||||
#include "sfdriver.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 )
|
||||
|
479
src/sfnt/ttkern.c
Normal file
479
src/sfnt/ttkern.c
Normal file
@ -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 <ft2build.h>
|
||||
#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 */
|
55
src/sfnt/ttkern.h
Normal file
55
src/sfnt/ttkern.h
Normal file
@ -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 <ft2build.h>
|
||||
#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 */
|
@ -800,6 +800,82 @@
|
||||
/* <Return> */
|
||||
/* 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 );
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Function> */
|
||||
/* tt_face_load_kern */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* 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. */
|
||||
/* */
|
||||
/* <Input> */
|
||||
/* face :: A handle to the target face object. */
|
||||
/* */
|
||||
/* stream :: The input stream. */
|
||||
/* */
|
||||
/* <Return> */
|
||||
/* 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
|
||||
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Function> */
|
||||
@ -1811,6 +1703,75 @@
|
||||
/* <Return> */
|
||||
/* 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 @@
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Function> */
|
||||
/* tt_face_free_hdmx */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* Frees the horizontal device metrics table. */
|
||||
/* */
|
||||
/* <Input> */
|
||||
/* 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 */
|
||||
|
@ -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 );
|
||||
|
@ -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
|
||||
|
@ -69,7 +69,7 @@
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Function> */
|
||||
/* Get_Kerning */
|
||||
/* tt_get_kerning */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* 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 */
|
||||
};
|
||||
|
@ -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 */
|
||||
|
@ -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 );
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user