|  | /* | 
|  | * 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-gdef-private.h" | 
|  | #include "harfbuzz-open-private.h" | 
|  |  | 
|  | static HB_Error  Load_AttachList( HB_AttachList*  al, | 
|  | HB_Stream        stream ); | 
|  | static HB_Error  Load_LigCaretList( HB_LigCaretList*  lcl, | 
|  | HB_Stream          stream ); | 
|  |  | 
|  | static void  Free_AttachList( HB_AttachList*  al); | 
|  | static void  Free_LigCaretList( HB_LigCaretList*  lcl); | 
|  |  | 
|  | static void  Free_NewGlyphClasses( HB_GDEFHeader*  gdef); | 
|  |  | 
|  |  | 
|  |  | 
|  | /* GDEF glyph classes */ | 
|  |  | 
|  | #define UNCLASSIFIED_GLYPH  0 | 
|  | #define SIMPLE_GLYPH        1 | 
|  | #define LIGATURE_GLYPH      2 | 
|  | #define MARK_GLYPH          3 | 
|  | #define COMPONENT_GLYPH     4 | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  | HB_Error  HB_New_GDEF_Table( HB_GDEFHeader** retptr ) | 
|  | { | 
|  | HB_Error         error; | 
|  |  | 
|  | HB_GDEFHeader*  gdef; | 
|  |  | 
|  | if ( !retptr ) | 
|  | return ERR(HB_Err_Invalid_Argument); | 
|  |  | 
|  | if ( ALLOC( gdef, sizeof( *gdef ) ) ) | 
|  | return error; | 
|  |  | 
|  | gdef->GlyphClassDef.loaded = FALSE; | 
|  | gdef->AttachList.loaded = FALSE; | 
|  | gdef->LigCaretList.loaded = FALSE; | 
|  | gdef->MarkAttachClassDef_offset = 0; | 
|  | gdef->MarkAttachClassDef.loaded = FALSE; | 
|  |  | 
|  | gdef->LastGlyph = 0; | 
|  | gdef->NewGlyphClasses = NULL; | 
|  |  | 
|  | *retptr = gdef; | 
|  |  | 
|  | return HB_Err_Ok; | 
|  | } | 
|  |  | 
|  |  | 
|  | HB_Error  HB_Load_GDEF_Table( HB_Stream stream, | 
|  | HB_GDEFHeader** retptr ) | 
|  | { | 
|  | HB_Error         error; | 
|  | HB_UInt         cur_offset, new_offset, base_offset; | 
|  |  | 
|  | HB_GDEFHeader*  gdef; | 
|  |  | 
|  |  | 
|  | if ( !retptr ) | 
|  | return ERR(HB_Err_Invalid_Argument); | 
|  |  | 
|  | if ( GOTO_Table( TTAG_GDEF ) ) | 
|  | return error; | 
|  |  | 
|  | if (( error = HB_New_GDEF_Table ( &gdef ) )) | 
|  | return error; | 
|  |  | 
|  | base_offset = FILE_Pos(); | 
|  |  | 
|  | /* skip version */ | 
|  |  | 
|  | if ( FILE_Seek( base_offset + 4L ) || | 
|  | ACCESS_Frame( 2L ) ) | 
|  | goto Fail0; | 
|  |  | 
|  | new_offset = GET_UShort(); | 
|  |  | 
|  | FORGET_Frame(); | 
|  |  | 
|  | /* all GDEF subtables are optional */ | 
|  |  | 
|  | if ( new_offset ) | 
|  | { | 
|  | new_offset += base_offset; | 
|  |  | 
|  | /* only classes 1-4 are allowed here */ | 
|  |  | 
|  | cur_offset = FILE_Pos(); | 
|  | if ( FILE_Seek( new_offset ) || | 
|  | ( error = _HB_OPEN_Load_ClassDefinition( &gdef->GlyphClassDef, 5, | 
|  | stream ) ) != HB_Err_Ok ) | 
|  | goto Fail0; | 
|  | (void)FILE_Seek( cur_offset ); | 
|  | } | 
|  |  | 
|  | if ( ACCESS_Frame( 2L ) ) | 
|  | goto Fail1; | 
|  |  | 
|  | new_offset = GET_UShort(); | 
|  |  | 
|  | FORGET_Frame(); | 
|  |  | 
|  | if ( new_offset ) | 
|  | { | 
|  | new_offset += base_offset; | 
|  |  | 
|  | cur_offset = FILE_Pos(); | 
|  | if ( FILE_Seek( new_offset ) || | 
|  | ( error = Load_AttachList( &gdef->AttachList, | 
|  | stream ) ) != HB_Err_Ok ) | 
|  | goto Fail1; | 
|  | (void)FILE_Seek( cur_offset ); | 
|  | } | 
|  |  | 
|  | if ( ACCESS_Frame( 2L ) ) | 
|  | goto Fail2; | 
|  |  | 
|  | new_offset = GET_UShort(); | 
|  |  | 
|  | FORGET_Frame(); | 
|  |  | 
|  | if ( new_offset ) | 
|  | { | 
|  | new_offset += base_offset; | 
|  |  | 
|  | cur_offset = FILE_Pos(); | 
|  | if ( FILE_Seek( new_offset ) || | 
|  | ( error = Load_LigCaretList( &gdef->LigCaretList, | 
|  | stream ) ) != HB_Err_Ok ) | 
|  | goto Fail2; | 
|  | (void)FILE_Seek( cur_offset ); | 
|  | } | 
|  |  | 
|  | /* OpenType 1.2 has introduced the `MarkAttachClassDef' field.  We | 
|  | first have to scan the LookupFlag values to find out whether we | 
|  | must load it or not.  Here we only store the offset of the table. */ | 
|  |  | 
|  | if ( ACCESS_Frame( 2L ) ) | 
|  | goto Fail3; | 
|  |  | 
|  | new_offset = GET_UShort(); | 
|  |  | 
|  | FORGET_Frame(); | 
|  |  | 
|  | if ( new_offset ) | 
|  | gdef->MarkAttachClassDef_offset = new_offset + base_offset; | 
|  | else | 
|  | gdef->MarkAttachClassDef_offset = 0; | 
|  |  | 
|  | *retptr = gdef; | 
|  |  | 
|  | return HB_Err_Ok; | 
|  |  | 
|  | Fail3: | 
|  | Free_LigCaretList( &gdef->LigCaretList ); | 
|  |  | 
|  | Fail2: | 
|  | Free_AttachList( &gdef->AttachList ); | 
|  |  | 
|  | Fail1: | 
|  | _HB_OPEN_Free_ClassDefinition( &gdef->GlyphClassDef ); | 
|  |  | 
|  | Fail0: | 
|  | FREE( gdef ); | 
|  |  | 
|  | return error; | 
|  | } | 
|  |  | 
|  |  | 
|  | HB_Error  HB_Done_GDEF_Table ( HB_GDEFHeader* gdef ) | 
|  | { | 
|  | Free_LigCaretList( &gdef->LigCaretList ); | 
|  | Free_AttachList( &gdef->AttachList ); | 
|  | _HB_OPEN_Free_ClassDefinition( &gdef->GlyphClassDef ); | 
|  | _HB_OPEN_Free_ClassDefinition( &gdef->MarkAttachClassDef ); | 
|  |  | 
|  | Free_NewGlyphClasses( gdef ); | 
|  |  | 
|  | FREE( gdef ); | 
|  |  | 
|  | return HB_Err_Ok; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  | /******************************* | 
|  | * AttachList related functions | 
|  | *******************************/ | 
|  |  | 
|  |  | 
|  | /* AttachPoint */ | 
|  |  | 
|  | static HB_Error  Load_AttachPoint( HB_AttachPoint*  ap, | 
|  | HB_Stream         stream ) | 
|  | { | 
|  | HB_Error  error; | 
|  |  | 
|  | HB_UShort   n, count; | 
|  | HB_UShort*  pi; | 
|  |  | 
|  |  | 
|  | if ( ACCESS_Frame( 2L ) ) | 
|  | return error; | 
|  |  | 
|  | count = ap->PointCount = GET_UShort(); | 
|  |  | 
|  | FORGET_Frame(); | 
|  |  | 
|  | ap->PointIndex = NULL; | 
|  |  | 
|  | if ( count ) | 
|  | { | 
|  | if ( ALLOC_ARRAY( ap->PointIndex, count, HB_UShort ) ) | 
|  | return error; | 
|  |  | 
|  | pi = ap->PointIndex; | 
|  |  | 
|  | if ( ACCESS_Frame( count * 2L ) ) | 
|  | { | 
|  | FREE( pi ); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | for ( n = 0; n < count; n++ ) | 
|  | pi[n] = GET_UShort(); | 
|  |  | 
|  | FORGET_Frame(); | 
|  | } | 
|  |  | 
|  | return HB_Err_Ok; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void  Free_AttachPoint( HB_AttachPoint*  ap ) | 
|  | { | 
|  | FREE( ap->PointIndex ); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* AttachList */ | 
|  |  | 
|  | static HB_Error  Load_AttachList( HB_AttachList*  al, | 
|  | HB_Stream        stream ) | 
|  | { | 
|  | HB_Error  error; | 
|  |  | 
|  | HB_UShort         n, m, count; | 
|  | HB_UInt          cur_offset, new_offset, base_offset; | 
|  |  | 
|  | HB_AttachPoint*  ap; | 
|  |  | 
|  |  | 
|  | base_offset = FILE_Pos(); | 
|  |  | 
|  | if ( ACCESS_Frame( 2L ) ) | 
|  | return error; | 
|  |  | 
|  | new_offset = GET_UShort() + base_offset; | 
|  |  | 
|  | FORGET_Frame(); | 
|  |  | 
|  | cur_offset = FILE_Pos(); | 
|  | if ( FILE_Seek( new_offset ) || | 
|  | ( error = _HB_OPEN_Load_Coverage( &al->Coverage, stream ) ) != HB_Err_Ok ) | 
|  | return error; | 
|  | (void)FILE_Seek( cur_offset ); | 
|  |  | 
|  | if ( ACCESS_Frame( 2L ) ) | 
|  | goto Fail2; | 
|  |  | 
|  | count = al->GlyphCount = GET_UShort(); | 
|  |  | 
|  | FORGET_Frame(); | 
|  |  | 
|  | al->AttachPoint = NULL; | 
|  |  | 
|  | if ( ALLOC_ARRAY( al->AttachPoint, count, HB_AttachPoint ) ) | 
|  | goto Fail2; | 
|  |  | 
|  | ap = al->AttachPoint; | 
|  |  | 
|  | 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_AttachPoint( &ap[n], stream ) ) != HB_Err_Ok ) | 
|  | goto Fail1; | 
|  | (void)FILE_Seek( cur_offset ); | 
|  | } | 
|  |  | 
|  | al->loaded = TRUE; | 
|  |  | 
|  | return HB_Err_Ok; | 
|  |  | 
|  | Fail1: | 
|  | for ( m = 0; m < n; m++ ) | 
|  | Free_AttachPoint( &ap[m] ); | 
|  |  | 
|  | FREE( ap ); | 
|  |  | 
|  | Fail2: | 
|  | _HB_OPEN_Free_Coverage( &al->Coverage ); | 
|  | return error; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void  Free_AttachList( HB_AttachList*  al) | 
|  | { | 
|  | HB_UShort         n, count; | 
|  |  | 
|  | HB_AttachPoint*  ap; | 
|  |  | 
|  |  | 
|  | if ( !al->loaded ) | 
|  | return; | 
|  |  | 
|  | if ( al->AttachPoint ) | 
|  | { | 
|  | count = al->GlyphCount; | 
|  | ap    = al->AttachPoint; | 
|  |  | 
|  | for ( n = 0; n < count; n++ ) | 
|  | Free_AttachPoint( &ap[n] ); | 
|  |  | 
|  | FREE( ap ); | 
|  | } | 
|  |  | 
|  | _HB_OPEN_Free_Coverage( &al->Coverage ); | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /********************************* | 
|  | * LigCaretList related functions | 
|  | *********************************/ | 
|  |  | 
|  |  | 
|  | /* CaretValueFormat1 */ | 
|  | /* CaretValueFormat2 */ | 
|  | /* CaretValueFormat3 */ | 
|  | /* CaretValueFormat4 */ | 
|  |  | 
|  | static HB_Error  Load_CaretValue( HB_CaretValue*  cv, | 
|  | HB_Stream        stream ) | 
|  | { | 
|  | HB_Error  error; | 
|  |  | 
|  | HB_UInt cur_offset, new_offset, base_offset; | 
|  |  | 
|  |  | 
|  | base_offset = FILE_Pos(); | 
|  |  | 
|  | if ( ACCESS_Frame( 2L ) ) | 
|  | return error; | 
|  |  | 
|  | cv->CaretValueFormat = GET_UShort(); | 
|  |  | 
|  | FORGET_Frame(); | 
|  |  | 
|  | switch ( cv->CaretValueFormat ) | 
|  | { | 
|  | case 1: | 
|  | if ( ACCESS_Frame( 2L ) ) | 
|  | return error; | 
|  |  | 
|  | cv->cvf.cvf1.Coordinate = GET_Short(); | 
|  |  | 
|  | FORGET_Frame(); | 
|  |  | 
|  | break; | 
|  |  | 
|  | case 2: | 
|  | if ( ACCESS_Frame( 2L ) ) | 
|  | return error; | 
|  |  | 
|  | cv->cvf.cvf2.CaretValuePoint = GET_UShort(); | 
|  |  | 
|  | FORGET_Frame(); | 
|  |  | 
|  | break; | 
|  |  | 
|  | case 3: | 
|  | if ( ACCESS_Frame( 4L ) ) | 
|  | return error; | 
|  |  | 
|  | cv->cvf.cvf3.Coordinate = GET_Short(); | 
|  |  | 
|  | new_offset = GET_UShort() + base_offset; | 
|  |  | 
|  | FORGET_Frame(); | 
|  |  | 
|  | cur_offset = FILE_Pos(); | 
|  | if ( FILE_Seek( new_offset ) || | 
|  | ( error = _HB_OPEN_Load_Device( &cv->cvf.cvf3.Device, | 
|  | stream ) ) != HB_Err_Ok ) | 
|  | return error; | 
|  | (void)FILE_Seek( cur_offset ); | 
|  |  | 
|  | break; | 
|  |  | 
|  | case 4: | 
|  | if ( ACCESS_Frame( 2L ) ) | 
|  | return error; | 
|  |  | 
|  | cv->cvf.cvf4.IdCaretValue = GET_UShort(); | 
|  |  | 
|  | FORGET_Frame(); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | return ERR(HB_Err_Invalid_SubTable_Format); | 
|  | } | 
|  |  | 
|  | return HB_Err_Ok; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void  Free_CaretValue( HB_CaretValue*  cv) | 
|  | { | 
|  | if ( cv->CaretValueFormat == 3 ) | 
|  | _HB_OPEN_Free_Device( &cv->cvf.cvf3.Device ); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* LigGlyph */ | 
|  |  | 
|  | static HB_Error  Load_LigGlyph( HB_LigGlyph*  lg, | 
|  | HB_Stream      stream ) | 
|  | { | 
|  | HB_Error  error; | 
|  |  | 
|  | HB_UShort        n, m, count; | 
|  | HB_UInt         cur_offset, new_offset, base_offset; | 
|  |  | 
|  | HB_CaretValue*  cv; | 
|  |  | 
|  |  | 
|  | base_offset = FILE_Pos(); | 
|  |  | 
|  | if ( ACCESS_Frame( 2L ) ) | 
|  | return error; | 
|  |  | 
|  | count = lg->CaretCount = GET_UShort(); | 
|  |  | 
|  | FORGET_Frame(); | 
|  |  | 
|  | lg->CaretValue = NULL; | 
|  |  | 
|  | if ( ALLOC_ARRAY( lg->CaretValue, count, HB_CaretValue ) ) | 
|  | return error; | 
|  |  | 
|  | cv = lg->CaretValue; | 
|  |  | 
|  | 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_CaretValue( &cv[n], stream ) ) != HB_Err_Ok ) | 
|  | goto Fail; | 
|  | (void)FILE_Seek( cur_offset ); | 
|  | } | 
|  |  | 
|  | return HB_Err_Ok; | 
|  |  | 
|  | Fail: | 
|  | for ( m = 0; m < n; m++ ) | 
|  | Free_CaretValue( &cv[m] ); | 
|  |  | 
|  | FREE( cv ); | 
|  | return error; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void  Free_LigGlyph( HB_LigGlyph*  lg) | 
|  | { | 
|  | HB_UShort        n, count; | 
|  |  | 
|  | HB_CaretValue*  cv; | 
|  |  | 
|  |  | 
|  | if ( lg->CaretValue ) | 
|  | { | 
|  | count = lg->CaretCount; | 
|  | cv    = lg->CaretValue; | 
|  |  | 
|  | for ( n = 0; n < count; n++ ) | 
|  | Free_CaretValue( &cv[n] ); | 
|  |  | 
|  | FREE( cv ); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* LigCaretList */ | 
|  |  | 
|  | static HB_Error  Load_LigCaretList( HB_LigCaretList*  lcl, | 
|  | HB_Stream          stream ) | 
|  | { | 
|  | HB_Error  error; | 
|  |  | 
|  | HB_UShort      m, n, count; | 
|  | HB_UInt       cur_offset, new_offset, base_offset; | 
|  |  | 
|  | HB_LigGlyph*  lg; | 
|  |  | 
|  |  | 
|  | base_offset = FILE_Pos(); | 
|  |  | 
|  | if ( ACCESS_Frame( 2L ) ) | 
|  | return error; | 
|  |  | 
|  | new_offset = GET_UShort() + base_offset; | 
|  |  | 
|  | FORGET_Frame(); | 
|  |  | 
|  | cur_offset = FILE_Pos(); | 
|  | if ( FILE_Seek( new_offset ) || | 
|  | ( error = _HB_OPEN_Load_Coverage( &lcl->Coverage, stream ) ) != HB_Err_Ok ) | 
|  | return error; | 
|  | (void)FILE_Seek( cur_offset ); | 
|  |  | 
|  | if ( ACCESS_Frame( 2L ) ) | 
|  | goto Fail2; | 
|  |  | 
|  | count = lcl->LigGlyphCount = GET_UShort(); | 
|  |  | 
|  | FORGET_Frame(); | 
|  |  | 
|  | lcl->LigGlyph = NULL; | 
|  |  | 
|  | if ( ALLOC_ARRAY( lcl->LigGlyph, count, HB_LigGlyph ) ) | 
|  | goto Fail2; | 
|  |  | 
|  | lg = lcl->LigGlyph; | 
|  |  | 
|  | 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_LigGlyph( &lg[n], stream ) ) != HB_Err_Ok ) | 
|  | goto Fail1; | 
|  | (void)FILE_Seek( cur_offset ); | 
|  | } | 
|  |  | 
|  | lcl->loaded = TRUE; | 
|  |  | 
|  | return HB_Err_Ok; | 
|  |  | 
|  | Fail1: | 
|  | for ( m = 0; m < n; m++ ) | 
|  | Free_LigGlyph( &lg[m] ); | 
|  |  | 
|  | FREE( lg ); | 
|  |  | 
|  | Fail2: | 
|  | _HB_OPEN_Free_Coverage( &lcl->Coverage ); | 
|  | return error; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void  Free_LigCaretList( HB_LigCaretList*  lcl ) | 
|  | { | 
|  | HB_UShort      n, count; | 
|  |  | 
|  | HB_LigGlyph*  lg; | 
|  |  | 
|  |  | 
|  | if ( !lcl->loaded ) | 
|  | return; | 
|  |  | 
|  | if ( lcl->LigGlyph ) | 
|  | { | 
|  | count = lcl->LigGlyphCount; | 
|  | lg    = lcl->LigGlyph; | 
|  |  | 
|  | for ( n = 0; n < count; n++ ) | 
|  | Free_LigGlyph( &lg[n] ); | 
|  |  | 
|  | FREE( lg ); | 
|  | } | 
|  |  | 
|  | _HB_OPEN_Free_Coverage( &lcl->Coverage ); | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /*********** | 
|  | * GDEF API | 
|  | ***********/ | 
|  |  | 
|  |  | 
|  | static HB_UShort  Get_New_Class( HB_GDEFHeader*  gdef, | 
|  | HB_UShort        glyphID, | 
|  | HB_UShort        index ) | 
|  | { | 
|  | HB_UShort              glyph_index, array_index, count; | 
|  | HB_UShort              byte, bits; | 
|  |  | 
|  | HB_ClassRangeRecord*  gcrr; | 
|  | HB_UShort**            ngc; | 
|  |  | 
|  |  | 
|  | if ( glyphID >= gdef->LastGlyph ) | 
|  | return 0; | 
|  |  | 
|  | count = gdef->GlyphClassDef.cd.cd2.ClassRangeCount; | 
|  | gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord; | 
|  | ngc  = gdef->NewGlyphClasses; | 
|  |  | 
|  | if ( index < count && glyphID < gcrr[index].Start ) | 
|  | { | 
|  | array_index = index; | 
|  | if ( index == 0 ) | 
|  | glyph_index = glyphID; | 
|  | else | 
|  | glyph_index = glyphID - gcrr[index - 1].End - 1; | 
|  | } | 
|  | else | 
|  | { | 
|  | array_index = index + 1; | 
|  | glyph_index = glyphID - gcrr[index].End - 1; | 
|  | } | 
|  |  | 
|  | byte = ngc[array_index][glyph_index / 4]; | 
|  | bits = byte >> ( 16 - ( glyph_index % 4 + 1 ) * 4 ); | 
|  |  | 
|  | return bits & 0x000F; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | HB_Error  HB_GDEF_Get_Glyph_Property( HB_GDEFHeader*  gdef, | 
|  | HB_UShort        glyphID, | 
|  | HB_UShort*       property ) | 
|  | { | 
|  | HB_UShort class = 0, index = 0; /* shut compiler up */ | 
|  |  | 
|  | HB_Error  error; | 
|  |  | 
|  |  | 
|  | if ( !gdef || !property ) | 
|  | return ERR(HB_Err_Invalid_Argument); | 
|  |  | 
|  | /* first, we check for mark attach classes */ | 
|  |  | 
|  | if ( gdef->MarkAttachClassDef.loaded ) | 
|  | { | 
|  | error = _HB_OPEN_Get_Class( &gdef->MarkAttachClassDef, glyphID, &class, &index ); | 
|  | if ( error && error != HB_Err_Not_Covered ) | 
|  | return error; | 
|  | if ( !error ) | 
|  | { | 
|  | *property = class << 8; | 
|  | return HB_Err_Ok; | 
|  | } | 
|  | } | 
|  |  | 
|  | error = _HB_OPEN_Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index ); | 
|  | if ( error && error != HB_Err_Not_Covered ) | 
|  | return error; | 
|  |  | 
|  | /* if we have a constructed class table, check whether additional | 
|  | values have been assigned                                      */ | 
|  |  | 
|  | if ( error == HB_Err_Not_Covered && gdef->NewGlyphClasses ) | 
|  | class = Get_New_Class( gdef, glyphID, index ); | 
|  |  | 
|  | switch ( class ) | 
|  | { | 
|  | default: | 
|  | case UNCLASSIFIED_GLYPH: | 
|  | *property = 0; | 
|  | break; | 
|  |  | 
|  | case SIMPLE_GLYPH: | 
|  | *property = HB_GDEF_BASE_GLYPH; | 
|  | break; | 
|  |  | 
|  | case LIGATURE_GLYPH: | 
|  | *property = HB_GDEF_LIGATURE; | 
|  | break; | 
|  |  | 
|  | case MARK_GLYPH: | 
|  | *property = HB_GDEF_MARK; | 
|  | break; | 
|  |  | 
|  | case COMPONENT_GLYPH: | 
|  | *property = HB_GDEF_COMPONENT; | 
|  | break; | 
|  | } | 
|  |  | 
|  | return HB_Err_Ok; | 
|  | } | 
|  |  | 
|  |  | 
|  | static HB_Error  Make_ClassRange( HB_ClassDefinition*  cd, | 
|  | HB_UShort             start, | 
|  | HB_UShort             end, | 
|  | HB_UShort             class ) | 
|  | { | 
|  | HB_Error               error; | 
|  | HB_UShort              index; | 
|  |  | 
|  | HB_ClassDefFormat2*   cdf2; | 
|  | HB_ClassRangeRecord*  crr; | 
|  |  | 
|  |  | 
|  | cdf2 = &cd->cd.cd2; | 
|  |  | 
|  | if ( REALLOC_ARRAY( cdf2->ClassRangeRecord, | 
|  | cdf2->ClassRangeCount + 1 , | 
|  | HB_ClassRangeRecord ) ) | 
|  | return error; | 
|  |  | 
|  | cdf2->ClassRangeCount++; | 
|  |  | 
|  | crr   = cdf2->ClassRangeRecord; | 
|  | index = cdf2->ClassRangeCount - 1; | 
|  |  | 
|  | crr[index].Start = start; | 
|  | crr[index].End   = end; | 
|  | crr[index].Class = class; | 
|  |  | 
|  | return HB_Err_Ok; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | HB_Error  HB_GDEF_Build_ClassDefinition( HB_GDEFHeader*  gdef, | 
|  | HB_UShort        num_glyphs, | 
|  | HB_UShort        glyph_count, | 
|  | HB_UShort*       glyph_array, | 
|  | HB_UShort*       class_array ) | 
|  | { | 
|  | HB_UShort              start, curr_glyph, curr_class; | 
|  | HB_UShort              n, m, count; | 
|  | HB_Error               error; | 
|  |  | 
|  | HB_ClassDefinition*   gcd; | 
|  | HB_ClassRangeRecord*  gcrr; | 
|  | HB_UShort**            ngc; | 
|  |  | 
|  |  | 
|  | if ( !gdef || !glyph_array || !class_array ) | 
|  | return ERR(HB_Err_Invalid_Argument); | 
|  |  | 
|  | gcd = &gdef->GlyphClassDef; | 
|  |  | 
|  | /* We build a format 2 table */ | 
|  |  | 
|  | gcd->ClassFormat = 2; | 
|  |  | 
|  | gcd->cd.cd2.ClassRangeCount  = 0; | 
|  | gcd->cd.cd2.ClassRangeRecord = NULL; | 
|  |  | 
|  | start      = glyph_array[0]; | 
|  | curr_class = class_array[0]; | 
|  | curr_glyph = start; | 
|  |  | 
|  | if ( curr_class >= 5 ) | 
|  | { | 
|  | error = ERR(HB_Err_Invalid_Argument); | 
|  | goto Fail4; | 
|  | } | 
|  |  | 
|  | glyph_count--; | 
|  |  | 
|  | for ( n = 0; n < glyph_count + 1; n++ ) | 
|  | { | 
|  | if ( curr_glyph == glyph_array[n] && curr_class == class_array[n] ) | 
|  | { | 
|  | if ( n == glyph_count ) | 
|  | { | 
|  | if ( ( error = Make_ClassRange( gcd, start, | 
|  | curr_glyph, | 
|  | curr_class) ) != HB_Err_Ok ) | 
|  | goto Fail3; | 
|  | } | 
|  | else | 
|  | { | 
|  | if ( curr_glyph == 0xFFFF ) | 
|  | { | 
|  | error = ERR(HB_Err_Invalid_Argument); | 
|  | goto Fail3; | 
|  | } | 
|  | else | 
|  | curr_glyph++; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | if ( ( error = Make_ClassRange( gcd, start, | 
|  | curr_glyph - 1, | 
|  | curr_class) ) != HB_Err_Ok ) | 
|  | goto Fail3; | 
|  |  | 
|  | if ( curr_glyph > glyph_array[n] ) | 
|  | { | 
|  | error = ERR(HB_Err_Invalid_Argument); | 
|  | goto Fail3; | 
|  | } | 
|  |  | 
|  | start      = glyph_array[n]; | 
|  | curr_class = class_array[n]; | 
|  | curr_glyph = start; | 
|  |  | 
|  | if ( curr_class >= 5 ) | 
|  | { | 
|  | error = ERR(HB_Err_Invalid_Argument); | 
|  | goto Fail3; | 
|  | } | 
|  |  | 
|  | if ( n == glyph_count ) | 
|  | { | 
|  | if ( ( error = Make_ClassRange( gcd, start, | 
|  | curr_glyph, | 
|  | curr_class) ) != HB_Err_Ok ) | 
|  | goto Fail3; | 
|  | } | 
|  | else | 
|  | { | 
|  | if ( curr_glyph == 0xFFFF ) | 
|  | { | 
|  | error = ERR(HB_Err_Invalid_Argument); | 
|  | goto Fail3; | 
|  | } | 
|  | else | 
|  | curr_glyph++; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* now prepare the arrays for class values assigned during the lookup | 
|  | process                                                            */ | 
|  |  | 
|  | if ( ALLOC_ARRAY( gdef->NewGlyphClasses, | 
|  | gcd->cd.cd2.ClassRangeCount + 1, HB_UShort* ) ) | 
|  | goto Fail3; | 
|  |  | 
|  | count = gcd->cd.cd2.ClassRangeCount; | 
|  | gcrr  = gcd->cd.cd2.ClassRangeRecord; | 
|  | ngc   = gdef->NewGlyphClasses; | 
|  |  | 
|  | /* We allocate arrays for all glyphs not covered by the class range | 
|  | records.  Each element holds four class values.                  */ | 
|  |  | 
|  | if ( count > 0 ) | 
|  | { | 
|  | if ( gcrr[0].Start ) | 
|  | { | 
|  | if ( ALLOC_ARRAY( ngc[0], ( gcrr[0].Start + 3 ) / 4, HB_UShort ) ) | 
|  | goto Fail2; | 
|  | } | 
|  |  | 
|  | for ( n = 1; n < count; n++ ) | 
|  | { | 
|  | if ( gcrr[n].Start - gcrr[n - 1].End > 1 ) | 
|  | if ( ALLOC_ARRAY( ngc[n], | 
|  | ( gcrr[n].Start - gcrr[n - 1].End + 2 ) / 4, | 
|  | HB_UShort ) ) | 
|  | goto Fail1; | 
|  | } | 
|  |  | 
|  | if ( gcrr[count - 1].End != num_glyphs - 1 ) | 
|  | { | 
|  | if ( ALLOC_ARRAY( ngc[count], | 
|  | ( num_glyphs - gcrr[count - 1].End + 2 ) / 4, | 
|  | HB_UShort ) ) | 
|  | goto Fail1; | 
|  | } | 
|  | } | 
|  | else if ( num_glyphs > 0 ) | 
|  | { | 
|  | if ( ALLOC_ARRAY( ngc[count], | 
|  | ( num_glyphs + 3 ) / 4, | 
|  | HB_UShort ) ) | 
|  | goto Fail2; | 
|  | } | 
|  |  | 
|  | gdef->LastGlyph = num_glyphs - 1; | 
|  |  | 
|  | gdef->MarkAttachClassDef_offset = 0L; | 
|  | gdef->MarkAttachClassDef.loaded = FALSE; | 
|  |  | 
|  | gcd->loaded = TRUE; | 
|  |  | 
|  | return HB_Err_Ok; | 
|  |  | 
|  | Fail1: | 
|  | for ( m = 0; m < n; m++ ) | 
|  | FREE( ngc[m] ); | 
|  |  | 
|  | Fail2: | 
|  | FREE( gdef->NewGlyphClasses ); | 
|  |  | 
|  | Fail3: | 
|  | FREE( gcd->cd.cd2.ClassRangeRecord ); | 
|  |  | 
|  | Fail4: | 
|  | return error; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void  Free_NewGlyphClasses( HB_GDEFHeader*  gdef ) | 
|  | { | 
|  | HB_UShort**  ngc; | 
|  | HB_UShort    n, count; | 
|  |  | 
|  |  | 
|  | if ( gdef->NewGlyphClasses ) | 
|  | { | 
|  | count = gdef->GlyphClassDef.cd.cd2.ClassRangeCount + 1; | 
|  | ngc   = gdef->NewGlyphClasses; | 
|  |  | 
|  | for ( n = 0; n < count; n++ ) | 
|  | FREE( ngc[n] ); | 
|  |  | 
|  | FREE( ngc ); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | HB_INTERNAL HB_Error | 
|  | _HB_GDEF_Add_Glyph_Property( HB_GDEFHeader* gdef, | 
|  | HB_UShort        glyphID, | 
|  | HB_UShort        property ) | 
|  | { | 
|  | HB_Error               error; | 
|  | HB_UShort              class, new_class, index = 0; /* shut compiler up */ | 
|  | HB_UShort              byte, bits, mask; | 
|  | HB_UShort              array_index, glyph_index, count; | 
|  |  | 
|  | HB_ClassRangeRecord*  gcrr; | 
|  | HB_UShort**            ngc; | 
|  |  | 
|  |  | 
|  | error = _HB_OPEN_Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index ); | 
|  | if ( error && error != HB_Err_Not_Covered ) | 
|  | return error; | 
|  |  | 
|  | /* we don't accept glyphs covered in `GlyphClassDef' */ | 
|  |  | 
|  | if ( !error ) | 
|  | return HB_Err_Not_Covered; | 
|  |  | 
|  | switch ( property ) | 
|  | { | 
|  | case 0: | 
|  | new_class = UNCLASSIFIED_GLYPH; | 
|  | break; | 
|  |  | 
|  | case HB_GDEF_BASE_GLYPH: | 
|  | new_class = SIMPLE_GLYPH; | 
|  | break; | 
|  |  | 
|  | case HB_GDEF_LIGATURE: | 
|  | new_class = LIGATURE_GLYPH; | 
|  | break; | 
|  |  | 
|  | case HB_GDEF_MARK: | 
|  | new_class = MARK_GLYPH; | 
|  | break; | 
|  |  | 
|  | case HB_GDEF_COMPONENT: | 
|  | new_class = COMPONENT_GLYPH; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | return ERR(HB_Err_Invalid_Argument); | 
|  | } | 
|  |  | 
|  | count = gdef->GlyphClassDef.cd.cd2.ClassRangeCount; | 
|  | gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord; | 
|  | ngc  = gdef->NewGlyphClasses; | 
|  |  | 
|  | if ( index < count && glyphID < gcrr[index].Start ) | 
|  | { | 
|  | array_index = index; | 
|  | if ( index == 0 ) | 
|  | glyph_index = glyphID; | 
|  | else | 
|  | glyph_index = glyphID - gcrr[index - 1].End - 1; | 
|  | } | 
|  | else | 
|  | { | 
|  | array_index = index + 1; | 
|  | glyph_index = glyphID - gcrr[index].End - 1; | 
|  | } | 
|  |  | 
|  | byte  = ngc[array_index][glyph_index / 4]; | 
|  | bits  = byte >> ( 16 - ( glyph_index % 4 + 1 ) * 4 ); | 
|  | class = bits & 0x000F; | 
|  |  | 
|  | /* we don't overwrite existing entries */ | 
|  |  | 
|  | if ( !class ) | 
|  | { | 
|  | bits = new_class << ( 16 - ( glyph_index % 4 + 1 ) * 4 ); | 
|  | mask = ~( 0x000F << ( 16 - ( glyph_index % 4 + 1 ) * 4 ) ); | 
|  |  | 
|  | ngc[array_index][glyph_index / 4] &= mask; | 
|  | ngc[array_index][glyph_index / 4] |= bits; | 
|  | } | 
|  |  | 
|  | return HB_Err_Ok; | 
|  | } | 
|  |  | 
|  |  | 
|  | HB_INTERNAL HB_Error | 
|  | _HB_GDEF_Check_Property( HB_GDEFHeader* gdef, | 
|  | HB_GlyphItem    gitem, | 
|  | HB_UShort        flags, | 
|  | HB_UShort*       property ) | 
|  | { | 
|  | HB_Error  error; | 
|  |  | 
|  | if ( gdef ) | 
|  | { | 
|  | HB_UShort basic_glyph_class; | 
|  | HB_UShort desired_attachment_class; | 
|  |  | 
|  | if ( gitem->gproperties == HB_GLYPH_PROPERTIES_UNKNOWN ) | 
|  | { | 
|  | error = HB_GDEF_Get_Glyph_Property( gdef, gitem->gindex, &gitem->gproperties ); | 
|  | if ( error ) | 
|  | return error; | 
|  | } | 
|  |  | 
|  | *property = gitem->gproperties; | 
|  |  | 
|  | /* If the glyph was found in the MarkAttachmentClass table, | 
|  | * then that class value is the high byte of the result, | 
|  | * otherwise the low byte contains the basic type of the glyph | 
|  | * as defined by the GlyphClassDef table. | 
|  | */ | 
|  | if ( *property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS  ) | 
|  | basic_glyph_class = HB_GDEF_MARK; | 
|  | else | 
|  | basic_glyph_class = *property; | 
|  |  | 
|  | /* Return Not_Covered, if, for example, basic_glyph_class | 
|  | * is HB_GDEF_LIGATURE and LookFlags includes HB_LOOKUP_FLAG_IGNORE_LIGATURES | 
|  | */ | 
|  | if ( flags & basic_glyph_class ) | 
|  | return HB_Err_Not_Covered; | 
|  |  | 
|  | /* The high byte of LookupFlags has the meaning | 
|  | * "ignore marks of attachment type different than | 
|  | * the attachment type specified." | 
|  | */ | 
|  | desired_attachment_class = flags & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS; | 
|  | if ( desired_attachment_class ) | 
|  | { | 
|  | if ( basic_glyph_class == HB_GDEF_MARK && | 
|  | *property != desired_attachment_class ) | 
|  | return HB_Err_Not_Covered; | 
|  | } | 
|  | } else { | 
|  | *property = 0; | 
|  | } | 
|  |  | 
|  | return HB_Err_Ok; | 
|  | } | 
|  |  | 
|  | HB_INTERNAL HB_Error | 
|  | _HB_GDEF_LoadMarkAttachClassDef_From_LookupFlags( HB_GDEFHeader* gdef, | 
|  | HB_Stream      stream, | 
|  | HB_Lookup*     lo, | 
|  | HB_UShort      num_lookups) | 
|  | { | 
|  | HB_Error   error = HB_Err_Ok; | 
|  | HB_UShort  i; | 
|  |  | 
|  | /* 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 ) | 
|  | { | 
|  | for ( i = 0; i < num_lookups; i++ ) | 
|  | { | 
|  |  | 
|  | if ( lo[i].LookupFlag & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) | 
|  | { | 
|  | if ( FILE_Seek( gdef->MarkAttachClassDef_offset ) || | 
|  | ( error = _HB_OPEN_Load_ClassDefinition( &gdef->MarkAttachClassDef, | 
|  | 256, stream ) ) != HB_Err_Ok ) | 
|  | goto Done; | 
|  |  | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | Done: | 
|  | return error; | 
|  | } | 
|  |  | 
|  | /* END */ |