Added a new function: AddNewEvent, which only adds an event to the list if it doesn't already exist. Kept the old one as it might me useful to be able to add an event more than once.
/***************************************************************************/
/* */
/* gxvmort.c */
/* */
/* TrueTypeGX/AAT mort table validation (body). */
/* */
/* Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/* 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. */
/* */
/***************************************************************************/
/***************************************************************************/
/* */
/* gxvalid is derived from both gxlayout module and otvalid module. */
/* Development of gxlayout is supported by the Information-technology */
/* Promotion Agency(IPA), Japan. */
/* */
/***************************************************************************/
#include "gxvmort.h"
#include "gxvfeat.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_gxvmort
static void
gxv_mort_feature_validate( GXV_mort_feature f,
GXV_Validator valid )
{
if ( f->featureType >= gxv_feat_registry_length )
{
GXV_TRACE(( "featureType %d is out of registered range, "
"setting %d is unchecked\n",
f->featureType, f->featureSetting ));
if ( valid->root->level >= FT_VALIDATE_PARANOID )
FT_INVALID_DATA;
}
else if ( !gxv_feat_registry[f->featureType].existence )
{
GXV_TRACE(( "featureType %d is within registered area "
"but undefined, setting %d is unchecked\n",
f->featureType, f->featureSetting ));
if ( valid->root->level >= FT_VALIDATE_PARANOID )
FT_INVALID_DATA;
}
else
{
FT_Byte nSettings_max;
/* nSettings in gxvfeat.c is halved for exclusive on/off settings */
nSettings_max = gxv_feat_registry[f->featureType].nSettings;
if ( gxv_feat_registry[f->featureType].exclusive )
nSettings_max = (FT_Byte)( 2 * nSettings_max );
GXV_TRACE(( "featureType %d is registered", f->featureType ));
GXV_TRACE(( "setting %d", f->featureSetting ));
if ( f->featureSetting > nSettings_max )
{
GXV_TRACE(( "out of defined range %d", nSettings_max ));
if ( valid->root->level >= FT_VALIDATE_PARANOID )
FT_INVALID_DATA;
}
GXV_TRACE(( "\n" ));
}
/* TODO: enableFlags must be unique value in specified chain? */
}
/*
* nFeatureFlags is typed to FT_ULong to accept that in
* mort (typed FT_UShort) and morx (typed FT_ULong).
*/
FT_LOCAL_DEF( void )
gxv_mort_featurearray_validate( FT_Bytes table,
FT_Bytes limit,
FT_ULong nFeatureFlags,
GXV_Validator valid )
{
FT_Bytes p = table;
FT_ULong i;
GXV_mort_featureRec f = GXV_MORT_FEATURE_OFF;
GXV_NAME_ENTER( "mort feature list" );
for ( i = 0; i < nFeatureFlags; i++ )
{
GXV_LIMIT_CHECK( 2 + 2 + 4 + 4 );
f.featureType = FT_NEXT_USHORT( p );
f.featureSetting = FT_NEXT_USHORT( p );
f.enableFlags = FT_NEXT_ULONG( p );
f.disableFlags = FT_NEXT_ULONG( p );
gxv_mort_feature_validate( &f, valid );
}
if ( !IS_GXV_MORT_FEATURE_OFF( f ) )
FT_INVALID_DATA;
valid->subtable_length = p - table;
GXV_EXIT;
}
FT_LOCAL_DEF( void )
gxv_mort_coverage_validate( FT_UShort coverage,
GXV_Validator valid )
{
FT_UNUSED( valid );
if ( coverage & 0x8000U )
GXV_TRACE(( " this subtable is for vertical text only\n" ));
else
GXV_TRACE(( " this subtable is for horizontal text only\n" ));
if ( coverage & 0x4000 )
GXV_TRACE(( " this subtable is applied to glyph array "
"in descending order\n" ));
else
GXV_TRACE(( " this subtable is applied to glyph array "
"in ascending order\n" ));
if ( coverage & 0x2000 )
GXV_TRACE(( " this subtable is forcibly applied to "
"vertical/horizontal text\n" ));
if ( coverage & 0x1FF8 )
GXV_TRACE(( " coverage has non-zero bits in reserved area\n" ));
}
static void
gxv_mort_subtables_validate( FT_Bytes table,
FT_Bytes limit,
FT_UShort nSubtables,
GXV_Validator valid )
{
FT_Bytes p = table;
GXV_Validate_Func fmt_funcs_table[] =
{
gxv_mort_subtable_type0_validate, /* 0 */
gxv_mort_subtable_type1_validate, /* 1 */
gxv_mort_subtable_type2_validate, /* 2 */
NULL, /* 3 */
gxv_mort_subtable_type4_validate, /* 4 */
gxv_mort_subtable_type5_validate, /* 5 */
};
GXV_Validate_Func func;
FT_UShort i;
GXV_NAME_ENTER( "subtables in a chain" );
for ( i = 0; i < nSubtables; i++ )
{
FT_UShort length;
FT_UShort coverage;
FT_ULong subFeatureFlags;
FT_UInt type;
FT_UInt rest;
GXV_LIMIT_CHECK( 2 + 2 + 4 );
length = FT_NEXT_USHORT( p );
coverage = FT_NEXT_USHORT( p );
subFeatureFlags = FT_NEXT_ULONG( p );
GXV_TRACE(( "validating chain subtable %d/%d (%d bytes)\n",
i + 1, nSubtables, length ));
type = coverage & 0x0007;
rest = length - ( 2 + 2 + 4 );
GXV_LIMIT_CHECK( rest );
gxv_mort_coverage_validate( coverage, valid );
if ( type > 5 )
FT_INVALID_FORMAT;
func = fmt_funcs_table[type];
if ( func == NULL )
GXV_TRACE(( "morx type %d is reserved\n", type ));
func( p, p + rest, valid );
p += rest;
}
valid->subtable_length = p - table;
GXV_EXIT;
}
static void
gxv_mort_chain_validate( FT_Bytes table,
FT_Bytes limit,
GXV_Validator valid )
{
FT_Bytes p = table;
FT_ULong defaultFlags;
FT_ULong chainLength;
FT_UShort nFeatureFlags;
FT_UShort nSubtables;
GXV_NAME_ENTER( "mort chain header" );
GXV_LIMIT_CHECK( 4 + 4 + 2 + 2 );
defaultFlags = FT_NEXT_ULONG( p );
chainLength = FT_NEXT_ULONG( p );
nFeatureFlags = FT_NEXT_USHORT( p );
nSubtables = FT_NEXT_USHORT( p );
gxv_mort_featurearray_validate( p, table + chainLength,
nFeatureFlags, valid );
p += valid->subtable_length;
gxv_mort_subtables_validate( p, table + chainLength, nSubtables, valid );
valid->subtable_length = chainLength;
GXV_EXIT;
}
FT_LOCAL_DEF( void )
gxv_mort_validate( FT_Bytes table,
FT_Face face,
FT_Validator ftvalid )
{
GXV_ValidatorRec validrec;
GXV_Validator valid = &validrec;
FT_Bytes p = table;
FT_Bytes limit = 0;
FT_ULong version;
FT_ULong nChains;
FT_ULong i;
valid->root = ftvalid;
valid->face = face;
limit = valid->root->limit;
FT_TRACE3(( "validating `mort' table\n" ));
GXV_INIT;
GXV_LIMIT_CHECK( 4 + 4 );
version = FT_NEXT_ULONG( p );
nChains = FT_NEXT_ULONG( p );
if (version != 0x00010000UL)
FT_INVALID_FORMAT;
for ( i = 0; i < nChains; i++ )
{
GXV_TRACE(( "validating chain %d/%d\n", i + 1, nChains ));
GXV_32BIT_ALIGNMENT_VALIDATE( p - table );
gxv_mort_chain_validate( p, limit, valid );
p += valid->subtable_length;
}
FT_TRACE4(( "\n" ));
}
/* END */