| /******************************************************************* |
| * |
| * ftxgdef.c |
| * |
| * TrueType Open GDEF table support. |
| * |
| * Copyright 1996-2000 by |
| * David Turner, Robert Wilhelm, and Werner Lemberg. |
| * |
| * This file is part of the FreeType project, and may only be used |
| * modified and distributed under the terms of the FreeType project |
| * license, LICENSE.TXT. By continuing to use, modify, or distribute |
| * this file you indicate that you have read the license and |
| * understand and accept it fully. |
| * |
| ******************************************************************/ |
| |
| #include "ftxopen.h" |
| #include "ftxopenf.h" |
| |
| #include "fterrcompat.h" |
| |
| #include FT_TRUETYPE_TAGS_H |
| |
| #include FT_INTERNAL_STREAM_H |
| #include FT_INTERNAL_MEMORY_H |
| #include FT_INTERNAL_TRUETYPE_TYPES_H |
| |
| #define TTAG_GDEF FT_MAKE_TAG( 'G', 'D', 'E', 'F' ) |
| |
| static FT_Error Load_AttachList( TTO_AttachList* al, |
| FT_Stream stream ); |
| static FT_Error Load_LigCaretList( TTO_LigCaretList* lcl, |
| FT_Stream stream ); |
| |
| static void Free_AttachList( TTO_AttachList* al, |
| FT_Memory memory ); |
| static void Free_LigCaretList( TTO_LigCaretList* lcl, |
| FT_Memory memory ); |
| |
| static void Free_NewGlyphClasses( TTO_GDEFHeader* gdef, |
| FT_Memory memory ); |
| |
| |
| |
| /********************** |
| * Extension Functions |
| **********************/ |
| |
| #if 0 |
| #define GDEF_ID Build_Extension_ID( 'G', 'D', 'E', 'F' ) |
| |
| |
| static FT_Error GDEF_Create( void* ext, |
| PFace face ) |
| { |
| DEFINE_LOAD_LOCALS( face->stream ); |
| |
| TTO_GDEFHeader* gdef = (TTO_GDEFHeader*)ext; |
| Long table; |
| |
| |
| /* by convention */ |
| |
| if ( !gdef ) |
| return TT_Err_Ok; |
| |
| /* a null offset indicates that there is no GDEF table */ |
| |
| gdef->offset = 0; |
| |
| /* we store the start offset and the size of the subtable */ |
| |
| table = TT_LookUp_Table( face, TTAG_GDEF ); |
| if ( table < 0 ) |
| return TT_Err_Ok; /* The table is optional */ |
| |
| if ( FILE_Seek( face->dirTables[table].Offset ) || |
| ACCESS_Frame( 4L ) ) |
| return error; |
| |
| gdef->offset = FILE_Pos() - 4L; /* undo ACCESS_Frame() */ |
| gdef->Version = GET_ULong(); |
| |
| FORGET_Frame(); |
| |
| gdef->loaded = FALSE; |
| |
| return TT_Err_Ok; |
| } |
| |
| |
| static FT_Error GDEF_Destroy( void* ext, |
| PFace face ) |
| { |
| TTO_GDEFHeader* gdef = (TTO_GDEFHeader*)ext; |
| |
| |
| /* by convention */ |
| |
| if ( !gdef ) |
| return TT_Err_Ok; |
| |
| if ( gdef->loaded ) |
| { |
| Free_LigCaretList( &gdef->LigCaretList, memory ); |
| Free_AttachList( &gdef->AttachList, memory ); |
| Free_ClassDefinition( &gdef->GlyphClassDef, memory ); |
| Free_ClassDefinition( &gdef->MarkAttachClassDef, memory ); |
| |
| Free_NewGlyphClasses( gdef, memory ); |
| } |
| |
| return TT_Err_Ok; |
| } |
| |
| |
| EXPORT_FUNC |
| FT_Error TT_Init_GDEF_Extension( TT_Engine engine ) |
| { |
| PEngine_Instance _engine = HANDLE_Engine( engine ); |
| |
| |
| if ( !_engine ) |
| return TT_Err_Invalid_Engine; |
| |
| return TT_Register_Extension( _engine, |
| GDEF_ID, |
| sizeof ( TTO_GDEFHeader ), |
| GDEF_Create, |
| GDEF_Destroy ); |
| } |
| #endif |
| |
| EXPORT_FUNC |
| FT_Error TT_New_GDEF_Table( FT_Face face, |
| TTO_GDEFHeader** retptr ) |
| { |
| FT_Error error; |
| FT_Memory memory = face->memory; |
| |
| TTO_GDEFHeader* gdef; |
| |
| if ( !retptr ) |
| return TT_Err_Invalid_Argument; |
| |
| if ( ALLOC( gdef, sizeof( *gdef ) ) ) |
| return error; |
| |
| gdef->memory = face->memory; |
| |
| 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 TT_Err_Ok; |
| } |
| |
| EXPORT_FUNC |
| FT_Error TT_Load_GDEF_Table( FT_Face face, |
| TTO_GDEFHeader** retptr ) |
| { |
| FT_Error error; |
| FT_Memory memory = face->memory; |
| FT_Stream stream = face->stream; |
| TT_Face tt_face = (TT_Face)face; |
| FT_ULong cur_offset, new_offset, base_offset; |
| |
| TTO_GDEFHeader* gdef; |
| |
| |
| if ( !retptr ) |
| return TT_Err_Invalid_Argument; |
| |
| if (( error = tt_face->goto_table( tt_face, TTAG_GDEF, stream, 0 ) )) |
| return error; |
| |
| if (( error = TT_New_GDEF_Table ( face, &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 = Load_ClassDefinition( &gdef->GlyphClassDef, 5, |
| stream ) ) != TT_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 ) ) != TT_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 ) ) != TT_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 TT_Err_Ok; |
| |
| Fail3: |
| Free_LigCaretList( &gdef->LigCaretList, memory ); |
| |
| Fail2: |
| Free_AttachList( &gdef->AttachList, memory ); |
| |
| Fail1: |
| Free_ClassDefinition( &gdef->GlyphClassDef, memory ); |
| |
| Fail0: |
| FREE( gdef ); |
| |
| return error; |
| } |
| |
| EXPORT_FUNC |
| FT_Error TT_Done_GDEF_Table ( TTO_GDEFHeader* gdef ) |
| { |
| FT_Memory memory = gdef->memory; |
| |
| Free_LigCaretList( &gdef->LigCaretList, memory ); |
| Free_AttachList( &gdef->AttachList, memory ); |
| Free_ClassDefinition( &gdef->GlyphClassDef, memory ); |
| Free_ClassDefinition( &gdef->MarkAttachClassDef, memory ); |
| |
| Free_NewGlyphClasses( gdef, memory ); |
| |
| FREE( gdef ); |
| |
| return TT_Err_Ok; |
| } |
| |
| |
| |
| |
| /******************************* |
| * AttachList related functions |
| *******************************/ |
| |
| |
| /* AttachPoint */ |
| |
| static FT_Error Load_AttachPoint( TTO_AttachPoint* ap, |
| FT_Stream stream ) |
| { |
| FT_Memory memory = stream->memory; |
| FT_Error error; |
| |
| FT_UShort n, count; |
| FT_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, FT_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 TT_Err_Ok; |
| } |
| |
| |
| static void Free_AttachPoint( TTO_AttachPoint* ap, |
| FT_Memory memory ) |
| { |
| FREE( ap->PointIndex ); |
| } |
| |
| |
| /* AttachList */ |
| |
| static FT_Error Load_AttachList( TTO_AttachList* al, |
| FT_Stream stream ) |
| { |
| FT_Memory memory = stream->memory; |
| FT_Error error; |
| |
| FT_UShort n, m, count; |
| FT_ULong cur_offset, new_offset, base_offset; |
| |
| TTO_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 = Load_Coverage( &al->Coverage, stream ) ) != TT_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, TTO_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 ) ) != TT_Err_Ok ) |
| goto Fail1; |
| (void)FILE_Seek( cur_offset ); |
| } |
| |
| al->loaded = TRUE; |
| |
| return TT_Err_Ok; |
| |
| Fail1: |
| for ( m = 0; m < n; m++ ) |
| Free_AttachPoint( &ap[m], memory ); |
| |
| FREE( ap ); |
| |
| Fail2: |
| Free_Coverage( &al->Coverage, memory ); |
| return error; |
| } |
| |
| |
| static void Free_AttachList( TTO_AttachList* al, |
| FT_Memory memory ) |
| { |
| FT_UShort n, count; |
| |
| TTO_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], memory ); |
| |
| FREE( ap ); |
| } |
| |
| Free_Coverage( &al->Coverage, memory ); |
| } |
| |
| |
| |
| /********************************* |
| * LigCaretList related functions |
| *********************************/ |
| |
| |
| /* CaretValueFormat1 */ |
| /* CaretValueFormat2 */ |
| /* CaretValueFormat3 */ |
| /* CaretValueFormat4 */ |
| |
| static FT_Error Load_CaretValue( TTO_CaretValue* cv, |
| FT_Stream stream ) |
| { |
| FT_Error error; |
| |
| FT_ULong 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 = Load_Device( &cv->cvf.cvf3.Device, |
| stream ) ) != TT_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 TTO_Err_Invalid_GDEF_SubTable_Format; |
| } |
| |
| return TT_Err_Ok; |
| } |
| |
| |
| static void Free_CaretValue( TTO_CaretValue* cv, |
| FT_Memory memory ) |
| { |
| if ( cv->CaretValueFormat == 3 ) |
| Free_Device( &cv->cvf.cvf3.Device, memory ); |
| } |
| |
| |
| /* LigGlyph */ |
| |
| static FT_Error Load_LigGlyph( TTO_LigGlyph* lg, |
| FT_Stream stream ) |
| { |
| FT_Memory memory = stream->memory; |
| FT_Error error; |
| |
| FT_UShort n, m, count; |
| FT_ULong cur_offset, new_offset, base_offset; |
| |
| TTO_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, TTO_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 ) ) != TT_Err_Ok ) |
| goto Fail; |
| (void)FILE_Seek( cur_offset ); |
| } |
| |
| return TT_Err_Ok; |
| |
| Fail: |
| for ( m = 0; m < n; m++ ) |
| Free_CaretValue( &cv[m], memory ); |
| |
| FREE( cv ); |
| return error; |
| } |
| |
| |
| static void Free_LigGlyph( TTO_LigGlyph* lg, |
| FT_Memory memory ) |
| { |
| FT_UShort n, count; |
| |
| TTO_CaretValue* cv; |
| |
| |
| if ( lg->CaretValue ) |
| { |
| count = lg->CaretCount; |
| cv = lg->CaretValue; |
| |
| for ( n = 0; n < count; n++ ) |
| Free_CaretValue( &cv[n], memory ); |
| |
| FREE( cv ); |
| } |
| } |
| |
| |
| /* LigCaretList */ |
| |
| static FT_Error Load_LigCaretList( TTO_LigCaretList* lcl, |
| FT_Stream stream ) |
| { |
| FT_Memory memory = stream->memory; |
| FT_Error error; |
| |
| FT_UShort m, n, count; |
| FT_ULong cur_offset, new_offset, base_offset; |
| |
| TTO_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 = Load_Coverage( &lcl->Coverage, stream ) ) != TT_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, TTO_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 ) ) != TT_Err_Ok ) |
| goto Fail1; |
| (void)FILE_Seek( cur_offset ); |
| } |
| |
| lcl->loaded = TRUE; |
| |
| return TT_Err_Ok; |
| |
| Fail1: |
| for ( m = 0; m < n; m++ ) |
| Free_LigGlyph( &lg[m], memory ); |
| |
| FREE( lg ); |
| |
| Fail2: |
| Free_Coverage( &lcl->Coverage, memory ); |
| return error; |
| } |
| |
| |
| static void Free_LigCaretList( TTO_LigCaretList* lcl, |
| FT_Memory memory ) |
| { |
| FT_UShort n, count; |
| |
| TTO_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], memory ); |
| |
| FREE( lg ); |
| } |
| |
| Free_Coverage( &lcl->Coverage, memory ); |
| } |
| |
| |
| |
| /*********** |
| * GDEF API |
| ***********/ |
| |
| |
| static FT_UShort Get_New_Class( TTO_GDEFHeader* gdef, |
| FT_UShort glyphID, |
| FT_UShort index ) |
| { |
| FT_UShort glyph_index, array_index; |
| FT_UShort byte, bits; |
| |
| TTO_ClassRangeRecord* gcrr; |
| FT_UShort** ngc; |
| |
| |
| if ( glyphID >= gdef->LastGlyph ) |
| return 0; |
| |
| gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord; |
| ngc = gdef->NewGlyphClasses; |
| |
| if ( glyphID < gcrr[index].Start ) |
| { |
| array_index = 0; |
| 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 + 1]; |
| bits = byte >> ( 16 - ( glyph_index % 4 + 1 ) * 4 ); |
| |
| return bits & 0x000F; |
| } |
| |
| |
| EXPORT_FUNC |
| FT_Error TT_GDEF_Get_Glyph_Property( TTO_GDEFHeader* gdef, |
| FT_UShort glyphID, |
| FT_UShort* property ) |
| { |
| FT_UShort class, index; |
| |
| FT_Error error; |
| |
| |
| if ( !gdef || !property ) |
| return TT_Err_Invalid_Argument; |
| |
| /* first, we check for mark attach classes */ |
| |
| if ( gdef->MarkAttachClassDef.loaded ) |
| { |
| error = Get_Class( &gdef->MarkAttachClassDef, glyphID, &class, &index ); |
| if ( error && error != TTO_Err_Not_Covered ) |
| return error; |
| if ( !error ) |
| { |
| *property = class << 8; |
| return TT_Err_Ok; |
| } |
| } |
| |
| error = Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index ); |
| if ( error && error != TTO_Err_Not_Covered ) |
| return error; |
| |
| /* if we have a constructed class table, check whether additional |
| values have been assigned */ |
| |
| if ( error == TTO_Err_Not_Covered && gdef->NewGlyphClasses ) |
| class = Get_New_Class( gdef, glyphID, index ); |
| |
| switch ( class ) |
| { |
| case UNCLASSIFIED_GLYPH: |
| *property = 0; |
| break; |
| |
| case SIMPLE_GLYPH: |
| *property = TTO_BASE_GLYPH; |
| break; |
| |
| case LIGATURE_GLYPH: |
| *property = TTO_LIGATURE; |
| break; |
| |
| case MARK_GLYPH: |
| *property = TTO_MARK; |
| break; |
| |
| case COMPONENT_GLYPH: |
| *property = TTO_COMPONENT; |
| break; |
| } |
| |
| return TT_Err_Ok; |
| } |
| |
| |
| static FT_Error Make_ClassRange( TTO_ClassDefinition* cd, |
| FT_UShort start, |
| FT_UShort end, |
| FT_UShort class, |
| FT_Memory memory ) |
| { |
| FT_Error error; |
| FT_UShort index; |
| |
| TTO_ClassDefFormat2* cdf2; |
| TTO_ClassRangeRecord* crr; |
| |
| |
| cdf2 = &cd->cd.cd2; |
| |
| if ( REALLOC_ARRAY( cdf2->ClassRangeRecord, |
| cdf2->ClassRangeCount, |
| cdf2->ClassRangeCount + 1 , |
| TTO_ClassRangeRecord ) ) |
| return error; |
| |
| cdf2->ClassRangeCount++; |
| |
| crr = cdf2->ClassRangeRecord; |
| index = cdf2->ClassRangeCount - 1; |
| |
| crr[index].Start = start; |
| crr[index].End = end; |
| crr[index].Class = class; |
| |
| cd->Defined[class] = TRUE; |
| |
| return TT_Err_Ok; |
| } |
| |
| |
| EXPORT_FUNC |
| FT_Error TT_GDEF_Build_ClassDefinition( TTO_GDEFHeader* gdef, |
| FT_UShort num_glyphs, |
| FT_UShort glyph_count, |
| FT_UShort* glyph_array, |
| FT_UShort* class_array ) |
| { |
| FT_UShort start, curr_glyph, curr_class; |
| FT_UShort n, m, count; |
| FT_Error error; |
| FT_Memory memory = gdef->memory; |
| |
| TTO_ClassDefinition* gcd; |
| TTO_ClassRangeRecord* gcrr; |
| FT_UShort** ngc; |
| |
| |
| if ( !gdef || !glyph_array || !class_array ) |
| return TT_Err_Invalid_Argument; |
| |
| gcd = &gdef->GlyphClassDef; |
| |
| /* We build a format 2 table */ |
| |
| gcd->ClassFormat = 2; |
| |
| /* A GlyphClassDef table contains at most 5 different class values */ |
| |
| if ( ALLOC_ARRAY( gcd->Defined, 5, FT_Bool ) ) |
| return error; |
| |
| 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 = TT_Err_Invalid_Argument; |
| goto Fail4; |
| } |
| |
| glyph_count--; |
| |
| for ( n = 0; n <= glyph_count; 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, |
| memory ) ) != TT_Err_Ok ) |
| goto Fail3; |
| } |
| else |
| { |
| if ( curr_glyph == 0xFFFF ) |
| { |
| error = TT_Err_Invalid_Argument; |
| goto Fail3; |
| } |
| else |
| curr_glyph++; |
| } |
| } |
| else |
| { |
| if ( ( error = Make_ClassRange( gcd, start, |
| curr_glyph - 1, |
| curr_class, |
| memory ) ) != TT_Err_Ok ) |
| goto Fail3; |
| |
| if ( curr_glyph > glyph_array[n] ) |
| { |
| error = TT_Err_Invalid_Argument; |
| goto Fail3; |
| } |
| |
| start = glyph_array[n]; |
| curr_class = class_array[n]; |
| curr_glyph = start; |
| |
| if ( curr_class >= 5 ) |
| { |
| error = TT_Err_Invalid_Argument; |
| goto Fail3; |
| } |
| |
| if ( n == glyph_count ) |
| { |
| if ( ( error = Make_ClassRange( gcd, start, |
| curr_glyph, |
| curr_class, |
| memory ) ) != TT_Err_Ok ) |
| goto Fail3; |
| } |
| else |
| { |
| if ( curr_glyph == 0xFFFF ) |
| { |
| error = TT_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, FT_UShort* ) ) |
| goto Fail2; |
| |
| 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 ( gcrr[0].Start ) |
| { |
| if ( ALLOC_ARRAY( ngc[0], gcrr[0].Start / 4 + 1, FT_UShort ) ) |
| goto Fail1; |
| } |
| |
| 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 - 1 ) / 4 + 1, |
| FT_UShort ) ) |
| goto Fail1; |
| } |
| |
| if ( gcrr[count - 1].End != num_glyphs - 1 ) |
| { |
| if ( ALLOC_ARRAY( ngc[count], |
| ( num_glyphs - gcrr[count - 1].End - 1 ) / 4 |
| + (((( num_glyphs - gcrr[count - 1].End - 1 ) % 4) == 0)? 0: 1) |
| + 1, |
| FT_UShort ) ) |
| goto Fail1; |
| } |
| |
| gdef->LastGlyph = num_glyphs - 1; |
| |
| gdef->MarkAttachClassDef_offset = 0L; |
| gdef->MarkAttachClassDef.loaded = FALSE; |
| |
| gcd->loaded = TRUE; /* ??? */ |
| return TT_Err_Ok; |
| |
| Fail1: |
| for ( m = 0; m < n; m++ ) |
| FREE( ngc[m] ); |
| |
| Fail2: |
| FREE( gdef->NewGlyphClasses ); |
| |
| Fail3: |
| FREE( gcd->cd.cd2.ClassRangeRecord ); |
| |
| Fail4: |
| FREE( gcd->Defined ); |
| return error; |
| } |
| |
| |
| static void Free_NewGlyphClasses( TTO_GDEFHeader* gdef, |
| FT_Memory memory ) |
| { |
| FT_UShort** ngc; |
| FT_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 ); |
| } |
| } |
| |
| |
| FT_Error Add_Glyph_Property( TTO_GDEFHeader* gdef, |
| FT_UShort glyphID, |
| FT_UShort property ) |
| { |
| FT_Error error; |
| FT_UShort class, new_class, index; |
| FT_UShort byte, bits, mask; |
| FT_UShort array_index, glyph_index; |
| |
| TTO_ClassRangeRecord* gcrr; |
| FT_UShort** ngc; |
| |
| |
| error = Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index ); |
| if ( error && error != TTO_Err_Not_Covered ) |
| return error; |
| |
| /* we don't accept glyphs covered in `GlyphClassDef' */ |
| |
| if ( !error ) |
| return TTO_Err_Not_Covered; |
| |
| switch ( property ) |
| { |
| case 0: |
| new_class = UNCLASSIFIED_GLYPH; |
| break; |
| |
| case TTO_BASE_GLYPH: |
| new_class = SIMPLE_GLYPH; |
| break; |
| |
| case TTO_LIGATURE: |
| new_class = LIGATURE_GLYPH; |
| break; |
| |
| case TTO_MARK: |
| new_class = MARK_GLYPH; |
| break; |
| |
| case TTO_COMPONENT: |
| new_class = COMPONENT_GLYPH; |
| break; |
| |
| default: |
| return TT_Err_Invalid_Argument; |
| } |
| |
| gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord; |
| ngc = gdef->NewGlyphClasses; |
| |
| if ( glyphID < gcrr[index].Start ) |
| { |
| array_index = 0; |
| 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 + 1]; |
| 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 + 1] &= mask; |
| ngc[array_index][glyph_index / 4 + 1] |= bits; |
| } |
| |
| return TT_Err_Ok; |
| } |
| |
| |
| FT_Error Check_Property( TTO_GDEFHeader* gdef, |
| FT_UShort index, |
| FT_UShort flags, |
| FT_UShort* property ) |
| { |
| FT_Error error; |
| |
| |
| if ( gdef ) |
| { |
| FT_UShort basic_glyph_class; |
| FT_UShort desired_attachment_class; |
| |
| error = TT_GDEF_Get_Glyph_Property( gdef, index, property ); |
| if ( error ) |
| return error; |
| |
| /* 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 & IGNORE_SPECIAL_MARKS ) |
| basic_glyph_class = TTO_MARK; |
| else |
| basic_glyph_class = *property; |
| |
| /* Return Not_Covered, if, for example, basic_glyph_class |
| * is TTO_LIGATURE and LookFlags includes IGNORE_LIGATURES |
| */ |
| if ( flags & basic_glyph_class ) |
| return TTO_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 & IGNORE_SPECIAL_MARKS; |
| if ( desired_attachment_class ) |
| { |
| if ( basic_glyph_class == TTO_MARK && |
| *property != desired_attachment_class ) |
| return TTO_Err_Not_Covered; |
| } |
| } |
| |
| return TT_Err_Ok; |
| } |
| |
| |
| /* END */ |