diff options
Diffstat (limited to 'Build/source/libs/libttf/extend/ftxgsub.c')
-rw-r--r-- | Build/source/libs/libttf/extend/ftxgsub.c | 4377 |
1 files changed, 4377 insertions, 0 deletions
diff --git a/Build/source/libs/libttf/extend/ftxgsub.c b/Build/source/libs/libttf/extend/ftxgsub.c new file mode 100644 index 00000000000..fbac7425d3c --- /dev/null +++ b/Build/source/libs/libttf/extend/ftxgsub.c @@ -0,0 +1,4377 @@ +/******************************************************************* + * + * ftxgsub.c + * + * TrueType Open GSUB table support. + * + * Copyright 1996-2000 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. + * + ******************************************************************/ + +/* XXX There is *a lot* of duplicated code (cf. formats 5 and 6), but + I don't care currently. I believe that it would be possible to + save about 50% of TTO code by carefully designing the structures, + sharing as much as possible with extensive use of macros. This + is something for a volunteer :-) */ + +#include "tttypes.h" +#include "tttags.h" +#include "ttload.h" +#include "ttextend.h" +#include "ttmemory.h" +#include "ttfile.h" + +#include "ftxopen.h" +#include "ftxopenf.h" + + +#define GSUB_ID Build_Extension_ID( 'G', 'S', 'U', 'B' ) + + +#define ADD_String( in, num_in, out, num_out, glyph_data, component, ligID ) \ + ( ( error = TT_GSUB_Add_String( (in), (num_in), \ + (out), (num_out), \ + (glyph_data), (component), (ligID) \ + ) ) != TT_Err_Ok ) + + + static TT_Error Do_Glyph_Lookup( TTO_GSUBHeader* gsub, + UShort lookup_index, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + UShort context_length, + int nesting_level ); + + + + /********************** + * Auxiliary functions + **********************/ + + + /* The following function copies `num_out' elements from `glyph_data' + to `out', advancing the array pointer in the `in' structure by + `num_in' elements, and in `out' by `num_out' elements. If the + string (resp. the properties) array in `out' is empty or too + small, it allocates resp. reallocates the string (and properties) + array. Finally, it sets the `length' field of `out' equal to + `pos' of the `out' structure. + + If `component' is 0xFFFF, the value `in->component[in->pos]' + will be copied `num_out' times, otherwise `component' itself will + be used to fill `out->component'. + + If `ligID' is 0xFFFF, the value `in->lig_IDs[in->pos]' will be + copied `num_out' times, otherwise `ligID' itself will be used to + fill `out->ligIDs'. + + The properties (if defined) for all replaced glyphs are taken + from the glyph at position `in->pos'. */ + + EXPORT_FUNC + TT_Error TT_GSUB_Add_String( TTO_GSUB_String* in, + UShort num_in, + TTO_GSUB_String* out, + UShort num_out, + UShort* glyph_data, + UShort component, + UShort ligID ) + { + TT_Error error; + UShort i; + UShort p_in; + UShort* p_out; + + + /* sanity check */ + + if ( !in || !out || + in->length == 0 || in->pos >= in->length || + in->length < in->pos + num_in ) + return TT_Err_Invalid_Argument; + + if ( out->pos + num_out >= out->allocated ) + { + ULong size = out->pos + num_out + 256L; + + + /* The following works because all fields in `out' must be + initialized to zero (including the `string' field) for the + first use. */ + + if ( REALLOC_ARRAY( out->string, size, UShort ) ) + return error; + if ( REALLOC_ARRAY( out->components, size, UShort ) ) + return error; + if ( REALLOC_ARRAY( out->ligIDs, size, UShort ) ) + return error; + if ( in->properties ) + if ( REALLOC_ARRAY( out->properties, size, UShort ) ) + return error; + + out->allocated = size; + } + + if ( num_out ) + { + MEM_Copy( &out->string[out->pos], glyph_data, + num_out * sizeof ( UShort ) ); + + if ( component == 0xFFFF ) + component = in->components[in->pos]; + + p_out = out->components; + + for ( i = out->pos; i < out->pos + num_out; i++ ) + p_out[i] = component; + + p_out = out->ligIDs; + + if ( ligID == 0xFFFF ) + ligID = in->ligIDs[in->pos]; + + for ( i = out->pos; i < out->pos + num_out; i++ ) + p_out[i] = ligID; + + if ( in->properties ) + { + p_in = in->properties[in->pos]; + p_out = out->properties; + + for ( i = out->pos; i < out->pos + num_out; i++ ) + p_out[i] = p_in; + } + } + + in->pos += num_in; + out->pos += num_out; + + out->length = out->pos; + + return TT_Err_Ok; + } + + + + /********************** + * Extension Functions + **********************/ + + + static TT_Error GSUB_Create( void* ext, + PFace face ) + { + DEFINE_LOAD_LOCALS( face->stream ); + + TTO_GSUBHeader* gsub = (TTO_GSUBHeader*)ext; + Long table; + + + /* by convention */ + + if ( !gsub ) + return TT_Err_Ok; + + /* a null offset indicates that there is no GSUB table */ + + gsub->offset = 0; + + /* we store the start offset and the size of the subtable */ + + table = TT_LookUp_Table( face, TTAG_GSUB ); + if ( table < 0 ) + return TT_Err_Ok; /* The table is optional */ + + if ( FILE_Seek( face->dirTables[table].Offset ) || + ACCESS_Frame( 4L ) ) + return error; + + gsub->offset = FILE_Pos() - 4L; /* undo ACCESS_Frame() */ + gsub->Version = GET_ULong(); + + FORGET_Frame(); + + gsub->loaded = FALSE; + + return TT_Err_Ok; + } + + + static TT_Error GSUB_Destroy( void* ext, + PFace face ) + { + TTO_GSUBHeader* gsub = (TTO_GSUBHeader*)ext; + + + /* by convention */ + + if ( !gsub ) + return TT_Err_Ok; + + if ( gsub->loaded ) + { + Free_LookupList( &gsub->LookupList, GSUB ); + Free_FeatureList( &gsub->FeatureList ); + Free_ScriptList( &gsub->ScriptList ); + } + + return TT_Err_Ok; + } + + + EXPORT_FUNC + TT_Error TT_Init_GSUB_Extension( TT_Engine engine ) + { + PEngine_Instance _engine = HANDLE_Engine( engine ); + + + if ( !_engine ) + return TT_Err_Invalid_Engine; + + return TT_Register_Extension( _engine, + GSUB_ID, + sizeof ( TTO_GSUBHeader ), + GSUB_Create, + GSUB_Destroy ); + } + + + EXPORT_FUNC + TT_Error TT_Load_GSUB_Table( TT_Face face, + TTO_GSUBHeader* retptr, + TTO_GDEFHeader* gdef ) + { + ULong cur_offset, new_offset, base_offset; + + UShort i, num_lookups; + TT_Error error; + TT_Stream stream; + TTO_GSUBHeader* gsub; + TTO_Lookup* lo; + + PFace faze = HANDLE_Face( face ); + + + if ( !retptr ) + return TT_Err_Invalid_Argument; + + if ( !faze ) + return TT_Err_Invalid_Face_Handle; + + error = TT_Extension_Get( faze, GSUB_ID, (void**)&gsub ); + if ( error ) + return error; + + if ( gsub->offset == 0 ) + return TT_Err_Table_Missing; /* no GSUB table; nothing to do */ + + /* now access stream */ + + if ( USE_Stream( faze->stream, stream ) ) + return error; + + base_offset = gsub->offset; + + /* skip version */ + + if ( FILE_Seek( base_offset + 4L ) || + ACCESS_Frame( 2L ) ) + return error; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_ScriptList( &gsub->ScriptList, + faze ) ) != TT_Err_Ok ) + return error; + (void)FILE_Seek( cur_offset ); + + if ( ACCESS_Frame( 2L ) ) + goto Fail3; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_FeatureList( &gsub->FeatureList, + faze ) ) != TT_Err_Ok ) + goto Fail3; + (void)FILE_Seek( cur_offset ); + + if ( ACCESS_Frame( 2L ) ) + goto Fail2; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_LookupList( &gsub->LookupList, + faze, GSUB ) ) != TT_Err_Ok ) + goto Fail2; + + gsub->gdef = gdef; /* can be NULL */ + + /* We now check the LookupFlags for values larger than 0xFF to find + out whether we need to load the `MarkAttachClassDef' field of the + GDEF table -- this hack is necessary for OpenType 1.2 tables since + the version field of the GDEF table hasn't been incremented. + + For constructed GDEF tables, we only load it if + `MarkAttachClassDef_offset' is not zero (nevertheless, a build of + a constructed mark attach table is not supported currently). */ + + if ( gdef && + gdef->MarkAttachClassDef_offset && !gdef->MarkAttachClassDef.loaded ) + { + lo = gsub->LookupList.Lookup; + num_lookups = gsub->LookupList.LookupCount; + + for ( i = 0; i < num_lookups; i++ ) + { + if ( lo[i].LookupFlag & IGNORE_SPECIAL_MARKS ) + { + if ( FILE_Seek( gdef->MarkAttachClassDef_offset ) || + ACCESS_Frame( 2L ) ) + goto Fail1; + + new_offset = GET_UShort(); + + FORGET_Frame(); + + if ( !new_offset ) + return TTO_Err_Invalid_GDEF_SubTable; + + new_offset += base_offset; + + if ( FILE_Seek( new_offset ) || + ( error = Load_ClassDefinition( &gdef->MarkAttachClassDef, + 256, faze ) ) != TT_Err_Ok ) + goto Fail1; + + break; + } + } + } + + gsub->loaded = TRUE; + *retptr = *gsub; + DONE_Stream( stream ); + + return TT_Err_Ok; + + Fail1: + Free_LookupList( &gsub->LookupList, GSUB ); + + Fail2: + Free_FeatureList( &gsub->FeatureList ); + + Fail3: + Free_ScriptList( &gsub->ScriptList ); + + /* release stream */ + + DONE_Stream( stream ); + + return error; + } + + + + /***************************** + * SubTable related functions + *****************************/ + + + /* LookupType 1 */ + + /* SingleSubstFormat1 */ + /* SingleSubstFormat2 */ + + TT_Error Load_SingleSubst( TTO_SingleSubst* ss, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + ULong cur_offset, new_offset, base_offset; + + UShort* s; + + + base_offset = FILE_Pos(); + + if ( ACCESS_Frame( 4L ) ) + return error; + + ss->SubstFormat = GET_UShort(); + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_Coverage( &ss->Coverage, input ) ) != TT_Err_Ok ) + return error; + (void)FILE_Seek( cur_offset ); + + switch ( ss->SubstFormat ) + { + case 1: + if ( ACCESS_Frame( 2L ) ) + goto Fail2; + + ss->ssf.ssf1.DeltaGlyphID = GET_UShort(); + + FORGET_Frame(); + + break; + + case 2: + if ( ACCESS_Frame( 2L ) ) + goto Fail2; + + count = ss->ssf.ssf2.GlyphCount = GET_UShort(); + + FORGET_Frame(); + + ss->ssf.ssf2.Substitute = NULL; + + if ( ALLOC_ARRAY( ss->ssf.ssf2.Substitute, count, UShort ) ) + goto Fail2; + + s = ss->ssf.ssf2.Substitute; + + if ( ACCESS_Frame( count * 2L ) ) + goto Fail1; + + for ( n = 0; n < count; n++ ) + s[n] = GET_UShort(); + + FORGET_Frame(); + + break; + + default: + return TTO_Err_Invalid_GSUB_SubTable_Format; + } + + return TT_Err_Ok; + + Fail1: + FREE( s ); + + Fail2: + Free_Coverage( &ss->Coverage ); + return error; + } + + + void Free_SingleSubst( TTO_SingleSubst* ss ) + { + switch ( ss->SubstFormat ) + { + case 1: + break; + + case 2: + FREE( ss->ssf.ssf2.Substitute ); + break; + } + + Free_Coverage( &ss->Coverage ); + } + + + static TT_Error Lookup_SingleSubst( TTO_SingleSubst* ss, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + UShort flags, + UShort context_length, + TTO_GDEFHeader* gdef ) + { + UShort index, value[1], property; + TT_Error error; + + + if ( context_length != 0xFFFF && context_length < 1 ) + return TTO_Err_Not_Covered; + + if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) ) + return error; + + error = Coverage_Index( &ss->Coverage, in->string[in->pos], &index ); + if ( error ) + return error; + + switch ( ss->SubstFormat ) + { + case 1: + value[0] = ( in->string[in->pos] + ss->ssf.ssf1.DeltaGlyphID ) & 0xFFFF; + if ( ADD_String( in, 1, out, 1, value, 0xFFFF, 0xFFFF ) ) + return error; + break; + + case 2: + if ( index >= ss->ssf.ssf2.GlyphCount ) + return TTO_Err_Invalid_GSUB_SubTable; + value[0] = ss->ssf.ssf2.Substitute[index]; + if ( ADD_String( in, 1, out, 1, value, 0xFFFF, 0xFFFF ) ) + return error; + break; + + default: + return TTO_Err_Invalid_GSUB_SubTable; + } + + if ( gdef && gdef->NewGlyphClasses ) + { + /* we inherit the old glyph class to the substituted glyph */ + + error = Add_Glyph_Property( gdef, value[0], property ); + if ( error && error != TTO_Err_Not_Covered ) + return error; + } + + return TT_Err_Ok; + } + + + /* LookupType 2 */ + + /* Sequence */ + + static TT_Error Load_Sequence( TTO_Sequence* s, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + UShort* sub; + + + if ( ACCESS_Frame( 2L ) ) + return error; + + count = s->GlyphCount = GET_UShort(); + + FORGET_Frame(); + + s->Substitute = NULL; + + if ( count ) + { + if ( ALLOC_ARRAY( s->Substitute, count, UShort ) ) + return error; + + sub = s->Substitute; + + if ( ACCESS_Frame( count * 2L ) ) + { + FREE( sub ); + return error; + } + + for ( n = 0; n < count; n++ ) + sub[n] = GET_UShort(); + + FORGET_Frame(); + } + + return TT_Err_Ok; + } + + + static void Free_Sequence( TTO_Sequence* s ) + { + FREE( s->Substitute ); + } + + + /* MultipleSubstFormat1 */ + + TT_Error Load_MultipleSubst( TTO_MultipleSubst* ms, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + ULong cur_offset, new_offset, base_offset; + + TTO_Sequence* s; + + + base_offset = FILE_Pos(); + + if ( ACCESS_Frame( 4L ) ) + return error; + + ms->SubstFormat = GET_UShort(); /* should be 1 */ + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_Coverage( &ms->Coverage, input ) ) != TT_Err_Ok ) + return error; + (void)FILE_Seek( cur_offset ); + + if ( ACCESS_Frame( 2L ) ) + goto Fail2; + + count = ms->SequenceCount = GET_UShort(); + + FORGET_Frame(); + + ms->Sequence = NULL; + + if ( ALLOC_ARRAY( ms->Sequence, count, TTO_Sequence ) ) + goto Fail2; + + s = ms->Sequence; + + for ( n = 0; n < count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail1; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_Sequence( &s[n], input ) ) != TT_Err_Ok ) + goto Fail1; + (void)FILE_Seek( cur_offset ); + } + + return TT_Err_Ok; + + Fail1: + for ( n = 0; n < count; n++ ) + Free_Sequence( &s[n] ); + + FREE( s ); + + Fail2: + Free_Coverage( &ms->Coverage ); + return error; + } + + + void Free_MultipleSubst( TTO_MultipleSubst* ms ) + { + UShort n, count; + + TTO_Sequence* s; + + + if ( ms->Sequence ) + { + count = ms->SequenceCount; + s = ms->Sequence; + + for ( n = 0; n < count; n++ ) + Free_Sequence( &s[n] ); + + FREE( s ); + } + + Free_Coverage( &ms->Coverage ); + } + + + static TT_Error Lookup_MultipleSubst( TTO_MultipleSubst* ms, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + UShort flags, + UShort context_length, + TTO_GDEFHeader* gdef ) + { + TT_Error error; + UShort index, property, n, count; + UShort* s; + + + if ( context_length != 0xFFFF && context_length < 1 ) + return TTO_Err_Not_Covered; + + if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) ) + return error; + + error = Coverage_Index( &ms->Coverage, in->string[in->pos], &index ); + if ( error ) + return error; + + if ( index >= ms->SequenceCount ) + return TTO_Err_Invalid_GSUB_SubTable; + + count = ms->Sequence[index].GlyphCount; + s = ms->Sequence[index].Substitute; + + if ( ADD_String( in, 1, out, count, s, 0xFFFF, 0xFFFF ) ) + return error; + + if ( gdef && gdef->NewGlyphClasses ) + { + /* this is a guess only ... */ + + if ( property == TTO_LIGATURE ) + property = TTO_BASE_GLYPH; + + for ( n = 0; n < count; n++ ) + { + error = Add_Glyph_Property( gdef, s[n], property ); + if ( error && error != TTO_Err_Not_Covered ) + return error; + } + } + + return TT_Err_Ok; + } + + + /* LookupType 3 */ + + /* AlternateSet */ + + static TT_Error Load_AlternateSet( TTO_AlternateSet* as, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + UShort* a; + + + if ( ACCESS_Frame( 2L ) ) + return error; + + count = as->GlyphCount = GET_UShort(); + + FORGET_Frame(); + + as->Alternate = NULL; + + if ( ALLOC_ARRAY( as->Alternate, count, UShort ) ) + return error; + + a = as->Alternate; + + if ( ACCESS_Frame( count * 2L ) ) + { + FREE( a ); + return error; + } + + for ( n = 0; n < count; n++ ) + a[n] = GET_UShort(); + + FORGET_Frame(); + + return TT_Err_Ok; + } + + + static void Free_AlternateSet( TTO_AlternateSet* as ) + { + FREE( as->Alternate ); + } + + + /* AlternateSubstFormat1 */ + + TT_Error Load_AlternateSubst( TTO_AlternateSubst* as, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + ULong cur_offset, new_offset, base_offset; + + TTO_AlternateSet* aset; + + + base_offset = FILE_Pos(); + + if ( ACCESS_Frame( 4L ) ) + return error; + + as->SubstFormat = GET_UShort(); /* should be 1 */ + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_Coverage( &as->Coverage, input ) ) != TT_Err_Ok ) + return error; + (void)FILE_Seek( cur_offset ); + + if ( ACCESS_Frame( 2L ) ) + goto Fail2; + + count = as->AlternateSetCount = GET_UShort(); + + FORGET_Frame(); + + as->AlternateSet = NULL; + + if ( ALLOC_ARRAY( as->AlternateSet, count, TTO_AlternateSet ) ) + goto Fail2; + + aset = as->AlternateSet; + + for ( n = 0; n < count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail1; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_AlternateSet( &aset[n], input ) ) != TT_Err_Ok ) + goto Fail1; + (void)FILE_Seek( cur_offset ); + } + + return TT_Err_Ok; + + Fail1: + for ( n = 0; n < count; n++ ) + Free_AlternateSet( &aset[n] ); + + FREE( aset ); + + Fail2: + Free_Coverage( &as->Coverage ); + return error; + } + + + void Free_AlternateSubst( TTO_AlternateSubst* as ) + { + UShort n, count; + + TTO_AlternateSet* aset; + + + if ( as->AlternateSet ) + { + count = as->AlternateSetCount; + aset = as->AlternateSet; + + for ( n = 0; n < count; n++ ) + Free_AlternateSet( &aset[n] ); + + FREE( aset ); + } + + Free_Coverage( &as->Coverage ); + } + + + static TT_Error Lookup_AlternateSubst( TTO_GSUBHeader* gsub, + TTO_AlternateSubst* as, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + UShort flags, + UShort context_length, + TTO_GDEFHeader* gdef ) + { + TT_Error error; + UShort index, alt_index, property; + + TTO_AlternateSet aset; + + + if ( context_length != 0xFFFF && context_length < 1 ) + return TTO_Err_Not_Covered; + + if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) ) + return error; + + error = Coverage_Index( &as->Coverage, in->string[in->pos], &index ); + if ( error ) + return error; + + aset = as->AlternateSet[index]; + + /* we use a user-defined callback function to get the alternate index */ + + if ( gsub->altfunc ) + alt_index = (gsub->altfunc)( out->pos, in->string[in->pos], + aset.GlyphCount, aset.Alternate, + gsub->data ); + else + alt_index = 0; + + if ( ADD_String( in, 1, out, 1, &aset.Alternate[alt_index], + 0xFFFF, 0xFFFF ) ) + return error; + + if ( gdef && gdef->NewGlyphClasses ) + { + /* we inherit the old glyph class to the substituted glyph */ + + error = Add_Glyph_Property( gdef, aset.Alternate[alt_index], + property ); + if ( error && error != TTO_Err_Not_Covered ) + return error; + } + + return TT_Err_Ok; + } + + + /* LookupType 4 */ + + /* Ligature */ + + static TT_Error Load_Ligature( TTO_Ligature* l, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + UShort* c; + + + if ( ACCESS_Frame( 4L ) ) + return error; + + l->LigGlyph = GET_UShort(); + l->ComponentCount = GET_UShort(); + + FORGET_Frame(); + + l->Component = NULL; + + count = l->ComponentCount - 1; /* only ComponentCount - 1 elements */ + + if ( ALLOC_ARRAY( l->Component, count, UShort ) ) + return error; + + c = l->Component; + + if ( ACCESS_Frame( count * 2L ) ) + { + FREE( c ); + return error; + } + + for ( n = 0; n < count; n++ ) + c[n] = GET_UShort(); + + FORGET_Frame(); + + return TT_Err_Ok; + } + + + static void Free_Ligature( TTO_Ligature* l ) + { + FREE( l->Component ); + } + + + /* LigatureSet */ + + static TT_Error Load_LigatureSet( TTO_LigatureSet* ls, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + ULong cur_offset, new_offset, base_offset; + + TTO_Ligature* l; + + + base_offset = FILE_Pos(); + + if ( ACCESS_Frame( 2L ) ) + return error; + + count = ls->LigatureCount = GET_UShort(); + + FORGET_Frame(); + + ls->Ligature = NULL; + + if ( ALLOC_ARRAY( ls->Ligature, count, TTO_Ligature ) ) + return error; + + l = ls->Ligature; + + for ( n = 0; n < count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_Ligature( &l[n], input ) ) != TT_Err_Ok ) + goto Fail; + (void)FILE_Seek( cur_offset ); + } + + return TT_Err_Ok; + + Fail: + for ( n = 0; n < count; n++ ) + Free_Ligature( &l[n] ); + + FREE( l ); + return error; + } + + + static void Free_LigatureSet( TTO_LigatureSet* ls ) + { + UShort n, count; + + TTO_Ligature* l; + + + if ( ls->Ligature ) + { + count = ls->LigatureCount; + l = ls->Ligature; + + for ( n = 0; n < count; n++ ) + Free_Ligature( &l[n] ); + + FREE( l ); + } + } + + + /* LigatureSubstFormat1 */ + + TT_Error Load_LigatureSubst( TTO_LigatureSubst* ls, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + ULong cur_offset, new_offset, base_offset; + + TTO_LigatureSet* lset; + + + base_offset = FILE_Pos(); + + if ( ACCESS_Frame( 4L ) ) + return error; + + ls->SubstFormat = GET_UShort(); /* should be 1 */ + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_Coverage( &ls->Coverage, input ) ) != TT_Err_Ok ) + return error; + (void)FILE_Seek( cur_offset ); + + if ( ACCESS_Frame( 2L ) ) + goto Fail2; + + count = ls->LigatureSetCount = GET_UShort(); + + FORGET_Frame(); + + ls->LigatureSet = NULL; + + if ( ALLOC_ARRAY( ls->LigatureSet, count, TTO_LigatureSet ) ) + goto Fail2; + + lset = ls->LigatureSet; + + for ( n = 0; n < count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail1; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_LigatureSet( &lset[n], input ) ) != TT_Err_Ok ) + goto Fail1; + (void)FILE_Seek( cur_offset ); + } + + return TT_Err_Ok; + + Fail1: + for ( n = 0; n < count; n++ ) + Free_LigatureSet( &lset[n] ); + + FREE( lset ); + + Fail2: + Free_Coverage( &ls->Coverage ); + return error; + } + + + void Free_LigatureSubst( TTO_LigatureSubst* ls ) + { + UShort n, count; + + TTO_LigatureSet* lset; + + + if ( ls->LigatureSet ) + { + count = ls->LigatureSetCount; + lset = ls->LigatureSet; + + for ( n = 0; n < count; n++ ) + Free_LigatureSet( &lset[n] ); + + FREE( lset ); + } + + Free_Coverage( &ls->Coverage ); + } + + + static TT_Error Lookup_LigatureSubst( TTO_LigatureSubst* ls, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + UShort flags, + UShort context_length, + TTO_GDEFHeader* gdef ) + { + UShort index, property; + TT_Error error; + UShort numlig, i, j, is_mark, first_is_mark = FALSE; + UShort* s_in; + UShort* c; + + TTO_Ligature* lig; + + + if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) ) + return error; + + if ( property == TTO_MARK ) + first_is_mark = TRUE; + + error = Coverage_Index( &ls->Coverage, in->string[in->pos], &index ); + if ( error ) + return error; + + if ( index >= ls->LigatureSetCount ) + return TTO_Err_Invalid_GSUB_SubTable; + + lig = ls->LigatureSet[index].Ligature; + + for ( numlig = ls->LigatureSet[index].LigatureCount; + numlig; + numlig--, lig++ ) + { + if ( in->pos + lig->ComponentCount > in->length ) + continue; /* Not enough glyphs in input */ + + s_in = &in->string[in->pos]; + c = lig->Component; + + is_mark = first_is_mark; + + if ( context_length != 0xFFFF && context_length < lig->ComponentCount ) + break; + + for ( i = 1, j = 1; i < lig->ComponentCount; i++, j++ ) + { + while ( CHECK_Property( gdef, s_in[j], flags, &property ) ) + { + if ( error && error != TTO_Err_Not_Covered ) + return error; + + if ( in->pos + j < in->length ) + j++; + else + break; + } + + if ( property != TTO_MARK ) + is_mark = FALSE; + + if ( s_in[j] != c[i - 1] ) + break; + } + + if ( i == lig->ComponentCount ) + { + if ( gdef && gdef->NewGlyphClasses ) + { + /* this is just a guess ... */ + + error = Add_Glyph_Property( gdef, lig->LigGlyph, + is_mark ? TTO_MARK : TTO_LIGATURE ); + if ( error && error != TTO_Err_Not_Covered ) + return error; + } + + if ( i == j ) + { + /* We don't use a new ligature ID if there are no skipped + glyphs and the ligature already has an ID. */ + + if ( in->ligIDs[in->pos] ) + { + if ( ADD_String( in, i, out, 1, &lig->LigGlyph, + 0xFFFF, 0xFFFF ) ) + return error; + } + else + { + if ( ADD_String( in, i, out, 1, &lig->LigGlyph, + 0xFFFF, in->max_ligID ) ) + return error; + + (in->max_ligID)++; + } + } + else + { + if ( ADD_String( in, 1, out, 1, &lig->LigGlyph, + 0xFFFF, in->max_ligID ) ) + return error; + + /* Now we must do a second loop to copy the skipped glyphs to + `out' and assign component values to it. We start with the + glyph after the first component. Glyphs between component + i and i+1 belong to component i. Together with the ligID + value it is later possible to check whether a specific + component value really belongs to a given ligature. */ + + for ( i = 0; i < lig->ComponentCount - 1; i++ ) + { + while ( CHECK_Property( gdef, in->string[in->pos], + flags, &property ) ) + if ( ADD_String( in, 1, out, 1, &in->string[in->pos], + i, in->max_ligID ) ) + return error; + + (in->pos)++; + } + + (in->max_ligID)++; + } + + return TT_Err_Ok; + } + } + + return TTO_Err_Not_Covered; + } + + + /* Do the actual substitution for a context substitution (either format + 5 or 6). This is only called after we've determined that the input + matches the subrule. */ + + static TT_Error Do_ContextSubst( TTO_GSUBHeader* gsub, + UShort GlyphCount, + UShort SubstCount, + TTO_SubstLookupRecord* subst, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + int nesting_level ) + { + TT_Error error; + UShort i, old_pos; + + + i = 0; + + while ( i < GlyphCount ) + { + if ( SubstCount && i == subst->SequenceIndex ) + { + old_pos = in->pos; + + /* Do a substitution */ + + error = Do_Glyph_Lookup( gsub, subst->LookupListIndex, in, out, + GlyphCount, nesting_level ); + + subst++; + SubstCount--; + i += in->pos - old_pos; + + if ( error == TTO_Err_Not_Covered ) + { + /* XXX "can't happen" -- but don't count on it */ + + if ( ADD_String( in, 1, out, 1, &in->string[in->pos], + 0xFFFF, 0xFFFF ) ) + return error; + i++; + } + else if ( error ) + return error; + } + else + { + /* No substitution for this index */ + + if ( ADD_String( in, 1, out, 1, &in->string[in->pos], + 0xFFFF, 0xFFFF ) ) + return error; + i++; + } + } + + return TT_Err_Ok; + } + + + /* LookupType 5 */ + + /* SubRule */ + + static TT_Error Load_SubRule( TTO_SubRule* sr, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + UShort* i; + + TTO_SubstLookupRecord* slr; + + + if ( ACCESS_Frame( 4L ) ) + return error; + + sr->GlyphCount = GET_UShort(); + sr->SubstCount = GET_UShort(); + + FORGET_Frame(); + + sr->Input = NULL; + + count = sr->GlyphCount - 1; /* only GlyphCount - 1 elements */ + + if ( ALLOC_ARRAY( sr->Input, count, UShort ) ) + return error; + + i = sr->Input; + + if ( ACCESS_Frame( count * 2L ) ) + goto Fail2; + + for ( n = 0; n < count; n++ ) + i[n] = GET_UShort(); + + FORGET_Frame(); + + sr->SubstLookupRecord = NULL; + + count = sr->SubstCount; + + if ( ALLOC_ARRAY( sr->SubstLookupRecord, count, TTO_SubstLookupRecord ) ) + goto Fail2; + + slr = sr->SubstLookupRecord; + + if ( ACCESS_Frame( count * 4L ) ) + goto Fail1; + + for ( n = 0; n < count; n++ ) + { + slr[n].SequenceIndex = GET_UShort(); + slr[n].LookupListIndex = GET_UShort(); + } + + FORGET_Frame(); + + return TT_Err_Ok; + + Fail1: + FREE( slr ); + + Fail2: + FREE( i ); + return error; + } + + + static void Free_SubRule( TTO_SubRule* sr ) + { + FREE( sr->SubstLookupRecord ); + FREE( sr->Input ); + } + + + /* SubRuleSet */ + + static TT_Error Load_SubRuleSet( TTO_SubRuleSet* srs, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + ULong cur_offset, new_offset, base_offset; + + TTO_SubRule* sr; + + + base_offset = FILE_Pos(); + + if ( ACCESS_Frame( 2L ) ) + return error; + + count = srs->SubRuleCount = GET_UShort(); + + FORGET_Frame(); + + srs->SubRule = NULL; + + if ( ALLOC_ARRAY( srs->SubRule, count, TTO_SubRule ) ) + return error; + + sr = srs->SubRule; + + for ( n = 0; n < count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_SubRule( &sr[n], input ) ) != TT_Err_Ok ) + goto Fail; + (void)FILE_Seek( cur_offset ); + } + + return TT_Err_Ok; + + Fail: + for ( n = 0; n < count; n++ ) + Free_SubRule( &sr[n] ); + + FREE( sr ); + return error; + } + + + static void Free_SubRuleSet( TTO_SubRuleSet* srs ) + { + UShort n, count; + + TTO_SubRule* sr; + + + if ( srs->SubRule ) + { + count = srs->SubRuleCount; + sr = srs->SubRule; + + for ( n = 0; n < count; n++ ) + Free_SubRule( &sr[n] ); + + FREE( sr ); + } + } + + + /* ContextSubstFormat1 */ + + static TT_Error Load_ContextSubst1( TTO_ContextSubstFormat1* csf1, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + ULong cur_offset, new_offset, base_offset; + + TTO_SubRuleSet* srs; + + + base_offset = FILE_Pos() - 2L; + + if ( ACCESS_Frame( 2L ) ) + return error; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_Coverage( &csf1->Coverage, input ) ) != TT_Err_Ok ) + return error; + (void)FILE_Seek( cur_offset ); + + if ( ACCESS_Frame( 2L ) ) + goto Fail2; + + count = csf1->SubRuleSetCount = GET_UShort(); + + FORGET_Frame(); + + csf1->SubRuleSet = NULL; + + if ( ALLOC_ARRAY( csf1->SubRuleSet, count, TTO_SubRuleSet ) ) + goto Fail2; + + srs = csf1->SubRuleSet; + + for ( n = 0; n < count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail1; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_SubRuleSet( &srs[n], input ) ) != TT_Err_Ok ) + goto Fail1; + (void)FILE_Seek( cur_offset ); + } + + return TT_Err_Ok; + + Fail1: + for ( n = 0; n < count; n++ ) + Free_SubRuleSet( &srs[n] ); + + FREE( srs ); + + Fail2: + Free_Coverage( &csf1->Coverage ); + return error; + } + + + static void Free_Context1( TTO_ContextSubstFormat1* csf1 ) + { + UShort n, count; + + TTO_SubRuleSet* srs; + + + if ( csf1->SubRuleSet ) + { + count = csf1->SubRuleSetCount; + srs = csf1->SubRuleSet; + + for ( n = 0; n < count; n++ ) + Free_SubRuleSet( &srs[n] ); + + FREE( srs ); + } + + Free_Coverage( &csf1->Coverage ); + } + + + /* SubClassRule */ + + static TT_Error Load_SubClassRule( TTO_ContextSubstFormat2* csf2, + TTO_SubClassRule* scr, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + + UShort* c; + TTO_SubstLookupRecord* slr; + Bool* d; + + + if ( ACCESS_Frame( 4L ) ) + return error; + + scr->GlyphCount = GET_UShort(); + scr->SubstCount = GET_UShort(); + + if ( scr->GlyphCount > csf2->MaxContextLength ) + csf2->MaxContextLength = scr->GlyphCount; + + FORGET_Frame(); + + scr->Class = NULL; + + count = scr->GlyphCount - 1; /* only GlyphCount - 1 elements */ + + if ( ALLOC_ARRAY( scr->Class, count, UShort ) ) + return error; + + c = scr->Class; + d = csf2->ClassDef.Defined; + + if ( ACCESS_Frame( count * 2L ) ) + goto Fail2; + + for ( n = 0; n < count; n++ ) + { + c[n] = GET_UShort(); + + /* We check whether the specific class is used at all. If not, + class 0 is used instead. */ + + if ( !d[c[n]] ) + c[n] = 0; + } + + FORGET_Frame(); + + scr->SubstLookupRecord = NULL; + + count = scr->SubstCount; + + if ( ALLOC_ARRAY( scr->SubstLookupRecord, count, TTO_SubstLookupRecord ) ) + goto Fail2; + + slr = scr->SubstLookupRecord; + + if ( ACCESS_Frame( count * 4L ) ) + goto Fail1; + + for ( n = 0; n < count; n++ ) + { + slr[n].SequenceIndex = GET_UShort(); + slr[n].LookupListIndex = GET_UShort(); + } + + FORGET_Frame(); + + return TT_Err_Ok; + + Fail1: + FREE( slr ); + + Fail2: + FREE( c ); + return error; + } + + + static void Free_SubClassRule( TTO_SubClassRule* scr ) + { + FREE( scr->SubstLookupRecord ); + FREE( scr->Class ); + } + + + /* SubClassSet */ + + static TT_Error Load_SubClassSet( TTO_ContextSubstFormat2* csf2, + TTO_SubClassSet* scs, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + ULong cur_offset, new_offset, base_offset; + + TTO_SubClassRule* scr; + + + base_offset = FILE_Pos(); + + if ( ACCESS_Frame( 2L ) ) + return error; + + count = scs->SubClassRuleCount = GET_UShort(); + + FORGET_Frame(); + + scs->SubClassRule = NULL; + + if ( ALLOC_ARRAY( scs->SubClassRule, count, TTO_SubClassRule ) ) + return error; + + scr = scs->SubClassRule; + + for ( n = 0; n < count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_SubClassRule( csf2, &scr[n], + input ) ) != TT_Err_Ok ) + goto Fail; + (void)FILE_Seek( cur_offset ); + } + + return TT_Err_Ok; + + Fail: + for ( n = 0; n < count; n++ ) + Free_SubClassRule( &scr[n] ); + + FREE( scr ); + return error; + } + + + static void Free_SubClassSet( TTO_SubClassSet* scs ) + { + UShort n, count; + + TTO_SubClassRule* scr; + + + if ( scs->SubClassRule ) + { + count = scs->SubClassRuleCount; + scr = scs->SubClassRule; + + for ( n = 0; n < count; n++ ) + Free_SubClassRule( &scr[n] ); + + FREE( scr ); + } + } + + + /* ContextSubstFormat2 */ + + static TT_Error Load_ContextSubst2( TTO_ContextSubstFormat2* csf2, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + ULong cur_offset, new_offset, base_offset; + + TTO_SubClassSet* scs; + + + base_offset = FILE_Pos() - 2; + + if ( ACCESS_Frame( 2L ) ) + return error; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_Coverage( &csf2->Coverage, input ) ) != TT_Err_Ok ) + return error; + (void)FILE_Seek( cur_offset ); + + if ( ACCESS_Frame( 4L ) ) + goto Fail3; + + new_offset = GET_UShort() + base_offset; + + /* `SubClassSetCount' is the upper limit for class values, thus we + read it now to make an additional safety check. */ + + count = csf2->SubClassSetCount = GET_UShort(); + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_ClassDefinition( &csf2->ClassDef, count, + input ) ) != TT_Err_Ok ) + goto Fail3; + (void)FILE_Seek( cur_offset ); + + csf2->SubClassSet = NULL; + csf2->MaxContextLength = 0; + + if ( ALLOC_ARRAY( csf2->SubClassSet, count, TTO_SubClassSet ) ) + goto Fail2; + + scs = csf2->SubClassSet; + + for ( n = 0; n < count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail1; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + if ( new_offset != base_offset ) /* not a NULL offset */ + { + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_SubClassSet( csf2, &scs[n], + input ) ) != TT_Err_Ok ) + goto Fail1; + (void)FILE_Seek( cur_offset ); + } + else + { + /* we create a SubClassSet table with no entries */ + + csf2->SubClassSet[n].SubClassRuleCount = 0; + csf2->SubClassSet[n].SubClassRule = NULL; + } + } + + return TT_Err_Ok; + + Fail1: + for ( n = 0; n < count; n++ ) + Free_SubClassSet( &scs[n] ); + + FREE( scs ); + + Fail2: + Free_ClassDefinition( &csf2->ClassDef ); + + Fail3: + Free_Coverage( &csf2->Coverage ); + return error; + } + + + static void Free_Context2( TTO_ContextSubstFormat2* csf2 ) + { + UShort n, count; + + TTO_SubClassSet* scs; + + + if ( csf2->SubClassSet ) + { + count = csf2->SubClassSetCount; + scs = csf2->SubClassSet; + + for ( n = 0; n < count; n++ ) + Free_SubClassSet( &scs[n] ); + + FREE( scs ); + } + + Free_ClassDefinition( &csf2->ClassDef ); + Free_Coverage( &csf2->Coverage ); + } + + + /* ContextSubstFormat3 */ + + static TT_Error Load_ContextSubst3( TTO_ContextSubstFormat3* csf3, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + ULong cur_offset, new_offset, base_offset; + + TTO_Coverage* c; + TTO_SubstLookupRecord* slr; + + + base_offset = FILE_Pos() - 2L; + + if ( ACCESS_Frame( 4L ) ) + return error; + + csf3->GlyphCount = GET_UShort(); + csf3->SubstCount = GET_UShort(); + + FORGET_Frame(); + + csf3->Coverage = NULL; + + count = csf3->GlyphCount; + + if ( ALLOC_ARRAY( csf3->Coverage, count, TTO_Coverage ) ) + return error; + + c = csf3->Coverage; + + for ( n = 0; n < count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail2; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_Coverage( &c[n], input ) ) != TT_Err_Ok ) + goto Fail2; + (void)FILE_Seek( cur_offset ); + } + + csf3->SubstLookupRecord = NULL; + + count = csf3->SubstCount; + + if ( ALLOC_ARRAY( csf3->SubstLookupRecord, count, + TTO_SubstLookupRecord ) ) + goto Fail2; + + slr = csf3->SubstLookupRecord; + + if ( ACCESS_Frame( count * 4L ) ) + goto Fail1; + + for ( n = 0; n < count; n++ ) + { + slr[n].SequenceIndex = GET_UShort(); + slr[n].LookupListIndex = GET_UShort(); + } + + FORGET_Frame(); + + return TT_Err_Ok; + + Fail1: + FREE( slr ); + + Fail2: + for ( n = 0; n < count; n++ ) + Free_Coverage( &c[n] ); + + FREE( c ); + return error; + } + + + static void Free_Context3( TTO_ContextSubstFormat3* csf3 ) + { + UShort n, count; + + TTO_Coverage* c; + + + FREE( csf3->SubstLookupRecord ); + + if ( csf3->Coverage ) + { + count = csf3->GlyphCount; + c = csf3->Coverage; + + for ( n = 0; n < count; n++ ) + Free_Coverage( &c[n] ); + + FREE( c ); + } + } + + + /* ContextSubst */ + + TT_Error Load_ContextSubst( TTO_ContextSubst* cs, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + + if ( ACCESS_Frame( 2L ) ) + return error; + + cs->SubstFormat = GET_UShort(); + + FORGET_Frame(); + + switch ( cs->SubstFormat ) + { + case 1: + return Load_ContextSubst1( &cs->csf.csf1, input ); + + case 2: + return Load_ContextSubst2( &cs->csf.csf2, input ); + + case 3: + return Load_ContextSubst3( &cs->csf.csf3, input ); + + default: + return TTO_Err_Invalid_GSUB_SubTable_Format; + } + + return TT_Err_Ok; /* never reached */ + } + + + void Free_ContextSubst( TTO_ContextSubst* cs ) + { + switch ( cs->SubstFormat ) + { + case 1: + Free_Context1( &cs->csf.csf1 ); + break; + + case 2: + Free_Context2( &cs->csf.csf2 ); + break; + + case 3: + Free_Context3( &cs->csf.csf3 ); + break; + } + } + + + static TT_Error Lookup_ContextSubst1( + TTO_GSUBHeader* gsub, + TTO_ContextSubstFormat1* csf1, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + UShort flags, + UShort context_length, + int nesting_level ) + { + UShort index, property; + UShort i, j, k, numsr; + TT_Error error; + UShort* s_in; + + TTO_SubRule* sr; + TTO_GDEFHeader* gdef; + + + gdef = gsub->gdef; + + if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) ) + return error; + + error = Coverage_Index( &csf1->Coverage, in->string[in->pos], &index ); + if ( error ) + return error; + + sr = csf1->SubRuleSet[index].SubRule; + numsr = csf1->SubRuleSet[index].SubRuleCount; + + for ( k = 0; k < numsr; k++ ) + { + if ( context_length != 0xFFFF && context_length < sr[k].GlyphCount ) + continue; + + if ( in->pos + sr[k].GlyphCount > in->length ) + continue; /* context is too long */ + + s_in = &in->string[in->pos]; + + for ( i = 1, j = 1; i < sr[k].GlyphCount; i++, j++ ) + { + while ( CHECK_Property( gdef, s_in[j], flags, &property ) ) + { + if ( error && error != TTO_Err_Not_Covered ) + return error; + + if ( in->pos + j < in->length ) + j++; + else + break; + } + + if ( s_in[j] != sr[k].Input[i - 1] ) + break; + } + + if ( i == sr[k].GlyphCount ) + return Do_ContextSubst( gsub, sr[k].GlyphCount, + sr[k].SubstCount, sr[k].SubstLookupRecord, + in, out, + nesting_level ); + } + + return TTO_Err_Not_Covered; + } + + + static TT_Error Lookup_ContextSubst2( + TTO_GSUBHeader* gsub, + TTO_ContextSubstFormat2* csf2, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + UShort flags, + UShort context_length, + int nesting_level ) + { + UShort index, property; + TT_Error error; + UShort i, j, k, known_classes; + + UShort* classes; + UShort* s_in; + UShort* cl; + + TTO_SubClassSet* scs; + TTO_SubClassRule* sr; + TTO_GDEFHeader* gdef; + + + gdef = gsub->gdef; + + if ( ALLOC_ARRAY( classes, csf2->MaxContextLength, UShort ) ) + return error; + + if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) ) + return error; + + /* Note: The coverage table in format 2 doesn't give an index into + anything. It just lets us know whether or not we need to + do any lookup at all. */ + + error = Coverage_Index( &csf2->Coverage, in->string[in->pos], &index ); + if ( error ) + goto End; + + error = Get_Class( &csf2->ClassDef, in->string[in->pos], + &classes[0], NULL ); + if ( error ) + goto End; + known_classes = 0; + + scs = &csf2->SubClassSet[classes[0]]; + if ( !scs ) + { + error = TTO_Err_Invalid_GSUB_SubTable; + goto End; + } + + for ( k = 0; k < scs->SubClassRuleCount; k++ ) + { + sr = &scs->SubClassRule[k]; + + if ( context_length != 0xFFFF && context_length < sr->GlyphCount ) + continue; + + if ( in->pos + sr->GlyphCount > in->length ) + continue; /* context is too long */ + + s_in = &in->string[in->pos]; + cl = sr->Class; + + /* Start at 1 because [0] is implied */ + + for ( i = 1, j = 1; i < sr->GlyphCount; i++, j++ ) + { + while ( CHECK_Property( gdef, s_in[j], flags, &property ) ) + { + if ( error && error != TTO_Err_Not_Covered ) + return error; + + if ( in->pos + j < in->length ) + j++; + else + break; + } + + if ( i > known_classes ) + { + /* Keeps us from having to do this for each rule */ + + error = Get_Class( &csf2->ClassDef, s_in[j], &classes[i], NULL ); + if ( error && error != TTO_Err_Not_Covered ) + return error; + known_classes = i; + } + + if ( cl[i - 1] != classes[i] ) + break; + } + + if ( i == sr->GlyphCount ) + { + error = Do_ContextSubst( gsub, sr->GlyphCount, + sr->SubstCount, sr->SubstLookupRecord, + in, out, + nesting_level ); + goto End; + } + } + + error = TTO_Err_Not_Covered; + + End: + FREE( classes ); + return error; + } + + + static TT_Error Lookup_ContextSubst3( + TTO_GSUBHeader* gsub, + TTO_ContextSubstFormat3* csf3, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + UShort flags, + UShort context_length, + int nesting_level ) + { + TT_Error error; + UShort index, i, j, property; + UShort* s_in; + + TTO_Coverage* c; + TTO_GDEFHeader* gdef; + + + gdef = gsub->gdef; + + if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) ) + return error; + + if ( context_length != 0xFFFF && context_length < csf3->GlyphCount ) + return TTO_Err_Not_Covered; + + if ( in->pos + csf3->GlyphCount > in->length ) + return TTO_Err_Not_Covered; /* context is too long */ + + s_in = &in->string[in->pos]; + c = csf3->Coverage; + + for ( i = 1, j = 1; i < csf3->GlyphCount; i++, j++ ) + { + while ( CHECK_Property( gdef, s_in[j], flags, &property ) ) + { + if ( error && error != TTO_Err_Not_Covered ) + return error; + + if ( in->pos + j < in->length ) + j++; + else + return TTO_Err_Not_Covered; + } + + error = Coverage_Index( &c[i], s_in[j], &index ); + if ( error ) + return error; + } + + return Do_ContextSubst( gsub, csf3->GlyphCount, + csf3->SubstCount, csf3->SubstLookupRecord, + in, out, + nesting_level ); + } + + + static TT_Error Lookup_ContextSubst( TTO_GSUBHeader* gsub, + TTO_ContextSubst* cs, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + UShort flags, + UShort context_length, + int nesting_level ) + { + switch ( cs->SubstFormat ) + { + case 1: + return Lookup_ContextSubst1( gsub, &cs->csf.csf1, in, out, + flags, context_length, nesting_level ); + + case 2: + return Lookup_ContextSubst2( gsub, &cs->csf.csf2, in, out, + flags, context_length, nesting_level ); + + case 3: + return Lookup_ContextSubst3( gsub, &cs->csf.csf3, in, out, + flags, context_length, nesting_level ); + + default: + return TTO_Err_Invalid_GSUB_SubTable_Format; + } + + return TT_Err_Ok; /* never reached */ + } + + + /* LookupType 6 */ + + /* ChainSubRule */ + + static TT_Error Load_ChainSubRule( TTO_ChainSubRule* csr, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + UShort* b; + UShort* i; + UShort* l; + + TTO_SubstLookupRecord* slr; + + + if ( ACCESS_Frame( 2L ) ) + return error; + + csr->BacktrackGlyphCount = GET_UShort(); + + FORGET_Frame(); + + csr->Backtrack = NULL; + + count = csr->BacktrackGlyphCount; + + if ( ALLOC_ARRAY( csr->Backtrack, count, UShort ) ) + return error; + + b = csr->Backtrack; + + if ( ACCESS_Frame( count * 2L ) ) + goto Fail4; + + for ( n = 0; n < count; n++ ) + b[n] = GET_UShort(); + + FORGET_Frame(); + + if ( ACCESS_Frame( 2L ) ) + goto Fail4; + + csr->InputGlyphCount = GET_UShort(); + + FORGET_Frame(); + + csr->Input = NULL; + + count = csr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */ + + if ( ALLOC_ARRAY( csr->Input, count, UShort ) ) + goto Fail4; + + i = csr->Input; + + if ( ACCESS_Frame( count * 2L ) ) + goto Fail3; + + for ( n = 0; n < count; n++ ) + i[n] = GET_UShort(); + + FORGET_Frame(); + + if ( ACCESS_Frame( 2L ) ) + goto Fail3; + + csr->LookaheadGlyphCount = GET_UShort(); + + FORGET_Frame(); + + csr->Lookahead = NULL; + + count = csr->LookaheadGlyphCount; + + if ( ALLOC_ARRAY( csr->Lookahead, count, UShort ) ) + goto Fail3; + + l = csr->Lookahead; + + if ( ACCESS_Frame( count * 2L ) ) + goto Fail2; + + for ( n = 0; n < count; n++ ) + l[n] = GET_UShort(); + + FORGET_Frame(); + + if ( ACCESS_Frame( 2L ) ) + goto Fail2; + + csr->SubstCount = GET_UShort(); + + FORGET_Frame(); + + csr->SubstLookupRecord = NULL; + + count = csr->SubstCount; + + if ( ALLOC_ARRAY( csr->SubstLookupRecord, count, TTO_SubstLookupRecord ) ) + goto Fail2; + + slr = csr->SubstLookupRecord; + + if ( ACCESS_Frame( count * 4L ) ) + goto Fail1; + + for ( n = 0; n < count; n++ ) + { + slr[n].SequenceIndex = GET_UShort(); + slr[n].LookupListIndex = GET_UShort(); + } + + FORGET_Frame(); + + return TT_Err_Ok; + + Fail1: + FREE( slr ); + + Fail2: + FREE( l ); + + Fail3: + FREE( i ); + + Fail4: + FREE( b ); + return error; + } + + + static void Free_ChainSubRule( TTO_ChainSubRule* csr ) + { + FREE( csr->SubstLookupRecord ); + FREE( csr->Lookahead ); + FREE( csr->Input ); + FREE( csr->Backtrack ); + } + + + /* ChainSubRuleSet */ + + static TT_Error Load_ChainSubRuleSet( TTO_ChainSubRuleSet* csrs, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + ULong cur_offset, new_offset, base_offset; + + TTO_ChainSubRule* csr; + + + base_offset = FILE_Pos(); + + if ( ACCESS_Frame( 2L ) ) + return error; + + count = csrs->ChainSubRuleCount = GET_UShort(); + + FORGET_Frame(); + + csrs->ChainSubRule = NULL; + + if ( ALLOC_ARRAY( csrs->ChainSubRule, count, TTO_ChainSubRule ) ) + return error; + + csr = csrs->ChainSubRule; + + for ( n = 0; n < count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_ChainSubRule( &csr[n], input ) ) != TT_Err_Ok ) + goto Fail; + (void)FILE_Seek( cur_offset ); + } + + return TT_Err_Ok; + + Fail: + for ( n = 0; n < count; n++ ) + Free_ChainSubRule( &csr[n] ); + + FREE( csr ); + return error; + } + + + static void Free_ChainSubRuleSet( TTO_ChainSubRuleSet* csrs ) + { + UShort n, count; + + TTO_ChainSubRule* csr; + + + if ( csrs->ChainSubRule ) + { + count = csrs->ChainSubRuleCount; + csr = csrs->ChainSubRule; + + for ( n = 0; n < count; n++ ) + Free_ChainSubRule( &csr[n] ); + + FREE( csr ); + } + } + + + /* ChainContextSubstFormat1 */ + + static TT_Error Load_ChainContextSubst1( + TTO_ChainContextSubstFormat1* ccsf1, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + ULong cur_offset, new_offset, base_offset; + + TTO_ChainSubRuleSet* csrs; + + + base_offset = FILE_Pos() - 2L; + + if ( ACCESS_Frame( 2L ) ) + return error; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_Coverage( &ccsf1->Coverage, input ) ) != TT_Err_Ok ) + return error; + (void)FILE_Seek( cur_offset ); + + if ( ACCESS_Frame( 2L ) ) + goto Fail2; + + count = ccsf1->ChainSubRuleSetCount = GET_UShort(); + + FORGET_Frame(); + + ccsf1->ChainSubRuleSet = NULL; + + if ( ALLOC_ARRAY( ccsf1->ChainSubRuleSet, count, TTO_ChainSubRuleSet ) ) + goto Fail2; + + csrs = ccsf1->ChainSubRuleSet; + + for ( n = 0; n < count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail1; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_ChainSubRuleSet( &csrs[n], input ) ) != TT_Err_Ok ) + goto Fail1; + (void)FILE_Seek( cur_offset ); + } + + return TT_Err_Ok; + + Fail1: + for ( n = 0; n < count; n++ ) + Free_ChainSubRuleSet( &csrs[n] ); + + FREE( csrs ); + + Fail2: + Free_Coverage( &ccsf1->Coverage ); + return error; + } + + + static void Free_ChainContext1( TTO_ChainContextSubstFormat1* ccsf1 ) + { + UShort n, count; + + TTO_ChainSubRuleSet* csrs; + + + if ( ccsf1->ChainSubRuleSet ) + { + count = ccsf1->ChainSubRuleSetCount; + csrs = ccsf1->ChainSubRuleSet; + + for ( n = 0; n < count; n++ ) + Free_ChainSubRuleSet( &csrs[n] ); + + FREE( csrs ); + } + + Free_Coverage( &ccsf1->Coverage ); + } + + + /* ChainSubClassRule */ + + static TT_Error Load_ChainSubClassRule( + TTO_ChainContextSubstFormat2* ccsf2, + TTO_ChainSubClassRule* cscr, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + + UShort* b; + UShort* i; + UShort* l; + TTO_SubstLookupRecord* slr; + Bool* d; + + + if ( ACCESS_Frame( 2L ) ) + return error; + + cscr->BacktrackGlyphCount = GET_UShort(); + + FORGET_Frame(); + + if ( cscr->BacktrackGlyphCount > ccsf2->MaxBacktrackLength ) + ccsf2->MaxBacktrackLength = cscr->BacktrackGlyphCount; + + cscr->Backtrack = NULL; + + count = cscr->BacktrackGlyphCount; + + if ( ALLOC_ARRAY( cscr->Backtrack, count, UShort ) ) + return error; + + b = cscr->Backtrack; + d = ccsf2->BacktrackClassDef.Defined; + + if ( ACCESS_Frame( count * 2L ) ) + goto Fail4; + + for ( n = 0; n < count; n++ ) + { + b[n] = GET_UShort(); + + /* We check whether the specific class is used at all. If not, + class 0 is used instead. */ + + if ( !d[b[n]] ) + b[n] = 0; + } + + FORGET_Frame(); + + if ( ACCESS_Frame( 2L ) ) + goto Fail4; + + cscr->InputGlyphCount = GET_UShort(); + + FORGET_Frame(); + + if ( cscr->InputGlyphCount > ccsf2->MaxInputLength ) + ccsf2->MaxInputLength = cscr->InputGlyphCount; + + cscr->Input = NULL; + + count = cscr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */ + + if ( ALLOC_ARRAY( cscr->Input, count, UShort ) ) + goto Fail4; + + i = cscr->Input; + d = ccsf2->InputClassDef.Defined; + + if ( ACCESS_Frame( count * 2L ) ) + goto Fail3; + + for ( n = 0; n < count; n++ ) + { + i[n] = GET_UShort(); + + if ( !d[i[n]] ) + i[n] = 0; + } + + FORGET_Frame(); + + if ( ACCESS_Frame( 2L ) ) + goto Fail3; + + cscr->LookaheadGlyphCount = GET_UShort(); + + FORGET_Frame(); + + if ( cscr->LookaheadGlyphCount > ccsf2->MaxLookaheadLength ) + ccsf2->MaxLookaheadLength = cscr->LookaheadGlyphCount; + + cscr->Lookahead = NULL; + + count = cscr->LookaheadGlyphCount; + + if ( ALLOC_ARRAY( cscr->Lookahead, count, UShort ) ) + goto Fail3; + + l = cscr->Lookahead; + d = ccsf2->LookaheadClassDef.Defined; + + if ( ACCESS_Frame( count * 2L ) ) + goto Fail2; + + for ( n = 0; n < count; n++ ) + { + l[n] = GET_UShort(); + + if ( !d[l[n]] ) + l[n] = 0; + } + + FORGET_Frame(); + + if ( ACCESS_Frame( 2L ) ) + goto Fail2; + + cscr->SubstCount = GET_UShort(); + + FORGET_Frame(); + + cscr->SubstLookupRecord = NULL; + + count = cscr->SubstCount; + + if ( ALLOC_ARRAY( cscr->SubstLookupRecord, count, + TTO_SubstLookupRecord ) ) + goto Fail2; + + slr = cscr->SubstLookupRecord; + + if ( ACCESS_Frame( count * 4L ) ) + goto Fail1; + + for ( n = 0; n < count; n++ ) + { + slr[n].SequenceIndex = GET_UShort(); + slr[n].LookupListIndex = GET_UShort(); + } + + FORGET_Frame(); + + return TT_Err_Ok; + + Fail1: + FREE( slr ); + + Fail2: + FREE( l ); + + Fail3: + FREE( i ); + + Fail4: + FREE( b ); + return error; + } + + + static void Free_ChainSubClassRule( TTO_ChainSubClassRule* cscr ) + { + FREE( cscr->SubstLookupRecord ); + FREE( cscr->Lookahead ); + FREE( cscr->Input ); + FREE( cscr->Backtrack ); + } + + + /* SubClassSet */ + + static TT_Error Load_ChainSubClassSet( + TTO_ChainContextSubstFormat2* ccsf2, + TTO_ChainSubClassSet* cscs, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + ULong cur_offset, new_offset, base_offset; + + TTO_ChainSubClassRule* cscr; + + + base_offset = FILE_Pos(); + + if ( ACCESS_Frame( 2L ) ) + return error; + + count = cscs->ChainSubClassRuleCount = GET_UShort(); + + FORGET_Frame(); + + cscs->ChainSubClassRule = NULL; + + if ( ALLOC_ARRAY( cscs->ChainSubClassRule, count, + TTO_ChainSubClassRule ) ) + return error; + + cscr = cscs->ChainSubClassRule; + + for ( n = 0; n < count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_ChainSubClassRule( ccsf2, &cscr[n], + input ) ) != TT_Err_Ok ) + goto Fail; + (void)FILE_Seek( cur_offset ); + } + + return TT_Err_Ok; + + Fail: + for ( n = 0; n < count; n++ ) + Free_ChainSubClassRule( &cscr[n] ); + + FREE( cscr ); + return error; + } + + + static void Free_ChainSubClassSet( TTO_ChainSubClassSet* cscs ) + { + UShort n, count; + + TTO_ChainSubClassRule* cscr; + + + if ( cscs->ChainSubClassRule ) + { + count = cscs->ChainSubClassRuleCount; + cscr = cscs->ChainSubClassRule; + + for ( n = 0; n < count; n++ ) + Free_ChainSubClassRule( &cscr[n] ); + + FREE( cscr ); + } + } + + + /* ChainContextSubstFormat2 */ + + static TT_Error Load_ChainContextSubst2( + TTO_ChainContextSubstFormat2* ccsf2, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + ULong cur_offset, new_offset, base_offset; + ULong backtrack_offset, input_offset, lookahead_offset; + + TTO_ChainSubClassSet* cscs; + + + base_offset = FILE_Pos() - 2; + + if ( ACCESS_Frame( 2L ) ) + return error; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_Coverage( &ccsf2->Coverage, input ) ) != TT_Err_Ok ) + return error; + (void)FILE_Seek( cur_offset ); + + if ( ACCESS_Frame( 8L ) ) + goto Fail5; + + backtrack_offset = GET_UShort() + base_offset; + input_offset = GET_UShort() + base_offset; + lookahead_offset = GET_UShort() + base_offset; + + /* `ChainSubClassSetCount' is the upper limit for input class values, + thus we read it now to make an additional safety check. */ + + count = ccsf2->ChainSubClassSetCount = GET_UShort(); + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( backtrack_offset ) || + ( error = Load_ClassDefinition( &ccsf2->BacktrackClassDef, count, + input ) ) != TT_Err_Ok ) + goto Fail5; + if ( FILE_Seek( input_offset ) || + ( error = Load_ClassDefinition( &ccsf2->InputClassDef, count, + input ) ) != TT_Err_Ok ) + goto Fail4; + if ( FILE_Seek( lookahead_offset ) || + ( error = Load_ClassDefinition( &ccsf2->LookaheadClassDef, count, + input ) ) != TT_Err_Ok ) + goto Fail3; + (void)FILE_Seek( cur_offset ); + + ccsf2->ChainSubClassSet = NULL; + ccsf2->MaxBacktrackLength = 0; + ccsf2->MaxInputLength = 0; + ccsf2->MaxLookaheadLength = 0; + + if ( ALLOC_ARRAY( ccsf2->ChainSubClassSet, count, TTO_ChainSubClassSet ) ) + goto Fail2; + + cscs = ccsf2->ChainSubClassSet; + + for ( n = 0; n < count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail1; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + if ( new_offset != base_offset ) /* not a NULL offset */ + { + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_ChainSubClassSet( ccsf2, &cscs[n], + input ) ) != TT_Err_Ok ) + goto Fail1; + (void)FILE_Seek( cur_offset ); + } + else + { + /* we create a ChainSubClassSet table with no entries */ + + ccsf2->ChainSubClassSet[n].ChainSubClassRuleCount = 0; + ccsf2->ChainSubClassSet[n].ChainSubClassRule = NULL; + } + } + + return TT_Err_Ok; + + Fail1: + for ( n = 0; n < count; n++ ) + Free_ChainSubClassSet( &cscs[n] ); + + FREE( cscs ); + + Fail2: + Free_ClassDefinition( &ccsf2->LookaheadClassDef ); + + Fail3: + Free_ClassDefinition( &ccsf2->InputClassDef ); + + Fail4: + Free_ClassDefinition( &ccsf2->BacktrackClassDef ); + + Fail5: + Free_Coverage( &ccsf2->Coverage ); + return error; + } + + + static void Free_ChainContext2( TTO_ChainContextSubstFormat2* ccsf2 ) + { + UShort n, count; + + TTO_ChainSubClassSet* cscs; + + + if ( ccsf2->ChainSubClassSet ) + { + count = ccsf2->ChainSubClassSetCount; + cscs = ccsf2->ChainSubClassSet; + + for ( n = 0; n < count; n++ ) + Free_ChainSubClassSet( &cscs[n] ); + + FREE( cscs ); + } + + Free_ClassDefinition( &ccsf2->LookaheadClassDef ); + Free_ClassDefinition( &ccsf2->InputClassDef ); + Free_ClassDefinition( &ccsf2->BacktrackClassDef ); + + Free_Coverage( &ccsf2->Coverage ); + } + + + /* ChainContextSubstFormat3 */ + + static TT_Error Load_ChainContextSubst3( + TTO_ChainContextSubstFormat3* ccsf3, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + UShort n, count; + UShort backtrack_count, input_count, lookahead_count; + ULong cur_offset, new_offset, base_offset; + + TTO_Coverage* b; + TTO_Coverage* i; + TTO_Coverage* l; + TTO_SubstLookupRecord* slr; + + + base_offset = FILE_Pos() - 2L; + + if ( ACCESS_Frame( 2L ) ) + return error; + + ccsf3->BacktrackGlyphCount = GET_UShort(); + + FORGET_Frame(); + + ccsf3->BacktrackCoverage = NULL; + + backtrack_count = ccsf3->BacktrackGlyphCount; + + if ( ALLOC_ARRAY( ccsf3->BacktrackCoverage, backtrack_count, + TTO_Coverage ) ) + return error; + + b = ccsf3->BacktrackCoverage; + + for ( n = 0; n < backtrack_count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail4; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_Coverage( &b[n], input ) ) != TT_Err_Ok ) + goto Fail4; + (void)FILE_Seek( cur_offset ); + } + + if ( ACCESS_Frame( 2L ) ) + goto Fail4; + + ccsf3->InputGlyphCount = GET_UShort(); + + FORGET_Frame(); + + ccsf3->InputCoverage = NULL; + + input_count = ccsf3->InputGlyphCount; + + if ( ALLOC_ARRAY( ccsf3->InputCoverage, input_count, TTO_Coverage ) ) + goto Fail4; + + i = ccsf3->InputCoverage; + + for ( n = 0; n < input_count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail3; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_Coverage( &i[n], input ) ) != TT_Err_Ok ) + goto Fail3; + (void)FILE_Seek( cur_offset ); + } + + if ( ACCESS_Frame( 2L ) ) + goto Fail3; + + ccsf3->LookaheadGlyphCount = GET_UShort(); + + FORGET_Frame(); + + ccsf3->LookaheadCoverage = NULL; + + lookahead_count = ccsf3->LookaheadGlyphCount; + + if ( ALLOC_ARRAY( ccsf3->LookaheadCoverage, lookahead_count, + TTO_Coverage ) ) + goto Fail3; + + l = ccsf3->LookaheadCoverage; + + for ( n = 0; n < lookahead_count; n++ ) + { + if ( ACCESS_Frame( 2L ) ) + goto Fail2; + + new_offset = GET_UShort() + base_offset; + + FORGET_Frame(); + + cur_offset = FILE_Pos(); + if ( FILE_Seek( new_offset ) || + ( error = Load_Coverage( &l[n], input ) ) != TT_Err_Ok ) + goto Fail2; + (void)FILE_Seek( cur_offset ); + } + + if ( ACCESS_Frame( 2L ) ) + goto Fail2; + + ccsf3->SubstCount = GET_UShort(); + + FORGET_Frame(); + + ccsf3->SubstLookupRecord = NULL; + + count = ccsf3->SubstCount; + + if ( ALLOC_ARRAY( ccsf3->SubstLookupRecord, count, + TTO_SubstLookupRecord ) ) + goto Fail2; + + slr = ccsf3->SubstLookupRecord; + + if ( ACCESS_Frame( count * 4L ) ) + goto Fail1; + + for ( n = 0; n < count; n++ ) + { + slr[n].SequenceIndex = GET_UShort(); + slr[n].LookupListIndex = GET_UShort(); + } + + FORGET_Frame(); + + return TT_Err_Ok; + + Fail1: + FREE( slr ); + + Fail2: + for ( n = 0; n < lookahead_count; n++ ) + Free_Coverage( &l[n] ); + + FREE( l ); + + Fail3: + for ( n = 0; n < input_count; n++ ) + Free_Coverage( &i[n] ); + + FREE( i ); + + Fail4: + for ( n = 0; n < backtrack_count; n++ ) + Free_Coverage( &b[n] ); + + FREE( b ); + return error; + } + + + static void Free_ChainContext3( TTO_ChainContextSubstFormat3* ccsf3 ) + { + UShort n, count; + + TTO_Coverage* c; + + + FREE( ccsf3->SubstLookupRecord ); + + if ( ccsf3->LookaheadCoverage ) + { + count = ccsf3->LookaheadGlyphCount; + c = ccsf3->LookaheadCoverage; + + for ( n = 0; n < count; n++ ) + Free_Coverage( &c[n] ); + + FREE( c ); + } + + if ( ccsf3->InputCoverage ) + { + count = ccsf3->InputGlyphCount; + c = ccsf3->InputCoverage; + + for ( n = 0; n < count; n++ ) + Free_Coverage( &c[n] ); + + FREE( c ); + } + + if ( ccsf3->BacktrackCoverage ) + { + count = ccsf3->BacktrackGlyphCount; + c = ccsf3->BacktrackCoverage; + + for ( n = 0; n < count; n++ ) + Free_Coverage( &c[n] ); + + FREE( c ); + } + } + + + /* ChainContextSubst */ + + TT_Error Load_ChainContextSubst( TTO_ChainContextSubst* ccs, + PFace input ) + { + DEFINE_LOAD_LOCALS( input->stream ); + + + if ( ACCESS_Frame( 2L ) ) + return error; + + ccs->SubstFormat = GET_UShort(); + + FORGET_Frame(); + + switch ( ccs->SubstFormat ) + { + case 1: + return Load_ChainContextSubst1( &ccs->ccsf.ccsf1, input ); + + case 2: + return Load_ChainContextSubst2( &ccs->ccsf.ccsf2, input ); + + case 3: + return Load_ChainContextSubst3( &ccs->ccsf.ccsf3, input ); + + default: + return TTO_Err_Invalid_GSUB_SubTable_Format; + } + + return TT_Err_Ok; /* never reached */ + } + + + void Free_ChainContextSubst( TTO_ChainContextSubst* ccs ) + { + switch ( ccs->SubstFormat ) + { + case 1: + Free_ChainContext1( &ccs->ccsf.ccsf1 ); + break; + + case 2: + Free_ChainContext2( &ccs->ccsf.ccsf2 ); + break; + + case 3: + Free_ChainContext3( &ccs->ccsf.ccsf3 ); + break; + } + } + + + static TT_Error Lookup_ChainContextSubst1( + TTO_GSUBHeader* gsub, + TTO_ChainContextSubstFormat1* ccsf1, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + UShort flags, + UShort context_length, + int nesting_level ) + { + UShort index, property; + UShort i, j, k, num_csr, curr_pos; + UShort bgc, igc, lgc; + TT_Error error; + UShort* s_in; + + TTO_ChainSubRule* csr; + TTO_ChainSubRule curr_csr; + TTO_GDEFHeader* gdef; + + + gdef = gsub->gdef; + + if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) ) + return error; + + error = Coverage_Index( &ccsf1->Coverage, in->string[in->pos], &index ); + if ( error ) + return error; + + csr = ccsf1->ChainSubRuleSet[index].ChainSubRule; + num_csr = ccsf1->ChainSubRuleSet[index].ChainSubRuleCount; + + for ( k = 0; k < num_csr; k++ ) + { + curr_csr = csr[k]; + bgc = curr_csr.BacktrackGlyphCount; + igc = curr_csr.InputGlyphCount; + lgc = curr_csr.LookaheadGlyphCount; + + if ( context_length != 0xFFFF && context_length < igc ) + continue; + + /* check whether context is too long; it is a first guess only */ + + if ( bgc > in->pos || in->pos + igc + lgc > in->length ) + continue; + + if ( bgc ) + { + /* Since we don't know in advance the number of glyphs to inspect, + we search backwards for matches in the backtrack glyph array */ + + curr_pos = 0; + s_in = &in->string[curr_pos]; + + for ( i = bgc, j = in->pos - 1; i > 0; i--, j-- ) + { + while ( CHECK_Property( gdef, s_in[j], flags, &property ) ) + { + if ( error && error != TTO_Err_Not_Covered ) + return error; + + if ( j > curr_pos ) + j--; + else + break; + } + + if ( s_in[j] != curr_csr.Backtrack[i - 1] ) + break; + } + + if ( i != 0 ) + continue; + } + + curr_pos = in->pos; + s_in = &in->string[curr_pos]; + + /* Start at 1 because [0] is implied */ + + for ( i = 1, j = 1; i < igc; i++, j++ ) + { + while ( CHECK_Property( gdef, s_in[j], flags, &property ) ) + { + if ( error && error != TTO_Err_Not_Covered ) + return error; + + if ( curr_pos + j < in->length ) + j++; + else + break; + } + + if ( s_in[j] != curr_csr.Input[i - 1] ) + break; + } + + if ( i != igc ) + continue; + + /* we are starting to check for lookahead glyphs right after the + last context glyph */ + + curr_pos = j; + s_in = &in->string[curr_pos]; + + for ( i = 0, j = 0; i < lgc; i++, j++ ) + { + while ( CHECK_Property( gdef, s_in[j], flags, &property ) ) + { + if ( error && error != TTO_Err_Not_Covered ) + return error; + + if ( curr_pos + j < in->length ) + j++; + else + break; + } + + if ( s_in[j] != curr_csr.Lookahead[i] ) + break; + } + + if ( i == lgc ) + return Do_ContextSubst( gsub, igc, + curr_csr.SubstCount, + curr_csr.SubstLookupRecord, + in, out, + nesting_level ); + } + + return TTO_Err_Not_Covered; + } + + + static TT_Error Lookup_ChainContextSubst2( + TTO_GSUBHeader* gsub, + TTO_ChainContextSubstFormat2* ccsf2, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + UShort flags, + UShort context_length, + int nesting_level ) + { + UShort index, property; + TT_Error error; + UShort i, j, k, curr_pos; + UShort bgc, igc, lgc; + UShort known_backtrack_classes, + known_input_classes, + known_lookahead_classes; + + UShort* backtrack_classes; + UShort* input_classes; + UShort* lookahead_classes; + + UShort* s_in; + + UShort* bc; + UShort* ic; + UShort* lc; + + TTO_ChainSubClassSet* cscs; + TTO_ChainSubClassRule ccsr; + TTO_GDEFHeader* gdef; + + + gdef = gsub->gdef; + + if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) ) + return error; + + /* Note: The coverage table in format 2 doesn't give an index into + anything. It just lets us know whether or not we need to + do any lookup at all. */ + + error = Coverage_Index( &ccsf2->Coverage, in->string[in->pos], &index ); + if ( error ) + return error; + + if ( ALLOC_ARRAY( backtrack_classes, ccsf2->MaxBacktrackLength, UShort ) ) + return error; + known_backtrack_classes = 0; + + if ( ALLOC_ARRAY( input_classes, ccsf2->MaxInputLength, UShort ) ) + goto End3; + known_input_classes = 1; + + if ( ALLOC_ARRAY( lookahead_classes, ccsf2->MaxLookaheadLength, UShort ) ) + goto End2; + known_lookahead_classes = 0; + + error = Get_Class( &ccsf2->InputClassDef, in->string[in->pos], + &input_classes[0], NULL ); + if ( error ) + goto End1; + + cscs = &ccsf2->ChainSubClassSet[input_classes[0]]; + if ( !cscs ) + { + error = TTO_Err_Invalid_GSUB_SubTable; + goto End1; + } + + for ( k = 0; k < cscs->ChainSubClassRuleCount; k++ ) + { + ccsr = cscs->ChainSubClassRule[k]; + bgc = ccsr.BacktrackGlyphCount; + igc = ccsr.InputGlyphCount; + lgc = ccsr.LookaheadGlyphCount; + + if ( context_length != 0xFFFF && context_length < igc ) + continue; + + /* check whether context is too long; it is a first guess only */ + + if ( bgc > in->pos || in->pos + igc + lgc > in->length ) + continue; + + if ( bgc ) + { + /* Since we don't know in advance the number of glyphs to inspect, + we search backwards for matches in the backtrack glyph array. + Note that `known_backtrack_classes' starts at index 0. */ + + curr_pos = 0; + s_in = &in->string[curr_pos]; + bc = ccsr.Backtrack; + + for ( i = 0, j = in->pos - 1; i < bgc; i++, j-- ) + { + while ( CHECK_Property( gdef, s_in[j], flags, &property ) ) + { + if ( error && error != TTO_Err_Not_Covered ) + return error; + + if ( j > curr_pos ) + j--; + else + break; + } + + if ( i >= known_backtrack_classes ) + { + /* Keeps us from having to do this for each rule */ + + error = Get_Class( &ccsf2->BacktrackClassDef, s_in[j], + &backtrack_classes[i], NULL ); + if ( error && error != TTO_Err_Not_Covered ) + goto End1; + known_backtrack_classes = i; + } + + if ( bc[bgc - 1 - i] != backtrack_classes[i] ) + break; + } + + if ( i != bgc ) + continue; + } + + curr_pos = in->pos; + s_in = &in->string[curr_pos]; + ic = ccsr.Input; + + /* Start at 1 because [0] is implied */ + + for ( i = 1, j = 1; i < igc; i++, j++ ) + { + while ( CHECK_Property( gdef, s_in[j], flags, &property ) ) + { + if ( error && error != TTO_Err_Not_Covered ) + goto End1; + + if ( curr_pos + j < in->length ) + j++; + else + break; + } + + if ( i >= known_input_classes ) + { + error = Get_Class( &ccsf2->InputClassDef, s_in[j], + &input_classes[i], NULL ); + if ( error && error != TTO_Err_Not_Covered ) + goto End1; + known_input_classes = i; + } + + if ( ic[i - 1] != input_classes[i] ) + break; + } + + if ( i != igc ) + continue; + + /* we are starting to check for lookahead glyphs right after the + last context glyph */ + + curr_pos = j; + s_in = &in->string[curr_pos]; + lc = ccsr.Lookahead; + + for ( i = 0, j = 0; i < lgc; i++, j++ ) + { + while ( CHECK_Property( gdef, s_in[j], flags, &property ) ) + { + if ( error && error != TTO_Err_Not_Covered ) + return error; + + if ( curr_pos + j < in->length ) + j++; + else + break; + } + + if ( i >= known_lookahead_classes ) + { + error = Get_Class( &ccsf2->LookaheadClassDef, s_in[j], + &lookahead_classes[i], NULL ); + if ( error && error != TTO_Err_Not_Covered ) + goto End1; + known_lookahead_classes = i; + } + + if ( lc[i] != lookahead_classes[i] ) + break; + } + + if ( i == lgc ) + { + error = Do_ContextSubst( gsub, igc, + ccsr.SubstCount, + ccsr.SubstLookupRecord, + in, out, + nesting_level ); + goto End1; + } + } + + error = TTO_Err_Not_Covered; + + End1: + FREE( lookahead_classes ); + + End2: + FREE( input_classes ); + + End3: + FREE( backtrack_classes ); + return error; + } + + + static TT_Error Lookup_ChainContextSubst3( + TTO_GSUBHeader* gsub, + TTO_ChainContextSubstFormat3* ccsf3, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + UShort flags, + UShort context_length, + int nesting_level ) + { + UShort index, i, j, curr_pos, property; + UShort bgc, igc, lgc; + TT_Error error; + UShort* s_in; + + TTO_Coverage* bc; + TTO_Coverage* ic; + TTO_Coverage* lc; + TTO_GDEFHeader* gdef; + + + gdef = gsub->gdef; + + if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) ) + return error; + + bgc = ccsf3->BacktrackGlyphCount; + igc = ccsf3->InputGlyphCount; + lgc = ccsf3->LookaheadGlyphCount; + + if ( context_length != 0xFFFF && context_length < igc ) + return TTO_Err_Not_Covered; + + /* check whether context is too long; it is a first guess only */ + + if ( bgc > in->pos || in->pos + igc + lgc > in->length ) + return TTO_Err_Not_Covered; + + if ( bgc ) + { + /* Since we don't know in advance the number of glyphs to inspect, + we search backwards for matches in the backtrack glyph array */ + + curr_pos = 0; + s_in = &in->string[curr_pos]; + bc = ccsf3->BacktrackCoverage; + + for ( i = bgc, j = in->pos - 1; i > 0; i--, j-- ) + { + while ( CHECK_Property( gdef, s_in[j], flags, &property ) ) + { + if ( error && error != TTO_Err_Not_Covered ) + return error; + + if ( j > curr_pos ) + j--; + else + return TTO_Err_Not_Covered; + } + + error = Coverage_Index( &bc[i - 1], s_in[j], &index ); + if ( error ) + return error; + } + } + + curr_pos = in->pos; + s_in = &in->string[curr_pos]; + ic = ccsf3->InputCoverage; + + /* Start at 1 because [0] is implied */ + + for ( i = 1, j = 1; i < igc; i++, j++ ) + { + while ( CHECK_Property( gdef, s_in[j], flags, &property ) ) + { + if ( error && error != TTO_Err_Not_Covered ) + return error; + + if ( curr_pos + j < in->length ) + j++; + else + return TTO_Err_Not_Covered; + } + + error = Coverage_Index( &ic[i], s_in[j], &index ); + if ( error ) + return error; + } + + /* we are starting for lookahead glyphs right after the last context + glyph */ + + curr_pos = j; + s_in = &in->string[curr_pos]; + lc = ccsf3->LookaheadCoverage; + + for ( i = 0, j = 0; i < lgc; i++, j++ ) + { + while ( CHECK_Property( gdef, s_in[j], flags, &property ) ) + { + if ( error && error != TTO_Err_Not_Covered ) + return error; + + if ( curr_pos + j < in->length ) + j++; + else + return TTO_Err_Not_Covered; + } + + error = Coverage_Index( &lc[i], s_in[j], &index ); + if ( error ) + return error; + } + + return Do_ContextSubst( gsub, igc, + ccsf3->SubstCount, + ccsf3->SubstLookupRecord, + in, out, + nesting_level ); + } + + + static TT_Error Lookup_ChainContextSubst( + TTO_GSUBHeader* gsub, + TTO_ChainContextSubst* ccs, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + UShort flags, + UShort context_length, + int nesting_level ) + { + switch ( ccs->SubstFormat ) + { + case 1: + return Lookup_ChainContextSubst1( gsub, &ccs->ccsf.ccsf1, in, out, + flags, context_length, + nesting_level ); + + case 2: + return Lookup_ChainContextSubst2( gsub, &ccs->ccsf.ccsf2, in, out, + flags, context_length, + nesting_level ); + + case 3: + return Lookup_ChainContextSubst3( gsub, &ccs->ccsf.ccsf3, in, out, + flags, context_length, + nesting_level ); + + default: + return TTO_Err_Invalid_GSUB_SubTable_Format; + } + + return TT_Err_Ok; /* never reached */ + } + + + + /*********** + * GSUB API + ***********/ + + + EXPORT_FUNC + TT_Error TT_GSUB_Select_Script( TTO_GSUBHeader* gsub, + TT_ULong script_tag, + TT_UShort* script_index ) + { + UShort n; + + TTO_ScriptList* sl; + TTO_ScriptRecord* sr; + + + if ( !gsub || !script_index ) + return TT_Err_Invalid_Argument; + + sl = &gsub->ScriptList; + sr = sl->ScriptRecord; + + for ( n = 0; n < sl->ScriptCount; n++ ) + if ( script_tag == sr[n].ScriptTag ) + { + *script_index = n; + + return TT_Err_Ok; + } + + return TTO_Err_Not_Covered; + } + + + EXPORT_FUNC + TT_Error TT_GSUB_Select_Language( TTO_GSUBHeader* gsub, + TT_ULong language_tag, + TT_UShort script_index, + TT_UShort* language_index, + TT_UShort* req_feature_index ) + { + UShort n; + + TTO_ScriptList* sl; + TTO_ScriptRecord* sr; + TTO_Script* s; + TTO_LangSysRecord* lsr; + + + if ( !gsub || !language_index || !req_feature_index ) + return TT_Err_Invalid_Argument; + + sl = &gsub->ScriptList; + sr = sl->ScriptRecord; + + if ( script_index >= sl->ScriptCount ) + return TT_Err_Invalid_Argument; + + s = &sr[script_index].Script; + lsr = s->LangSysRecord; + + for ( n = 0; n < s->LangSysCount; n++ ) + if ( language_tag == lsr[n].LangSysTag ) + { + *language_index = n; + *req_feature_index = lsr[n].LangSys.ReqFeatureIndex; + + return TT_Err_Ok; + } + + return TTO_Err_Not_Covered; + } + + + /* selecting 0xFFFF for language_index asks for the values of the + default language (DefaultLangSys) */ + + EXPORT_FUNC + TT_Error TT_GSUB_Select_Feature( TTO_GSUBHeader* gsub, + TT_ULong feature_tag, + TT_UShort script_index, + TT_UShort language_index, + TT_UShort* feature_index ) + { + UShort n; + + TTO_ScriptList* sl; + TTO_ScriptRecord* sr; + TTO_Script* s; + TTO_LangSysRecord* lsr; + TTO_LangSys* ls; + UShort* fi; + + TTO_FeatureList* fl; + TTO_FeatureRecord* fr; + + + if ( !gsub || !feature_index ) + return TT_Err_Invalid_Argument; + + sl = &gsub->ScriptList; + sr = sl->ScriptRecord; + + fl = &gsub->FeatureList; + fr = fl->FeatureRecord; + + if ( script_index >= sl->ScriptCount ) + return TT_Err_Invalid_Argument; + + s = &sr[script_index].Script; + lsr = s->LangSysRecord; + + if ( language_index == 0xFFFF ) + ls = &s->DefaultLangSys; + else + { + if ( language_index >= s->LangSysCount ) + return TT_Err_Invalid_Argument; + + ls = &lsr[language_index].LangSys; + } + + fi = ls->FeatureIndex; + + for ( n = 0; n < ls->FeatureCount; n++ ) + { + if ( fi[n] >= fl->FeatureCount ) + return TTO_Err_Invalid_GSUB_SubTable_Format; + + if ( feature_tag == fr[fi[n]].FeatureTag ) + { + *feature_index = fi[n]; + + return TT_Err_Ok; + } + } + + return TTO_Err_Not_Covered; + } + + + /* The next three functions return a null-terminated list */ + + EXPORT_FUNC + TT_Error TT_GSUB_Query_Scripts( TTO_GSUBHeader* gsub, + TT_ULong** script_tag_list ) + { + UShort n; + TT_Error error; + ULong* stl; + + TTO_ScriptList* sl; + TTO_ScriptRecord* sr; + + + if ( !gsub || !script_tag_list ) + return TT_Err_Invalid_Argument; + + sl = &gsub->ScriptList; + sr = sl->ScriptRecord; + + if ( ALLOC_ARRAY( stl, sl->ScriptCount + 1, ULong ) ) + return error; + + for ( n = 0; n < sl->ScriptCount; n++ ) + stl[n] = sr[n].ScriptTag; + stl[n] = 0; + + *script_tag_list = stl; + + return TT_Err_Ok; + } + + + EXPORT_FUNC + TT_Error TT_GSUB_Query_Languages( TTO_GSUBHeader* gsub, + TT_UShort script_index, + TT_ULong** language_tag_list ) + { + UShort n; + TT_Error error; + ULong* ltl; + + TTO_ScriptList* sl; + TTO_ScriptRecord* sr; + TTO_Script* s; + TTO_LangSysRecord* lsr; + + + if ( !gsub || !language_tag_list ) + return TT_Err_Invalid_Argument; + + sl = &gsub->ScriptList; + sr = sl->ScriptRecord; + + if ( script_index >= sl->ScriptCount ) + return TT_Err_Invalid_Argument; + + s = &sr[script_index].Script; + lsr = s->LangSysRecord; + + if ( ALLOC_ARRAY( ltl, s->LangSysCount + 1, ULong ) ) + return error; + + for ( n = 0; n < s->LangSysCount; n++ ) + ltl[n] = lsr[n].LangSysTag; + ltl[n] = 0; + + *language_tag_list = ltl; + + return TT_Err_Ok; + } + + + /* selecting 0xFFFF for language_index asks for the values of the + default language (DefaultLangSys) */ + + EXPORT_FUNC + TT_Error TT_GSUB_Query_Features( TTO_GSUBHeader* gsub, + TT_UShort script_index, + TT_UShort language_index, + TT_ULong** feature_tag_list ) + { + UShort n; + TT_Error error; + ULong* ftl; + + TTO_ScriptList* sl; + TTO_ScriptRecord* sr; + TTO_Script* s; + TTO_LangSysRecord* lsr; + TTO_LangSys* ls; + UShort* fi; + + TTO_FeatureList* fl; + TTO_FeatureRecord* fr; + + + if ( !gsub || !feature_tag_list ) + return TT_Err_Invalid_Argument; + + sl = &gsub->ScriptList; + sr = sl->ScriptRecord; + + fl = &gsub->FeatureList; + fr = fl->FeatureRecord; + + if ( script_index >= sl->ScriptCount ) + return TT_Err_Invalid_Argument; + + s = &sr[script_index].Script; + lsr = s->LangSysRecord; + + if ( language_index == 0xFFFF ) + ls = &s->DefaultLangSys; + else + { + if ( language_index >= s->LangSysCount ) + return TT_Err_Invalid_Argument; + + ls = &lsr[language_index].LangSys; + } + + fi = ls->FeatureIndex; + + if ( ALLOC_ARRAY( ftl, ls->FeatureCount + 1, ULong ) ) + return error; + + for ( n = 0; n < ls->FeatureCount; n++ ) + { + if ( fi[n] >= fl->FeatureCount ) + { + FREE( ftl ); + return TTO_Err_Invalid_GSUB_SubTable_Format; + } + ftl[n] = fr[fi[n]].FeatureTag; + } + ftl[n] = 0; + + *feature_tag_list = ftl; + + return TT_Err_Ok; + } + + + /* Do an individual subtable lookup. Returns TT_Err_Ok if substitution + has been done, or TTO_Err_Not_Covered if not. */ + + static TT_Error Do_Glyph_Lookup( TTO_GSUBHeader* gsub, + UShort lookup_index, + TTO_GSUB_String* in, + TTO_GSUB_String* out, + UShort context_length, + int nesting_level ) + { + TT_Error error = TT_Err_Ok; + UShort i, flags; + TTO_Lookup* lo; + + + nesting_level++; + + if ( nesting_level > TTO_MAX_NESTING_LEVEL ) + return TTO_Err_Too_Many_Nested_Contexts; + + lo = &gsub->LookupList.Lookup[lookup_index]; + flags = lo->LookupFlag; + + for ( i = 0; i < lo->SubTableCount; i++ ) + { + switch ( lo->LookupType ) + { + case GSUB_LOOKUP_SINGLE: + error = Lookup_SingleSubst( &lo->SubTable[i].st.gsub.single, + in, out, + flags, context_length, gsub->gdef ); + break; + + case GSUB_LOOKUP_MULTIPLE: + error = Lookup_MultipleSubst( &lo->SubTable[i].st.gsub.multiple, + in, out, + flags, context_length, gsub->gdef ); + break; + + case GSUB_LOOKUP_ALTERNATE: + error = Lookup_AlternateSubst( gsub, + &lo->SubTable[i].st.gsub.alternate, + in, out, + flags, context_length, gsub->gdef ); + break; + + case GSUB_LOOKUP_LIGATURE: + error = Lookup_LigatureSubst( &lo->SubTable[i].st.gsub.ligature, + in, out, + flags, context_length, gsub->gdef ); + break; + + case GSUB_LOOKUP_CONTEXT: + error = Lookup_ContextSubst( gsub, &lo->SubTable[i].st.gsub.context, + in, out, + flags, context_length, nesting_level ); + break; + + case GSUB_LOOKUP_CHAIN: + error = Lookup_ChainContextSubst( gsub, + &lo->SubTable[i].st.gsub.chain, + in, out, + flags, context_length, + nesting_level ); + break; + } + + /* Check whether we have a successful substitution or an error other + than TTO_Err_Not_Covered */ + + if ( error != TTO_Err_Not_Covered ) + return error; + } + + return TTO_Err_Not_Covered; + } + + + /* apply one lookup to the input string object */ + + static TT_Error Do_String_Lookup( TTO_GSUBHeader* gsub, + UShort lookup_index, + TTO_GSUB_String* in, + TTO_GSUB_String* out ) + { + TT_Error error = TTO_Err_Not_Covered; + + UShort* properties = gsub->LookupList.Properties; + UShort* p_in = in->properties; + UShort* s_in = in->string; + + int nesting_level = 0; + + + while ( in->pos < in->length ) + { + if ( ~p_in[in->pos] & properties[lookup_index] ) + { + /* 0xFFFF indicates that we don't have a context length yet */ + error = Do_Glyph_Lookup( gsub, lookup_index, in, out, + 0xFFFF, nesting_level ); + if ( error && error != TTO_Err_Not_Covered ) + return error; + } + else + error = TTO_Err_Not_Covered; + + if ( error == TTO_Err_Not_Covered ) + if ( ADD_String( in, 1, out, 1, &s_in[in->pos], 0xFFFF, 0xFFFF ) ) + return error; + } + + return error; + } + + + EXPORT_FUNC + TT_Error TT_GSUB_Add_Feature( TTO_GSUBHeader* gsub, + TT_UShort feature_index, + TT_UShort property ) + { + UShort i; + + TTO_Feature feature; + UShort* properties; + UShort* index; + + + if ( !gsub || + feature_index >= gsub->FeatureList.FeatureCount ) + return TT_Err_Invalid_Argument; + + properties = gsub->LookupList.Properties; + + feature = gsub->FeatureList.FeatureRecord[feature_index].Feature; + index = feature.LookupListIndex; + + for ( i = 0; i < feature.LookupListCount; i++ ) + properties[index[i]] |= property; + + return TT_Err_Ok; + } + + + EXPORT_FUNC + TT_Error TT_GSUB_Clear_Features( TTO_GSUBHeader* gsub ) + { + UShort i; + + UShort* properties; + + + if ( !gsub ) + return TT_Err_Invalid_Argument; + + properties = gsub->LookupList.Properties; + + for ( i = 0; i < gsub->LookupList.LookupCount; i++ ) + properties[i] = 0; + + return TT_Err_Ok; + } + + + EXPORT_FUNC + TT_Error TT_GSUB_Register_Alternate_Function( TTO_GSUBHeader* gsub, + TTO_AltFunction altfunc, + void* data ) + { + if ( !gsub ) + return TT_Err_Invalid_Argument; + + gsub->altfunc = altfunc; + gsub->data = data; + + return TT_Err_Ok; + } + + + EXPORT_FUNC + TT_Error TT_GSUB_Apply_String( TTO_GSUBHeader* gsub, + TTO_GSUB_String* in, + TTO_GSUB_String* out ) + { + TT_Error error = TTO_Err_Not_Covered; + UShort j; + + TTO_GSUB_String tmp1; + TTO_GSUB_String* ptmp1; + TTO_GSUB_String tmp2; + TTO_GSUB_String* ptmp2; + TTO_GSUB_String* t; + + UShort* properties; + + + if ( !gsub || + !in || !out || in->length == 0 || in->pos >= in->length ) + return TT_Err_Invalid_Argument; + + properties = gsub->LookupList.Properties; + + tmp1.length = in->length; + tmp1.allocated = in->length; + tmp1.pos = in->pos; + tmp1.max_ligID = 1; + + if ( ALLOC_ARRAY( tmp1.string, tmp1.length, UShort ) ) + return error; + MEM_Copy( tmp1.string, in->string, in->length * sizeof ( UShort ) ); + + /* make sure that we always have a `properties', `components', and + `ligIDs' array in the string object */ + + if ( ALLOC_ARRAY( tmp1.components, tmp1.length, UShort ) ) + return error; + if ( ALLOC_ARRAY( tmp1.ligIDs, tmp1.length, UShort ) ) + return error; + if ( ALLOC_ARRAY( tmp1.properties, tmp1.length, UShort ) ) + return error; + if ( in->properties ) + MEM_Copy( tmp1.properties, in->properties, + in->length * sizeof( UShort ) ); + + tmp2.allocated = 0; + tmp2.pos = 0; + tmp2.string = NULL; + tmp2.properties = NULL; + tmp2.components = NULL; + tmp2.ligIDs = NULL; + + ptmp1 = &tmp1; + ptmp2 = &tmp2; + + for ( j = 0; j < gsub->LookupList.LookupCount; j++ ) + if ( properties[j] ) + { + error = Do_String_Lookup( gsub, j, ptmp1, ptmp2 ); + if ( error && error != TTO_Err_Not_Covered ) + return error; + + /* flipping `in' and `out', preparing the next loop */ + + ptmp1->pos = in->pos; + ptmp2->length = ptmp2->pos; + ptmp2->pos = in->pos; + ptmp2->max_ligID = ptmp1->max_ligID; + + t = ptmp2; + ptmp2 = ptmp1; + ptmp1 = t; + } + + out->length = ptmp1->length; + out->pos = 0; + out->allocated = ptmp1->allocated; + out->string = ptmp1->string; + out->components = ptmp1->components; + out->ligIDs = ptmp1->ligIDs; + + if ( in->properties ) + out->properties = ptmp1->properties; + else + { + FREE( ptmp1->properties ); + out->properties = NULL; + } + + FREE( ptmp2->string ); + FREE( ptmp2->properties ); + FREE( ptmp2->components ); + FREE( ptmp2->ligIDs ); + + return error; + } + + +/* END */ |