/* * Copyright (C) 1998-2004 David Turner and Werner Lemberg * Copyright (C) 2006 Behdad Esfahbod * * This is part of HarfBuzz, an OpenType Layout engine library. * * Permission is hereby granted, without written agreement and without * license or royalty fees, to use, copy, modify, and distribute this * software and its documentation for any purpose, provided that the * above copyright notice and the following two paragraphs appear in * all copies of this software. * * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ #include "harfbuzz-impl.h" #include "harfbuzz-open-private.h" /*************************** * Script related functions ***************************/ /* LangSys */ static HB_Error Load_LangSys( HB_LangSys* ls, HB_Stream stream ) { HB_Error error; HB_UShort n, count; HB_UShort* fi; if ( ACCESS_Frame( 6L ) ) return error; ls->LookupOrderOffset = GET_UShort(); /* should be 0 */ ls->ReqFeatureIndex = GET_UShort(); count = ls->FeatureCount = GET_UShort(); FORGET_Frame(); ls->FeatureIndex = NULL; if ( ALLOC_ARRAY( ls->FeatureIndex, count, HB_UShort ) ) return error; if ( ACCESS_Frame( count * 2L ) ) { FREE( ls->FeatureIndex ); return error; } fi = ls->FeatureIndex; for ( n = 0; n < count; n++ ) fi[n] = GET_UShort(); FORGET_Frame(); return HB_Err_Ok; } static void Free_LangSys( HB_LangSys* ls ) { FREE( ls->FeatureIndex ); } /* Script */ static HB_Error Load_Script( HB_ScriptTable* s, HB_Stream stream ) { HB_Error error; HB_UShort n, m, count; HB_UInt cur_offset, new_offset, base_offset; HB_LangSysRecord* lsr; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; 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_LangSys( &s->DefaultLangSys, stream ) ) != HB_Err_Ok ) return error; (void)FILE_Seek( cur_offset ); } else { /* we create a DefaultLangSys table with no entries */ s->DefaultLangSys.LookupOrderOffset = 0; s->DefaultLangSys.ReqFeatureIndex = 0xFFFF; s->DefaultLangSys.FeatureCount = 0; s->DefaultLangSys.FeatureIndex = NULL; } if ( ACCESS_Frame( 2L ) ) goto Fail2; count = s->LangSysCount = GET_UShort(); /* safety check; otherwise the official handling of TrueType Open fonts won't work */ if ( s->LangSysCount == 0 && s->DefaultLangSys.FeatureCount == 0 ) { error = HB_Err_Not_Covered; goto Fail2; } FORGET_Frame(); s->LangSysRecord = NULL; if ( ALLOC_ARRAY( s->LangSysRecord, count, HB_LangSysRecord ) ) goto Fail2; lsr = s->LangSysRecord; for ( n = 0; n < count; n++ ) { if ( ACCESS_Frame( 6L ) ) goto Fail1; lsr[n].LangSysTag = GET_ULong(); new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_LangSys( &lsr[n].LangSys, stream ) ) != HB_Err_Ok ) goto Fail1; (void)FILE_Seek( cur_offset ); } return HB_Err_Ok; Fail1: for ( m = 0; m < n; m++ ) Free_LangSys( &lsr[m].LangSys ); FREE( s->LangSysRecord ); Fail2: Free_LangSys( &s->DefaultLangSys ); return error; } static void Free_Script( HB_ScriptTable* s ) { HB_UShort n, count; HB_LangSysRecord* lsr; Free_LangSys( &s->DefaultLangSys ); if ( s->LangSysRecord ) { count = s->LangSysCount; lsr = s->LangSysRecord; for ( n = 0; n < count; n++ ) Free_LangSys( &lsr[n].LangSys ); FREE( lsr ); } } /* ScriptList */ HB_INTERNAL HB_Error _HB_OPEN_Load_ScriptList( HB_ScriptList* sl, HB_Stream stream ) { HB_Error error; HB_UShort n, script_count; HB_UInt cur_offset, new_offset, base_offset; HB_ScriptRecord* sr; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; script_count = GET_UShort(); FORGET_Frame(); sl->ScriptRecord = NULL; if ( ALLOC_ARRAY( sl->ScriptRecord, script_count, HB_ScriptRecord ) ) return error; sr = sl->ScriptRecord; sl->ScriptCount= 0; for ( n = 0; n < script_count; n++ ) { if ( ACCESS_Frame( 6L ) ) goto Fail; sr[sl->ScriptCount].ScriptTag = GET_ULong(); new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) ) goto Fail; error = Load_Script( &sr[sl->ScriptCount].Script, stream ); if ( error == HB_Err_Ok ) sl->ScriptCount += 1; else if ( error != HB_Err_Not_Covered ) goto Fail; (void)FILE_Seek( cur_offset ); } /* Empty tables are harmless and generated by fontforge. * See http://bugzilla.gnome.org/show_bug.cgi?id=347073 */ #if 0 if ( sl->ScriptCount == 0 ) { error = ERR(HB_Err_Invalid_SubTable); goto Fail; } #endif return HB_Err_Ok; Fail: for ( n = 0; n < sl->ScriptCount; n++ ) Free_Script( &sr[n].Script ); FREE( sl->ScriptRecord ); return error; } HB_INTERNAL void _HB_OPEN_Free_ScriptList( HB_ScriptList* sl ) { HB_UShort n, count; HB_ScriptRecord* sr; if ( sl->ScriptRecord ) { count = sl->ScriptCount; sr = sl->ScriptRecord; for ( n = 0; n < count; n++ ) Free_Script( &sr[n].Script ); FREE( sr ); } } /********************************* * Feature List related functions *********************************/ /* Feature */ static HB_Error Load_Feature( HB_Feature* f, HB_Stream stream ) { HB_Error error; HB_UShort n, count; HB_UShort* lli; if ( ACCESS_Frame( 4L ) ) return error; f->FeatureParams = GET_UShort(); /* should be 0 */ count = f->LookupListCount = GET_UShort(); FORGET_Frame(); f->LookupListIndex = NULL; if ( ALLOC_ARRAY( f->LookupListIndex, count, HB_UShort ) ) return error; lli = f->LookupListIndex; if ( ACCESS_Frame( count * 2L ) ) { FREE( f->LookupListIndex ); return error; } for ( n = 0; n < count; n++ ) lli[n] = GET_UShort(); FORGET_Frame(); return HB_Err_Ok; } static void Free_Feature( HB_Feature* f ) { FREE( f->LookupListIndex ); } /* FeatureList */ HB_INTERNAL HB_Error _HB_OPEN_Load_FeatureList( HB_FeatureList* fl, HB_Stream stream ) { HB_Error error; HB_UShort n, m, count; HB_UInt cur_offset, new_offset, base_offset; HB_FeatureRecord* fr; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; count = fl->FeatureCount = GET_UShort(); FORGET_Frame(); fl->FeatureRecord = NULL; if ( ALLOC_ARRAY( fl->FeatureRecord, count, HB_FeatureRecord ) ) return error; if ( ALLOC_ARRAY( fl->ApplyOrder, count, HB_UShort ) ) goto Fail2; fl->ApplyCount = 0; fr = fl->FeatureRecord; for ( n = 0; n < count; n++ ) { if ( ACCESS_Frame( 6L ) ) goto Fail1; fr[n].FeatureTag = GET_ULong(); new_offset = GET_UShort() + base_offset; FORGET_Frame(); cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = Load_Feature( &fr[n].Feature, stream ) ) != HB_Err_Ok ) goto Fail1; (void)FILE_Seek( cur_offset ); } return HB_Err_Ok; Fail1: for ( m = 0; m < n; m++ ) Free_Feature( &fr[m].Feature ); FREE( fl->ApplyOrder ); Fail2: FREE( fl->FeatureRecord ); return error; } HB_INTERNAL void _HB_OPEN_Free_FeatureList( HB_FeatureList* fl ) { HB_UShort n, count; HB_FeatureRecord* fr; if ( fl->FeatureRecord ) { count = fl->FeatureCount; fr = fl->FeatureRecord; for ( n = 0; n < count; n++ ) Free_Feature( &fr[n].Feature ); FREE( fr ); } FREE( fl->ApplyOrder ); } /******************************** * Lookup List related functions ********************************/ /* the subroutines of the following two functions are defined in ftxgsub.c and ftxgpos.c respectively */ /* SubTable */ static HB_Error Load_SubTable( HB_SubTable* st, HB_Stream stream, HB_Type table_type, HB_UShort lookup_type ) { if ( table_type == HB_Type_GSUB ) return _HB_GSUB_Load_SubTable ( &st->st.gsub, stream, lookup_type ); else return _HB_GPOS_Load_SubTable ( &st->st.gpos, stream, lookup_type ); } static void Free_SubTable( HB_SubTable* st, HB_Type table_type, HB_UShort lookup_type ) { if ( table_type == HB_Type_GSUB ) _HB_GSUB_Free_SubTable ( &st->st.gsub, lookup_type ); else _HB_GPOS_Free_SubTable ( &st->st.gpos, lookup_type ); } /* Lookup */ static HB_Error Load_Lookup( HB_Lookup* l, HB_Stream stream, HB_Type type ) { HB_Error error; HB_UShort n, m, count; HB_UInt cur_offset, new_offset, base_offset; HB_SubTable* st; HB_Bool is_extension = FALSE; base_offset = FILE_Pos(); if ( ACCESS_Frame( 6L ) ) return error; l->LookupType = GET_UShort(); l->LookupFlag = GET_UShort(); count = l->SubTableCount = GET_UShort(); FORGET_Frame(); l->SubTable = NULL; if ( ALLOC_ARRAY( l->SubTable, count, HB_SubTable ) ) return error; st = l->SubTable; if ( ( type == HB_Type_GSUB && l->LookupType == HB_GSUB_LOOKUP_EXTENSION ) || ( type == HB_Type_GPOS && l->LookupType == HB_GPOS_LOOKUP_EXTENSION ) ) is_extension = TRUE; 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 ( is_extension ) { if ( FILE_Seek( new_offset ) || ACCESS_Frame( 8L ) ) goto Fail; if (GET_UShort() != 1) /* format should be 1 */ goto Fail; l->LookupType = GET_UShort(); new_offset += GET_ULong(); FORGET_Frame(); } if ( FILE_Seek( new_offset ) || ( error = Load_SubTable( &st[n], stream, type, l->LookupType ) ) != HB_Err_Ok ) goto Fail; (void)FILE_Seek( cur_offset ); } return HB_Err_Ok; Fail: for ( m = 0; m < n; m++ ) Free_SubTable( &st[m], type, l->LookupType ); FREE( l->SubTable ); return error; } static void Free_Lookup( HB_Lookup* l, HB_Type type) { HB_UShort n, count; HB_SubTable* st; if ( l->SubTable ) { count = l->SubTableCount; st = l->SubTable; for ( n = 0; n < count; n++ ) Free_SubTable( &st[n], type, l->LookupType ); FREE( st ); } } /* LookupList */ HB_INTERNAL HB_Error _HB_OPEN_Load_LookupList( HB_LookupList* ll, HB_Stream stream, HB_Type type ) { HB_Error error; HB_UShort n, m, count; HB_UInt cur_offset, new_offset, base_offset; HB_Lookup* l; base_offset = FILE_Pos(); if ( ACCESS_Frame( 2L ) ) return error; count = ll->LookupCount = GET_UShort(); FORGET_Frame(); ll->Lookup = NULL; if ( ALLOC_ARRAY( ll->Lookup, count, HB_Lookup ) ) return error; if ( ALLOC_ARRAY( ll->Properties, count, HB_UInt ) ) goto Fail2; l = ll->Lookup; 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_Lookup( &l[n], stream, type ) ) != HB_Err_Ok ) goto Fail1; (void)FILE_Seek( cur_offset ); } return HB_Err_Ok; Fail1: FREE( ll->Properties ); for ( m = 0; m < n; m++ ) Free_Lookup( &l[m], type ); Fail2: FREE( ll->Lookup ); return error; } HB_INTERNAL void _HB_OPEN_Free_LookupList( HB_LookupList* ll, HB_Type type ) { HB_UShort n, count; HB_Lookup* l; FREE( ll->Properties ); if ( ll->Lookup ) { count = ll->LookupCount; l = ll->Lookup; for ( n = 0; n < count; n++ ) Free_Lookup( &l[n], type ); FREE( l ); } } /***************************** * Coverage related functions *****************************/ /* CoverageFormat1 */ static HB_Error Load_Coverage1( HB_CoverageFormat1* cf1, HB_Stream stream ) { HB_Error error; HB_UShort n, count; HB_UShort* ga; if ( ACCESS_Frame( 2L ) ) return error; count = cf1->GlyphCount = GET_UShort(); FORGET_Frame(); cf1->GlyphArray = NULL; if ( ALLOC_ARRAY( cf1->GlyphArray, count, HB_UShort ) ) return error; ga = cf1->GlyphArray; if ( ACCESS_Frame( count * 2L ) ) { FREE( cf1->GlyphArray ); return error; } for ( n = 0; n < count; n++ ) ga[n] = GET_UShort(); FORGET_Frame(); return HB_Err_Ok; } static void Free_Coverage1( HB_CoverageFormat1* cf1) { FREE( cf1->GlyphArray ); } /* CoverageFormat2 */ static HB_Error Load_Coverage2( HB_CoverageFormat2* cf2, HB_Stream stream ) { HB_Error error; HB_UShort n, count; HB_RangeRecord* rr; if ( ACCESS_Frame( 2L ) ) return error; count = cf2->RangeCount = GET_UShort(); FORGET_Frame(); cf2->RangeRecord = NULL; if ( ALLOC_ARRAY( cf2->RangeRecord, count, HB_RangeRecord ) ) return error; rr = cf2->RangeRecord; if ( ACCESS_Frame( count * 6L ) ) goto Fail; for ( n = 0; n < count; n++ ) { rr[n].Start = GET_UShort(); rr[n].End = GET_UShort(); rr[n].StartCoverageIndex = GET_UShort(); /* sanity check; we are limited to 16bit integers */ if ( rr[n].Start > rr[n].End || ( rr[n].End - rr[n].Start + (long)rr[n].StartCoverageIndex ) >= 0x10000L ) { error = ERR(HB_Err_Invalid_SubTable); goto Fail; } } FORGET_Frame(); return HB_Err_Ok; Fail: FREE( cf2->RangeRecord ); return error; } static void Free_Coverage2( HB_CoverageFormat2* cf2 ) { FREE( cf2->RangeRecord ); } HB_INTERNAL HB_Error _HB_OPEN_Load_Coverage( HB_Coverage* c, HB_Stream stream ) { HB_Error error; if ( ACCESS_Frame( 2L ) ) return error; c->CoverageFormat = GET_UShort(); FORGET_Frame(); switch ( c->CoverageFormat ) { case 1: return Load_Coverage1( &c->cf.cf1, stream ); case 2: return Load_Coverage2( &c->cf.cf2, stream ); default: return ERR(HB_Err_Invalid_SubTable_Format); } return HB_Err_Ok; /* never reached */ } HB_INTERNAL void _HB_OPEN_Free_Coverage( HB_Coverage* c ) { switch ( c->CoverageFormat ) { case 1: Free_Coverage1( &c->cf.cf1 ); break; case 2: Free_Coverage2( &c->cf.cf2 ); break; default: break; } } static HB_Error Coverage_Index1( HB_CoverageFormat1* cf1, HB_UShort glyphID, HB_UShort* index ) { HB_UShort min, max, new_min, new_max, middle; HB_UShort* array = cf1->GlyphArray; /* binary search */ if ( cf1->GlyphCount == 0 ) return HB_Err_Not_Covered; new_min = 0; new_max = cf1->GlyphCount - 1; do { min = new_min; max = new_max; /* we use (min + max) / 2 = max - (max - min) / 2 to avoid overflow and rounding errors */ middle = max - ( ( max - min ) >> 1 ); if ( glyphID == array[middle] ) { *index = middle; return HB_Err_Ok; } else if ( glyphID < array[middle] ) { if ( middle == min ) break; new_max = middle - 1; } else { if ( middle == max ) break; new_min = middle + 1; } } while ( min < max ); return HB_Err_Not_Covered; } static HB_Error Coverage_Index2( HB_CoverageFormat2* cf2, HB_UShort glyphID, HB_UShort* index ) { HB_UShort min, max, new_min, new_max, middle; HB_RangeRecord* rr = cf2->RangeRecord; /* binary search */ if ( cf2->RangeCount == 0 ) return HB_Err_Not_Covered; new_min = 0; new_max = cf2->RangeCount - 1; do { min = new_min; max = new_max; /* we use (min + max) / 2 = max - (max - min) / 2 to avoid overflow and rounding errors */ middle = max - ( ( max - min ) >> 1 ); if ( glyphID >= rr[middle].Start && glyphID <= rr[middle].End ) { *index = rr[middle].StartCoverageIndex + glyphID - rr[middle].Start; return HB_Err_Ok; } else if ( glyphID < rr[middle].Start ) { if ( middle == min ) break; new_max = middle - 1; } else { if ( middle == max ) break; new_min = middle + 1; } } while ( min < max ); return HB_Err_Not_Covered; } HB_INTERNAL HB_Error _HB_OPEN_Coverage_Index( HB_Coverage* c, HB_UShort glyphID, HB_UShort* index ) { switch ( c->CoverageFormat ) { case 1: return Coverage_Index1( &c->cf.cf1, glyphID, index ); case 2: return Coverage_Index2( &c->cf.cf2, glyphID, index ); default: return ERR(HB_Err_Invalid_SubTable_Format); } return HB_Err_Ok; /* never reached */ } /************************************* * Class Definition related functions *************************************/ /* ClassDefFormat1 */ static HB_Error Load_ClassDef1( HB_ClassDefinition* cd, HB_UShort limit, HB_Stream stream ) { HB_Error error; HB_UShort n, count; HB_UShort* cva; HB_ClassDefFormat1* cdf1; cdf1 = &cd->cd.cd1; if ( ACCESS_Frame( 4L ) ) return error; cdf1->StartGlyph = GET_UShort(); count = cdf1->GlyphCount = GET_UShort(); FORGET_Frame(); /* sanity check; we are limited to 16bit integers */ if ( cdf1->StartGlyph + (long)count >= 0x10000L ) return ERR(HB_Err_Invalid_SubTable); cdf1->ClassValueArray = NULL; if ( ALLOC_ARRAY( cdf1->ClassValueArray, count, HB_UShort ) ) return error; cva = cdf1->ClassValueArray; if ( ACCESS_Frame( count * 2L ) ) goto Fail; for ( n = 0; n < count; n++ ) { cva[n] = GET_UShort(); if ( cva[n] >= limit ) { error = ERR(HB_Err_Invalid_SubTable); goto Fail; } } FORGET_Frame(); return HB_Err_Ok; Fail: FREE( cva ); return error; } static void Free_ClassDef1( HB_ClassDefFormat1* cdf1 ) { FREE( cdf1->ClassValueArray ); } /* ClassDefFormat2 */ static HB_Error Load_ClassDef2( HB_ClassDefinition* cd, HB_UShort limit, HB_Stream stream ) { HB_Error error; HB_UShort n, count; HB_ClassRangeRecord* crr; HB_ClassDefFormat2* cdf2; cdf2 = &cd->cd.cd2; if ( ACCESS_Frame( 2L ) ) return error; count = GET_UShort(); cdf2->ClassRangeCount = 0; /* zero for now. we fill with the number of good entries later */ FORGET_Frame(); cdf2->ClassRangeRecord = NULL; if ( ALLOC_ARRAY( cdf2->ClassRangeRecord, count, HB_ClassRangeRecord ) ) return error; crr = cdf2->ClassRangeRecord; if ( ACCESS_Frame( count * 6L ) ) goto Fail; for ( n = 0; n < count; n++ ) { crr[n].Start = GET_UShort(); crr[n].End = GET_UShort(); crr[n].Class = GET_UShort(); /* sanity check */ if ( crr[n].Start > crr[n].End || crr[n].Class >= limit ) { /* XXX * Corrupt entry. Skip it. * This is hit by Nafees Nastaliq font for example */ n--; count--; } } FORGET_Frame(); cdf2->ClassRangeCount = count; return HB_Err_Ok; Fail: FREE( crr ); return error; } static void Free_ClassDef2( HB_ClassDefFormat2* cdf2 ) { FREE( cdf2->ClassRangeRecord ); } /* ClassDefinition */ HB_INTERNAL HB_Error _HB_OPEN_Load_ClassDefinition( HB_ClassDefinition* cd, HB_UShort limit, HB_Stream stream ) { HB_Error error; if ( ACCESS_Frame( 2L ) ) return error; cd->ClassFormat = GET_UShort(); FORGET_Frame(); switch ( cd->ClassFormat ) { case 1: error = Load_ClassDef1( cd, limit, stream ); break; case 2: error = Load_ClassDef2( cd, limit, stream ); break; default: error = ERR(HB_Err_Invalid_SubTable_Format); break; } if ( error ) return error; cd->loaded = TRUE; return HB_Err_Ok; } static HB_Error _HB_OPEN_Load_EmptyClassDefinition( HB_ClassDefinition* cd ) { HB_Error error; cd->ClassFormat = 1; /* Meaningless */ if ( ALLOC_ARRAY( cd->cd.cd1.ClassValueArray, 1, HB_UShort ) ) return error; cd->loaded = TRUE; return HB_Err_Ok; } HB_INTERNAL HB_Error _HB_OPEN_Load_EmptyOrClassDefinition( HB_ClassDefinition* cd, HB_UShort limit, HB_UInt class_offset, HB_UInt base_offset, HB_Stream stream ) { HB_Error error; HB_UInt cur_offset; cur_offset = FILE_Pos(); if ( class_offset ) { if ( !FILE_Seek( class_offset + base_offset ) ) error = _HB_OPEN_Load_ClassDefinition( cd, limit, stream ); } else error = _HB_OPEN_Load_EmptyClassDefinition ( cd ); if (error == HB_Err_Ok) (void)FILE_Seek( cur_offset ); /* Changes error as a side-effect */ return error; } HB_INTERNAL void _HB_OPEN_Free_ClassDefinition( HB_ClassDefinition* cd ) { if ( !cd->loaded ) return; switch ( cd->ClassFormat ) { case 1: Free_ClassDef1( &cd->cd.cd1 ); break; case 2: Free_ClassDef2( &cd->cd.cd2 ); break; default: break; } } static HB_Error Get_Class1( HB_ClassDefFormat1* cdf1, HB_UShort glyphID, HB_UShort* klass, HB_UShort* index ) { HB_UShort* cva = cdf1->ClassValueArray; if ( index ) *index = 0; if ( glyphID >= cdf1->StartGlyph && glyphID < cdf1->StartGlyph + cdf1->GlyphCount ) { *klass = cva[glyphID - cdf1->StartGlyph]; return HB_Err_Ok; } else { *klass = 0; return HB_Err_Not_Covered; } } /* we need the index value of the last searched class range record in case of failure for constructed GDEF tables */ static HB_Error Get_Class2( HB_ClassDefFormat2* cdf2, HB_UShort glyphID, HB_UShort* klass, HB_UShort* index ) { HB_Error error = HB_Err_Ok; HB_UShort min, max, new_min, new_max, middle; HB_ClassRangeRecord* crr = cdf2->ClassRangeRecord; /* binary search */ if ( cdf2->ClassRangeCount == 0 ) { *klass = 0; if ( index ) *index = 0; return HB_Err_Not_Covered; } new_min = 0; new_max = cdf2->ClassRangeCount - 1; do { min = new_min; max = new_max; /* we use (min + max) / 2 = max - (max - min) / 2 to avoid overflow and rounding errors */ middle = max - ( ( max - min ) >> 1 ); if ( glyphID >= crr[middle].Start && glyphID <= crr[middle].End ) { *klass = crr[middle].Class; error = HB_Err_Ok; break; } else if ( glyphID < crr[middle].Start ) { if ( middle == min ) { *klass = 0; error = HB_Err_Not_Covered; break; } new_max = middle - 1; } else { if ( middle == max ) { *klass = 0; error = HB_Err_Not_Covered; break; } new_min = middle + 1; } } while ( min < max ); if ( index ) *index = middle; return error; } HB_INTERNAL HB_Error _HB_OPEN_Get_Class( HB_ClassDefinition* cd, HB_UShort glyphID, HB_UShort* klass, HB_UShort* index ) { switch ( cd->ClassFormat ) { case 1: return Get_Class1( &cd->cd.cd1, glyphID, klass, index ); case 2: return Get_Class2( &cd->cd.cd2, glyphID, klass, index ); default: return ERR(HB_Err_Invalid_SubTable_Format); } return HB_Err_Ok; /* never reached */ } /*************************** * Device related functions ***************************/ HB_INTERNAL HB_Error _HB_OPEN_Load_Device( HB_Device** device, HB_Stream stream ) { HB_Device* d; HB_Error error; HB_UShort n, count; HB_UShort* dv; if ( ACCESS_Frame( 6L ) ) return error; if ( ALLOC( *device, sizeof(HB_Device)) ) { *device = 0; return error; } d = *device; d->StartSize = GET_UShort(); d->EndSize = GET_UShort(); d->DeltaFormat = GET_UShort(); FORGET_Frame(); d->DeltaValue = NULL; if ( d->StartSize > d->EndSize || d->DeltaFormat == 0 || d->DeltaFormat > 3 ) { /* XXX * I've seen fontforge generate DeltaFormat == 0. * Just return Ok and let the NULL DeltaValue disable * this table. */ return HB_Err_Ok; } count = ( ( d->EndSize - d->StartSize + 1 ) >> ( 4 - d->DeltaFormat ) ) + 1; if ( ALLOC_ARRAY( d->DeltaValue, count, HB_UShort ) ) { FREE( *device ); *device = 0; return error; } if ( ACCESS_Frame( count * 2L ) ) { FREE( d->DeltaValue ); FREE( *device ); *device = 0; return error; } dv = d->DeltaValue; for ( n = 0; n < count; n++ ) dv[n] = GET_UShort(); FORGET_Frame(); return HB_Err_Ok; } HB_INTERNAL void _HB_OPEN_Free_Device( HB_Device* d ) { if ( d ) { FREE( d->DeltaValue ); FREE( d ); } } /* Since we have the delta values stored in compressed form, we must uncompress it now. To simplify the interface, the function always returns a meaningful value in `value'; the error is just for information. | | format = 1: 0011223344556677|8899101112131415|... | | byte 1 byte 2 00: (byte >> 14) & mask 11: (byte >> 12) & mask ... mask = 0x0003 | | format = 2: 0000111122223333|4444555566667777|... | | byte 1 byte 2 0000: (byte >> 12) & mask 1111: (byte >> 8) & mask ... mask = 0x000F | | format = 3: 0000000011111111|2222222233333333|... | | byte 1 byte 2 00000000: (byte >> 8) & mask 11111111: (byte >> 0) & mask .... mask = 0x00FF */ HB_INTERNAL HB_Error _HB_OPEN_Get_Device( HB_Device* d, HB_UShort size, HB_Short* value ) { HB_UShort byte, bits, mask, s; if ( d && d->DeltaValue && size >= d->StartSize && size <= d->EndSize ) { HB_UShort f = d->DeltaFormat; s = size - d->StartSize; byte = d->DeltaValue[s >> ( 4 - f )]; bits = byte >> ( 16 - ( ( s % ( 1 << ( 4 - f ) ) + 1 ) << f ) ); mask = 0xFFFF >> ( 16 - ( 1 << f ) ); *value = (HB_Short)( bits & mask ); /* conversion to a signed value */ if ( *value >= ( ( mask + 1 ) >> 1 ) ) *value -= mask + 1; return HB_Err_Ok; } else { *value = 0; return HB_Err_Not_Covered; } } /* END */