1 /**************************************************************************** 2 * 3 * gxvmort1.c 4 * 5 * TrueTypeGX/AAT mort table validation 6 * body for type1 (Contextual Substitution) subtable. 7 * 8 * Copyright 2005-2018 by 9 * suzuki toshiya, Masatake YAMATO, Red Hat K.K., 10 * David Turner, Robert Wilhelm, and Werner Lemberg. 11 * 12 * This file is part of the FreeType project, and may only be used, 13 * modified, and distributed under the terms of the FreeType project 14 * license, LICENSE.TXT. By continuing to use, modify, or distribute 15 * this file you indicate that you have read the license and 16 * understand and accept it fully. 17 * 18 */ 19 20 /**************************************************************************** 21 * 22 * gxvalid is derived from both gxlayout module and otvalid module. 23 * Development of gxlayout is supported by the Information-technology 24 * Promotion Agency(IPA), Japan. 25 * 26 */ 27 28 29 #include "gxvmort.h" 30 31 32 /************************************************************************** 33 * 34 * The macro FT_COMPONENT is used in trace mode. It is an implicit 35 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log 36 * messages during execution. 37 */ 38 #undef FT_COMPONENT 39 #define FT_COMPONENT trace_gxvmort 40 41 42 typedef struct GXV_mort_subtable_type1_StateOptRec_ 43 { 44 FT_UShort substitutionTable; 45 FT_UShort substitutionTable_length; 46 47 } GXV_mort_subtable_type1_StateOptRec, 48 *GXV_mort_subtable_type1_StateOptRecData; 49 50 #define GXV_MORT_SUBTABLE_TYPE1_HEADER_SIZE \ 51 ( GXV_STATETABLE_HEADER_SIZE + 2 ) 52 53 54 static void gxv_mort_subtable_type1_substitutionTable_load(FT_Bytes table,FT_Bytes limit,GXV_Validator gxvalid)55 gxv_mort_subtable_type1_substitutionTable_load( FT_Bytes table, 56 FT_Bytes limit, 57 GXV_Validator gxvalid ) 58 { 59 FT_Bytes p = table; 60 61 GXV_mort_subtable_type1_StateOptRecData optdata = 62 (GXV_mort_subtable_type1_StateOptRecData)gxvalid->statetable.optdata; 63 64 65 GXV_LIMIT_CHECK( 2 ); 66 optdata->substitutionTable = FT_NEXT_USHORT( p ); 67 } 68 69 70 static void gxv_mort_subtable_type1_subtable_setup(FT_UShort table_size,FT_UShort classTable,FT_UShort stateArray,FT_UShort entryTable,FT_UShort * classTable_length_p,FT_UShort * stateArray_length_p,FT_UShort * entryTable_length_p,GXV_Validator gxvalid)71 gxv_mort_subtable_type1_subtable_setup( FT_UShort table_size, 72 FT_UShort classTable, 73 FT_UShort stateArray, 74 FT_UShort entryTable, 75 FT_UShort* classTable_length_p, 76 FT_UShort* stateArray_length_p, 77 FT_UShort* entryTable_length_p, 78 GXV_Validator gxvalid ) 79 { 80 FT_UShort o[4]; 81 FT_UShort *l[4]; 82 FT_UShort buff[5]; 83 84 GXV_mort_subtable_type1_StateOptRecData optdata = 85 (GXV_mort_subtable_type1_StateOptRecData)gxvalid->statetable.optdata; 86 87 88 o[0] = classTable; 89 o[1] = stateArray; 90 o[2] = entryTable; 91 o[3] = optdata->substitutionTable; 92 l[0] = classTable_length_p; 93 l[1] = stateArray_length_p; 94 l[2] = entryTable_length_p; 95 l[3] = &( optdata->substitutionTable_length ); 96 97 gxv_set_length_by_ushort_offset( o, l, buff, 4, table_size, gxvalid ); 98 } 99 100 101 static void gxv_mort_subtable_type1_offset_to_subst_validate(FT_Short wordOffset,const FT_String * tag,FT_Byte state,GXV_Validator gxvalid)102 gxv_mort_subtable_type1_offset_to_subst_validate( 103 FT_Short wordOffset, 104 const FT_String* tag, 105 FT_Byte state, 106 GXV_Validator gxvalid ) 107 { 108 FT_UShort substTable; 109 FT_UShort substTable_limit; 110 111 FT_UNUSED( tag ); 112 FT_UNUSED( state ); 113 114 115 substTable = 116 ((GXV_mort_subtable_type1_StateOptRec *) 117 (gxvalid->statetable.optdata))->substitutionTable; 118 substTable_limit = 119 (FT_UShort)( substTable + 120 ((GXV_mort_subtable_type1_StateOptRec *) 121 (gxvalid->statetable.optdata))->substitutionTable_length ); 122 123 gxvalid->min_gid = (FT_UShort)( ( substTable - wordOffset * 2 ) / 2 ); 124 gxvalid->max_gid = (FT_UShort)( ( substTable_limit - wordOffset * 2 ) / 2 ); 125 gxvalid->max_gid = (FT_UShort)( FT_MAX( gxvalid->max_gid, 126 gxvalid->face->num_glyphs ) ); 127 128 /* XXX: check range? */ 129 130 /* TODO: min_gid & max_gid comparison with ClassTable contents */ 131 } 132 133 134 static void gxv_mort_subtable_type1_entry_validate(FT_Byte state,FT_UShort flags,GXV_StateTable_GlyphOffsetCPtr glyphOffset_p,FT_Bytes table,FT_Bytes limit,GXV_Validator gxvalid)135 gxv_mort_subtable_type1_entry_validate( 136 FT_Byte state, 137 FT_UShort flags, 138 GXV_StateTable_GlyphOffsetCPtr glyphOffset_p, 139 FT_Bytes table, 140 FT_Bytes limit, 141 GXV_Validator gxvalid ) 142 { 143 #ifdef GXV_LOAD_UNUSED_VARS 144 FT_UShort setMark; 145 FT_UShort dontAdvance; 146 #endif 147 FT_UShort reserved; 148 FT_Short markOffset; 149 FT_Short currentOffset; 150 151 FT_UNUSED( table ); 152 FT_UNUSED( limit ); 153 154 155 #ifdef GXV_LOAD_UNUSED_VARS 156 setMark = (FT_UShort)( flags >> 15 ); 157 dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 ); 158 #endif 159 reserved = (FT_UShort)( flags & 0x3FFF ); 160 161 markOffset = (FT_Short)( glyphOffset_p->ul >> 16 ); 162 currentOffset = (FT_Short)( glyphOffset_p->ul ); 163 164 if ( 0 < reserved ) 165 { 166 GXV_TRACE(( " non-zero bits found in reserved range\n" )); 167 GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); 168 } 169 170 gxv_mort_subtable_type1_offset_to_subst_validate( markOffset, 171 "markOffset", 172 state, 173 gxvalid ); 174 175 gxv_mort_subtable_type1_offset_to_subst_validate( currentOffset, 176 "currentOffset", 177 state, 178 gxvalid ); 179 } 180 181 182 static void gxv_mort_subtable_type1_substTable_validate(FT_Bytes table,FT_Bytes limit,GXV_Validator gxvalid)183 gxv_mort_subtable_type1_substTable_validate( FT_Bytes table, 184 FT_Bytes limit, 185 GXV_Validator gxvalid ) 186 { 187 FT_Bytes p = table; 188 FT_UShort num_gids = (FT_UShort)( 189 ((GXV_mort_subtable_type1_StateOptRec *) 190 (gxvalid->statetable.optdata))->substitutionTable_length / 2 ); 191 FT_UShort i; 192 193 194 GXV_NAME_ENTER( "validating contents of substitutionTable" ); 195 for ( i = 0; i < num_gids; i++ ) 196 { 197 FT_UShort dst_gid; 198 199 200 GXV_LIMIT_CHECK( 2 ); 201 dst_gid = FT_NEXT_USHORT( p ); 202 203 if ( dst_gid >= 0xFFFFU ) 204 continue; 205 206 if ( dst_gid < gxvalid->min_gid || gxvalid->max_gid < dst_gid ) 207 { 208 GXV_TRACE(( "substTable include a strange gid[%d]=%d >" 209 " out of define range (%d..%d)\n", 210 i, dst_gid, gxvalid->min_gid, gxvalid->max_gid )); 211 GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); 212 } 213 } 214 215 GXV_EXIT; 216 } 217 218 219 /* 220 * subtable for Contextual glyph substitution is a modified StateTable. 221 * In addition to classTable, stateArray, and entryTable, the field 222 * `substitutionTable' is added. 223 */ 224 FT_LOCAL_DEF( void ) gxv_mort_subtable_type1_validate(FT_Bytes table,FT_Bytes limit,GXV_Validator gxvalid)225 gxv_mort_subtable_type1_validate( FT_Bytes table, 226 FT_Bytes limit, 227 GXV_Validator gxvalid ) 228 { 229 FT_Bytes p = table; 230 231 GXV_mort_subtable_type1_StateOptRec st_rec; 232 233 234 GXV_NAME_ENTER( "mort chain subtable type1 (Contextual Glyph Subst)" ); 235 236 GXV_LIMIT_CHECK( GXV_MORT_SUBTABLE_TYPE1_HEADER_SIZE ); 237 238 gxvalid->statetable.optdata = 239 &st_rec; 240 gxvalid->statetable.optdata_load_func = 241 gxv_mort_subtable_type1_substitutionTable_load; 242 gxvalid->statetable.subtable_setup_func = 243 gxv_mort_subtable_type1_subtable_setup; 244 gxvalid->statetable.entry_glyphoffset_fmt = 245 GXV_GLYPHOFFSET_ULONG; 246 gxvalid->statetable.entry_validate_func = 247 248 gxv_mort_subtable_type1_entry_validate; 249 gxv_StateTable_validate( p, limit, gxvalid ); 250 251 gxv_mort_subtable_type1_substTable_validate( 252 table + st_rec.substitutionTable, 253 table + st_rec.substitutionTable + st_rec.substitutionTable_length, 254 gxvalid ); 255 256 GXV_EXIT; 257 } 258 259 260 /* END */ 261