/***************************************************************************/ | |
/* */ | |
/* otvgpos.c */ | |
/* */ | |
/* OpenType GPOS table validation (body). */ | |
/* */ | |
/* Copyright 2002-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" | |
#include "otvgpos.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_otvgpos | |
static void | |
otv_Anchor_validate( FT_Bytes table, | |
OTV_Validator valid ); | |
static void | |
otv_MarkArray_validate( FT_Bytes table, | |
OTV_Validator valid ); | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/***** *****/ | |
/***** UTILITY FUNCTIONS *****/ | |
/***** *****/ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
#define BaseArrayFunc otv_x_sxy | |
#define LigatureAttachFunc otv_x_sxy | |
#define Mark2ArrayFunc otv_x_sxy | |
/* uses valid->extra1 (counter) */ | |
/* uses valid->extra2 (boolean to handle NULL anchor field) */ | |
static void | |
otv_x_sxy( FT_Bytes table, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt Count, count1, table_size; | |
OTV_ENTER; | |
OTV_LIMIT_CHECK( 2 ); | |
Count = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (Count = %d)\n", Count )); | |
OTV_LIMIT_CHECK( Count * otvalid->extra1 * 2 ); | |
table_size = Count * otvalid->extra1 * 2 + 2; | |
for ( ; Count > 0; Count-- ) | |
for ( count1 = otvalid->extra1; count1 > 0; count1-- ) | |
{ | |
OTV_OPTIONAL_TABLE( anchor_offset ); | |
OTV_OPTIONAL_OFFSET( anchor_offset ); | |
if ( otvalid->extra2 ) | |
{ | |
OTV_SIZE_CHECK( anchor_offset ); | |
if ( anchor_offset ) | |
otv_Anchor_validate( table + anchor_offset, otvalid ); | |
} | |
else | |
otv_Anchor_validate( table + anchor_offset, otvalid ); | |
} | |
OTV_EXIT; | |
} | |
#define MarkBasePosFormat1Func otv_u_O_O_u_O_O | |
#define MarkLigPosFormat1Func otv_u_O_O_u_O_O | |
#define MarkMarkPosFormat1Func otv_u_O_O_u_O_O | |
/* sets otvalid->extra1 (class count) */ | |
static void | |
otv_u_O_O_u_O_O( FT_Bytes table, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt Coverage1, Coverage2, ClassCount; | |
FT_UInt Array1, Array2; | |
OTV_Validate_Func func; | |
OTV_ENTER; | |
p += 2; /* skip PosFormat */ | |
OTV_LIMIT_CHECK( 10 ); | |
Coverage1 = FT_NEXT_USHORT( p ); | |
Coverage2 = FT_NEXT_USHORT( p ); | |
ClassCount = FT_NEXT_USHORT( p ); | |
Array1 = FT_NEXT_USHORT( p ); | |
Array2 = FT_NEXT_USHORT( p ); | |
otv_Coverage_validate( table + Coverage1, otvalid, -1 ); | |
otv_Coverage_validate( table + Coverage2, otvalid, -1 ); | |
otv_MarkArray_validate( table + Array1, otvalid ); | |
otvalid->nesting_level++; | |
func = otvalid->func[otvalid->nesting_level]; | |
otvalid->extra1 = ClassCount; | |
func( table + Array2, otvalid ); | |
otvalid->nesting_level--; | |
OTV_EXIT; | |
} | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/***** *****/ | |
/***** VALUE RECORDS *****/ | |
/***** *****/ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
static FT_UInt | |
otv_value_length( FT_UInt format ) | |
{ | |
FT_UInt count; | |
count = ( ( format & 0xAA ) >> 1 ) + ( format & 0x55 ); | |
count = ( ( count & 0xCC ) >> 2 ) + ( count & 0x33 ); | |
count = ( ( count & 0xF0 ) >> 4 ) + ( count & 0x0F ); | |
return count * 2; | |
} | |
/* uses otvalid->extra3 (pointer to base table) */ | |
static void | |
otv_ValueRecord_validate( FT_Bytes table, | |
FT_UInt format, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt count; | |
#ifdef FT_DEBUG_LEVEL_TRACE | |
FT_Int loop; | |
FT_ULong res = 0; | |
OTV_NAME_ENTER( "ValueRecord" ); | |
/* display `format' in dual representation */ | |
for ( loop = 7; loop >= 0; loop-- ) | |
{ | |
res <<= 4; | |
res += ( format >> loop ) & 1; | |
} | |
OTV_TRACE(( " (format 0b%08lx)\n", res )); | |
#endif | |
if ( format >= 0x100 ) | |
FT_INVALID_FORMAT; | |
for ( count = 4; count > 0; count-- ) | |
{ | |
if ( format & 1 ) | |
{ | |
/* XPlacement, YPlacement, XAdvance, YAdvance */ | |
OTV_LIMIT_CHECK( 2 ); | |
p += 2; | |
} | |
format >>= 1; | |
} | |
for ( count = 4; count > 0; count-- ) | |
{ | |
if ( format & 1 ) | |
{ | |
FT_PtrDist table_size; | |
OTV_OPTIONAL_TABLE( device ); | |
/* XPlaDevice, YPlaDevice, XAdvDevice, YAdvDevice */ | |
OTV_LIMIT_CHECK( 2 ); | |
OTV_OPTIONAL_OFFSET( device ); | |
/* XXX: this value is usually too small, especially if the current */ | |
/* ValueRecord is part of an array -- getting the correct table */ | |
/* size is probably not worth the trouble */ | |
table_size = p - otvalid->extra3; | |
OTV_SIZE_CHECK( device ); | |
if ( device ) | |
otv_Device_validate( otvalid->extra3 + device, otvalid ); | |
} | |
format >>= 1; | |
} | |
OTV_EXIT; | |
} | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/***** *****/ | |
/***** ANCHORS *****/ | |
/***** *****/ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
static void | |
otv_Anchor_validate( FT_Bytes table, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt AnchorFormat; | |
OTV_NAME_ENTER( "Anchor"); | |
OTV_LIMIT_CHECK( 6 ); | |
AnchorFormat = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (format %d)\n", AnchorFormat )); | |
p += 4; /* skip XCoordinate and YCoordinate */ | |
switch ( AnchorFormat ) | |
{ | |
case 1: | |
break; | |
case 2: | |
OTV_LIMIT_CHECK( 2 ); /* AnchorPoint */ | |
break; | |
case 3: | |
{ | |
FT_UInt table_size; | |
OTV_OPTIONAL_TABLE( XDeviceTable ); | |
OTV_OPTIONAL_TABLE( YDeviceTable ); | |
OTV_LIMIT_CHECK( 4 ); | |
OTV_OPTIONAL_OFFSET( XDeviceTable ); | |
OTV_OPTIONAL_OFFSET( YDeviceTable ); | |
table_size = 6 + 4; | |
OTV_SIZE_CHECK( XDeviceTable ); | |
if ( XDeviceTable ) | |
otv_Device_validate( table + XDeviceTable, otvalid ); | |
OTV_SIZE_CHECK( YDeviceTable ); | |
if ( YDeviceTable ) | |
otv_Device_validate( table + YDeviceTable, otvalid ); | |
} | |
break; | |
default: | |
FT_INVALID_FORMAT; | |
} | |
OTV_EXIT; | |
} | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/***** *****/ | |
/***** MARK ARRAYS *****/ | |
/***** *****/ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
static void | |
otv_MarkArray_validate( FT_Bytes table, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt MarkCount; | |
OTV_NAME_ENTER( "MarkArray" ); | |
OTV_LIMIT_CHECK( 2 ); | |
MarkCount = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (MarkCount = %d)\n", MarkCount )); | |
OTV_LIMIT_CHECK( MarkCount * 4 ); | |
/* MarkRecord */ | |
for ( ; MarkCount > 0; MarkCount-- ) | |
{ | |
p += 2; /* skip Class */ | |
/* MarkAnchor */ | |
otv_Anchor_validate( table + FT_NEXT_USHORT( p ), otvalid ); | |
} | |
OTV_EXIT; | |
} | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/***** *****/ | |
/***** GPOS LOOKUP TYPE 1 *****/ | |
/***** *****/ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/* sets otvalid->extra3 (pointer to base table) */ | |
static void | |
otv_SinglePos_validate( FT_Bytes table, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt PosFormat; | |
OTV_NAME_ENTER( "SinglePos" ); | |
OTV_LIMIT_CHECK( 2 ); | |
PosFormat = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (format %d)\n", PosFormat )); | |
otvalid->extra3 = table; | |
switch ( PosFormat ) | |
{ | |
case 1: /* SinglePosFormat1 */ | |
{ | |
FT_UInt Coverage, ValueFormat; | |
OTV_LIMIT_CHECK( 4 ); | |
Coverage = FT_NEXT_USHORT( p ); | |
ValueFormat = FT_NEXT_USHORT( p ); | |
otv_Coverage_validate( table + Coverage, otvalid, -1 ); | |
otv_ValueRecord_validate( p, ValueFormat, otvalid ); /* Value */ | |
} | |
break; | |
case 2: /* SinglePosFormat2 */ | |
{ | |
FT_UInt Coverage, ValueFormat, ValueCount, len_value; | |
OTV_LIMIT_CHECK( 6 ); | |
Coverage = FT_NEXT_USHORT( p ); | |
ValueFormat = FT_NEXT_USHORT( p ); | |
ValueCount = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (ValueCount = %d)\n", ValueCount )); | |
len_value = otv_value_length( ValueFormat ); | |
otv_Coverage_validate( table + Coverage, | |
otvalid, | |
(FT_Int)ValueCount ); | |
OTV_LIMIT_CHECK( ValueCount * len_value ); | |
/* Value */ | |
for ( ; ValueCount > 0; ValueCount-- ) | |
{ | |
otv_ValueRecord_validate( p, ValueFormat, otvalid ); | |
p += len_value; | |
} | |
} | |
break; | |
default: | |
FT_INVALID_FORMAT; | |
} | |
OTV_EXIT; | |
} | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/***** *****/ | |
/***** GPOS LOOKUP TYPE 2 *****/ | |
/***** *****/ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
static void | |
otv_PairSet_validate( FT_Bytes table, | |
FT_UInt format1, | |
FT_UInt format2, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt value_len1, value_len2, PairValueCount; | |
OTV_NAME_ENTER( "PairSet" ); | |
OTV_LIMIT_CHECK( 2 ); | |
PairValueCount = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (PairValueCount = %d)\n", PairValueCount )); | |
value_len1 = otv_value_length( format1 ); | |
value_len2 = otv_value_length( format2 ); | |
OTV_LIMIT_CHECK( PairValueCount * ( value_len1 + value_len2 + 2 ) ); | |
/* PairValueRecord */ | |
for ( ; PairValueCount > 0; PairValueCount-- ) | |
{ | |
p += 2; /* skip SecondGlyph */ | |
if ( format1 ) | |
otv_ValueRecord_validate( p, format1, otvalid ); /* Value1 */ | |
p += value_len1; | |
if ( format2 ) | |
otv_ValueRecord_validate( p, format2, otvalid ); /* Value2 */ | |
p += value_len2; | |
} | |
OTV_EXIT; | |
} | |
/* sets otvalid->extra3 (pointer to base table) */ | |
static void | |
otv_PairPos_validate( FT_Bytes table, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt PosFormat; | |
OTV_NAME_ENTER( "PairPos" ); | |
OTV_LIMIT_CHECK( 2 ); | |
PosFormat = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (format %d)\n", PosFormat )); | |
otvalid->extra3 = table; | |
switch ( PosFormat ) | |
{ | |
case 1: /* PairPosFormat1 */ | |
{ | |
FT_UInt Coverage, ValueFormat1, ValueFormat2, PairSetCount; | |
OTV_LIMIT_CHECK( 8 ); | |
Coverage = FT_NEXT_USHORT( p ); | |
ValueFormat1 = FT_NEXT_USHORT( p ); | |
ValueFormat2 = FT_NEXT_USHORT( p ); | |
PairSetCount = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (PairSetCount = %d)\n", PairSetCount )); | |
otv_Coverage_validate( table + Coverage, otvalid, -1 ); | |
OTV_LIMIT_CHECK( PairSetCount * 2 ); | |
/* PairSetOffset */ | |
for ( ; PairSetCount > 0; PairSetCount-- ) | |
otv_PairSet_validate( table + FT_NEXT_USHORT( p ), | |
ValueFormat1, ValueFormat2, otvalid ); | |
} | |
break; | |
case 2: /* PairPosFormat2 */ | |
{ | |
FT_UInt Coverage, ValueFormat1, ValueFormat2, ClassDef1, ClassDef2; | |
FT_UInt ClassCount1, ClassCount2, len_value1, len_value2, count; | |
OTV_LIMIT_CHECK( 14 ); | |
Coverage = FT_NEXT_USHORT( p ); | |
ValueFormat1 = FT_NEXT_USHORT( p ); | |
ValueFormat2 = FT_NEXT_USHORT( p ); | |
ClassDef1 = FT_NEXT_USHORT( p ); | |
ClassDef2 = FT_NEXT_USHORT( p ); | |
ClassCount1 = FT_NEXT_USHORT( p ); | |
ClassCount2 = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (ClassCount1 = %d)\n", ClassCount1 )); | |
OTV_TRACE(( " (ClassCount2 = %d)\n", ClassCount2 )); | |
len_value1 = otv_value_length( ValueFormat1 ); | |
len_value2 = otv_value_length( ValueFormat2 ); | |
otv_Coverage_validate( table + Coverage, otvalid, -1 ); | |
otv_ClassDef_validate( table + ClassDef1, otvalid ); | |
otv_ClassDef_validate( table + ClassDef2, otvalid ); | |
OTV_LIMIT_CHECK( ClassCount1 * ClassCount2 * | |
( len_value1 + len_value2 ) ); | |
/* Class1Record */ | |
for ( ; ClassCount1 > 0; ClassCount1-- ) | |
{ | |
/* Class2Record */ | |
for ( count = ClassCount2; count > 0; count-- ) | |
{ | |
if ( ValueFormat1 ) | |
/* Value1 */ | |
otv_ValueRecord_validate( p, ValueFormat1, otvalid ); | |
p += len_value1; | |
if ( ValueFormat2 ) | |
/* Value2 */ | |
otv_ValueRecord_validate( p, ValueFormat2, otvalid ); | |
p += len_value2; | |
} | |
} | |
} | |
break; | |
default: | |
FT_INVALID_FORMAT; | |
} | |
OTV_EXIT; | |
} | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/***** *****/ | |
/***** GPOS LOOKUP TYPE 3 *****/ | |
/***** *****/ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
static void | |
otv_CursivePos_validate( FT_Bytes table, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt PosFormat; | |
OTV_NAME_ENTER( "CursivePos" ); | |
OTV_LIMIT_CHECK( 2 ); | |
PosFormat = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (format %d)\n", PosFormat )); | |
switch ( PosFormat ) | |
{ | |
case 1: /* CursivePosFormat1 */ | |
{ | |
FT_UInt table_size; | |
FT_UInt Coverage, EntryExitCount; | |
OTV_OPTIONAL_TABLE( EntryAnchor ); | |
OTV_OPTIONAL_TABLE( ExitAnchor ); | |
OTV_LIMIT_CHECK( 4 ); | |
Coverage = FT_NEXT_USHORT( p ); | |
EntryExitCount = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (EntryExitCount = %d)\n", EntryExitCount )); | |
otv_Coverage_validate( table + Coverage, | |
otvalid, | |
(FT_Int)EntryExitCount ); | |
OTV_LIMIT_CHECK( EntryExitCount * 4 ); | |
table_size = EntryExitCount * 4 + 4; | |
/* EntryExitRecord */ | |
for ( ; EntryExitCount > 0; EntryExitCount-- ) | |
{ | |
OTV_OPTIONAL_OFFSET( EntryAnchor ); | |
OTV_OPTIONAL_OFFSET( ExitAnchor ); | |
OTV_SIZE_CHECK( EntryAnchor ); | |
if ( EntryAnchor ) | |
otv_Anchor_validate( table + EntryAnchor, otvalid ); | |
OTV_SIZE_CHECK( ExitAnchor ); | |
if ( ExitAnchor ) | |
otv_Anchor_validate( table + ExitAnchor, otvalid ); | |
} | |
} | |
break; | |
default: | |
FT_INVALID_FORMAT; | |
} | |
OTV_EXIT; | |
} | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/***** *****/ | |
/***** GPOS LOOKUP TYPE 4 *****/ | |
/***** *****/ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/* UNDOCUMENTED (in OpenType 1.5): */ | |
/* BaseRecord tables can contain NULL pointers. */ | |
/* sets otvalid->extra2 (1) */ | |
static void | |
otv_MarkBasePos_validate( FT_Bytes table, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt PosFormat; | |
OTV_NAME_ENTER( "MarkBasePos" ); | |
OTV_LIMIT_CHECK( 2 ); | |
PosFormat = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (format %d)\n", PosFormat )); | |
switch ( PosFormat ) | |
{ | |
case 1: | |
otvalid->extra2 = 1; | |
OTV_NEST2( MarkBasePosFormat1, BaseArray ); | |
OTV_RUN( table, otvalid ); | |
break; | |
default: | |
FT_INVALID_FORMAT; | |
} | |
OTV_EXIT; | |
} | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/***** *****/ | |
/***** GPOS LOOKUP TYPE 5 *****/ | |
/***** *****/ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/* sets otvalid->extra2 (1) */ | |
static void | |
otv_MarkLigPos_validate( FT_Bytes table, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt PosFormat; | |
OTV_NAME_ENTER( "MarkLigPos" ); | |
OTV_LIMIT_CHECK( 2 ); | |
PosFormat = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (format %d)\n", PosFormat )); | |
switch ( PosFormat ) | |
{ | |
case 1: | |
otvalid->extra2 = 1; | |
OTV_NEST3( MarkLigPosFormat1, LigatureArray, LigatureAttach ); | |
OTV_RUN( table, otvalid ); | |
break; | |
default: | |
FT_INVALID_FORMAT; | |
} | |
OTV_EXIT; | |
} | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/***** *****/ | |
/***** GPOS LOOKUP TYPE 6 *****/ | |
/***** *****/ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/* sets otvalid->extra2 (0) */ | |
static void | |
otv_MarkMarkPos_validate( FT_Bytes table, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt PosFormat; | |
OTV_NAME_ENTER( "MarkMarkPos" ); | |
OTV_LIMIT_CHECK( 2 ); | |
PosFormat = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (format %d)\n", PosFormat )); | |
switch ( PosFormat ) | |
{ | |
case 1: | |
otvalid->extra2 = 0; | |
OTV_NEST2( MarkMarkPosFormat1, Mark2Array ); | |
OTV_RUN( table, otvalid ); | |
break; | |
default: | |
FT_INVALID_FORMAT; | |
} | |
OTV_EXIT; | |
} | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/***** *****/ | |
/***** GPOS LOOKUP TYPE 7 *****/ | |
/***** *****/ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/* sets otvalid->extra1 (lookup count) */ | |
static void | |
otv_ContextPos_validate( FT_Bytes table, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt PosFormat; | |
OTV_NAME_ENTER( "ContextPos" ); | |
OTV_LIMIT_CHECK( 2 ); | |
PosFormat = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (format %d)\n", PosFormat )); | |
switch ( PosFormat ) | |
{ | |
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( ContextPosFormat1, PosRuleSet, PosRule ); | |
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( ContextPosFormat2, PosClassSet, PosClassRule ); | |
OTV_RUN( table, otvalid ); | |
break; | |
case 3: | |
OTV_NEST1( ContextPosFormat3 ); | |
OTV_RUN( table, otvalid ); | |
break; | |
default: | |
FT_INVALID_FORMAT; | |
} | |
OTV_EXIT; | |
} | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/***** *****/ | |
/***** GPOS LOOKUP TYPE 8 *****/ | |
/***** *****/ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/* sets otvalid->extra1 (lookup count) */ | |
static void | |
otv_ChainContextPos_validate( FT_Bytes table, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt PosFormat; | |
OTV_NAME_ENTER( "ChainContextPos" ); | |
OTV_LIMIT_CHECK( 2 ); | |
PosFormat = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (format %d)\n", PosFormat )); | |
switch ( PosFormat ) | |
{ | |
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( ChainContextPosFormat1, | |
ChainPosRuleSet, ChainPosRule ); | |
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( ChainContextPosFormat2, | |
ChainPosClassSet, ChainPosClassRule ); | |
OTV_RUN( table, otvalid ); | |
break; | |
case 3: | |
OTV_NEST1( ChainContextPosFormat3 ); | |
OTV_RUN( table, otvalid ); | |
break; | |
default: | |
FT_INVALID_FORMAT; | |
} | |
OTV_EXIT; | |
} | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/***** *****/ | |
/***** GPOS LOOKUP TYPE 9 *****/ | |
/***** *****/ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/* uses otvalid->type_funcs */ | |
static void | |
otv_ExtensionPos_validate( FT_Bytes table, | |
OTV_Validator otvalid ) | |
{ | |
FT_Bytes p = table; | |
FT_UInt PosFormat; | |
OTV_NAME_ENTER( "ExtensionPos" ); | |
OTV_LIMIT_CHECK( 2 ); | |
PosFormat = FT_NEXT_USHORT( p ); | |
OTV_TRACE(( " (format %d)\n", PosFormat )); | |
switch ( PosFormat ) | |
{ | |
case 1: /* ExtensionPosFormat1 */ | |
{ | |
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 >= 9 ) | |
FT_INVALID_DATA; | |
validate = otvalid->type_funcs[ExtensionLookupType - 1]; | |
validate( table + ExtensionOffset, otvalid ); | |
} | |
break; | |
default: | |
FT_INVALID_FORMAT; | |
} | |
OTV_EXIT; | |
} | |
static const OTV_Validate_Func otv_gpos_validate_funcs[9] = | |
{ | |
otv_SinglePos_validate, | |
otv_PairPos_validate, | |
otv_CursivePos_validate, | |
otv_MarkBasePos_validate, | |
otv_MarkLigPos_validate, | |
otv_MarkMarkPos_validate, | |
otv_ContextPos_validate, | |
otv_ChainContextPos_validate, | |
otv_ExtensionPos_validate | |
}; | |
/* sets otvalid->type_count */ | |
/* sets otvalid->type_funcs */ | |
FT_LOCAL_DEF( void ) | |
otv_GPOS_subtable_validate( FT_Bytes table, | |
OTV_Validator otvalid ) | |
{ | |
otvalid->type_count = 9; | |
otvalid->type_funcs = (OTV_Validate_Func*)otv_gpos_validate_funcs; | |
otv_Lookup_validate( table, otvalid ); | |
} | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/***** *****/ | |
/***** GPOS TABLE *****/ | |
/***** *****/ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/* sets otvalid->glyph_count */ | |
FT_LOCAL_DEF( void ) | |
otv_GPOS_validate( FT_Bytes table, | |
FT_UInt glyph_count, | |
FT_Validator ftvalid ) | |
{ | |
OTV_ValidatorRec validrec; | |
OTV_Validator otvalid = &validrec; | |
FT_Bytes p = table; | |
FT_UInt ScriptList, FeatureList, LookupList; | |
otvalid->root = ftvalid; | |
FT_TRACE3(( "validating GPOS 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 = 9; | |
otvalid->type_funcs = (OTV_Validate_Func*)otv_gpos_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 */ |