/**************************************************************************** * * otvgsub.c * * OpenType GSUB table validation (body). * * Copyright (C) 2004-2019 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 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 table_size; FT_UShort version; FT_UInt ScriptList, FeatureList, LookupList; OTV_OPTIONAL_TABLE32( featureVariations ); otvalid->root = ftvalid; FT_TRACE3(( "validating GSUB table\n" )); OTV_INIT; OTV_LIMIT_CHECK( 4 ); if ( FT_NEXT_USHORT( p ) != 1 ) /* majorVersion */ FT_INVALID_FORMAT; version = FT_NEXT_USHORT( p ); /* minorVersion */ table_size = 10; switch ( version ) { case 0: OTV_LIMIT_CHECK( 6 ); break; case 1: OTV_LIMIT_CHECK( 10 ); table_size += 4; break; default: 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 ); if ( version > 0 ) { OTV_OPTIONAL_OFFSET32( featureVariations ); OTV_SIZE_CHECK32( featureVariations ); if ( featureVariations ) OTV_TRACE(( " [omitting featureVariations validation]\n" )); /* XXX */ } FT_TRACE4(( "\n" )); } /* END */