/******************************************************************* * * ftxgpos.c * * TrueType Open GPOS table support. * * Copyright 1996-1999 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 7 and 8), 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 GPOS_ID Build_Extension_ID( 'G', 'P', 'O', 'S' ) /********************** * Extension Functions **********************/ static TT_Error GPOS_Create( void* ext, PFace face ) { DEFINE_LOAD_LOCALS( face->stream ); TTO_GPOSHeader* gpos = (TTO_GPOSHeader*)ext; Long table; /* by convention */ if ( !gpos ) return TT_Err_Ok; /* a null offset indicates that there is no GPOS table */ gpos->offset = 0; /* we store the start offset and the size of the subtable */ table = TT_LookUp_Table( face, TTAG_GPOS ); if ( table < 0 ) return TT_Err_Ok; /* The table is optional */ if ( FILE_Seek( face->dirTables[table].Offset ) || ACCESS_Frame( 4L ) ) return error; gpos->offset = FILE_Pos() - 4L; /* undo ACCESS_Frame() */ gpos->Version = GET_ULong(); FORGET_Frame(); gpos->loaded = FALSE; return TT_Err_Ok; } static TT_Error GPOS_Destroy( void* ext, PFace face ) { TTO_GPOSHeader* gpos = (TTO_GPOSHeader*)ext; /* by convention */ if ( !gpos ) return TT_Err_Ok; if ( gpos->loaded ) { Free_LookupList( &gpos->LookupList, GPOS ); Free_FeatureList( &gpos->FeatureList ); Free_ScriptList( &gpos->ScriptList ); } return TT_Err_Ok; } EXPORT_FUNC TT_Error TT_Init_GPOS_Extension( TT_Engine engine ) { PEngine_Instance _engine = HANDLE_Engine( engine ); if ( !_engine ) return TT_Err_Invalid_Engine; return TT_Register_Extension( _engine, GPOS_ID, sizeof ( TTO_GPOSHeader ), GPOS_Create, GPOS_Destroy ); } EXPORT_FUNC TT_Error TT_Load_GPOS_Table( TT_Face face, TTO_GPOSHeader* retptr, TTO_GDEFHeader* gdef ) { ULong cur_offset, new_offset, base_offset; TT_UShort i, num_lookups; TT_Error error; TT_Stream stream; TTO_GPOSHeader* gpos; 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, GPOS_ID, (void**)&gpos ); if ( error ) return error; if ( gpos->offset == 0 ) return TT_Err_Table_Missing; /* no GPOS table; nothing to do */ /* now access stream */ if ( USE_Stream( faze->stream, stream ) ) return error; base_offset = gpos->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( &gpos->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( &gpos->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( &gpos->LookupList, faze, GPOS ) ) != TT_Err_Ok ) goto Fail2; gpos->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 = gpos->LookupList.Lookup; num_lookups = gpos->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; } } } gpos->loaded = TRUE; *retptr = *gpos; DONE_Stream( stream ); return TT_Err_Ok; Fail1: Free_LookupList( &gpos->LookupList, GPOS ); Fail2: Free_FeatureList( &gpos->FeatureList ); Fail3: Free_ScriptList( &gpos->ScriptList ); /* release stream */ DONE_Stream( stream ); return error; } /***************************** * SubTable related functions *****************************/ /* shared tables */ /* ValueRecord */ static TT_Error Load_ValueRecord( TTO_ValueRecord* vr, TT_UShort format, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); ULong cur_offset, new_offset, base_offset; base_offset = FILE_Pos(); if ( format & HAVE_X_PLACEMENT ) { if ( ACCESS_Frame( 2L ) ) return error; vr->XPlacement = GET_Short(); FORGET_Frame(); } else vr->XPlacement = 0; if ( format & HAVE_Y_PLACEMENT ) { if ( ACCESS_Frame( 2L ) ) return error; vr->YPlacement = GET_Short(); FORGET_Frame(); } else vr->YPlacement = 0; if ( format & HAVE_X_ADVANCE ) { if ( ACCESS_Frame( 2L ) ) return error; vr->XAdvance = GET_Short(); FORGET_Frame(); } else vr->XAdvance = 0; if ( format & HAVE_Y_ADVANCE ) { if ( ACCESS_Frame( 2L ) ) return error; vr->YAdvance = GET_Short(); FORGET_Frame(); } else vr->YAdvance = 0; if ( format & HAVE_X_PLACEMENT_DEVICE ) { 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_Device( &vr->XPlacementDevice, input ) ) != TT_Err_Ok ) return error; (void)FILE_Seek( cur_offset ); } else { vr->XPlacementDevice.StartSize = 0; vr->XPlacementDevice.EndSize = 0; vr->XPlacementDevice.DeltaValue = NULL; } if ( format & HAVE_Y_PLACEMENT_DEVICE ) { 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_Device( &vr->YPlacementDevice, input ) ) != TT_Err_Ok ) goto Fail3; (void)FILE_Seek( cur_offset ); } else { vr->YPlacementDevice.StartSize = 0; vr->YPlacementDevice.EndSize = 0; vr->YPlacementDevice.DeltaValue = NULL; } if ( format & HAVE_X_ADVANCE_DEVICE ) { 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_Device( &vr->XAdvanceDevice, input ) ) != TT_Err_Ok ) goto Fail2; (void)FILE_Seek( cur_offset ); } else { vr->XAdvanceDevice.StartSize = 0; vr->XAdvanceDevice.EndSize = 0; vr->XAdvanceDevice.DeltaValue = NULL; } if ( format & HAVE_Y_ADVANCE_DEVICE ) { 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_Device( &vr->YAdvanceDevice, input ) ) != TT_Err_Ok ) goto Fail1; (void)FILE_Seek( cur_offset ); } else { vr->YAdvanceDevice.StartSize = 0; vr->YAdvanceDevice.EndSize = 0; vr->YAdvanceDevice.DeltaValue = NULL; } if ( format & HAVE_X_ID_PLACEMENT ) { if ( ACCESS_Frame( 2L ) ) goto Fail1; vr->XIdPlacement = GET_UShort(); FORGET_Frame(); } else vr->XIdPlacement = 0; if ( format & HAVE_Y_ID_PLACEMENT ) { if ( ACCESS_Frame( 2L ) ) goto Fail1; vr->YIdPlacement = GET_UShort(); FORGET_Frame(); } else vr->YIdPlacement = 0; if ( format & HAVE_X_ID_ADVANCE ) { if ( ACCESS_Frame( 2L ) ) goto Fail1; vr->XIdAdvance = GET_UShort(); FORGET_Frame(); } else vr->XIdAdvance = 0; if ( format & HAVE_Y_ID_ADVANCE ) { if ( ACCESS_Frame( 2L ) ) goto Fail1; vr->YIdAdvance = GET_UShort(); FORGET_Frame(); } else vr->YIdAdvance = 0; return TT_Err_Ok; Fail1: Free_Device( &vr->YAdvanceDevice ); Fail2: Free_Device( &vr->XAdvanceDevice ); Fail3: Free_Device( &vr->YPlacementDevice ); return error; } static void Free_ValueRecord( TTO_ValueRecord* vr, UShort format ) { if ( format & HAVE_Y_ADVANCE_DEVICE ) Free_Device( &vr->YAdvanceDevice ); if ( format & HAVE_X_ADVANCE_DEVICE ) Free_Device( &vr->XAdvanceDevice ); if ( format & HAVE_Y_PLACEMENT_DEVICE ) Free_Device( &vr->YPlacementDevice ); if ( format & HAVE_X_PLACEMENT_DEVICE ) Free_Device( &vr->XPlacementDevice ); } /* AnchorFormat1 */ /* AnchorFormat2 */ /* AnchorFormat3 */ /* AnchorFormat4 */ static TT_Error Load_Anchor( TTO_Anchor* an, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); ULong cur_offset, new_offset, base_offset; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; an->PosFormat = GET_UShort(); FORGET_Frame(); switch ( an->PosFormat ) { case 1: if ( ACCESS_Frame( 4L ) ) return error; an->af.af1.XCoordinate = GET_Short(); an->af.af1.YCoordinate = GET_Short(); FORGET_Frame(); break; case 2: if ( ACCESS_Frame( 6L ) ) return error; an->af.af2.XCoordinate = GET_Short(); an->af.af2.YCoordinate = GET_Short(); an->af.af2.AnchorPoint = GET_UShort(); FORGET_Frame(); break; case 3: if ( ACCESS_Frame( 6L ) ) return error; an->af.af3.XCoordinate = GET_Short(); an->af.af3.YCoordinate = GET_Short(); new_offset = GET_UShort(); FORGET_Frame(); if ( new_offset ) { new_offset += base_offset; cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Device( &an->af.af3.XDeviceTable, input ) ) != TT_Err_Ok ) return error; (void)FILE_Seek( cur_offset ); } else { an->af.af3.XDeviceTable.StartSize = 0; an->af.af3.XDeviceTable.EndSize = 0; an->af.af3.XDeviceTable.DeltaValue = 0; } if ( ACCESS_Frame( 2L ) ) goto Fail; new_offset = GET_UShort(); FORGET_Frame(); if ( new_offset ) { new_offset += base_offset; cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Device( &an->af.af3.YDeviceTable, input ) ) != TT_Err_Ok ) goto Fail; (void)FILE_Seek( cur_offset ); } else { an->af.af3.YDeviceTable.StartSize = 0; an->af.af3.YDeviceTable.EndSize = 0; an->af.af3.YDeviceTable.DeltaValue = 0; } break; case 4: if ( ACCESS_Frame( 4L ) ) return error; an->af.af4.XIdAnchor = GET_UShort(); an->af.af4.YIdAnchor = GET_UShort(); FORGET_Frame(); break; default: return TTO_Err_Invalid_GPOS_SubTable_Format; } return TT_Err_Ok; Fail: Free_Device( &an->af.af3.XDeviceTable ); return error; } static void Free_Anchor( TTO_Anchor* an ) { if ( an->PosFormat == 3 ) { Free_Device( &an->af.af3.YDeviceTable ); Free_Device( &an->af.af3.XDeviceTable ); } } /* MarkArray */ static TT_Error Load_MarkArray ( TTO_MarkArray* ma, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; ULong cur_offset, new_offset, base_offset; TTO_MarkRecord* mr; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; count = ma->MarkCount = GET_UShort(); FORGET_Frame(); ma->MarkRecord = NULL; if ( ALLOC_ARRAY( ma->MarkRecord, count, TTO_MarkRecord ) ) return error; mr = ma->MarkRecord; for ( n = 0; n < count; n++ ) { if ( ACCESS_Frame( 4L ) ) goto Fail; mr[n].Class = GET_UShort(); new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Anchor( &mr[n].MarkAnchor, input ) ) != TT_Err_Ok ) goto Fail; (void)FILE_Seek( cur_offset ); } return TT_Err_Ok; Fail: for ( n = 0; n < count; n++ ) Free_Anchor( &mr[n].MarkAnchor ); FREE( mr ); return error; } static void Free_MarkArray( TTO_MarkArray* ma ) { UShort n, count; TTO_MarkRecord* mr; if ( ma->MarkRecord ) { count = ma->MarkCount; mr = ma->MarkRecord; for ( n = 0; n < count; n++ ) Free_Anchor( &mr[n].MarkAnchor ); FREE( mr ); } } /* LookupType 1 */ /* SinglePosFormat1 */ /* SinglePosFormat2 */ TT_Error Load_SinglePos( TTO_SinglePos* sp, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count, format; ULong cur_offset, new_offset, base_offset; TTO_ValueRecord* v; base_offset = FILE_Pos(); if ( ACCESS_Frame( 6L ) ) return error; sp->PosFormat = GET_UShort(); new_offset = GET_UShort() + base_offset; format = sp->ValueFormat = GET_UShort(); FORGET_Frame(); if ( !format ) return TTO_Err_Invalid_GPOS_SubTable; cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Coverage( &sp->Coverage, input ) ) != TT_Err_Ok ) return error; (void)FILE_Seek( cur_offset ); switch ( sp->PosFormat ) { case 1: error = Load_ValueRecord( &sp->spf.spf1.Value, format, input ); if ( error ) goto Fail2; break; case 2: if ( ACCESS_Frame( 2L ) ) goto Fail2; count = sp->spf.spf2.ValueCount = GET_UShort(); FORGET_Frame(); sp->spf.spf2.Value = NULL; if ( ALLOC_ARRAY( sp->spf.spf2.Value, count, TTO_ValueRecord ) ) goto Fail2; v = sp->spf.spf2.Value; for ( n = 0; n < count; n++ ) { error = Load_ValueRecord( &v[n], format, input ); if ( error ) goto Fail1; } break; default: return TTO_Err_Invalid_GPOS_SubTable_Format; } return TT_Err_Ok; Fail1: for ( n = 0; n < count; n++ ) Free_ValueRecord( &v[n], format ); FREE( v ); Fail2: Free_Coverage( &sp->Coverage ); return error; } void Free_SinglePos( TTO_SinglePos* sp ) { UShort n, count, format; TTO_ValueRecord* v; format = sp->ValueFormat; switch ( sp->PosFormat ) { case 1: Free_ValueRecord( &sp->spf.spf1.Value, format ); break; case 2: if ( sp->spf.spf2.Value ) { count = sp->spf.spf2.ValueCount; v = sp->spf.spf2.Value; for ( n = 0; n < count; n++ ) Free_ValueRecord( &v[n], format ); FREE( v ); } break; } Free_Coverage( &sp->Coverage ); } /* LookupType 2 */ /* PairSet */ static TT_Error Load_PairSet ( TTO_PairSet* ps, UShort format1, UShort format2, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; TTO_PairValueRecord* pvr; if ( ACCESS_Frame( 2L ) ) return error; count = ps->PairValueCount = GET_UShort(); FORGET_Frame(); ps->PairValueRecord = NULL; if ( ALLOC_ARRAY( ps->PairValueRecord, count, TTO_PairValueRecord ) ) return error; pvr = ps->PairValueRecord; for ( n = 0; n < count; n++ ) { if ( ACCESS_Frame( 2L ) ) goto Fail; pvr[n].SecondGlyph = GET_UShort(); FORGET_Frame(); if ( format1 ) { error = Load_ValueRecord( &pvr[n].Value1, format1, input ); if ( error ) goto Fail; } if ( format2 ) { error = Load_ValueRecord( &pvr[n].Value2, format2, input ); if ( error ) goto Fail; } } return TT_Err_Ok; Fail: for ( n = 0; n < count; n++ ) { if ( format1 ) Free_ValueRecord( &pvr[n].Value1, format1 ); if ( format2 ) Free_ValueRecord( &pvr[n].Value2, format2 ); } FREE( pvr ); return error; } static void Free_PairSet( TTO_PairSet* ps, UShort format1, UShort format2 ) { UShort n, count; TTO_PairValueRecord* pvr; if ( ps->PairValueRecord ) { count = ps->PairValueCount; pvr = ps->PairValueRecord; for ( n = 0; n < count; n++ ) { if ( format1 ) Free_ValueRecord( &pvr[n].Value1, format1 ); if ( format2 ) Free_ValueRecord( &pvr[n].Value2, format2 ); } FREE( pvr ); } } /* PairPosFormat1 */ static TT_Error Load_PairPosFormat1( TTO_PairPosFormat1* ppf1, UShort format1, UShort format2, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; ULong cur_offset, new_offset, base_offset; TTO_PairSet* ps; base_offset = FILE_Pos() - 8L; if ( ACCESS_Frame( 2L ) ) return error; count = ppf1->PairSetCount = GET_UShort(); FORGET_Frame(); ppf1->PairSet = NULL; if ( ALLOC_ARRAY( ppf1->PairSet, count, TTO_PairSet ) ) goto Fail; ps = ppf1->PairSet; 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_PairSet( &ps[n], format1, format2, input ) ) != TT_Err_Ok ) goto Fail; (void)FILE_Seek( cur_offset ); } return TT_Err_Ok; Fail: for ( n = 0; n < count; n++ ) Free_PairSet( &ps[n], format1, format2 ); FREE( ps ); return error; } static void Free_PairPosFormat1( TTO_PairPosFormat1* ppf1, UShort format1, UShort format2 ) { UShort n, count; TTO_PairSet* ps; if ( ppf1->PairSet ) { count = ppf1->PairSetCount; ps = ppf1->PairSet; for ( n = 0; n < count; n++ ) Free_PairSet( &ps[n], format1, format2 ); FREE( ps ); } } /* PairPosFormat2 */ static TT_Error Load_PairPosFormat2( TTO_PairPosFormat2* ppf2, UShort format1, UShort format2, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort m, n, count1, count2; ULong cur_offset, new_offset1, new_offset2, base_offset; TTO_Class1Record* c1r; TTO_Class2Record* c2r; base_offset = FILE_Pos() - 8L; if ( ACCESS_Frame( 8L ) ) return error; new_offset1 = GET_UShort() + base_offset; new_offset2 = GET_UShort() + base_offset; /* `Class1Count' and `Class2Count' are the upper limits for class values, thus we read it now to make additional safety checks. */ count1 = ppf2->Class1Count = GET_UShort(); count2 = ppf2->Class2Count = GET_UShort(); FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset1 ) || ( error = Load_ClassDefinition( &ppf2->ClassDef1, count1, input ) ) != TT_Err_Ok ) return error; (void)FILE_Seek( cur_offset ); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset2 ) || ( error = Load_ClassDefinition( &ppf2->ClassDef2, count2, input ) ) != TT_Err_Ok ) goto Fail2; (void)FILE_Seek( cur_offset ); ppf2->Class1Record = NULL; if ( ALLOC_ARRAY( ppf2->Class1Record, count1, TTO_Class1Record ) ) goto Fail1; c1r = ppf2->Class1Record; for ( m = 0; m < count1; m++ ) { c1r[m].Class2Record = NULL; if ( ALLOC_ARRAY( c1r[m].Class2Record, count2, TTO_Class2Record ) ) goto Fail1; c2r = c1r[m].Class2Record; for ( n = 0; n < count2; n++ ) { if ( format1 ) { Load_ValueRecord( &c2r[n].Value1, format1, input ); if ( error ) goto Fail1; } if ( format2 ) { Load_ValueRecord( &c2r[n].Value2, format2, input ); if ( error ) goto Fail1; } } } return TT_Err_Ok; Fail1: for ( m = 0; m < count1; m++ ) { c2r = c1r[m].Class2Record; for ( n = 0; n < count2; n++ ) { if ( format1 ) Free_ValueRecord( &c2r[n].Value1, format1 ); if ( format2 ) Free_ValueRecord( &c2r[n].Value2, format2 ); } FREE( c2r ); } FREE( c1r ); Free_ClassDefinition( &ppf2->ClassDef2 ); Fail2: Free_ClassDefinition( &ppf2->ClassDef1 ); return error; } static void Free_PairPosFormat2( TTO_PairPosFormat2* ppf2, UShort format1, UShort format2 ) { UShort m, n, count1, count2; TTO_Class1Record* c1r; TTO_Class2Record* c2r; if ( ppf2->Class1Record ) { c1r = ppf2->Class1Record; count1 = ppf2->Class1Count; count2 = ppf2->Class2Count; for ( m = 0; m < count1; m++ ) { c2r = c1r[m].Class2Record; for ( n = 0; n < count2; n++ ) { if ( format1 ) Free_ValueRecord( &c2r[n].Value1, format1 ); if ( format2 ) Free_ValueRecord( &c2r[n].Value2, format2 ); } FREE( c2r ); } FREE( c1r ); Free_ClassDefinition( &ppf2->ClassDef2 ); Free_ClassDefinition( &ppf2->ClassDef1 ); } } TT_Error Load_PairPos( TTO_PairPos* pp, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort format1, format2; ULong cur_offset, new_offset, base_offset; base_offset = FILE_Pos(); if ( ACCESS_Frame( 8L ) ) return error; pp->PosFormat = GET_UShort(); new_offset = GET_UShort() + base_offset; format1 = pp->ValueFormat1 = GET_UShort(); format2 = pp->ValueFormat2 = GET_UShort(); FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Coverage( &pp->Coverage, input ) ) != TT_Err_Ok ) return error; (void)FILE_Seek( cur_offset ); switch ( pp->PosFormat ) { case 1: error = Load_PairPosFormat1( &pp->ppf.ppf1, format1, format2, input ); if ( error ) goto Fail; break; case 2: error = Load_PairPosFormat2( &pp->ppf.ppf2, format1, format2, input ); if ( error ) goto Fail; break; default: return TTO_Err_Invalid_GPOS_SubTable_Format; } return TT_Err_Ok; Fail: Free_Coverage( &pp->Coverage ); return error; } void Free_PairPos( TTO_PairPos* pp ) { UShort format1, format2; format1 = pp->ValueFormat1; format2 = pp->ValueFormat2; switch ( pp->PosFormat ) { case 1: Free_PairPosFormat1( &pp->ppf.ppf1, format1, format2 ); break; case 2: Free_PairPosFormat2( &pp->ppf.ppf2, format1, format2 ); break; } Free_Coverage( &pp->Coverage ); } /* LookupType 3 */ /* CursivePosFormat1 */ TT_Error Load_CursivePos( TTO_CursivePos* cp, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; ULong cur_offset, new_offset, base_offset; TTO_EntryExitRecord* eer; base_offset = FILE_Pos(); if ( ACCESS_Frame( 4L ) ) return error; cp->PosFormat = GET_UShort(); new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Coverage( &cp->Coverage, input ) ) != TT_Err_Ok ) return error; (void)FILE_Seek( cur_offset ); if ( ACCESS_Frame( 2L ) ) goto Fail2; count = cp->EntryExitCount = GET_UShort(); FORGET_Frame(); cp->EntryExitRecord = NULL; if ( ALLOC_ARRAY( cp->EntryExitRecord, count, TTO_EntryExitRecord ) ) goto Fail2; eer = cp->EntryExitRecord; for ( n = 0; n < count; n++ ) { if ( ACCESS_Frame( 2L ) ) return error; new_offset = GET_UShort(); FORGET_Frame(); if ( new_offset ) { new_offset += base_offset; cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Anchor( &eer[n].EntryAnchor, input ) ) != TT_Err_Ok ) goto Fail1; (void)FILE_Seek( cur_offset ); } else eer[n].EntryAnchor.PosFormat = 0; if ( ACCESS_Frame( 2L ) ) return error; new_offset = GET_UShort(); FORGET_Frame(); if ( new_offset ) { new_offset += base_offset; cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Anchor( &eer[n].ExitAnchor, input ) ) != TT_Err_Ok ) goto Fail1; (void)FILE_Seek( cur_offset ); } else eer[n].ExitAnchor.PosFormat = 0; } return TT_Err_Ok; Fail1: for ( n = 0; n < count; n++ ) { Free_Anchor( &eer[n].EntryAnchor ); Free_Anchor( &eer[n].ExitAnchor ); } FREE( eer ); Fail2: Free_Coverage( &cp->Coverage ); return error; } void Free_CursivePos( TTO_CursivePos* cp ) { UShort n, count; TTO_EntryExitRecord* eer; if ( cp->EntryExitRecord ) { count = cp->EntryExitCount; eer = cp->EntryExitRecord; for ( n = 0; n < count; n++ ) { Free_Anchor( &eer[n].EntryAnchor ); Free_Anchor( &eer[n].ExitAnchor ); } FREE( eer ); } Free_Coverage( &cp->Coverage ); } /* LookupType 4 */ /* BaseArray */ static TT_Error Load_BaseArray( TTO_BaseArray* ba, UShort num_classes, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort m, n, count; ULong cur_offset, new_offset, base_offset; TTO_BaseRecord* br; TTO_Anchor* ban; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; count = ba->BaseCount = GET_UShort(); FORGET_Frame(); ba->BaseRecord = NULL; if ( ALLOC_ARRAY( ba->BaseRecord, count, TTO_BaseRecord ) ) return error; br = ba->BaseRecord; for ( m = 0; m < count; m++ ) { br[m].BaseAnchor = NULL; if ( ALLOC_ARRAY( br[m].BaseAnchor, num_classes, TTO_Anchor ) ) goto Fail; ban = br[m].BaseAnchor; for ( n = 0; n < num_classes; 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_Anchor( &ban[n], input ) ) != TT_Err_Ok ) goto Fail; (void)FILE_Seek( cur_offset ); } } return TT_Err_Ok; Fail: for ( m = 0; m < count; m++ ) { ban = br[m].BaseAnchor; for ( n = 0; n < num_classes; n++ ) Free_Anchor( &ban[n] ); FREE( ban ); } FREE( br ); return error; } static void Free_BaseArray( TTO_BaseArray* ba, UShort num_classes ) { UShort m, n, count; TTO_BaseRecord* br; TTO_Anchor* ban; if ( ba->BaseRecord ) { count = ba->BaseCount; br = ba->BaseRecord; for ( m = 0; m < count; m++ ) { ban = br[m].BaseAnchor; for ( n = 0; n < num_classes; n++ ) Free_Anchor( &ban[n] ); FREE( ban ); } FREE( br ); } } /* MarkBasePosFormat1 */ TT_Error Load_MarkBasePos( TTO_MarkBasePos* mbp, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); ULong cur_offset, new_offset, base_offset; base_offset = FILE_Pos(); if ( ACCESS_Frame( 4L ) ) return error; mbp->PosFormat = GET_UShort(); new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Coverage( &mbp->MarkCoverage, input ) ) != 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_Coverage( &mbp->BaseCoverage, input ) ) != TT_Err_Ok ) goto Fail3; (void)FILE_Seek( cur_offset ); if ( ACCESS_Frame( 4L ) ) goto Fail2; mbp->ClassCount = GET_UShort(); new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_MarkArray( &mbp->MarkArray, input ) ) != TT_Err_Ok ) goto Fail2; (void)FILE_Seek( cur_offset ); 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_BaseArray( &mbp->BaseArray, mbp->ClassCount, input ) ) != TT_Err_Ok ) goto Fail1; return TT_Err_Ok; Fail1: Free_MarkArray( &mbp->MarkArray ); Fail2: Free_Coverage( &mbp->BaseCoverage ); Fail3: Free_Coverage( &mbp->MarkCoverage ); return error; } void Free_MarkBasePos( TTO_MarkBasePos* mbp ) { Free_BaseArray( &mbp->BaseArray, mbp->ClassCount ); Free_MarkArray( &mbp->MarkArray ); Free_Coverage( &mbp->BaseCoverage ); Free_Coverage( &mbp->MarkCoverage ); } /* LookupType 5 */ /* LigatureAttach */ static TT_Error Load_LigatureAttach( TTO_LigatureAttach* lat, UShort num_classes, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort m, n, count; ULong cur_offset, new_offset, base_offset; TTO_ComponentRecord* cr; TTO_Anchor* lan; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; count = lat->ComponentCount = GET_UShort(); FORGET_Frame(); lat->ComponentRecord = NULL; if ( ALLOC_ARRAY( lat->ComponentRecord, count, TTO_ComponentRecord ) ) return error; cr = lat->ComponentRecord; for ( m = 0; m < count; m++ ) { cr[m].LigatureAnchor = NULL; if ( ALLOC_ARRAY( cr[m].LigatureAnchor, num_classes, TTO_Anchor ) ) goto Fail; lan = cr[m].LigatureAnchor; for ( n = 0; n < num_classes; n++ ) { if ( ACCESS_Frame( 2L ) ) goto Fail; new_offset = GET_UShort(); FORGET_Frame(); if ( new_offset ) { new_offset += base_offset; cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Anchor( &lan[n], input ) ) != TT_Err_Ok ) goto Fail; (void)FILE_Seek( cur_offset ); } else lan[n].PosFormat = 0; } } return TT_Err_Ok; Fail: for ( m = 0; m < count; m++ ) { lan = cr[m].LigatureAnchor; for ( n = 0; n < num_classes; n++ ) Free_Anchor( &lan[n] ); FREE( lan ); } FREE( cr ); return error; } static void Free_LigatureAttach( TTO_LigatureAttach* lat, UShort num_classes ) { UShort m, n, count; TTO_ComponentRecord* cr; TTO_Anchor* lan; if ( lat->ComponentRecord ) { count = lat->ComponentCount; cr = lat->ComponentRecord; for ( m = 0; m < count; m++ ) { lan = cr[m].LigatureAnchor; for ( n = 0; n < num_classes; n++ ) Free_Anchor( &lan[n] ); FREE( lan ); } FREE( cr ); } } /* LigatureArray */ static TT_Error Load_LigatureArray( TTO_LigatureArray* la, UShort num_classes, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; ULong cur_offset, new_offset, base_offset; TTO_LigatureAttach* lat; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; count = la->LigatureCount = GET_UShort(); FORGET_Frame(); la->LigatureAttach = NULL; if ( ALLOC_ARRAY( la->LigatureAttach, count, TTO_LigatureAttach ) ) return error; lat = la->LigatureAttach; 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_LigatureAttach( &lat[n], num_classes, input ) ) != TT_Err_Ok ) goto Fail; (void)FILE_Seek( cur_offset ); } return TT_Err_Ok; Fail: for ( n = 0; n < count; n++ ) Free_LigatureAttach( &lat[n], num_classes ); FREE( lat ); return error; } static void Free_LigatureArray( TTO_LigatureArray* la, UShort num_classes ) { UShort n, count; TTO_LigatureAttach* lat; if ( la->LigatureAttach ) { count = la->LigatureCount; lat = la->LigatureAttach; for ( n = 0; n < count; n++ ) Free_LigatureAttach( &lat[n], num_classes ); FREE( lat ); } } /* MarkLigPosFormat1 */ TT_Error Load_MarkLigPos( TTO_MarkLigPos* mlp, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); ULong cur_offset, new_offset, base_offset; base_offset = FILE_Pos(); if ( ACCESS_Frame( 4L ) ) return error; mlp->PosFormat = GET_UShort(); new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Coverage( &mlp->MarkCoverage, input ) ) != 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_Coverage( &mlp->LigatureCoverage, input ) ) != TT_Err_Ok ) goto Fail3; (void)FILE_Seek( cur_offset ); if ( ACCESS_Frame( 4L ) ) goto Fail2; mlp->ClassCount = GET_UShort(); new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_MarkArray( &mlp->MarkArray, input ) ) != TT_Err_Ok ) goto Fail2; (void)FILE_Seek( cur_offset ); 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_LigatureArray( &mlp->LigatureArray, mlp->ClassCount, input ) ) != TT_Err_Ok ) goto Fail1; return TT_Err_Ok; Fail1: Free_MarkArray( &mlp->MarkArray ); Fail2: Free_Coverage( &mlp->LigatureCoverage ); Fail3: Free_Coverage( &mlp->MarkCoverage ); return error; } void Free_MarkLigPos( TTO_MarkLigPos* mlp ) { Free_LigatureArray( &mlp->LigatureArray, mlp->ClassCount ); Free_MarkArray( &mlp->MarkArray ); Free_Coverage( &mlp->LigatureCoverage ); Free_Coverage( &mlp->MarkCoverage ); } /* LookupType 6 */ /* Mark2Array */ static TT_Error Load_Mark2Array( TTO_Mark2Array* m2a, UShort num_classes, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort m, n, count; ULong cur_offset, new_offset, base_offset; TTO_Mark2Record* m2r; TTO_Anchor* m2an; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; count = m2a->Mark2Count = GET_UShort(); FORGET_Frame(); m2a->Mark2Record = NULL; if ( ALLOC_ARRAY( m2a->Mark2Record, count, TTO_Mark2Record ) ) return error; m2r = m2a->Mark2Record; for ( m = 0; m < count; m++ ) { m2r[m].Mark2Anchor = NULL; if ( ALLOC_ARRAY( m2r[m].Mark2Anchor, num_classes, TTO_Anchor ) ) goto Fail; m2an = m2r[m].Mark2Anchor; for ( n = 0; n < num_classes; 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_Anchor( &m2an[n], input ) ) != TT_Err_Ok ) goto Fail; (void)FILE_Seek( cur_offset ); } } return TT_Err_Ok; Fail: for ( m = 0; m < count; m++ ) { m2an = m2r[m].Mark2Anchor; for ( n = 0; n < num_classes; n++ ) Free_Anchor( &m2an[n] ); FREE( m2an ); } FREE( m2r ); return error; } static void Free_Mark2Array( TTO_Mark2Array* m2a, UShort num_classes ) { UShort m, n, count; TTO_Mark2Record* m2r; TTO_Anchor* m2an; if ( m2a->Mark2Record ) { count = m2a->Mark2Count; m2r = m2a->Mark2Record; for ( m = 0; m < count; m++ ) { m2an = m2r[m].Mark2Anchor; for ( n = 0; n < num_classes; n++ ) Free_Anchor( &m2an[n] ); FREE( m2an ); } FREE( m2r ); } } /* MarkMarkPosFormat1 */ TT_Error Load_MarkMarkPos( TTO_MarkMarkPos* mmp, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); ULong cur_offset, new_offset, base_offset; base_offset = FILE_Pos(); if ( ACCESS_Frame( 4L ) ) return error; mmp->PosFormat = GET_UShort(); new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Coverage( &mmp->Mark1Coverage, input ) ) != 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_Coverage( &mmp->Mark2Coverage, input ) ) != TT_Err_Ok ) goto Fail3; (void)FILE_Seek( cur_offset ); if ( ACCESS_Frame( 4L ) ) goto Fail2; mmp->ClassCount = GET_UShort(); new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_MarkArray( &mmp->Mark1Array, input ) ) != TT_Err_Ok ) goto Fail2; (void)FILE_Seek( cur_offset ); 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_Mark2Array( &mmp->Mark2Array, mmp->ClassCount, input ) ) != TT_Err_Ok ) goto Fail1; return TT_Err_Ok; Fail1: Free_MarkArray( &mmp->Mark1Array ); Fail2: Free_Coverage( &mmp->Mark2Coverage ); Fail3: Free_Coverage( &mmp->Mark1Coverage ); return error; } void Free_MarkMarkPos( TTO_MarkMarkPos* mmp ) { Free_Mark2Array( &mmp->Mark2Array, mmp->ClassCount ); Free_MarkArray( &mmp->Mark1Array ); Free_Coverage( &mmp->Mark2Coverage ); Free_Coverage( &mmp->Mark1Coverage ); } /* LookupType 7 */ /* PosRule */ static TT_Error Load_PosRule( TTO_PosRule* pr, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; UShort* i; TTO_PosLookupRecord* plr; if ( ACCESS_Frame( 4L ) ) return error; pr->GlyphCount = GET_UShort(); pr->PosCount = GET_UShort(); FORGET_Frame(); pr->Input = NULL; count = pr->GlyphCount - 1; /* only GlyphCount - 1 elements */ if ( ALLOC_ARRAY( pr->Input, count, UShort ) ) return error; i = pr->Input; if ( ACCESS_Frame( count * 2L ) ) goto Fail2; for ( n = 0; n < count; n++ ) i[n] = GET_UShort(); FORGET_Frame(); pr->PosLookupRecord = NULL; count = pr->PosCount; if ( ALLOC_ARRAY( pr->PosLookupRecord, count, TTO_PosLookupRecord ) ) goto Fail2; plr = pr->PosLookupRecord; if ( ACCESS_Frame( count * 4L ) ) goto Fail1; for ( n = 0; n < count; n++ ) { plr[n].SequenceIndex = GET_UShort(); plr[n].LookupListIndex = GET_UShort(); } FORGET_Frame(); return TT_Err_Ok; Fail1: FREE( plr ); Fail2: FREE( i ); return error; } static void Free_PosRule( TTO_PosRule* pr ) { FREE( pr->PosLookupRecord ); FREE( pr->Input ); } /* PosRuleSet */ static TT_Error Load_PosRuleSet( TTO_PosRuleSet* prs, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; ULong cur_offset, new_offset, base_offset; TTO_PosRule* pr; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; count = prs->PosRuleCount = GET_UShort(); FORGET_Frame(); prs->PosRule = NULL; if ( ALLOC_ARRAY( prs->PosRule, count, TTO_PosRule ) ) return error; pr = prs->PosRule; 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_PosRule( &pr[n], input ) ) != TT_Err_Ok ) goto Fail; (void)FILE_Seek( cur_offset ); } return TT_Err_Ok; Fail: for ( n = 0; n < count; n++ ) Free_PosRule( &pr[n] ); FREE( pr ); return error; } static void Free_PosRuleSet( TTO_PosRuleSet* prs ) { UShort n, count; TTO_PosRule* pr; if ( prs->PosRule ) { count = prs->PosRuleCount; pr = prs->PosRule; for ( n = 0; n < count; n++ ) Free_PosRule( &pr[n] ); FREE( pr ); } } /* ContextPosFormat1 */ static TT_Error Load_ContextPos1( TTO_ContextPosFormat1* cpf1, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; ULong cur_offset, new_offset, base_offset; TTO_PosRuleSet* prs; 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( &cpf1->Coverage, input ) ) != TT_Err_Ok ) return error; (void)FILE_Seek( cur_offset ); if ( ACCESS_Frame( 2L ) ) goto Fail2; count = cpf1->PosRuleSetCount = GET_UShort(); FORGET_Frame(); cpf1->PosRuleSet = NULL; if ( ALLOC_ARRAY( cpf1->PosRuleSet, count, TTO_PosRuleSet ) ) goto Fail2; prs = cpf1->PosRuleSet; 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_PosRuleSet( &prs[n], input ) ) != TT_Err_Ok ) goto Fail1; (void)FILE_Seek( cur_offset ); } return TT_Err_Ok; Fail1: for ( n = 0; n < count; n++ ) Free_PosRuleSet( &prs[n] ); FREE( prs ); Fail2: Free_Coverage( &cpf1->Coverage ); return error; } static void Free_Context1( TTO_ContextPosFormat1* cpf1 ) { UShort n, count; TTO_PosRuleSet* prs; if ( cpf1->PosRuleSet ) { count = cpf1->PosRuleSetCount; prs = cpf1->PosRuleSet; for ( n = 0; n < count; n++ ) Free_PosRuleSet( &prs[n] ); FREE( prs ); } Free_Coverage( &cpf1->Coverage ); } /* PosClassRule */ static TT_Error Load_PosClassRule( TTO_ContextPosFormat2* cpf2, TTO_PosClassRule* pcr, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; UShort* c; TTO_PosLookupRecord* plr; Bool* d; if ( ACCESS_Frame( 4L ) ) return error; pcr->GlyphCount = GET_UShort(); pcr->PosCount = GET_UShort(); FORGET_Frame(); if ( pcr->GlyphCount > cpf2->MaxContextLength ) cpf2->MaxContextLength = pcr->GlyphCount; pcr->Class = NULL; count = pcr->GlyphCount - 1; /* only GlyphCount - 1 elements */ if ( ALLOC_ARRAY( pcr->Class, count, UShort ) ) return error; c = pcr->Class; d = cpf2->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(); pcr->PosLookupRecord = NULL; count = pcr->PosCount; if ( ALLOC_ARRAY( pcr->PosLookupRecord, count, TTO_PosLookupRecord ) ) goto Fail2; plr = pcr->PosLookupRecord; if ( ACCESS_Frame( count * 4L ) ) goto Fail1; for ( n = 0; n < count; n++ ) { plr[n].SequenceIndex = GET_UShort(); plr[n].LookupListIndex = GET_UShort(); } FORGET_Frame(); return TT_Err_Ok; Fail1: FREE( plr ); Fail2: FREE( c ); return error; } static void Free_PosClassRule( TTO_PosClassRule* pcr ) { FREE( pcr->PosLookupRecord ); FREE( pcr->Class ); } /* PosClassSet */ static TT_Error Load_PosClassSet( TTO_ContextPosFormat2* cpf2, TTO_PosClassSet* pcs, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; ULong cur_offset, new_offset, base_offset; TTO_PosClassRule* pcr; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; count = pcs->PosClassRuleCount = GET_UShort(); FORGET_Frame(); pcs->PosClassRule = NULL; if ( ALLOC_ARRAY( pcs->PosClassRule, count, TTO_PosClassRule ) ) return error; pcr = pcs->PosClassRule; 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_PosClassRule( cpf2, &pcr[n], input ) ) != TT_Err_Ok ) goto Fail; (void)FILE_Seek( cur_offset ); } return TT_Err_Ok; Fail: for ( n = 0; n < count; n++ ) Free_PosClassRule( &pcr[n] ); FREE( pcr ); return error; } static void Free_PosClassSet( TTO_PosClassSet* pcs ) { UShort n, count; TTO_PosClassRule* pcr; if ( pcs->PosClassRule ) { count = pcs->PosClassRuleCount; pcr = pcs->PosClassRule; for ( n = 0; n < count; n++ ) Free_PosClassRule( &pcr[n] ); FREE( pcr ); } } /* ContextPosFormat2 */ static TT_Error Load_ContextPos2( TTO_ContextPosFormat2* cpf2, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; ULong cur_offset, new_offset, base_offset; TTO_PosClassSet* pcs; 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( &cpf2->Coverage, input ) ) != TT_Err_Ok ) return error; (void)FILE_Seek( cur_offset ); if ( ACCESS_Frame( 4L ) ) goto Fail3; new_offset = GET_UShort() + base_offset; /* `PosClassSetCount' is the upper limit for class values, thus we read it now to make an additional safety check. */ count = cpf2->PosClassSetCount = GET_UShort(); FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_ClassDefinition( &cpf2->ClassDef, count, input ) ) != TT_Err_Ok ) goto Fail3; (void)FILE_Seek( cur_offset ); cpf2->PosClassSet = NULL; cpf2->MaxContextLength = 0; if ( ALLOC_ARRAY( cpf2->PosClassSet, count, TTO_PosClassSet ) ) goto Fail2; pcs = cpf2->PosClassSet; 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_PosClassSet( cpf2, &pcs[n], input ) ) != TT_Err_Ok ) goto Fail1; (void)FILE_Seek( cur_offset ); } else { /* we create a PosClassSet table with no entries */ cpf2->PosClassSet[n].PosClassRuleCount = 0; cpf2->PosClassSet[n].PosClassRule = NULL; } } return TT_Err_Ok; Fail1: for ( n = 0; n < count; n++ ) Free_PosClassSet( &pcs[n] ); FREE( pcs ); Fail2: Free_ClassDefinition( &cpf2->ClassDef ); Fail3: Free_Coverage( &cpf2->Coverage ); return error; } static void Free_Context2( TTO_ContextPosFormat2* cpf2 ) { UShort n, count; TTO_PosClassSet* pcs; if ( cpf2->PosClassSet ) { count = cpf2->PosClassSetCount; pcs = cpf2->PosClassSet; for ( n = 0; n < count; n++ ) Free_PosClassSet( &pcs[n] ); FREE( pcs ); } Free_ClassDefinition( &cpf2->ClassDef ); Free_Coverage( &cpf2->Coverage ); } /* ContextPosFormat3 */ static TT_Error Load_ContextPos3( TTO_ContextPosFormat3* cpf3, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; ULong cur_offset, new_offset, base_offset; TTO_Coverage* c; TTO_PosLookupRecord* plr; base_offset = FILE_Pos() - 2L; if ( ACCESS_Frame( 4L ) ) return error; cpf3->GlyphCount = GET_UShort(); cpf3->PosCount = GET_UShort(); FORGET_Frame(); cpf3->Coverage = NULL; count = cpf3->GlyphCount; if ( ALLOC_ARRAY( cpf3->Coverage, count, TTO_Coverage ) ) return error; c = cpf3->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 ); } cpf3->PosLookupRecord = NULL; count = cpf3->PosCount; if ( ALLOC_ARRAY( cpf3->PosLookupRecord, count, TTO_PosLookupRecord ) ) goto Fail2; plr = cpf3->PosLookupRecord; if ( ACCESS_Frame( count * 4L ) ) goto Fail1; for ( n = 0; n < count; n++ ) { plr[n].SequenceIndex = GET_UShort(); plr[n].LookupListIndex = GET_UShort(); } FORGET_Frame(); return TT_Err_Ok; Fail1: FREE( plr ); Fail2: for ( n = 0; n < count; n++ ) Free_Coverage( &c[n] ); FREE( c ); return error; } static void Free_Context3( TTO_ContextPosFormat3* cpf3 ) { UShort n, count; TTO_Coverage* c; FREE( cpf3->PosLookupRecord ); if ( cpf3->Coverage ) { count = cpf3->GlyphCount; c = cpf3->Coverage; for ( n = 0; n < count; n++ ) Free_Coverage( &c[n] ); FREE( c ); } } /* ContextPos */ TT_Error Load_ContextPos( TTO_ContextPos* cp, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); if ( ACCESS_Frame( 2L ) ) return error; cp->PosFormat = GET_UShort(); FORGET_Frame(); switch ( cp->PosFormat ) { case 1: return Load_ContextPos1( &cp->cpf.cpf1, input ); case 2: return Load_ContextPos2( &cp->cpf.cpf2, input ); case 3: return Load_ContextPos3( &cp->cpf.cpf3, input ); default: return TTO_Err_Invalid_GPOS_SubTable_Format; } return TT_Err_Ok; /* never reached */ } void Free_ContextPos( TTO_ContextPos* cp ) { switch ( cp->PosFormat ) { case 1: Free_Context1( &cp->cpf.cpf1 ); break; case 2: Free_Context2( &cp->cpf.cpf2 ); break; case 3: Free_Context3( &cp->cpf.cpf3 ); break; } } /* LookupType 8 */ /* ChainPosRule */ static TT_Error Load_ChainPosRule( TTO_ChainPosRule* cpr, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; UShort* b; UShort* i; UShort* l; TTO_PosLookupRecord* plr; if ( ACCESS_Frame( 2L ) ) return error; cpr->BacktrackGlyphCount = GET_UShort(); FORGET_Frame(); cpr->Backtrack = NULL; count = cpr->BacktrackGlyphCount; if ( ALLOC_ARRAY( cpr->Backtrack, count, UShort ) ) return error; b = cpr->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; cpr->InputGlyphCount = GET_UShort(); FORGET_Frame(); cpr->Input = NULL; count = cpr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */ if ( ALLOC_ARRAY( cpr->Input, count, UShort ) ) goto Fail4; i = cpr->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; cpr->LookaheadGlyphCount = GET_UShort(); FORGET_Frame(); cpr->Lookahead = NULL; count = cpr->LookaheadGlyphCount; if ( ALLOC_ARRAY( cpr->Lookahead, count, UShort ) ) goto Fail3; l = cpr->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; cpr->PosCount = GET_UShort(); FORGET_Frame(); cpr->PosLookupRecord = NULL; count = cpr->PosCount; if ( ALLOC_ARRAY( cpr->PosLookupRecord, count, TTO_PosLookupRecord ) ) goto Fail2; plr = cpr->PosLookupRecord; if ( ACCESS_Frame( count * 4L ) ) goto Fail1; for ( n = 0; n < count; n++ ) { plr[n].SequenceIndex = GET_UShort(); plr[n].LookupListIndex = GET_UShort(); } FORGET_Frame(); return TT_Err_Ok; Fail1: FREE( plr ); Fail2: FREE( l ); Fail3: FREE( i ); Fail4: FREE( b ); return error; } static void Free_ChainPosRule( TTO_ChainPosRule* cpr ) { FREE( cpr->PosLookupRecord ); FREE( cpr->Lookahead ); FREE( cpr->Input ); FREE( cpr->Backtrack ); } /* ChainPosRuleSet */ static TT_Error Load_ChainPosRuleSet( TTO_ChainPosRuleSet* cprs, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; ULong cur_offset, new_offset, base_offset; TTO_ChainPosRule* cpr; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; count = cprs->ChainPosRuleCount = GET_UShort(); FORGET_Frame(); cprs->ChainPosRule = NULL; if ( ALLOC_ARRAY( cprs->ChainPosRule, count, TTO_ChainPosRule ) ) return error; cpr = cprs->ChainPosRule; 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_ChainPosRule( &cpr[n], input ) ) != TT_Err_Ok ) goto Fail; (void)FILE_Seek( cur_offset ); } return TT_Err_Ok; Fail: for ( n = 0; n < count; n++ ) Free_ChainPosRule( &cpr[n] ); FREE( cpr ); return error; } static void Free_ChainPosRuleSet( TTO_ChainPosRuleSet* cprs ) { UShort n, count; TTO_ChainPosRule* cpr; if ( cprs->ChainPosRule ) { count = cprs->ChainPosRuleCount; cpr = cprs->ChainPosRule; for ( n = 0; n < count; n++ ) Free_ChainPosRule( &cpr[n] ); FREE( cpr ); } } /* ChainContextPosFormat1 */ static TT_Error Load_ChainContextPos1( TTO_ChainContextPosFormat1* ccpf1, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; ULong cur_offset, new_offset, base_offset; TTO_ChainPosRuleSet* cprs; 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( &ccpf1->Coverage, input ) ) != TT_Err_Ok ) return error; (void)FILE_Seek( cur_offset ); if ( ACCESS_Frame( 2L ) ) goto Fail2; count = ccpf1->ChainPosRuleSetCount = GET_UShort(); FORGET_Frame(); ccpf1->ChainPosRuleSet = NULL; if ( ALLOC_ARRAY( ccpf1->ChainPosRuleSet, count, TTO_ChainPosRuleSet ) ) goto Fail2; cprs = ccpf1->ChainPosRuleSet; 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_ChainPosRuleSet( &cprs[n], input ) ) != TT_Err_Ok ) goto Fail1; (void)FILE_Seek( cur_offset ); } return TT_Err_Ok; Fail1: for ( n = 0; n < count; n++ ) Free_ChainPosRuleSet( &cprs[n] ); FREE( cprs ); Fail2: Free_Coverage( &ccpf1->Coverage ); return error; } static void Free_ChainContext1( TTO_ChainContextPosFormat1* ccpf1 ) { UShort n, count; TTO_ChainPosRuleSet* cprs; if ( ccpf1->ChainPosRuleSet ) { count = ccpf1->ChainPosRuleSetCount; cprs = ccpf1->ChainPosRuleSet; for ( n = 0; n < count; n++ ) Free_ChainPosRuleSet( &cprs[n] ); FREE( cprs ); } Free_Coverage( &ccpf1->Coverage ); } /* ChainPosClassRule */ static TT_Error Load_ChainPosClassRule( TTO_ChainContextPosFormat2* ccpf2, TTO_ChainPosClassRule* cpcr, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; UShort* b; UShort* i; UShort* l; TTO_PosLookupRecord* plr; Bool* d; if ( ACCESS_Frame( 2L ) ) return error; cpcr->BacktrackGlyphCount = GET_UShort(); FORGET_Frame(); if ( cpcr->BacktrackGlyphCount > ccpf2->MaxBacktrackLength ) ccpf2->MaxBacktrackLength = cpcr->BacktrackGlyphCount; cpcr->Backtrack = NULL; count = cpcr->BacktrackGlyphCount; if ( ALLOC_ARRAY( cpcr->Backtrack, count, UShort ) ) return error; b = cpcr->Backtrack; d = ccpf2->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; cpcr->InputGlyphCount = GET_UShort(); if ( cpcr->InputGlyphCount > ccpf2->MaxInputLength ) ccpf2->MaxInputLength = cpcr->InputGlyphCount; FORGET_Frame(); cpcr->Input = NULL; count = cpcr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */ if ( ALLOC_ARRAY( cpcr->Input, count, UShort ) ) goto Fail4; i = cpcr->Input; d = ccpf2->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; cpcr->LookaheadGlyphCount = GET_UShort(); FORGET_Frame(); if ( cpcr->LookaheadGlyphCount > ccpf2->MaxLookaheadLength ) ccpf2->MaxLookaheadLength = cpcr->LookaheadGlyphCount; cpcr->Lookahead = NULL; count = cpcr->LookaheadGlyphCount; if ( ALLOC_ARRAY( cpcr->Lookahead, count, UShort ) ) goto Fail3; l = cpcr->Lookahead; d = ccpf2->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; cpcr->PosCount = GET_UShort(); FORGET_Frame(); cpcr->PosLookupRecord = NULL; count = cpcr->PosCount; if ( ALLOC_ARRAY( cpcr->PosLookupRecord, count, TTO_PosLookupRecord ) ) goto Fail2; plr = cpcr->PosLookupRecord; if ( ACCESS_Frame( count * 4L ) ) goto Fail1; for ( n = 0; n < count; n++ ) { plr[n].SequenceIndex = GET_UShort(); plr[n].LookupListIndex = GET_UShort(); } FORGET_Frame(); return TT_Err_Ok; Fail1: FREE( plr ); Fail2: FREE( l ); Fail3: FREE( i ); Fail4: FREE( b ); return error; } static void Free_ChainPosClassRule( TTO_ChainPosClassRule* cpcr ) { FREE( cpcr->PosLookupRecord ); FREE( cpcr->Lookahead ); FREE( cpcr->Input ); FREE( cpcr->Backtrack ); } /* PosClassSet */ static TT_Error Load_ChainPosClassSet( TTO_ChainContextPosFormat2* ccpf2, TTO_ChainPosClassSet* cpcs, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); UShort n, count; ULong cur_offset, new_offset, base_offset; TTO_ChainPosClassRule* cpcr; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; count = cpcs->ChainPosClassRuleCount = GET_UShort(); FORGET_Frame(); cpcs->ChainPosClassRule = NULL; if ( ALLOC_ARRAY( cpcs->ChainPosClassRule, count, TTO_ChainPosClassRule ) ) return error; cpcr = cpcs->ChainPosClassRule; 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_ChainPosClassRule( ccpf2, &cpcr[n], input ) ) != TT_Err_Ok ) goto Fail; (void)FILE_Seek( cur_offset ); } return TT_Err_Ok; Fail: for ( n = 0; n < count; n++ ) Free_ChainPosClassRule( &cpcr[n] ); FREE( cpcr ); return error; } static void Free_ChainPosClassSet( TTO_ChainPosClassSet* cpcs ) { UShort n, count; TTO_ChainPosClassRule* cpcr; if ( cpcs->ChainPosClassRule ) { count = cpcs->ChainPosClassRuleCount; cpcr = cpcs->ChainPosClassRule; for ( n = 0; n < count; n++ ) Free_ChainPosClassRule( &cpcr[n] ); FREE( cpcr ); } } /* ChainContextPosFormat2 */ static TT_Error Load_ChainContextPos2( TTO_ChainContextPosFormat2* ccpf2, 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_ChainPosClassSet* cpcs; 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( &ccpf2->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; /* `ChainPosClassSetCount' is the upper limit for input class values, thus we read it now to make an additional safety check. */ count = ccpf2->ChainPosClassSetCount = GET_UShort(); FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( backtrack_offset ) || ( error = Load_ClassDefinition( &ccpf2->BacktrackClassDef, count, input ) ) != TT_Err_Ok ) goto Fail5; if ( FILE_Seek( input_offset ) || ( error = Load_ClassDefinition( &ccpf2->InputClassDef, count, input ) ) != TT_Err_Ok ) goto Fail4; if ( FILE_Seek( lookahead_offset ) || ( error = Load_ClassDefinition( &ccpf2->LookaheadClassDef, count, input ) ) != TT_Err_Ok ) goto Fail3; (void)FILE_Seek( cur_offset ); ccpf2->ChainPosClassSet = NULL; ccpf2->MaxBacktrackLength = 0; ccpf2->MaxInputLength = 0; ccpf2->MaxLookaheadLength = 0; if ( ALLOC_ARRAY( ccpf2->ChainPosClassSet, count, TTO_ChainPosClassSet ) ) goto Fail2; cpcs = ccpf2->ChainPosClassSet; 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_ChainPosClassSet( ccpf2, &cpcs[n], input ) ) != TT_Err_Ok ) goto Fail1; (void)FILE_Seek( cur_offset ); } else { /* we create a ChainPosClassSet table with no entries */ ccpf2->ChainPosClassSet[n].ChainPosClassRuleCount = 0; ccpf2->ChainPosClassSet[n].ChainPosClassRule = NULL; } } return TT_Err_Ok; Fail1: for ( n = 0; n < count; n++ ) Free_ChainPosClassSet( &cpcs[n] ); FREE( cpcs ); Fail2: Free_ClassDefinition( &ccpf2->LookaheadClassDef ); Fail3: Free_ClassDefinition( &ccpf2->InputClassDef ); Fail4: Free_ClassDefinition( &ccpf2->BacktrackClassDef ); Fail5: Free_Coverage( &ccpf2->Coverage ); return error; } static void Free_ChainContext2( TTO_ChainContextPosFormat2* ccpf2 ) { UShort n, count; TTO_ChainPosClassSet* cpcs; if ( ccpf2->ChainPosClassSet ) { count = ccpf2->ChainPosClassSetCount; cpcs = ccpf2->ChainPosClassSet; for ( n = 0; n < count; n++ ) Free_ChainPosClassSet( &cpcs[n] ); FREE( cpcs ); } Free_ClassDefinition( &ccpf2->LookaheadClassDef ); Free_ClassDefinition( &ccpf2->InputClassDef ); Free_ClassDefinition( &ccpf2->BacktrackClassDef ); Free_Coverage( &ccpf2->Coverage ); } /* ChainContextPosFormat3 */ static TT_Error Load_ChainContextPos3( TTO_ChainContextPosFormat3* ccpf3, 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_PosLookupRecord* plr; base_offset = FILE_Pos() - 2L; if ( ACCESS_Frame( 2L ) ) return error; ccpf3->BacktrackGlyphCount = GET_UShort(); FORGET_Frame(); ccpf3->BacktrackCoverage = NULL; backtrack_count = ccpf3->BacktrackGlyphCount; if ( ALLOC_ARRAY( ccpf3->BacktrackCoverage, backtrack_count, TTO_Coverage ) ) return error; b = ccpf3->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; ccpf3->InputGlyphCount = GET_UShort(); FORGET_Frame(); ccpf3->InputCoverage = NULL; input_count = ccpf3->InputGlyphCount; if ( ALLOC_ARRAY( ccpf3->InputCoverage, input_count, TTO_Coverage ) ) goto Fail4; i = ccpf3->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; ccpf3->LookaheadGlyphCount = GET_UShort(); FORGET_Frame(); ccpf3->LookaheadCoverage = NULL; lookahead_count = ccpf3->LookaheadGlyphCount; if ( ALLOC_ARRAY( ccpf3->LookaheadCoverage, lookahead_count, TTO_Coverage ) ) goto Fail3; l = ccpf3->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; ccpf3->PosCount = GET_UShort(); FORGET_Frame(); ccpf3->PosLookupRecord = NULL; count = ccpf3->PosCount; if ( ALLOC_ARRAY( ccpf3->PosLookupRecord, count, TTO_PosLookupRecord ) ) goto Fail2; plr = ccpf3->PosLookupRecord; if ( ACCESS_Frame( count * 4L ) ) goto Fail1; for ( n = 0; n < count; n++ ) { plr[n].SequenceIndex = GET_UShort(); plr[n].LookupListIndex = GET_UShort(); } FORGET_Frame(); return TT_Err_Ok; Fail1: FREE( plr ); 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_ChainContextPosFormat3* ccpf3 ) { UShort n, count; TTO_Coverage* c; FREE( ccpf3->PosLookupRecord ); if ( ccpf3->LookaheadCoverage ) { count = ccpf3->LookaheadGlyphCount; c = ccpf3->LookaheadCoverage; for ( n = 0; n < count; n++ ) Free_Coverage( &c[n] ); FREE( c ); } if ( ccpf3->InputCoverage ) { count = ccpf3->InputGlyphCount; c = ccpf3->InputCoverage; for ( n = 0; n < count; n++ ) Free_Coverage( &c[n] ); FREE( c ); } if ( ccpf3->BacktrackCoverage ) { count = ccpf3->BacktrackGlyphCount; c = ccpf3->BacktrackCoverage; for ( n = 0; n < count; n++ ) Free_Coverage( &c[n] ); FREE( c ); } } /* ChainContextPos */ TT_Error Load_ChainContextPos( TTO_ChainContextPos* ccp, PFace input ) { DEFINE_LOAD_LOCALS( input->stream ); if ( ACCESS_Frame( 2L ) ) return error; ccp->PosFormat = GET_UShort(); FORGET_Frame(); switch ( ccp->PosFormat ) { case 1: return Load_ChainContextPos1( &ccp->ccpf.ccpf1, input ); case 2: return Load_ChainContextPos2( &ccp->ccpf.ccpf2, input ); case 3: return Load_ChainContextPos3( &ccp->ccpf.ccpf3, input ); default: return TTO_Err_Invalid_GPOS_SubTable_Format; } return TT_Err_Ok; /* never reached */ } void Free_ChainContextPos( TTO_ChainContextPos* ccp ) { switch ( ccp->PosFormat ) { case 1: Free_ChainContext1( &ccp->ccpf.ccpf1 ); break; case 2: Free_ChainContext2( &ccp->ccpf.ccpf2 ); break; case 3: Free_ChainContext3( &ccp->ccpf.ccpf3 ); break; } } /*********** * GPOS API ***********/ EXPORT_FUNC TT_Error TT_GPOS_Select_Script( TTO_GPOSHeader* gpos, TT_ULong script_tag, TT_UShort* script_index ) { UShort n; TTO_ScriptList* sl; TTO_ScriptRecord* sr; if ( !gpos || !script_index ) return TT_Err_Invalid_Argument; sl = &gpos->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_GPOS_Select_Language( TTO_GPOSHeader* gpos, 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 ( !gpos || !language_index || !req_feature_index ) return TT_Err_Invalid_Argument; sl = &gpos->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_GPOS_Select_Feature( TTO_GPOSHeader* gpos, 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 ( !gpos || !feature_index ) return TT_Err_Invalid_Argument; sl = &gpos->ScriptList; sr = sl->ScriptRecord; fl = &gpos->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_GPOS_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_GPOS_Query_Scripts( TTO_GPOSHeader* gpos, TT_ULong** script_tag_list ) { UShort n; TT_Error error; ULong* stl; TTO_ScriptList* sl; TTO_ScriptRecord* sr; if ( !gpos || !script_tag_list ) return TT_Err_Invalid_Argument; sl = &gpos->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_GPOS_Query_Languages( TTO_GPOSHeader* gpos, 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 ( !gpos || !language_tag_list ) return TT_Err_Invalid_Argument; sl = &gpos->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_GPOS_Query_Features( TTO_GPOSHeader* gpos, 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 ( !gpos || !feature_tag_list ) return TT_Err_Invalid_Argument; sl = &gpos->ScriptList; sr = sl->ScriptRecord; fl = &gpos->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_GPOS_SubTable_Format; } ftl[n] = fr[fi[n]].FeatureTag; } ftl[n] = 0; *feature_tag_list = ftl; return TT_Err_Ok; } EXPORT_FUNC TT_Error TT_GPOS_Add_Feature( TTO_GPOSHeader* gpos, TT_UShort feature_index, TT_UShort property ) { UShort i; TTO_Feature feature; UShort* properties; UShort* index; if ( !gpos || feature_index >= gpos->FeatureList.FeatureCount ) return TT_Err_Invalid_Argument; properties = gpos->LookupList.Properties; feature = gpos->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_GPOS_Clear_Features( TTO_GPOSHeader* gpos ) { UShort i; UShort* properties; if ( !gpos ) return TT_Err_Invalid_Argument; properties = gpos->LookupList.Properties; for ( i = 0; i < gpos->LookupList.LookupCount; i++ ) properties[i] = 0; return TT_Err_Ok; } /* END */