#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 GDEF_ID Build_Extension_ID( 'G', 'D', 'E', 'F' )
static TT_Error Load_AttachList( TTO_AttachList* al,
PFace input );
static TT_Error Load_LigCaretList( TTO_LigCaretList* lcl,
PFace input );
static void Free_AttachList( TTO_AttachList* al );
static void Free_LigCaretList( TTO_LigCaretList* lcl );
static void Free_NewGlyphClasses( TTO_GDEFHeader* gdef );
static TT_Error GDEF_Create( void* ext,
PFace face )
{
DEFINE_LOAD_LOCALS( face->stream );
TTO_GDEFHeader* gdef = (TTO_GDEFHeader*)ext;
Long table;
if ( !gdef )
return TT_Err_Ok;
gdef->offset = 0;
table = TT_LookUp_Table( face, TTAG_GDEF );
if ( table < 0 )
return TT_Err_Ok;
if ( FILE_Seek( face->dirTables[table].Offset ) ||
ACCESS_Frame( 4L ) )
return error;
gdef->offset = FILE_Pos() - 4L;
gdef->Version = GET_ULong();
FORGET_Frame();
gdef->loaded = FALSE;
return TT_Err_Ok;
}
static TT_Error GDEF_Destroy( void* ext,
PFace face )
{
TTO_GDEFHeader* gdef = (TTO_GDEFHeader*)ext;
if ( !gdef )
return TT_Err_Ok;
if ( gdef->loaded )
{
Free_LigCaretList( &gdef->LigCaretList );
Free_AttachList( &gdef->AttachList );
Free_ClassDefinition( &gdef->GlyphClassDef );
Free_ClassDefinition( &gdef->MarkAttachClassDef );
Free_NewGlyphClasses( gdef );
}
return TT_Err_Ok;
}
EXPORT_FUNC
TT_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 );
}
EXPORT_FUNC
TT_Error TT_Load_GDEF_Table( TT_Face face,
TTO_GDEFHeader* retptr )
{
ULong cur_offset, new_offset, base_offset;
TT_Error error;
TT_Stream stream;
TTO_GDEFHeader* gdef;
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, GDEF_ID, (void**)&gdef );
if ( error )
return error;
if ( gdef->offset == 0 )
return TT_Err_Table_Missing;
if ( USE_Stream( faze->stream, stream ) )
return error;
base_offset = gdef->offset;
if ( FILE_Seek( base_offset + 4L ) ||
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_ClassDefinition( &gdef->GlyphClassDef, 5,
faze ) ) != TT_Err_Ok )
return error;
(void)FILE_Seek( cur_offset );
}
else
gdef->GlyphClassDef.loaded = FALSE;
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_AttachList( &gdef->AttachList,
faze ) ) != TT_Err_Ok )
goto Fail1;
(void)FILE_Seek( cur_offset );
}
else
gdef->AttachList.loaded = FALSE;
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_LigCaretList( &gdef->LigCaretList,
faze ) ) != TT_Err_Ok )
goto Fail2;
(void)FILE_Seek( cur_offset );
}
else
gdef->LigCaretList.loaded = FALSE;
gdef->MarkAttachClassDef_offset = FILE_Pos();
gdef->MarkAttachClassDef.loaded = FALSE;
gdef->LastGlyph = 0;
gdef->NewGlyphClasses = NULL;
gdef->loaded = TRUE;
*retptr = *gdef;
DONE_Stream( stream );
return TT_Err_Ok;
Fail2:
Free_AttachList( &gdef->AttachList );
Fail1:
Free_ClassDefinition( &gdef->GlyphClassDef );
DONE_Stream( stream );
return error;
}
static TT_Error Load_AttachPoint( TTO_AttachPoint* ap,
PFace input )
{
DEFINE_LOAD_LOCALS( input->stream );
UShort n, count;
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, 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 )
{
FREE( ap->PointIndex );
}
static TT_Error Load_AttachList( TTO_AttachList* al,
PFace input )
{
DEFINE_LOAD_LOCALS( input->stream );
UShort n, count;
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, input ) ) != 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], input ) ) != TT_Err_Ok )
goto Fail1;
(void)FILE_Seek( cur_offset );
}
al->loaded = TRUE;
return TT_Err_Ok;
Fail1:
for ( n = 0; n < count; n++ )
Free_AttachPoint( &ap[n] );
FREE( ap );
Fail2:
Free_Coverage( &al->Coverage );
return error;
}
static void Free_AttachList( TTO_AttachList* al )
{
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] );
FREE( ap );
}
Free_Coverage( &al->Coverage );
}
static TT_Error Load_CaretValue( TTO_CaretValue* cv,
PFace input )
{
DEFINE_LOAD_LOCALS( input->stream );
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,
input ) ) != 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 )
{
if ( cv->CaretValueFormat == 3 )
Free_Device( &cv->cvf.cvf3.Device );
}
static TT_Error Load_LigGlyph( TTO_LigGlyph* lg,
PFace input )
{
DEFINE_LOAD_LOCALS( input->stream );
UShort n, count;
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], input ) ) != TT_Err_Ok )
goto Fail;
(void)FILE_Seek( cur_offset );
}
return TT_Err_Ok;
Fail:
for ( n = 0; n < count; n++ )
Free_CaretValue( &cv[n] );
FREE( cv );
return error;
}
static void Free_LigGlyph( TTO_LigGlyph* lg )
{
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] );
FREE( cv );
}
}
static TT_Error Load_LigCaretList( TTO_LigCaretList* lcl,
PFace input )
{
DEFINE_LOAD_LOCALS( input->stream );
UShort n, count;
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, input ) ) != 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], input ) ) != TT_Err_Ok )
goto Fail1;
(void)FILE_Seek( cur_offset );
}
lcl->loaded = TRUE;
return TT_Err_Ok;
Fail1:
for ( n = 0; n < count; n++ )
Free_LigGlyph( &lg[n] );
FREE( lg );
Fail2:
Free_Coverage( &lcl->Coverage );
return error;
}
static void Free_LigCaretList( TTO_LigCaretList* lcl )
{
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] );
FREE( lg );
}
Free_Coverage( &lcl->Coverage );
}
static UShort Get_New_Class( TTO_GDEFHeader* gdef,
UShort glyphID,
UShort index )
{
UShort glyph_index, array_index;
UShort byte, bits;
TTO_ClassRangeRecord* gcrr;
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
TT_Error TT_GDEF_Get_Glyph_Property( TTO_GDEFHeader* gdef,
TT_UShort glyphID,
TT_UShort* property )
{
UShort class, index;
TT_Error error;
if ( !gdef || !property )
return TT_Err_Invalid_Argument;
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 ( 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 TT_Error Make_ClassRange( TTO_ClassDefinition* cd,
UShort start,
UShort end,
UShort class )
{
TT_Error error;
UShort index;
TTO_ClassDefFormat2* cdf2;
TTO_ClassRangeRecord* crr;
cdf2 = &cd->cd.cd2;
cdf2->ClassRangeCount++;
if ( REALLOC_ARRAY( cdf2->ClassRangeRecord, cdf2->ClassRangeCount,
TTO_ClassRangeRecord ) )
return error;
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
TT_Error TT_GDEF_Build_ClassDefinition( TTO_GDEFHeader* gdef,
TT_UShort num_glyphs,
TT_UShort glyph_count,
TT_UShort* glyph_array,
TT_UShort* class_array )
{
UShort start, curr_glyph, curr_class;
UShort n, count;
TT_Error error;
TTO_ClassDefinition* gcd;
TTO_ClassRangeRecord* gcrr;
UShort** ngc;
if ( !gdef || !glyph_array || !class_array )
return TT_Err_Invalid_Argument;
gcd = &gdef->GlyphClassDef;
gcd->ClassFormat = 2;
if ( ALLOC_ARRAY( gcd->Defined, 5, 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 ) ) != 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 ) ) != 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 ) ) != TT_Err_Ok )
goto Fail3;
}
else
{
if ( curr_glyph == 0xFFFF )
{
error = TT_Err_Invalid_Argument;
goto Fail3;
}
else
curr_glyph++;
}
}
}
if ( ALLOC_ARRAY( gdef->NewGlyphClasses,
gcd->cd.cd2.ClassRangeCount + 1, UShort* ) )
goto Fail2;
count = gcd->cd.cd2.ClassRangeCount;
gcrr = gcd->cd.cd2.ClassRangeRecord;
ngc = gdef->NewGlyphClasses;
if ( gcrr[0].Start )
{
if ( ALLOC_ARRAY( ngc[0], gcrr[0].Start / 4 + 1, 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,
UShort ) )
goto Fail1;
}
if ( gcrr[count - 1].End != num_glyphs - 1 )
{
if ( ALLOC_ARRAY( ngc[count],
( num_glyphs - gcrr[count - 1].End - 1 ) / 4 + 1,
UShort ) )
goto Fail1;
}
gdef->LastGlyph = num_glyphs - 1;
gdef->MarkAttachClassDef_offset = 0L;
gdef->MarkAttachClassDef.loaded = FALSE;
return TT_Err_Ok;
Fail1:
for ( n = 0; n < count; n++ )
FREE( ngc[n] );
Fail2:
FREE( gdef->NewGlyphClasses );
Fail3:
FREE( gcd->cd.cd2.ClassRangeRecord );
Fail4:
FREE( gcd->Defined );
return error;
}
static void Free_NewGlyphClasses( TTO_GDEFHeader* gdef )
{
UShort** ngc;
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 );
}
}
TT_Error Add_Glyph_Property( TTO_GDEFHeader* gdef,
UShort glyphID,
UShort property )
{
TT_Error error;
UShort class, new_class, index;
UShort byte, bits, mask;
UShort array_index, glyph_index;
TTO_ClassRangeRecord* gcrr;
UShort** ngc;
error = Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index );
if ( error && error != TTO_Err_Not_Covered )
return error;
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;
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;
}