#include "ftxkern.h"
#include "ttextend.h"
#include "tttypes.h"
#include "ttdebug.h"
#include "ttmemory.h"
#include "ttfile.h"
#include "ttobjs.h"
#include "ttload.h"
#include "tttags.h"
#undef TT_COMPONENT
#define TT_COMPONENT trace_any
#define KERNING_ID Build_Extension_ID( 'k', 'e', 'r', 'n' )
static TT_Error Subtable_Load_0( TT_Kern_0* kern0,
PFace input )
{
DEFINE_LOAD_LOCALS( input->stream );
UShort num_pairs, n;
if ( ACCESS_Frame( 8L ) )
return error;
num_pairs = GET_UShort();
kern0->nPairs = 0;
kern0->searchRange = GET_UShort();
kern0->entrySelector = GET_UShort();
kern0->rangeShift = GET_UShort();
FORGET_Frame();
if ( ALLOC_ARRAY( kern0->pairs, num_pairs, TT_Kern_0_Pair ) )
return error;
if ( ACCESS_Frame( num_pairs * 6L ) )
goto Fail;
for ( n = 0; n < num_pairs; n++ )
{
kern0->pairs[n].left = GET_UShort();
kern0->pairs[n].right = GET_UShort();
kern0->pairs[n].value = GET_UShort();
if ( kern0->pairs[n].left >= input->numGlyphs ||
kern0->pairs[n].right >= input->numGlyphs )
{
FORGET_Frame();
error = TT_Err_Invalid_Kerning_Table;
goto Fail;
}
}
FORGET_Frame();
kern0->nPairs = num_pairs;
return TT_Err_Ok;
Fail:
FREE( kern0->pairs );
return error;
}
static TT_Error Subtable_Load_2( TT_Kern_2* kern2,
PFace input )
{
DEFINE_LOAD_LOCALS( input->stream );
Long table_base;
UShort left_offset, right_offset, array_offset;
ULong array_size;
UShort left_max, right_max, n;
table_base = FILE_Pos();
if ( ACCESS_Frame( 8L ) )
return error;
kern2->rowWidth = GET_UShort();
left_offset = GET_UShort();
right_offset = GET_UShort();
array_offset = GET_UShort();
FORGET_Frame();
if ( FILE_Seek( table_base + left_offset ) ||
ACCESS_Frame( 4L ) )
return error;
kern2->leftClass.firstGlyph = GET_UShort();
kern2->leftClass.nGlyphs = GET_UShort();
FORGET_Frame();
if ( ALLOC_ARRAY( kern2->leftClass.classes,
kern2->leftClass.nGlyphs,
UShort ) )
return error;
if ( ACCESS_Frame( kern2->leftClass.nGlyphs * 2L ) )
goto Fail_Left;
for ( n = 0; n < kern2->leftClass.nGlyphs; n++ )
kern2->leftClass.classes[n] = GET_UShort();
FORGET_Frame();
if ( FILE_Seek( table_base + right_offset ) ||
ACCESS_Frame( 4L ) )
goto Fail_Left;
kern2->rightClass.firstGlyph = GET_UShort();
kern2->rightClass.nGlyphs = GET_UShort();
FORGET_Frame();
if ( ALLOC_ARRAY( kern2->rightClass.classes,
kern2->rightClass.nGlyphs,
UShort ) )
goto Fail_Left;
if ( ACCESS_Frame( kern2->rightClass.nGlyphs * 2L ) )
goto Fail_Right;
for ( n = 0; n < kern2->rightClass.nGlyphs; n++ )
kern2->rightClass.classes[n] = GET_UShort();
FORGET_Frame();
left_max = right_max = 0;
for ( n = 0; n < kern2->leftClass.nGlyphs; n++ )
left_max = MAX( left_max, kern2->leftClass.classes[n] );
for ( n = 0; n < kern2->rightClass.nGlyphs; n++ )
right_max = MAX( right_max, kern2->leftClass.classes[n] );
array_size = left_max + right_max + 2;
if ( ALLOC( kern2->array, array_size ) )
goto Fail_Right;
if ( ACCESS_Frame( array_size ) )
goto Fail_Array;
for ( n = 0; n < array_size/2; n++ )
kern2->array[n] = GET_Short();
FORGET_Frame();
return TT_Err_Ok;
Fail_Array:
FREE( kern2->array );
Fail_Right:
FREE( kern2->rightClass.classes );
kern2->rightClass.nGlyphs = 0;
Fail_Left:
FREE( kern2->leftClass.classes );
kern2->leftClass.nGlyphs = 0;
return error;
}
static TT_Error Kerning_Create( void* ext,
PFace face )
{
DEFINE_LOAD_LOCALS( face->stream );
TT_Kerning* kern = (TT_Kerning*)ext;
UShort num_tables;
Long table;
TT_Kern_Subtable* sub;
if ( !kern )
return TT_Err_Ok;
kern->version = 0;
kern->nTables = 0;
kern->tables = NULL;
table = TT_LookUp_Table( face, TTAG_kern );
if ( table < 0 )
return TT_Err_Ok;
if ( FILE_Seek( face->dirTables[table].Offset ) ||
ACCESS_Frame( 4L ) )
return error;
kern->version = GET_UShort();
num_tables = GET_UShort();
FORGET_Frame();
if ( ALLOC_ARRAY( kern->tables, num_tables, TT_Kern_Subtable ) )
return error;
kern->nTables = num_tables;
sub = kern->tables;
for ( table = 0; table < num_tables; table++ )
{
if ( ACCESS_Frame( 6L ) )
return error;
sub->loaded = FALSE;
sub->version = GET_UShort();
sub->length = GET_UShort() - 6;
sub->format = GET_Byte();
sub->coverage = GET_Byte();
FORGET_Frame();
sub->offset = FILE_Pos();
if ( FILE_Skip( sub->length ) )
return error;
sub++;
}
return TT_Err_Ok;
}
static TT_Error Kerning_Destroy( void* ext,
PFace face )
{
TT_Kerning* kern = (TT_Kerning*)ext;
TT_Kern_Subtable* sub;
UShort n;
if ( !kern )
return TT_Err_Ok;
if ( kern->nTables == 0 )
return TT_Err_Ok;
sub = kern->tables;
for ( n = 0; n < kern->nTables; n++ )
{
if ( sub->loaded )
{
switch ( sub->format )
{
case 0:
FREE( sub->t.kern0.pairs );
sub->t.kern0.nPairs = 0;
sub->t.kern0.searchRange = 0;
sub->t.kern0.entrySelector = 0;
sub->t.kern0.rangeShift = 0;
break;
case 2:
FREE( sub->t.kern2.leftClass.classes );
sub->t.kern2.leftClass.firstGlyph = 0;
sub->t.kern2.leftClass.nGlyphs = 0;
FREE( sub->t.kern2.rightClass.classes );
sub->t.kern2.rightClass.firstGlyph = 0;
sub->t.kern2.rightClass.nGlyphs = 0;
FREE( sub->t.kern2.array );
sub->t.kern2.rowWidth = 0;
break;
default:
;
}
sub->loaded = FALSE;
sub->version = 0;
sub->offset = 0;
sub->length = 0;
sub->coverage = 0;
sub->format = 0;
}
sub++;
}
FREE( kern->tables );
kern->nTables = 0;
return TT_Err_Ok;
}
EXPORT_FUNC
TT_Error TT_Get_Kerning_Directory( TT_Face face,
TT_Kerning* directory )
{
PFace faze = HANDLE_Face( face );
TT_Error error;
TT_Kerning* kerning;
if ( !faze )
return TT_Err_Invalid_Face_Handle;
error = TT_Extension_Get( faze, KERNING_ID, (void**)&kerning );
if ( !error )
*directory = *kerning;
return error;
}
EXPORT_FUNC
TT_Error TT_Load_Kerning_Table( TT_Face face,
TT_UShort kern_index )
{
TT_Error error;
TT_Stream stream;
TT_Kerning* kern;
TT_Kern_Subtable* sub;
PFace faze = HANDLE_Face( face );
if ( !faze )
return TT_Err_Invalid_Face_Handle;
error = TT_Extension_Get( faze, KERNING_ID, (void**)&kern );
if ( error )
return error;
if ( kern->nTables == 0 )
return TT_Err_Table_Missing;
if ( kern_index >= kern->nTables )
return TT_Err_Invalid_Argument;
sub = kern->tables + kern_index;
if ( sub->format != 0 && sub->format != 2 )
return TT_Err_Invalid_Kerning_Table_Format;
if ( USE_Stream( faze->stream, stream ) )
return error;
if ( FILE_Seek( sub->offset ) )
goto Fail;
if ( sub->format == 0 )
error = Subtable_Load_0( &sub->t.kern0, faze );
else if ( sub->format == 2 )
error = Subtable_Load_2( &sub->t.kern2, faze );
if ( !error )
sub->loaded = TRUE;
Fail:
DONE_Stream( stream );
return error;
}
EXPORT_FUNC
TT_Error TT_Init_Kerning_Extension( TT_Engine engine )
{
PEngine_Instance _engine = HANDLE_Engine( engine );
TT_Error error;
if ( !_engine )
return TT_Err_Invalid_Engine;
error = TT_Register_Extension( _engine,
KERNING_ID,
sizeof ( TT_Kerning ),
Kerning_Create,
Kerning_Destroy );
return error;
}