/***************************************************************************/ /* */ /* otvgsub.c */ /* */ /* OpenType GSUB table validation (body). */ /* */ /* Copyright 2004-2015 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 otvalid->glyph_count */ static void otv_SingleSubst_validate( FT_Bytes table, OTV_Validator otvalid ) { 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, otvalid, -1 ); idx = (FT_Long)otv_Coverage_get_first( Coverage ) + DeltaGlyphID; if ( idx < 0 ) FT_INVALID_DATA; idx = (FT_Long)otv_Coverage_get_last( Coverage ) + DeltaGlyphID; if ( (FT_UInt)idx >= otvalid->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, otvalid, (FT_Int)GlyphCount ); OTV_LIMIT_CHECK( GlyphCount * 2 ); /* Substitute */ for ( ; GlyphCount > 0; GlyphCount-- ) if ( FT_NEXT_USHORT( p ) >= otvalid->glyph_count ) FT_INVALID_GLYPH_ID; } break; default: FT_INVALID_FORMAT; } OTV_EXIT; } /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GSUB LOOKUP TYPE 2 *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ /* sets otvalid->extra1 (glyph count) */ static void otv_MultipleSubst_validate( FT_Bytes table, OTV_Validator otvalid ) { 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: otvalid->extra1 = otvalid->glyph_count; OTV_NEST2( MultipleSubstFormat1, Sequence ); OTV_RUN( table, otvalid ); break; default: FT_INVALID_FORMAT; } OTV_EXIT; } /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GSUB LOOKUP TYPE 3 *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ /* sets otvalid->extra1 (glyph count) */ static void otv_AlternateSubst_validate( FT_Bytes table, OTV_Validator otvalid ) { 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: otvalid->extra1 = otvalid->glyph_count; OTV_NEST2( AlternateSubstFormat1, AlternateSet ); OTV_RUN( table, otvalid ); break; default: FT_INVALID_FORMAT; } OTV_EXIT; } /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GSUB LOOKUP TYPE 4 *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ #define LigatureFunc otv_Ligature_validate /* uses otvalid->glyph_count */ static void otv_Ligature_validate( FT_Bytes table, OTV_Validator otvalid ) { FT_Bytes p = table; FT_UInt LigatureGlyph, CompCount; OTV_ENTER; OTV_LIMIT_CHECK( 4 ); LigatureGlyph = FT_NEXT_USHORT( p ); if ( LigatureGlyph >= otvalid->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 otvalid ) { 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, otvalid ); break; default: FT_INVALID_FORMAT; } OTV_EXIT; } /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GSUB LOOKUP TYPE 5 *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ /* sets otvalid->extra1 (lookup count) */ static void otv_ContextSubst_validate( FT_Bytes table, OTV_Validator otvalid ) { 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 */ otvalid->extra1 = otvalid->lookup_count; OTV_NEST3( ContextSubstFormat1, SubRuleSet, SubRule ); OTV_RUN( table, otvalid ); 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, otvalid ); break; case 3: OTV_NEST1( ContextSubstFormat3 ); OTV_RUN( table, otvalid ); break; default: FT_INVALID_FORMAT; } OTV_EXIT; } /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GSUB LOOKUP TYPE 6 *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ /* sets otvalid->extra1 (lookup count) */ static void otv_ChainContextSubst_validate( FT_Bytes table, OTV_Validator otvalid ) { 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 */ otvalid->extra1 = otvalid->lookup_count; OTV_NEST3( ChainContextSubstFormat1, ChainSubRuleSet, ChainSubRule ); OTV_RUN( table, otvalid ); 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, otvalid ); break; case 3: OTV_NEST1( ChainContextSubstFormat3 ); OTV_RUN( table, otvalid ); break; default: FT_INVALID_FORMAT; } OTV_EXIT; } /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GSUB LOOKUP TYPE 7 *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ /* uses otvalid->type_funcs */ static void otv_ExtensionSubst_validate( FT_Bytes table, OTV_Validator otvalid ) { 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 = otvalid->type_funcs[ExtensionLookupType - 1]; validate( table + ExtensionOffset, otvalid ); } break; default: FT_INVALID_FORMAT; } OTV_EXIT; } /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GSUB LOOKUP TYPE 8 *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ /* uses otvalid->glyph_count */ static void otv_ReverseChainSingleSubst_validate( FT_Bytes table, OTV_Validator otvalid ) { 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, otvalid, -1 ); OTV_LIMIT_CHECK( BacktrackGlyphCount * 2 + 2 ); for ( ; BacktrackGlyphCount > 0; BacktrackGlyphCount-- ) otv_Coverage_validate( table + FT_NEXT_USHORT( p ), otvalid, -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 ), otvalid, -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 ) >= otvalid->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 otvalid->type_count */ /* sets otvalid->type_funcs */ /* sets otvalid->glyph_count */ FT_LOCAL_DEF( void ) otv_GSUB_validate( FT_Bytes table, FT_UInt glyph_count, FT_Validator ftvalid ) { OTV_ValidatorRec otvalidrec; OTV_Validator otvalid = &otvalidrec; FT_Bytes p = table; FT_UInt ScriptList, FeatureList, LookupList; otvalid->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 ); otvalid->type_count = 8; otvalid->type_funcs = (OTV_Validate_Func*)otv_gsub_validate_funcs; otvalid->glyph_count = glyph_count; otv_LookupList_validate( table + LookupList, otvalid ); otv_FeatureList_validate( table + FeatureList, table + LookupList, otvalid ); otv_ScriptList_validate( table + ScriptList, table + FeatureList, otvalid ); FT_TRACE4(( "\n" )); } /* END */