#include "otlgpos.h" #include "otlcommn.h" /* forward declaration */ static OTL_ValidateFunc otl_gpos_validate_funcs[]; /************************************************************************/ /************************************************************************/ /***** *****/ /***** VALUE RECORDS *****/ /***** *****/ /************************************************************************/ /************************************************************************/ static OTL_UInt otl_value_length( OTL_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; } static void otl_value_validate( OTL_Bytes table, OTL_Bytes pos_table, OTL_UInt format, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt count, device; if ( format >= 0x100U ) OTL_INVALID_DATA; for ( count = 4; count > 0; count-- ) { if ( format & 1 ) { OTL_CHECK( 2 ); p += 2; } format >>= 1; } for ( count = 4; count > 0; count-- ) { if ( format & 1 ) { OTL_CHECK( 2 ); device = OTL_NEXT_USHORT( p ); if ( device ) otl_device_table_validate( pos_table + device, valid ); } format >>= 1; } } /************************************************************************/ /************************************************************************/ /***** *****/ /***** ANCHORS *****/ /***** *****/ /************************************************************************/ /************************************************************************/ static void otl_anchor_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt format; OTL_CHECK( 6 ); format = OTL_NEXT_USHORT( p ); p += 4; switch ( format ) { case 1: break; case 2: OTL_CHECK( 2 ); /* anchor point */ break; case 3: { OTL_UInt x_device, y_device; OTL_CHECK( 4 ); x_device = OTL_NEXT_USHORT( p ); y_device = OTL_NEXT_USHORT( p ); if ( x_device ) otl_device_table_validate( table + x_device, valid ); if ( y_device ) otl_device_table_validate( table + y_device, valid ); } break; default: OTL_INVALID_DATA; } } /************************************************************************/ /************************************************************************/ /***** *****/ /***** MARK ARRAY *****/ /***** *****/ /************************************************************************/ /************************************************************************/ static void otl_mark_array_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt count; OTL_CHECK( 2 ); count = OTL_NEXT_USHORT( p ); OTL_CHECK( count * 4 ); for ( ; count > 0; count-- ) { p += 2; /* ignore class index */ otl_anchor_validate( table + OTL_NEXT_USHORT( p ), valid ); } } /************************************************************************/ /************************************************************************/ /***** *****/ /***** GPOS LOOKUP TYPE 1 *****/ /***** *****/ /************************************************************************/ /************************************************************************/ static void otl_gpos_lookup1_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt format; OTL_CHECK( 2 ); format = OTL_NEXT_USHORT( p ); switch ( format ) { case 1: { FT_UInt coverage, value_format; OTL_CHECK( 4 ); coverage = OTL_NEXT_USHORT( p ); value_format = OTL_NEXT_USHORT( p ); otl_coverage_validate( table + coverage, valid ); otl_value_validate( p, table, value_format, valid ); } break; case 2: { FT_UInt coverage, value_format, count, len; OTL_CHECK( 6 ); coverage = OTL_NEXT_USHORT( p ); value_format = OTL_NEXT_USHORT( p ); count = OTL_NEXT_USHORT( p ); len = otl_value_length( value_format ); otl_coverage_validate( table + coverage, valid ); OTL_CHECK( count * len ); for ( ; count > 0; count-- ) { otl_value_validate( p, table, value_format, valid ); p += len; } } break; default: OTL_INVALID_DATA; } } /************************************************************************/ /************************************************************************/ /***** *****/ /***** GPOS LOOKUP TYPE 2 *****/ /***** *****/ /************************************************************************/ /************************************************************************/ static otl_gpos_pairset_validate( OTL_Bytes table, OTL_Bytes pos_table, OTL_UInt format1, OTL_UInt format2, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt len1, len2, count; OTL_CHECK( 2 ); count = OTL_NEXT_USHORT( p ); len1 = otl_value_length( format1 ); len2 = otl_value_length( format2 ); OTL_CHECK( count * (len1+len2+2) ); for ( ; count > 0; count-- ) { p += 2; /* ignore glyph id */ otl_value_validate( p, pos_table, format1, valid ); p += len1; otl_value_validate( p, pos_table, format2, valid ); p += len2; } } static void otl_gpos_lookup2_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt format; OTL_CHECK( 2 ); format = OTL_NEXT_USHORT( p ); switch (format) { case 1: { OTL_UInt coverage, value1, value2, count; OTL_CHECK( 8 ); coverage = OTL_NEXT_USHORT( p ); value1 = OTL_NEXT_USHORT( p ); value2 = OTL_NEXT_USHORT( p ); count = OTL_NEXT_USHORT( p ); otl_coverage_validate( table + coverage, valid ); OTL_CHECK( count*2 ); for ( ; count > 0; count-- ) { otl_gpos_pairset_validate( table + OTL_NEXT_USHORT( p ), table, value1, value2, valid ); } } break; case 2: { OTL_UInt coverage, value1, value2, class1, class2, count1, count2; OTL_UInt len1, len2; OTL_CHECK( 14 ); coverage = OTL_NEXT_USHORT( p ); value1 = OTL_NEXT_USHORT( p ); value2 = OTL_NEXT_USHORT( p ); class1 = OTL_NEXT_USHORT( p ); class2 = OTL_NEXT_USHORT( p ); count1 = OTL_NEXT_USHORT( p ); count2 = OTL_NEXT_USHORT( p ); len1 = otl_value_length( value1 ); len2 = otl_value_length( value2 ); otl_coverage_validate( table + coverage, valid ); OTL_CHECK( count1*count2*(len1+len2) ); for ( ; count1 > 0; count1-- ) { for ( ; count2 > 0; count2-- ) { otl_value_validate( p, table, value1, valid ); p += len1; otl_value_validate( p, table, value2, valid ); p += len2; } } } break; default: OTL_INVALID_DATA; } } /************************************************************************/ /************************************************************************/ /***** *****/ /***** GPOS LOOKUP TYPE 3 *****/ /***** *****/ /************************************************************************/ /************************************************************************/ static void otl_gpos_lookup3_validate( OTL_Bytes table, OTL_Valid valid ) { OTL_Bytes p = table; OTL_UInt format; OTL_CHECK( 2 ); format = OTL_NEXT_USHORT( p ); switch (format) { case 1: { OTL_UInt coverage, count, anchor1, anchor2; OTL_CHECK( 4 ); coverage = OTL_NEXT_USHORT( p ); count = OTL_NEXT_USHORT( p ); otl_coverage_validate( table + coverage, valid ); OTL_CHECK( count*4 ); for ( ; count > 0; count-- ) { anchor1 = OTL_NEXT_USHORT( p ); anchor2 = OTL_NEXT_USHORT( p ); if ( anchor1 ) otl_anchor_validate( table + anchor1, valid ); if ( anchor2 ) otl_anchor_validate( table + anchor2, valid ); } } break; default: OTL_INVALID_DATA; } } /************************************************************************/ /************************************************************************/ /***** *****/ /***** GPOS LOOKUP TYPE 4 *****/ /***** *****/ /************************************************************************/ /************************************************************************/ static void otl_base_array_validate( OTL_Bytes table, OTL_UInt class_count, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt count, count2; OTL_CHECK( 2 ); count = OTL_NEXT_USHORT( p ); OTL_CHECK( count*class_count*2 ); for ( ; count > 0; count-- ) for ( count2 = class_count; count2 > 0; count2-- ) otl_anchor_validate( table + OTL_NEXT_USHORT( p ) ); } static void otl_gpos_lookup4_validate( OTL_Bytes table, OTL_Valid valid ) { OTL_Bytes p = table; OTL_UInt format; OTL_CHECK( 2 ); format = OTL_NEXT_USHORT( p ); switch (format) { case 1: { OTL_UInt mark_coverage, base_coverage, class_count; OTL_UInt mark_array, base_array; OTL_CHECK( 10 ); mark_coverage = OTL_NEXT_USHORT( p ); base_coverage = OTL_NEXT_USHORT( p ); class_count = OTL_NEXT_USHORT( p ); mark_array = OTL_NEXT_USHORT( p ); base_array = OTL_NEXT_USHORT( p ); otl_coverage_validate( table + mark_coverage, valid ); otl_coverage_validate( table + base_coverage, valid ); otl_mark_array_validate( table + mark_array, valid ); otl_base_array_validate( table, class_count, valid ); } break; default: OTL_INVALID_DATA; } } /************************************************************************/ /************************************************************************/ /***** *****/ /***** GPOS LOOKUP TYPE 5 *****/ /***** *****/ /************************************************************************/ /************************************************************************/ static void otl_liga_attach_validate( OTL_Bytes table, OTL_UInt class_count, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt count, count2; OTL_CHECK( 2 ); count = OTL_NEXT_USHORT( p ); OTL_CHECK( count*class_count*2 ); for ( ; count > 0; count-- ) for ( count2 = class_count; class_count > 0; class_count-- ) otl_anchor_validate( table + OTL_NEXT_USHORT( p ), valid ); } static void otl_liga_array_validate( OTL_Bytes table, OTL_UInt class_count, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt count, count2; OTL_CHECK( 2 ); count = OTL_NEXT_USHORT( p ); OTL_CHECK( count*2 ); for ( ; count > 0; count-- ) otl_liga_attach_validate( table + OTL_NEXT_USHORT( p ), valid ); } static void otl_gpos_lookup5_validate( OTL_Bytes table, OTL_Valid valid ) { OTL_Bytes p = table; OTL_UInt format; OTL_CHECK( 2 ); format = OTL_NEXT_USHORT( p ); switch (format) { case 1: { OTL_UInt mark_coverage, lig_coverage, class_count; OTL_UInt mar_array, lig_array; OTL_CHECK( 10 ); mark_coverage = OTL_NEXT_USHORT( p ); liga_coverage = OTL_NEXT_USHORT( p ); class_count = OTL_NEXT_USHORT( p ); mark_array = OTL_NEXT_USHORT( p ); liga_array = OTL_NEXT_USHORT( p ); otl_coverage_validate( table + mark_coverage, valid ); otl_coverage_validate( table + liga_coverage, valid ); otl_mark_array_validate( table + mark_array, valid ); otl_liga_array_validate( table + liga_array, class_count, valid ); } break; default: OTL_INVALID_DATA; } } /************************************************************************/ /************************************************************************/ /***** *****/ /***** GPOS LOOKUP TYPE 6 *****/ /***** *****/ /************************************************************************/ /************************************************************************/ static void otl_mark2_array_validate( OTL_Bytes table, OTL_UInt class_count, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt count, count2; OTL_CHECK( 2 ); count = OTL_NEXT_USHORT( p ); OTL_CHECK( count*class_count*2 ); for ( ; count > 0; count-- ) for ( count2 = class_count; class_count > 0; class_count-- ) otl_anchor_validate( table + OTL_NEXT_USHORT( p ), valid ); } static void otl_gpos_lookup6_validate( OTL_Bytes table, OTL_Valid valid ) { OTL_Bytes p = table; OTL_UInt format; OTL_CHECK( 2 ); format = OTL_NEXT_USHORT( p ); switch (format) { case 1: { OTL_UInt coverage1, coverage2, class_count, array1, array2; OTL_CHECK( 10 ); coverage1 = OTL_NEXT_USHORT( p ); coverage2 = OTL_NEXT_USHORT( p ); class_count = OTL_NEXT_USHORT( p ); array1 = OTL_NEXT_USHORT( p ); array2 = OTL_NEXT_USHORT( p ); otl_coverage_validate( table + coverage1, valid ); otl_coverage_validate( table + coverage2, valid ); otl_mark_array_validate( table + array1, valid ); otl_mark2_array_validate( table + array2, valid ); } break; default: OTL_INVALID_DATA; } } /************************************************************************/ /************************************************************************/ /***** *****/ /***** GPOS LOOKUP TYPE 7 *****/ /***** *****/ /************************************************************************/ /************************************************************************/ static void otl_pos_rule_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt glyph_count, pos_count; OTL_CHECK( 4 ); glyph_count = OTL_NEXT_USHORT( p ); pos_count = OTL_NEXT_USHORT( p ); if ( glyph_count == 0 ) OTL_INVALID_DATA; OTL_CHECK( (glyph_count-1)*2 + pos_count*4 ); /* XXX: check glyph indices and pos lookups */ } static void otl_pos_rule_set_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt count; OTL_CHECK( 2 ); count = OTL_NEXT_USHORT( p ); OTL_CHECK( count*2 ); for ( ; count > 0; count-- ) otl_pos_rule_validate( table + OTL_NEXT_USHORT(p), valid ); } static void otl_pos_class_rule_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt glyph_count, pos_count; OTL_CHECK( 4 ); glyph_count = OTL_NEXT_USHORT( p ); pos_count = OTL_NEXT_USHORT( p ); if ( glyph_count == 0 ) OTL_INVALID_DATA; OTL_CHECK( (glyph_count-1)*2 + pos_count*4 ); /* XXX: check glyph indices and pos lookups */ } static void otl_pos_class_set_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt count; OTL_CHECK( 2 ); count = OTL_NEXT_USHORT( p ); OTL_CHECK( count*2 ); for ( ; count > 0; count-- ) otl_pos_rule_validate( table + OTL_NEXT_USHORT(p), valid ); } static void otl_gpos_lookup7_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt format; OTL_CHECK( 2 ); format = OTL_NEXT_USHORT( p ); switch (format) { case 1: { OTL_UInt coverage, count; OTL_CHECK( 4 ); coverage = OTL_NEXT_USHORT( p ); count = OTL_NEXT_USHORT( p ); otl_coverage_validate( table + coverage, valid ); OTL_CHECK( count*2 ); for ( ; count > 0; count-- ) otl_pos_rule_set_validate( table + OTL_NEXT_USHORT( p ), valid ); } break; case 2: { OTL_UInt coverage, class_def, count; OTL_CHECK( 6 ); coverage = OTL_NEXT_USHORT( p ); class_def = OTL_NEXT_USHORT( p ); count = OTL_NEXT_USHORT( p ); otl_coverage_validate ( table + coverage, valid ); otl_class_definition_validate( table + class_def, valid ); OTL_CHECK( count*2 ); for ( ; count > 0; count-- ) otl_ } break; case 3: { OTL_UInt glyph_count, pos_count; OTL_CHECK( 4 ); glyph_count = OTL_NEXT_USHORT( p ); pos_count = OTL_NEXT_USHORT( p ); OTL_CHECK( glyph_count*2 + pos_count*4 ); for ( ; glyph_count > 0; glyph_count ) otl_coverage_validate( table + OTL_NEXT_USHORT( p ), valid ); /* XXX: check pos lookups */ } break; default: OTL_INVALID_DATA; } } /************************************************************************/ /************************************************************************/ /***** *****/ /***** GPOS LOOKUP TYPE 8 *****/ /***** *****/ /************************************************************************/ /************************************************************************/ static void otl_chain_pos_rule_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt back_count, input_count, ahead_count, pos_count; OTL_CHECK( 2 ); back_count = OTL_NEXT_USHORT( p ); OTL_CHECK( back_count*2 + 2 ); p += back_count*2; input_count = OTL_NEXT_USHORT( p ); if ( input_count == 0 ) OTL_INVALID_DATA; OTL_CHECK( input_count*2 ); p += (input_count-1)*2; ahead_count = OTL_NEXT_USHORT( p ); OTL_CHECK( ahead_count*2 + 2 ); p += ahead_count*2; pos_count = OTL_NEXT_USHORT( p ); OTL_CHECK( pos_count*4 ); } static void otl_chain_pos_rule_set_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt count; OTL_CHECK( 2 ); count = OTL_NEXT_USHORT( p ); OTL_CHECK( 2*count ); for ( ; count > 0; count-- ) otl_chain_pos_rule_validate( table + OTL_NEXT_USHORT( p ), valid ); } static void otl_chain_pos_class_rule_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt back_count, input_count, ahead_count, pos_count; OTL_CHECK( 2 ); back_count = OTL_NEXT_USHORT( p ); OTL_CHECK( back_count*2 + 2 ); p += back_count*2; input_count = OTL_NEXT_USHORT( p ); if ( input_count == 0 ) OTL_INVALID_DATA; OTL_CHECK( input_count*2 ); p += (input_count-1)*2; ahead_count = OTL_NEXT_USHORT( p ); OTL_CHECK( ahead_count*2 + 2 ); p += ahead_count*2; pos_count = OTL_NEXT_USHORT( p ); OTL_CHECK( pos_count*4 ); } static void otl_chain_pos_class_set_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt count; OTL_CHECK( 2 ); count = OTL_NEXT_USHORT( p ); OTL_CHECK( 2*count ); for ( ; count > 0; count-- ) otl_chain_pos_class_rule_validate( table + OTL_NEXT_USHORT( p ), valid ); } static void otl_gpos_lookup8_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt format; OTL_CHECK( 2 ); format = OTL_NEXT_USHORT( p ); switch (format) { case 1: { OTL_UInt coverage, count; OTL_CHECK( 4 ); coverage = OTL_NEXT_USHORT( p ); count = OTL_NEXT_USHORT( p ); otl_coverage_validate( table + coverage, valid ); OTL_CHECK( count*2 ); for ( ; count > 0; count-- ) otl_chain_pos_rule_set_validate( table + OTL_NEXT_USHORT( p ), valid ); } break; case 2: { OTL_UInt coverage, back_class, input_class, ahead_class, count; OTL_CHECK( 10 ); coverage = OTL_NEXT_USHORT( p ); back_class = OTL_NEXT_USHORT( p ); input_class = OTL_NEXT_USHORT( p ); ahead_class = OTL_NEXT_USHORT( p ); count = OTL_NEXT_USHORT( p ); otl_coverage_validate( table + coverage, valid ); otl_class_definition_validate( table + back_class, valid ); otl_class_definition_validate( table + input_class, valid ); otl_class_definition_validate( table + ahead_class, valid ); OTL_CHECK( count*2 ); for ( ; count > 0; count-- ) otl_chain_pos_class_set_validate( table + OTL_NEXT_USHORT( p ), valid ); } break; case 3: { OTL_UInt back_count, input_count, ahead_count, pos_count, count; OTL_CHECK( 2 ); back_count = OTL_NEXT_USHORT( p ); OTL_CHECK( 2*back_count+2 ); for ( count = back_count; count > 0; count-- ) otl_coverage_validate( table + OTL_NEXT_USHORT( p ), valid ); input_count = OTL_NEXT_USHORT( p ); OTL_CHECK( 2*input_count+2 ); for ( count = input_count; count > 0; count-- ) otl_coverage_validate( table + OTL_NEXT_USHORT( p ), valid ); ahead_count = OTL_NEXT_USHORT( p ); OTL_CHECK( 2*ahead_count+2 ); for ( count = ahead_count; count > 0; count-- ) otl_coverage_validate( table + OTL_NEXT_USHORT( p ), valid ); pos_count = OTL_NEXT_USHORT( p ); OTL_CHECK( pos_count*4 ); } break; default: OTL_INVALID_DATA; } } /************************************************************************/ /************************************************************************/ /***** *****/ /***** GPOS LOOKUP TYPE 9 *****/ /***** *****/ /************************************************************************/ /************************************************************************/ static void otl_gpos_lookup9_validate( OTL_Bytes table, OTL_Valid valid ) { OTL_Bytes p = table; OTL_UInt format; OTL_CHECK( 2 ); format = OTL_NEXT_USHORT( p ); switch (format) { case 1: { OTL_UInt lookup_type, lookup_offset; OTL_ValidateFunc validate; OTL_CHECK( 6 ); lookup_type = OTL_NEXT_USHORT( p ); lookup_offset = OTL_NEXT_ULONG( p ); if ( lookup_type == 0 || lookup_type >= 9 ) OTL_INVALID_DATA; validate = otl_gpos_validate_funcs[ lookup_type-1 ]; validate( table + lookup_offset, valid ); } break; default: OTL_INVALID_DATA; } } static OTL_ValidateFunc otl_gpos_validate_funcs[ 9 ] = { otl_gpos_lookup1_validate, otl_gpos_lookup2_validate, otl_gpos_lookup3_validate, otl_gpos_lookup4_validate, otl_gpos_lookup5_validate, otl_gpos_lookup6_validate, otl_gpos_lookup7_validate, otl_gpos_lookup8_validate, otl_gpos_lookup9_validate, }; /************************************************************************/ /************************************************************************/ /***** *****/ /***** GPOS TABLE *****/ /***** *****/ /************************************************************************/ /************************************************************************/ OTL_LOCALDEF( void ) otl_gpos_validate( OTL_Bytes table, OTL_Validator valid ) { OTL_Bytes p = table; OTL_UInt scripts, features, lookups; OTL_CHECK( 10 ); if ( OTL_NEXT_USHORT( p ) != 0x10000UL ) OTL_INVALID_DATA; scripts = OTL_NEXT_USHORT( p ); features = OTL_NEXT_USHORT( p ); lookups = OTL_NEXT_USHORT( p ); otl_script_list_validate ( table + scripts, valid ); otl_feature_list_validate( table + features, valid ); otl_lookup_list_validate( table + lookups, 9, otl_gpos_validate_funcs, valid ); }