/***************************************************************************/
/* */
/* otvgsub.c */
/* */
/* OpenType GSUB table validation (body). */
/* */
/* Copyright 2004, 2005, 2007 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 "otvalid.h"
#include "otvcommn.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_otvgsub
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GSUB LOOKUP TYPE 1 *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/* uses valid->glyph_count */
static void
otv_SingleSubst_validate( FT_Bytes table,
OTV_Validator valid )
{
FT_Bytes p = table;
FT_UInt SubstFormat;
OTV_NAME_ENTER( "SingleSubst" );
OTV_LIMIT_CHECK( 2 );
SubstFormat = FT_NEXT_USHORT( p );
OTV_TRACE(( " (format %d)\n", SubstFormat ));
switch ( SubstFormat )
{
case 1: /* SingleSubstFormat1 */
{
FT_Bytes Coverage;
FT_Int DeltaGlyphID;
FT_Long idx;
OTV_LIMIT_CHECK( 4 );
Coverage = table + FT_NEXT_USHORT( p );
DeltaGlyphID = FT_NEXT_SHORT( p );
otv_Coverage_validate( Coverage, valid, -1 );
idx = otv_Coverage_get_first( Coverage ) + DeltaGlyphID;
if ( idx < 0 )
FT_INVALID_DATA;
idx = otv_Coverage_get_last( Coverage ) + DeltaGlyphID;
if ( (FT_UInt)idx >= valid->glyph_count )
FT_INVALID_DATA;
}
break;
case 2: /* SingleSubstFormat2 */
{
FT_UInt Coverage, GlyphCount;
OTV_LIMIT_CHECK( 4 );
Coverage = FT_NEXT_USHORT( p );
GlyphCount = FT_NEXT_USHORT( p );
OTV_TRACE(( " (GlyphCount = %d)\n", GlyphCount ));
otv_Coverage_validate( table + Coverage, valid, GlyphCount );
OTV_LIMIT_CHECK( GlyphCount * 2 );
/* Substitute */
for ( ; GlyphCount > 0; GlyphCount-- )
if ( FT_NEXT_USHORT( p ) >= valid->glyph_count )
FT_INVALID_GLYPH_ID;
}
break;
default:
FT_INVALID_FORMAT;
}
OTV_EXIT;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GSUB LOOKUP TYPE 2 *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/* sets valid->extra1 (glyph count) */
static void
otv_MultipleSubst_validate( FT_Bytes table,
OTV_Validator valid )
{
FT_Bytes p = table;
FT_UInt SubstFormat;
OTV_NAME_ENTER( "MultipleSubst" );
OTV_LIMIT_CHECK( 2 );
SubstFormat = FT_NEXT_USHORT( p );
OTV_TRACE(( " (format %d)\n", SubstFormat ));
switch ( SubstFormat )
{
case 1:
valid->extra1 = valid->glyph_count;
OTV_NEST2( MultipleSubstFormat1, Sequence );
OTV_RUN( table, valid );
break;
default:
FT_INVALID_FORMAT;
}
OTV_EXIT;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GSUB LOOKUP TYPE 3 *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/* sets valid->extra1 (glyph count) */
static void
otv_AlternateSubst_validate( FT_Bytes table,
OTV_Validator valid )
{
FT_Bytes p = table;
FT_UInt SubstFormat;
OTV_NAME_ENTER( "AlternateSubst" );
OTV_LIMIT_CHECK( 2 );
SubstFormat = FT_NEXT_USHORT( p );
OTV_TRACE(( " (format %d)\n", SubstFormat ));
switch ( SubstFormat )
{
case 1:
valid->extra1 = valid->glyph_count;
OTV_NEST2( AlternateSubstFormat1, AlternateSet );
OTV_RUN( table, valid );
break;
default:
FT_INVALID_FORMAT;
}
OTV_EXIT;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GSUB LOOKUP TYPE 4 *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
#define LigatureFunc otv_Ligature_validate
/* uses valid->glyph_count */
static void
otv_Ligature_validate( FT_Bytes table,
OTV_Validator valid )
{
FT_Bytes p = table;
FT_UInt LigatureGlyph, CompCount;
OTV_ENTER;
OTV_LIMIT_CHECK( 4 );
LigatureGlyph = FT_NEXT_USHORT( p );
if ( LigatureGlyph >= valid->glyph_count )
FT_INVALID_DATA;
CompCount = FT_NEXT_USHORT( p );
OTV_TRACE(( " (CompCount = %d)\n", CompCount ));
if ( CompCount == 0 )
FT_INVALID_DATA;
CompCount--;
OTV_LIMIT_CHECK( CompCount * 2 ); /* Component */
/* no need to check the Component glyph indices */
OTV_EXIT;
}
static void
otv_LigatureSubst_validate( FT_Bytes table,
OTV_Validator valid )
{
FT_Bytes p = table;
FT_UInt SubstFormat;
OTV_NAME_ENTER( "LigatureSubst" );
OTV_LIMIT_CHECK( 2 );
SubstFormat = FT_NEXT_USHORT( p );
OTV_TRACE(( " (format %d)\n", SubstFormat ));
switch ( SubstFormat )
{
case 1:
OTV_NEST3( LigatureSubstFormat1, LigatureSet, Ligature );
OTV_RUN( table, valid );
break;
default:
FT_INVALID_FORMAT;
}
OTV_EXIT;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GSUB LOOKUP TYPE 5 *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/* sets valid->extra1 (lookup count) */
static void
otv_ContextSubst_validate( FT_Bytes table,
OTV_Validator valid )
{
FT_Bytes p = table;
FT_UInt SubstFormat;
OTV_NAME_ENTER( "ContextSubst" );
OTV_LIMIT_CHECK( 2 );
SubstFormat = FT_NEXT_USHORT( p );
OTV_TRACE(( " (format %d)\n", SubstFormat ));
switch ( SubstFormat )
{
case 1:
/* no need to check glyph indices/classes used as input for these */
/* context rules since even invalid glyph indices/classes return */
/* meaningful results */
valid->extra1 = valid->lookup_count;
OTV_NEST3( ContextSubstFormat1, SubRuleSet, SubRule );
OTV_RUN( table, valid );
break;
case 2:
/* no need to check glyph indices/classes used as input for these */
/* context rules since even invalid glyph indices/classes return */
/* meaningful results */
OTV_NEST3( ContextSubstFormat2, SubClassSet, SubClassRule );
OTV_RUN( table, valid );
break;
case 3:
OTV_NEST1( ContextSubstFormat3 );
OTV_RUN( table, valid );
break;
default:
FT_INVALID_FORMAT;
}
OTV_EXIT;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GSUB LOOKUP TYPE 6 *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/* sets valid->extra1 (lookup count) */
static void
otv_ChainContextSubst_validate( FT_Bytes table,
OTV_Validator valid )
{
FT_Bytes p = table;
FT_UInt SubstFormat;
OTV_NAME_ENTER( "ChainContextSubst" );
OTV_LIMIT_CHECK( 2 );
SubstFormat = FT_NEXT_USHORT( p );
OTV_TRACE(( " (format %d)\n", SubstFormat ));
switch ( SubstFormat )
{
case 1:
/* no need to check glyph indices/classes used as input for these */
/* context rules since even invalid glyph indices/classes return */
/* meaningful results */
valid->extra1 = valid->lookup_count;
OTV_NEST3( ChainContextSubstFormat1,
ChainSubRuleSet, ChainSubRule );
OTV_RUN( table, valid );
break;
case 2:
/* no need to check glyph indices/classes used as input for these */
/* context rules since even invalid glyph indices/classes return */
/* meaningful results */
OTV_NEST3( ChainContextSubstFormat2,
ChainSubClassSet, ChainSubClassRule );
OTV_RUN( table, valid );
break;
case 3:
OTV_NEST1( ChainContextSubstFormat3 );
OTV_RUN( table, valid );
break;
default:
FT_INVALID_FORMAT;
}
OTV_EXIT;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GSUB LOOKUP TYPE 7 *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/* uses valid->type_funcs */
static void
otv_ExtensionSubst_validate( FT_Bytes table,
OTV_Validator valid )
{
FT_Bytes p = table;
FT_UInt SubstFormat;
OTV_NAME_ENTER( "ExtensionSubst" );
OTV_LIMIT_CHECK( 2 );
SubstFormat = FT_NEXT_USHORT( p );
OTV_TRACE(( " (format %d)\n", SubstFormat ));
switch ( SubstFormat )
{
case 1: /* ExtensionSubstFormat1 */
{
FT_UInt ExtensionLookupType;
FT_ULong ExtensionOffset;
OTV_Validate_Func validate;
OTV_LIMIT_CHECK( 6 );
ExtensionLookupType = FT_NEXT_USHORT( p );
ExtensionOffset = FT_NEXT_ULONG( p );
if ( ExtensionLookupType == 0 ||
ExtensionLookupType == 7 ||
ExtensionLookupType > 8 )
FT_INVALID_DATA;
validate = valid->type_funcs[ExtensionLookupType - 1];
validate( table + ExtensionOffset, valid );
}
break;
default:
FT_INVALID_FORMAT;
}
OTV_EXIT;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GSUB LOOKUP TYPE 8 *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/* uses valid->glyph_count */
static void
otv_ReverseChainSingleSubst_validate( FT_Bytes table,
OTV_Validator valid )
{
FT_Bytes p = table, Coverage;
FT_UInt SubstFormat;
FT_UInt BacktrackGlyphCount, LookaheadGlyphCount, GlyphCount;
OTV_NAME_ENTER( "ReverseChainSingleSubst" );
OTV_LIMIT_CHECK( 2 );
SubstFormat = FT_NEXT_USHORT( p );
OTV_TRACE(( " (format %d)\n", SubstFormat ));
switch ( SubstFormat )
{
case 1: /* ReverseChainSingleSubstFormat1 */
OTV_LIMIT_CHECK( 4 );
Coverage = table + FT_NEXT_USHORT( p );
BacktrackGlyphCount = FT_NEXT_USHORT( p );
OTV_TRACE(( " (BacktrackGlyphCount = %d)\n", BacktrackGlyphCount ));
otv_Coverage_validate( Coverage, valid, -1 );
OTV_LIMIT_CHECK( BacktrackGlyphCount * 2 + 2 );
for ( ; BacktrackGlyphCount > 0; BacktrackGlyphCount-- )
otv_Coverage_validate( table + FT_NEXT_USHORT( p ), valid, -1 );
LookaheadGlyphCount = FT_NEXT_USHORT( p );
OTV_TRACE(( " (LookaheadGlyphCount = %d)\n", LookaheadGlyphCount ));
OTV_LIMIT_CHECK( LookaheadGlyphCount * 2 + 2 );
for ( ; LookaheadGlyphCount > 0; LookaheadGlyphCount-- )
otv_Coverage_validate( table + FT_NEXT_USHORT( p ), valid, -1 );
GlyphCount = FT_NEXT_USHORT( p );
OTV_TRACE(( " (GlyphCount = %d)\n", GlyphCount ));
if ( GlyphCount != otv_Coverage_get_count( Coverage ) )
FT_INVALID_DATA;
OTV_LIMIT_CHECK( GlyphCount * 2 );
/* Substitute */
for ( ; GlyphCount > 0; GlyphCount-- )
if ( FT_NEXT_USHORT( p ) >= valid->glyph_count )
FT_INVALID_DATA;
break;
default:
FT_INVALID_FORMAT;
}
OTV_EXIT;
}
static const OTV_Validate_Func otv_gsub_validate_funcs[8] =
{
otv_SingleSubst_validate,
otv_MultipleSubst_validate,
otv_AlternateSubst_validate,
otv_LigatureSubst_validate,
otv_ContextSubst_validate,
otv_ChainContextSubst_validate,
otv_ExtensionSubst_validate,
otv_ReverseChainSingleSubst_validate
};
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GSUB TABLE *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/* sets valid->type_count */
/* sets valid->type_funcs */
/* sets valid->glyph_count */
FT_LOCAL_DEF( void )
otv_GSUB_validate( FT_Bytes table,
FT_UInt glyph_count,
FT_Validator ftvalid )
{
OTV_ValidatorRec validrec;
OTV_Validator valid = &validrec;
FT_Bytes p = table;
FT_UInt ScriptList, FeatureList, LookupList;
valid->root = ftvalid;
FT_TRACE3(( "validating GSUB table\n" ));
OTV_INIT;
OTV_LIMIT_CHECK( 10 );
if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */
FT_INVALID_FORMAT;
ScriptList = FT_NEXT_USHORT( p );
FeatureList = FT_NEXT_USHORT( p );
LookupList = FT_NEXT_USHORT( p );
valid->type_count = 8;
valid->type_funcs = (OTV_Validate_Func*)otv_gsub_validate_funcs;
valid->glyph_count = glyph_count;
otv_LookupList_validate( table + LookupList,
valid );
otv_FeatureList_validate( table + FeatureList, table + LookupList,
valid );
otv_ScriptList_validate( table + ScriptList, table + FeatureList,
valid );
FT_TRACE4(( "\n" ));
}
/* END */