1035 lines
32 KiB
C
1035 lines
32 KiB
C
/***************************************************************************/
|
||
/* */
|
||
/* gxaccess.c */
|
||
/* */
|
||
/* AAT/TrueTypeGX private data accessor implementation(body). */
|
||
/* */
|
||
/* Copyright 2003 by */
|
||
/* Masatake YAMATO and Redhat K.K. */
|
||
/* */
|
||
/* This file 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. */
|
||
/* */
|
||
/***************************************************************************/
|
||
|
||
/***************************************************************************/
|
||
/* Development of the code in this file is support of */
|
||
/* Information-technology Promotion Agency, Japan. */
|
||
/***************************************************************************/
|
||
|
||
#include <ft2build.h>
|
||
#include FT_INTERNAL_DEBUG_H
|
||
#include FT_INTERNAL_CALC_H
|
||
#include "gxlookuptbl.h"
|
||
#include "gxstatetbl.h"
|
||
#include "gxutils.h"
|
||
#include "gxaccess.h"
|
||
#include "gxobjs.h"
|
||
#include "gxerrors.h"
|
||
#include "gxvm.h"
|
||
#include "gxltypes.h"
|
||
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/**** ****/
|
||
/**** ****/
|
||
/**** Features ****/
|
||
/**** ****/
|
||
/**** ****/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
|
||
|
||
FT_LOCAL_DEF ( FT_Bool )
|
||
gx_feat_has_feature_type ( GX_Feat feat, FT_UShort feature_type )
|
||
{
|
||
FT_Int i;
|
||
for ( i = 0; i < feat->featureNameCount; i++ )
|
||
{
|
||
if ( feat->names[i].feature == feature_type )
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/**** ****/
|
||
/**** ****/
|
||
/**** Glyph Properties ****/
|
||
/**** ****/
|
||
/**** ****/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
|
||
FT_LOCAL_DEF ( FT_UShort )
|
||
gx_prop_get( GX_Prop prop, FT_Long glyph )
|
||
{
|
||
GX_LookupTable lookup_table = &prop->lookup_data;
|
||
FT_UShort default_properties = prop->default_properties;
|
||
FT_UShort properties = 0;
|
||
GX_LookupResultRec result;
|
||
FT_UShort * segment_array;
|
||
FT_Long index_in_segment;
|
||
|
||
|
||
result = gx_LookupTable_lookup( lookup_table, glyph );
|
||
|
||
if ( result.value == NULL )
|
||
properties = default_properties;
|
||
else if ( result.firstGlyph == GX_LOOKUP_RESULT_NO_FIRST_GLYPH )
|
||
properties = result.value->raw.s;
|
||
else
|
||
{
|
||
index_in_segment = glyph - result.firstGlyph;
|
||
segment_array = result.value->extra.word;
|
||
properties = segment_array[index_in_segment];
|
||
}
|
||
return properties;
|
||
}
|
||
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/**** ****/
|
||
/**** ****/
|
||
/**** Ligature Carret ****/
|
||
/**** ****/
|
||
/**** ****/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
|
||
FT_LOCAL_DEF ( GX_LigCaretClassEntry )
|
||
gx_lcar_get ( GX_Lcar lcar, FT_UShort glyphID )
|
||
{ /* TODO: We could put cache mechanism here. */
|
||
GX_LookupResultRec result;
|
||
|
||
result = gx_LookupTable_lookup ( &lcar->lookup, glyphID );
|
||
if ( result.value == NULL )
|
||
return NULL;
|
||
else if ( result.firstGlyph == GX_LOOKUP_RESULT_NO_FIRST_GLYPH )
|
||
return result.value->extra.lcar_class_entry;
|
||
else
|
||
return &result.value->extra.lcar_segment->class_entry[glyphID - result.firstGlyph];
|
||
}
|
||
|
||
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/**** ****/
|
||
/**** ****/
|
||
/**** Glyph Metamorphosis ****/
|
||
/**** ****/
|
||
/**** ****/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
|
||
#undef FT_COMPONENT
|
||
#define FT_COMPONENT trace_gxchain
|
||
|
||
FT_LOCAL_DEF ( FT_Error )
|
||
gx_mort_foreach_feature ( GX_Mort mort, GX_Mort_Feature_Func func, FT_Pointer user )
|
||
{
|
||
FT_Error error = GX_Err_Ok;
|
||
FT_Int i, j;
|
||
GX_MetamorphosisChain chain;
|
||
GX_MetamorphosisFeatureTable feat_Subtbl;
|
||
for ( i = 0; i < mort->nChains; i++ )
|
||
{
|
||
chain = &mort->chain[i];
|
||
for ( j = 0; j < chain->header.nFeatureEntries; j++ )
|
||
{
|
||
feat_Subtbl = &chain->feat_Subtbl[j];
|
||
if (( error = func ( feat_Subtbl, user ) ))
|
||
return error;
|
||
}
|
||
}
|
||
return error;
|
||
}
|
||
|
||
#define GX_MORT_COUNT_FEAT_DATA_ZERO {0, NULL}
|
||
typedef struct gx_mort_count_feat_data_rec_
|
||
{
|
||
FT_UShort count;
|
||
GX_Feat feat;
|
||
} gx_mort_count_feat_data_rec, *gx_mort_count_feat_data;
|
||
|
||
static FT_Error
|
||
gx_mort_count_feat_not_in_feat_cb ( GX_MetamorphosisFeatureTable feat_Subtbl, FT_Pointer user )
|
||
{
|
||
gx_mort_count_feat_data data = user;
|
||
FT_UShort featureType = feat_Subtbl->featureType;
|
||
if ( !gx_feat_has_feature_type ( data->feat, featureType ) )
|
||
data->count++;
|
||
return GX_Err_Ok;
|
||
}
|
||
|
||
FT_LOCAL_DEF ( FT_UShort )
|
||
gx_mort_count_feat_not_in_feat ( GX_Mort mort, GX_Feat feat )
|
||
{
|
||
gx_mort_count_feat_data_rec data;
|
||
data.count = 0;
|
||
data.feat = feat;
|
||
gx_mort_foreach_feature ( mort, gx_mort_count_feat_not_in_feat_cb, &data );
|
||
return data.count;
|
||
}
|
||
|
||
static FT_ULong
|
||
gx_chain_calc_selector ( GX_MetamorphosisChain chain, GXL_FeaturesRequest request)
|
||
{
|
||
FT_ULong j_features;
|
||
FT_ULong result;
|
||
GX_MetamorphosisFeatureTable feat_Subtbl;
|
||
GXL_Feature feature;
|
||
|
||
result = chain->header.defaultFlags;
|
||
for ( j_features = 0; j_features < chain->header.nFeatureEntries; j_features++ )
|
||
{
|
||
feat_Subtbl = &chain->feat_Subtbl[j_features];
|
||
feature = gxl_features_request_get_feature_by_type(request,
|
||
feat_Subtbl->featureType);
|
||
if ( !feature )
|
||
continue ;
|
||
|
||
if ( gxl_feature_get_setting_by_value(feature,
|
||
feat_Subtbl->featureSetting ) )
|
||
{
|
||
result &= feat_Subtbl->disableFlags;
|
||
result |= feat_Subtbl->enableFlags ;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
FT_LOCAL_DEF( FT_Error )
|
||
gx_mort_substitute_glyph ( GX_Mort mort,
|
||
GXL_FeaturesRequest request,
|
||
FTL_GlyphArray in,
|
||
FTL_GlyphArray out )
|
||
{
|
||
FT_Error error = GX_Err_Ok;
|
||
GX_MetamorphosisChain chain;
|
||
FT_ULong i_chain, k_subtbl;
|
||
FT_ULong selector;
|
||
GX_MetamorphosisSubtable chain_Subtbl;
|
||
FT_UShort coverage;
|
||
FT_UShort subtable_type;
|
||
GXL_Order order;
|
||
|
||
if (( error = FTL_Copy_Glyphs_Array ( in, out ) ))
|
||
return error;
|
||
|
||
|
||
for ( i_chain = 0; i_chain < mort->nChains; i_chain++ )
|
||
{
|
||
chain = &mort->chain[i_chain];
|
||
selector = gx_chain_calc_selector( chain, request );
|
||
|
||
FT_TRACE2(( "Mort Chain No.%d, Address 0x%p\n", i_chain, chain ));
|
||
|
||
for ( k_subtbl = 0; k_subtbl < chain->header.nSubtables; k_subtbl++ )
|
||
{
|
||
chain_Subtbl = &chain->chain_Subtbl[k_subtbl];
|
||
if ( !(chain_Subtbl->header.subFeatureFlags & selector) )
|
||
continue;
|
||
|
||
coverage = chain_Subtbl->header.coverage;
|
||
if ( ( !(coverage & GX_MORT_COVERAGE_ORIENTATION_INDEPENDENT) )
|
||
/* Orientation dependent */
|
||
&& ((( coverage & GX_MORT_COVERAGE_HORIZONTAL_OR_VERTICAL_TEXT )
|
||
/* Vertical only */
|
||
&& (FTL_Get_FeaturesRequest_Direction(&request->root) != FTL_VERTICAL)
|
||
/* But the request is NOT vertical */)
|
||
|| (!( coverage & GX_MORT_COVERAGE_HORIZONTAL_OR_VERTICAL_TEXT )
|
||
/* Horizontal only */
|
||
&& (FTL_Get_FeaturesRequest_Direction(&request->root) != FTL_HORIZONTAL)
|
||
/* But the request is NOT horizontal */)))
|
||
continue;
|
||
|
||
order = (coverage & GX_MORT_COVERAGE_ORDER_OF_PROCESSING_GLYPH_ARRAY)
|
||
? GXL_DESCENDING
|
||
: GXL_ASCENDING;
|
||
if ( order == GXL_DESCENDING )
|
||
gx_glyphs_array_reverse( out->glyphs, out->length );
|
||
|
||
subtable_type = coverage & GX_MORT_COVERAGE_SUBTABLE_TYPE;
|
||
FT_TRACE2(( "\tSubtable No.%d, Address 0x%p, Type %d\n",
|
||
k_subtbl, chain_Subtbl, subtable_type ));
|
||
|
||
switch ( subtable_type )
|
||
{
|
||
case GX_MORT_REARRANGEMENT_SUBTABLE:
|
||
gx_rearrangement_subst( chain_Subtbl->body.rearrangement,
|
||
request->initial_state,
|
||
out );
|
||
break;
|
||
case GX_MORT_CONTEXTUAL_SUBTABLE:
|
||
error = gx_contextual_subst( chain_Subtbl->body.contextual,
|
||
request->initial_state,
|
||
out );
|
||
break;
|
||
case GX_MORT_LIGATURE_SUBTABLE:
|
||
error = gx_ligature_subst( chain_Subtbl->body.ligature,
|
||
request->initial_state,
|
||
out );
|
||
break;
|
||
case GX_MORT_RESERVED_SUBTABLE:
|
||
FT_ERROR(("Reserved\n"));
|
||
break;
|
||
case GX_MORT_NONCONTEXTUAL_SUBTABLE:
|
||
error = gx_xnoncontextual_subst( chain_Subtbl->body.noncontextual, out );
|
||
break;
|
||
case GX_MORT_INSERTION_SUBTABLE:
|
||
error = gx_insertion_subst( chain_Subtbl->body.insertion,
|
||
request->initial_state,
|
||
out );
|
||
break;
|
||
}
|
||
|
||
if ( order == GXL_DESCENDING )
|
||
gx_glyphs_array_reverse( out->glyphs, out->length );
|
||
}
|
||
}
|
||
return error;
|
||
}
|
||
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/**** ****/
|
||
/**** ****/
|
||
/**** Extended Glyph Metamorphosis ****/
|
||
/**** ****/
|
||
/**** ****/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
|
||
FT_LOCAL_DEF ( FT_Error )
|
||
gx_morx_foreach_feature ( GX_Morx morx, GX_Morx_Feature_Func func, FT_Pointer user )
|
||
{
|
||
FT_Error error = GX_Err_Ok;
|
||
FT_Int i, j;
|
||
GX_XMetamorphosisChain chain;
|
||
GX_XMetamorphosisFeatureTable feat_Subtbl;
|
||
for ( i = 0; i < morx->nChains; i++ )
|
||
{
|
||
chain = &morx->chain[i];
|
||
for ( j = 0; j < chain->header.nFeatureEntries; j++ )
|
||
{
|
||
feat_Subtbl = &chain->feat_Subtbl[j];
|
||
if (( error = func ( feat_Subtbl, user ) ))
|
||
return error;
|
||
|
||
}
|
||
}
|
||
return error;
|
||
}
|
||
|
||
#define GX_MORT_COUNT_FEAT_DATA_ZERO {0, NULL}
|
||
typedef struct gx_morx_count_feat_data_rec_
|
||
{
|
||
FT_UShort count;
|
||
GX_Feat feat;
|
||
} gx_morx_count_feat_data_rec, *gx_morx_count_feat_data;
|
||
|
||
static FT_Error
|
||
gx_morx_count_feat_not_in_feat_cb ( GX_MetamorphosisFeatureTable feat_Subtbl, FT_Pointer user )
|
||
{
|
||
gx_morx_count_feat_data data = user;
|
||
FT_UShort featureType = feat_Subtbl->featureType;
|
||
if ( !gx_feat_has_feature_type ( data->feat, featureType ) )
|
||
data->count++;
|
||
return GX_Err_Ok;
|
||
}
|
||
|
||
FT_LOCAL_DEF ( FT_UShort )
|
||
gx_morx_count_feat_not_in_feat ( GX_Morx morx, GX_Feat feat )
|
||
{
|
||
gx_morx_count_feat_data_rec data;
|
||
data.count = 0;
|
||
data.feat = feat;
|
||
gx_morx_foreach_feature ( morx, gx_morx_count_feat_not_in_feat_cb, &data );
|
||
return data.count;
|
||
}
|
||
|
||
static FT_ULong
|
||
gx_xchain_calc_selector ( GX_XMetamorphosisChain chain, GXL_FeaturesRequest request)
|
||
{
|
||
FT_ULong j_features;
|
||
FT_ULong result;
|
||
GX_MetamorphosisFeatureTable feat_Subtbl;
|
||
GXL_Feature feature;
|
||
|
||
result = chain->header.defaultFlags;
|
||
for ( j_features = 0; j_features < chain->header.nFeatureEntries; j_features++ )
|
||
{
|
||
feat_Subtbl = &chain->feat_Subtbl[j_features];
|
||
feature = gxl_features_request_get_feature_by_type(request,
|
||
feat_Subtbl->featureType);
|
||
if ( !feature )
|
||
continue ;
|
||
|
||
if ( gxl_feature_get_setting_by_value(feature,
|
||
feat_Subtbl->featureSetting ) )
|
||
{
|
||
result &= feat_Subtbl->disableFlags;
|
||
result |= feat_Subtbl->enableFlags;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
FT_LOCAL_DEF( FT_Error )
|
||
gx_morx_substitute_glyph ( GX_Morx morx,
|
||
GXL_FeaturesRequest request,
|
||
FTL_GlyphArray in,
|
||
FTL_GlyphArray out )
|
||
{
|
||
FT_Error error = GX_Err_Ok;
|
||
GX_XMetamorphosisChain xchain;
|
||
FT_ULong i_chain, k_subtbl;
|
||
FT_ULong selector;
|
||
GX_XMetamorphosisSubtable xchain_Subtbl;
|
||
FT_UShort coverage;
|
||
FT_UShort subtable_type;
|
||
GXL_Order order;
|
||
|
||
if (( error = FTL_Copy_Glyphs_Array ( in, out ) ))
|
||
return error;
|
||
|
||
for ( i_chain = 0; i_chain < morx->nChains; i_chain++ )
|
||
{
|
||
xchain = &morx->chain[i_chain];
|
||
selector = gx_xchain_calc_selector( xchain, request );
|
||
|
||
FT_TRACE2(( "Morx Chain No.%d, Address 0x%p\n", i_chain, xchain ));
|
||
|
||
for ( k_subtbl = 0; k_subtbl < xchain->header.nSubtables; k_subtbl++ )
|
||
{
|
||
xchain_Subtbl = &xchain->chain_Subtbl[k_subtbl];
|
||
if ( !(xchain_Subtbl->header.subFeatureFlags & selector) )
|
||
continue;
|
||
|
||
coverage = xchain_Subtbl->header.coverage;
|
||
if ( ( !(coverage & GX_MORX_COVERAGE_ORIENTATION_INDEPENDENT) )
|
||
/* Orientation dependent */
|
||
&& ((( coverage & GX_MORX_COVERAGE_HORIZONTAL_OR_VERTICAL_TEXT )
|
||
/* Vertical only */
|
||
&& (FTL_Get_FeaturesRequest_Direction(&request->root) != FTL_VERTICAL)
|
||
/* But the request is NOT vertical */)
|
||
|| (!( coverage & GX_MORX_COVERAGE_HORIZONTAL_OR_VERTICAL_TEXT )
|
||
/* Horizontal only */
|
||
&& (FTL_Get_FeaturesRequest_Direction(&request->root) != FTL_HORIZONTAL)
|
||
/* But the request is NOT horizontal */)))
|
||
continue;
|
||
order = (coverage & GX_MORX_COVERAGE_ORDER_OF_PROCESSING_GLYPH_ARRAY)
|
||
? GXL_DESCENDING
|
||
: GXL_ASCENDING;
|
||
if ( order == GXL_DESCENDING )
|
||
gx_glyphs_array_reverse( out->glyphs, out->length );
|
||
|
||
subtable_type = coverage & GX_MORX_COVERAGE_SUBTABLE_TYPE;
|
||
FT_TRACE2(( "\tSubtable No.%d, Address 0x%p, Type %d\n",
|
||
k_subtbl, xchain_Subtbl, subtable_type ));
|
||
switch ( subtable_type )
|
||
{
|
||
case GX_MORX_REARRANGEMENT_SUBTABLE:
|
||
error = gx_xrearrangement_subst( xchain_Subtbl->body.rearrangement,
|
||
request->initial_state,
|
||
out );
|
||
break;
|
||
case GX_MORX_CONTEXTUAL_SUBTABLE:
|
||
error = gx_xcontextual_subst( xchain_Subtbl->body.contextual,
|
||
request->initial_state,
|
||
out );
|
||
break;
|
||
case GX_MORX_LIGATURE_SUBTABLE:
|
||
error = gx_xligature_subst( xchain_Subtbl->body.ligature,
|
||
request->initial_state,
|
||
out );
|
||
break;
|
||
case GX_MORX_RESERVED_SUBTABLE:
|
||
FT_ERROR(("Reserved format\n"));
|
||
break;
|
||
case GX_MORX_NONCONTEXTUAL_SUBTABLE:
|
||
error = gx_xnoncontextual_subst( xchain_Subtbl->body.noncontextual, out );
|
||
break;
|
||
case GX_MORX_INSERTION_SUBTABLE:
|
||
error = gx_xinsertion_subst ( xchain_Subtbl->body.insertion,
|
||
request->initial_state,
|
||
out );
|
||
break;
|
||
}
|
||
if ( order == GXL_DESCENDING )
|
||
gx_glyphs_array_reverse( out->glyphs, out->length );
|
||
}
|
||
}
|
||
return FT_Err_Ok;
|
||
}
|
||
|
||
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/**** ****/
|
||
/**** ****/
|
||
/**** Kerning ****/
|
||
/**** ****/
|
||
/**** ****/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
|
||
#undef PAIR_TAG
|
||
#define PAIR_TAG( left, right ) ( ( (FT_ULong)left << 16 ) | \
|
||
(FT_ULong)right )
|
||
|
||
static FT_Pos gx_kern_get_fmt0( GX_KerningSubtableFormat0Body fmt0,
|
||
FT_UShort left_glyph,
|
||
FT_UShort right_glyph );
|
||
static int gx_kern_fmt0_compar ( const void * a, const void * b);
|
||
|
||
static FT_Pos gx_kern_get_fmt1( GX_KerningSubtableFormat1Body fmt1,
|
||
FT_UShort left_glyph,
|
||
FT_UShort right_glyph );
|
||
static FT_Pos gx_kern_get_fmt2( GX_KerningSubtableFormat2Body fmt2,
|
||
FT_UShort left_glyph,
|
||
FT_UShort right_glyph );
|
||
static FT_Pos gx_kern_get_fmt3( GX_KerningSubtableFormat3Body fmt3,
|
||
FT_UShort left_glyph,
|
||
FT_UShort right_glyph );
|
||
|
||
FT_LOCAL_DEF( FT_Error )
|
||
gx_kern_get_pair_kerning ( GX_Kern kern,
|
||
FT_UShort left_glyph,
|
||
FT_UShort right_glyph,
|
||
FTL_Direction dir,
|
||
FT_Vector* kerning )
|
||
{
|
||
FT_Error error = GX_Err_Ok;
|
||
FT_ULong i;
|
||
GX_KerningSubtable subtable;
|
||
GX_KerningSubtableHeader header;
|
||
FT_UShort coverage;
|
||
GX_KerningFormat fmt;
|
||
GX_KerningSubtableBody body;
|
||
FT_Pos * value;
|
||
|
||
kerning->x = 0;
|
||
kerning->y = 0;
|
||
|
||
for ( i = 0; i < kern->nTables; i++ )
|
||
{
|
||
subtable = &(kern->subtables[i]);
|
||
header = &subtable->header;
|
||
coverage = header->coverage;
|
||
if ( coverage & GX_KERN_COVERAGE_VERTICAL )
|
||
{
|
||
if ( dir != FTL_VERTICAL )
|
||
continue;
|
||
if ( coverage & GX_KERN_COVERAGE_CROSS_STREAM )
|
||
value = &kerning->x;
|
||
else
|
||
value = &kerning->y;
|
||
}
|
||
else
|
||
{
|
||
if ( dir != FTL_HORIZONTAL )
|
||
continue;
|
||
if ( coverage & GX_KERN_COVERAGE_CROSS_STREAM )
|
||
value = &kerning->y;
|
||
else
|
||
value = &kerning->x;
|
||
}
|
||
|
||
fmt = coverage & GX_KERN_COVERAGE_FORMAT_MASK;
|
||
body = &subtable->body;
|
||
switch ( fmt )
|
||
{
|
||
case GX_KERN_FMT_ORDERED_LIST_OF_KERNING_PAIRS:
|
||
*value += gx_kern_get_fmt0 ( body->fmt0, left_glyph, right_glyph );
|
||
break;
|
||
case GX_KERN_FMT_STATE_TABLE_FOR_CONTEXTUAL_KERNING:
|
||
*value += gx_kern_get_fmt1 ( body->fmt1, left_glyph, right_glyph );
|
||
break;
|
||
case GX_KERN_FMT_SIMPLE_NXM_ARRAY_OF_KERNING_VALUES:
|
||
*value += gx_kern_get_fmt2 ( body->fmt2, left_glyph, right_glyph );
|
||
break;
|
||
case GX_KERN_FMT_SIMPLE_NXM_ARRAY_OF_KERNING_INDICES:
|
||
*value += gx_kern_get_fmt3 ( body->fmt3, left_glyph, right_glyph );
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
return error;
|
||
}
|
||
|
||
static FT_Pos
|
||
gx_kern_get_fmt0( GX_KerningSubtableFormat0Body fmt0,
|
||
FT_UShort left_glyph,
|
||
FT_UShort right_glyph )
|
||
{
|
||
GX_KerningSubtableFormat0Entry entry;
|
||
FT_ULong search_tag = PAIR_TAG( left_glyph, right_glyph );
|
||
if ( !fmt0->nPairs )
|
||
return 0;
|
||
|
||
entry = ft_bsearch(&search_tag,
|
||
fmt0->entries,
|
||
fmt0->nPairs,
|
||
sizeof(*fmt0->entries),
|
||
gx_kern_fmt0_compar);
|
||
|
||
if ( entry )
|
||
return entry->value;
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
gx_kern_fmt0_compar ( const void * search_tag, const void * entry)
|
||
{
|
||
FT_ULong tag;
|
||
tag = PAIR_TAG( ((GX_KerningSubtableFormat0Entry)entry)->left,
|
||
((GX_KerningSubtableFormat0Entry)entry)->right );
|
||
if ( *(FT_ULong*)search_tag < tag )
|
||
return -1;
|
||
else if ( *(FT_ULong*)search_tag > tag )
|
||
return 1;
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
static FT_Pos
|
||
gx_kern_get_fmt1( GX_KerningSubtableFormat1Body fmt1,
|
||
FT_UShort left_glyph, FT_UShort right_glyph )
|
||
{
|
||
return 0; /* Do nothing */
|
||
}
|
||
|
||
static FT_Pos
|
||
gx_kern_get_fmt2( GX_KerningSubtableFormat2Body fmt2,
|
||
FT_UShort left_glyph, FT_UShort right_glyph )
|
||
{
|
||
GX_KerningSubtableFormat2ClassTable left_class, right_class;
|
||
FT_UShort left_index, right_index;
|
||
FT_Byte kern_index;
|
||
|
||
left_class = &fmt2->leftClass;
|
||
right_class = &fmt2->rightClass;
|
||
|
||
if (( left_class->firstGlyph > left_glyph )
|
||
|| ( !( left_class->firstGlyph + left_class->nGlyphs > left_glyph ) ))
|
||
return 0;
|
||
if (( right_class->firstGlyph > right_glyph )
|
||
|| ( !( right_class->firstGlyph + right_class->nGlyphs > right_glyph ) ))
|
||
return 0;
|
||
|
||
left_index = left_glyph - left_class->firstGlyph;
|
||
right_index = right_glyph - right_class->firstGlyph;
|
||
|
||
FT_ASSERT( left_index < left_class->max_class );
|
||
FT_ASSERT( right_index < right_class->max_class );
|
||
|
||
kern_index = left_class->classes[left_index]
|
||
+ right_class->classes[right_index];
|
||
return fmt2->values[kern_index];
|
||
}
|
||
|
||
static FT_Pos
|
||
gx_kern_get_fmt3( GX_KerningSubtableFormat3Body fmt3,
|
||
FT_UShort left_glyph, FT_UShort right_glyph )
|
||
{
|
||
FT_Byte left_class, right_class;
|
||
FT_Byte kern_index;
|
||
|
||
if ( !( fmt3->glyphCount > left_glyph ) &&
|
||
( fmt3->glyphCount > right_glyph ) )
|
||
return 0;
|
||
|
||
left_class = fmt3->leftClass[left_glyph];
|
||
right_class = fmt3->rightClass[right_glyph];
|
||
kern_index = fmt3->kernIndex[left_class * fmt3->rightClassCount + right_class];
|
||
FT_ASSERT ( kern_index < fmt3->kernValueCount );
|
||
return fmt3->kernValue[kern_index];
|
||
}
|
||
|
||
FT_LOCAL_DEF( FT_Error )
|
||
gx_kern_get_contextual_kerning( GX_Kern kern,
|
||
FTL_GlyphArray garray,
|
||
FTL_Direction dir,
|
||
GXL_Initial_State initial_state,
|
||
FT_Vector * kerning )
|
||
{
|
||
GX_KerningSubtable subtable;
|
||
GX_KerningSubtableHeader header;
|
||
FT_UShort coverage;
|
||
GX_KerningFormat fmt;
|
||
GX_KerningSubtableFormat1Body fmt1;
|
||
FT_Bool cross_stream = 0;
|
||
|
||
FT_ULong i;
|
||
|
||
for ( i = 0; i < kern->nTables; i++ )
|
||
{
|
||
subtable = &(kern->subtables[i]);
|
||
header = &subtable->header;
|
||
coverage = header->coverage;
|
||
if ( coverage & GX_KERN_COVERAGE_VERTICAL )
|
||
{
|
||
if ( dir != FTL_VERTICAL )
|
||
continue;
|
||
if ( coverage & GX_KERN_COVERAGE_CROSS_STREAM )
|
||
cross_stream = 1;
|
||
}
|
||
else
|
||
{
|
||
if ( dir != FTL_HORIZONTAL )
|
||
continue;
|
||
if ( coverage & GX_KERN_COVERAGE_CROSS_STREAM )
|
||
cross_stream = 1;
|
||
}
|
||
fmt = coverage & GX_KERN_COVERAGE_FORMAT_MASK;
|
||
if ( fmt != GX_KERN_FMT_STATE_TABLE_FOR_CONTEXTUAL_KERNING )
|
||
continue;
|
||
fmt1 = subtable->body.fmt1;
|
||
gx_contextual_kerning_calc( fmt1,
|
||
garray, dir, cross_stream, initial_state,
|
||
kerning );
|
||
}
|
||
return FT_Err_Ok;
|
||
}
|
||
|
||
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/**** ****/
|
||
/**** ****/
|
||
/**** Tracking ****/
|
||
/**** ****/
|
||
/**** ****/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
/*************************************************************************/
|
||
|
||
typedef enum track_interpolation_range_ {
|
||
TRACK_NO_INTERPOLATION = 0,
|
||
TRACK_INTERPOLATION,
|
||
TRACK_EXTRAPOLATION_SMALLER,
|
||
TRACK_EXTRAPOLATION_LARGER
|
||
} track_interpolation_range;
|
||
|
||
|
||
static track_interpolation_range track_find_entry ( GX_TrackData track_data,
|
||
FT_Fixed track,
|
||
GX_TrackTableEntry * smaller_entry,
|
||
GX_TrackTableEntry * larger_entry );
|
||
static track_interpolation_range track_find_size ( GX_TrackData track_data,
|
||
FT_Fixed size,
|
||
FT_Fixed *smaller_size, FT_UShort *smaller_size_index,
|
||
FT_Fixed *larger_size, FT_UShort *larger_size_index );
|
||
|
||
static FT_FWord track_interpolate( FT_Fixed x_t,
|
||
FT_Fixed x_p, FT_Fixed x_q,
|
||
FT_FWord y_p, FT_FWord y_q );
|
||
|
||
|
||
|
||
FT_LOCAL_DEF( FT_Error )
|
||
gx_trak_get( GX_Trak trak, FT_Fixed track, FT_Fixed size, FTL_Direction dir, FT_FWord* value )
|
||
{
|
||
GX_TrackData track_data = ( dir == FTL_HORIZONTAL )
|
||
? (&trak->horizData)
|
||
: (&trak->vertData);
|
||
|
||
GX_TrackTableEntry larger_entry = NULL, smaller_entry;
|
||
FT_Fixed larger_track, smaller_track;
|
||
track_interpolation_range entry_range;
|
||
|
||
FT_UShort larger_size_index, smaller_size_index;
|
||
FT_Fixed larger_size = 0, smaller_size;
|
||
track_interpolation_range size_range;
|
||
|
||
FT_FWord a, b, c, d;
|
||
FT_FWord p, q;
|
||
|
||
if ( !track_data )
|
||
{
|
||
*value = 0;
|
||
return FT_Err_Ok;
|
||
}
|
||
|
||
|
||
entry_range = track_find_entry ( track_data, track, &smaller_entry, &larger_entry );
|
||
larger_track = larger_entry->track;
|
||
smaller_track = smaller_entry->track;
|
||
|
||
size_range = track_find_size ( track_data, size,
|
||
&smaller_size, &smaller_size_index,
|
||
&larger_size, &larger_size_index );
|
||
|
||
a = smaller_entry->tracking_value[smaller_size_index];
|
||
b = smaller_entry->tracking_value[larger_size_index];
|
||
c = larger_entry->tracking_value[smaller_size_index];
|
||
d = larger_entry->tracking_value[larger_size_index];
|
||
|
||
/* -------------------------------------------------------------------
|
||
|
||
^ size
|
||
|
|
||
B | D
|
||
|
|
||
A | C
|
||
--------------+---------------> track
|
||
O|
|
||
|
||
A = ( smaller_track, smaller_size, a )
|
||
B = ( smaller_track, larger_size, b )
|
||
C = ( larger_track, smaller_size, c )
|
||
D = ( larger_track, larger_size, d )
|
||
-----------V---------------------------
|
||
P = ( track, smaller_size, p )
|
||
Q = ( track larger_size, q )
|
||
--------------------------V------------
|
||
T = ( track, size, *value)
|
||
|
||
------------------------------------------------------------------- */
|
||
|
||
if ( entry_range == TRACK_NO_INTERPOLATION )
|
||
{
|
||
p = a;
|
||
q = b;
|
||
}
|
||
else
|
||
{
|
||
p = track_interpolate ( track,
|
||
smaller_track, larger_track,
|
||
a, c );
|
||
q = track_interpolate ( track,
|
||
smaller_track, larger_track,
|
||
b, d );
|
||
}
|
||
|
||
if ( size_range == TRACK_NO_INTERPOLATION )
|
||
*value = p;
|
||
else
|
||
*value = track_interpolate( size,
|
||
smaller_size, larger_size,
|
||
p, q );
|
||
return FT_Err_Ok;
|
||
}
|
||
|
||
static track_interpolation_range
|
||
track_find_entry ( GX_TrackData track_data,
|
||
FT_Fixed track,
|
||
GX_TrackTableEntry * smaller_entry,
|
||
GX_TrackTableEntry * larger_entry )
|
||
{
|
||
FT_UShort i;
|
||
track_interpolation_range range;
|
||
FT_Bool extrapolation = FALSE;
|
||
|
||
if ( track < track_data->trackTable[0].track )
|
||
{
|
||
i = 0;
|
||
range = TRACK_EXTRAPOLATION_SMALLER;
|
||
extrapolation = TRUE;
|
||
}
|
||
else if ( track_data->trackTable[track_data->nTracks - 1].track < track )
|
||
{
|
||
i = track_data->nTracks - 2;
|
||
range = TRACK_EXTRAPOLATION_LARGER;
|
||
extrapolation = TRUE;
|
||
}
|
||
|
||
if ( extrapolation )
|
||
{
|
||
*smaller_entry = &track_data->trackTable[i];
|
||
*larger_entry = &track_data->trackTable[i + 1];
|
||
return range;
|
||
}
|
||
|
||
|
||
for ( i = 0; i < track_data->nTracks; i++ )
|
||
{
|
||
*smaller_entry = *larger_entry;
|
||
*larger_entry = &track_data->trackTable[i];
|
||
if ( track == (*larger_entry)->track )
|
||
{
|
||
*smaller_entry = *larger_entry;
|
||
return TRACK_NO_INTERPOLATION;
|
||
}
|
||
else if ( track < (*larger_entry)->track )
|
||
return TRACK_INTERPOLATION;
|
||
}
|
||
GX_ASSERT_NOT_REACHED();
|
||
return TRACK_INTERPOLATION;
|
||
}
|
||
|
||
static track_interpolation_range
|
||
track_find_size ( GX_TrackData track_data,
|
||
FT_Fixed size,
|
||
FT_Fixed *smaller_size, FT_UShort *smaller_size_index,
|
||
FT_Fixed *larger_size, FT_UShort *larger_size_index)
|
||
{
|
||
FT_UShort i;
|
||
track_interpolation_range range;
|
||
FT_Bool extrapolation = FALSE;
|
||
|
||
if ( size < track_data->sizeTable[0] )
|
||
{
|
||
i = 0;
|
||
range = TRACK_EXTRAPOLATION_SMALLER;
|
||
extrapolation = TRUE;
|
||
}
|
||
else if ( track_data->sizeTable[track_data->nSizes - 1] < size )
|
||
{
|
||
i = track_data->nSizes - 2;
|
||
range = TRACK_EXTRAPOLATION_LARGER;
|
||
extrapolation = TRUE;
|
||
}
|
||
if ( extrapolation )
|
||
{
|
||
*smaller_size = track_data->sizeTable[i];
|
||
*smaller_size_index = i;
|
||
*larger_size = track_data->sizeTable[i + 1];
|
||
*larger_size_index = i + 1;
|
||
return range;
|
||
}
|
||
|
||
for ( i = 0; i < track_data->nSizes; i++ )
|
||
{
|
||
*smaller_size = *larger_size;
|
||
*smaller_size_index = *larger_size_index;
|
||
*larger_size = track_data->sizeTable[i];
|
||
*larger_size_index = i;
|
||
if ( size == *larger_size )
|
||
{
|
||
*smaller_size = *larger_size;
|
||
*smaller_size_index = *larger_size_index;
|
||
return TRACK_NO_INTERPOLATION;
|
||
}
|
||
else if ( size < *larger_size )
|
||
return TRACK_INTERPOLATION;
|
||
}
|
||
GX_ASSERT_NOT_REACHED();
|
||
return TRACK_INTERPOLATION;
|
||
}
|
||
|
||
static FT_FWord
|
||
track_interpolate( FT_Fixed x_t,
|
||
FT_Fixed x_p, FT_Fixed x_q,
|
||
FT_FWord y_p, FT_FWord y_q )
|
||
{
|
||
FT_FWord y_t;
|
||
|
||
/* TODO: better to use FT_MulDiv_No_Round. */
|
||
y_t = ( y_q * ( x_t - x_p ) + y_p * ( x_q - x_t )) / (x_q - x_p);
|
||
return y_t;
|
||
}
|
||
|
||
FT_LOCAL_DEF( FT_UShort )
|
||
gx_trak_count_name_index( GX_Trak trak )
|
||
{
|
||
GX_TrackData track_data;
|
||
GX_TrackTableEntry track_table;
|
||
FT_UShort nTracks;
|
||
FT_UShort i;
|
||
|
||
FT_UShort h = 0;
|
||
FT_UShort v = 0;
|
||
|
||
/*
|
||
* Horiz
|
||
*/
|
||
track_data = &trak->horizData;
|
||
nTracks = track_data->nTracks;
|
||
for ( i = 0; i < nTracks; i++ )
|
||
{
|
||
track_table = &track_data->trackTable[i];
|
||
if ( track_table->nameIndex )
|
||
h++;
|
||
}
|
||
|
||
/*
|
||
* Vert
|
||
*/
|
||
track_data = &trak->vertData;
|
||
nTracks = track_data->nTracks;
|
||
for ( i = 0; i < nTracks; i++ )
|
||
{
|
||
track_table = &track_data->trackTable[i];
|
||
if ( track_table->nameIndex )
|
||
h++;
|
||
}
|
||
|
||
return h + v;
|
||
}
|
||
|
||
FT_LOCAL( FT_Error )
|
||
gx_trak_get_name ( GX_Trak trak,
|
||
FT_UShort index,
|
||
FT_UShort * name_index,
|
||
FTL_Direction * dir,
|
||
FT_Fixed * track )
|
||
{
|
||
GX_TrackData track_data;
|
||
GX_TrackTableEntry track_table;
|
||
FT_UShort i;
|
||
|
||
/*
|
||
* Horiz
|
||
*/
|
||
track_data = &trak->horizData;
|
||
for ( i = 0; i < track_data->nTracks; i++ )
|
||
{
|
||
track_table = &track_data->trackTable[i];
|
||
if ( track_table->nameIndex == 0 )
|
||
continue ;
|
||
|
||
if ( index == 0 )
|
||
goto Set_Values;
|
||
else
|
||
index--;
|
||
}
|
||
|
||
/*
|
||
* Vert
|
||
*/
|
||
track_data = &trak->vertData;
|
||
for ( i = 0; i < track_data->nTracks; i++ )
|
||
{
|
||
track_table = &track_data->trackTable[i];
|
||
if ( track_table->nameIndex == 0 )
|
||
continue ;
|
||
|
||
if ( index == 0 )
|
||
goto Set_Values;
|
||
else
|
||
index--;
|
||
}
|
||
|
||
return GX_Err_Invalid_Argument;
|
||
|
||
Set_Values:
|
||
*name_index = track_table->nameIndex;
|
||
*dir = ( track_data = &trak->horizData )
|
||
? FTL_HORIZONTAL
|
||
: FTL_VERTICAL;
|
||
*track = track_table->track;
|
||
return FT_Err_Ok;
|
||
}
|