1521 lines
32 KiB
C
1521 lines
32 KiB
C
/*******************************************************************
|
|
*
|
|
* ftxopen.c
|
|
*
|
|
* TrueType Open common 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_INTERNAL_STREAM_H
|
|
#include FT_INTERNAL_MEMORY_H
|
|
#include FT_INTERNAL_TRUETYPE_TYPES_H
|
|
|
|
|
|
/***************************
|
|
* Script related functions
|
|
***************************/
|
|
|
|
|
|
/* LangSys */
|
|
|
|
static FT_Error Load_LangSys( TTO_LangSys* ls,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = stream->memory;
|
|
FT_UShort n, count;
|
|
FT_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, FT_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 TT_Err_Ok;
|
|
}
|
|
|
|
|
|
static void Free_LangSys( TTO_LangSys* ls,
|
|
FT_Memory memory )
|
|
{
|
|
FREE( ls->FeatureIndex );
|
|
}
|
|
|
|
|
|
/* Script */
|
|
|
|
static FT_Error Load_Script( TTO_Script* s,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = stream->memory;
|
|
FT_UShort n, m, count;
|
|
FT_ULong cur_offset, new_offset, base_offset;
|
|
|
|
TTO_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 ) ) != TT_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 = TTO_Err_Empty_Script;
|
|
goto Fail2;
|
|
}
|
|
|
|
FORGET_Frame();
|
|
|
|
s->LangSysRecord = NULL;
|
|
|
|
if ( ALLOC_ARRAY( s->LangSysRecord, count, TTO_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 ) ) != TT_Err_Ok )
|
|
goto Fail1;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail1:
|
|
for ( m = 0; m < n; m++ )
|
|
Free_LangSys( &lsr[m].LangSys, memory );
|
|
|
|
FREE( s->LangSysRecord );
|
|
|
|
Fail2:
|
|
Free_LangSys( &s->DefaultLangSys, memory );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_Script( TTO_Script* s,
|
|
FT_Memory memory )
|
|
{
|
|
FT_UShort n, count;
|
|
|
|
TTO_LangSysRecord* lsr;
|
|
|
|
|
|
Free_LangSys( &s->DefaultLangSys, memory );
|
|
|
|
if ( s->LangSysRecord )
|
|
{
|
|
count = s->LangSysCount;
|
|
lsr = s->LangSysRecord;
|
|
|
|
for ( n = 0; n < count; n++ )
|
|
Free_LangSys( &lsr[n].LangSys, memory );
|
|
|
|
FREE( lsr );
|
|
}
|
|
}
|
|
|
|
|
|
/* ScriptList */
|
|
|
|
FT_Error Load_ScriptList( TTO_ScriptList* sl,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = stream->memory;
|
|
|
|
FT_UShort n, script_count;
|
|
FT_ULong cur_offset, new_offset, base_offset;
|
|
|
|
TTO_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, TTO_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 == TT_Err_Ok )
|
|
sl->ScriptCount += 1;
|
|
else if ( error != TTO_Err_Empty_Script )
|
|
goto Fail;
|
|
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
if ( sl->ScriptCount == 0 )
|
|
{
|
|
error = TTO_Err_Invalid_SubTable;
|
|
goto Fail;
|
|
}
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail:
|
|
for ( n = 0; n < sl->ScriptCount; n++ )
|
|
Free_Script( &sr[n].Script, memory );
|
|
|
|
FREE( sl->ScriptRecord );
|
|
return error;
|
|
}
|
|
|
|
|
|
void Free_ScriptList( TTO_ScriptList* sl,
|
|
FT_Memory memory )
|
|
{
|
|
FT_UShort n, count;
|
|
|
|
TTO_ScriptRecord* sr;
|
|
|
|
|
|
if ( sl->ScriptRecord )
|
|
{
|
|
count = sl->ScriptCount;
|
|
sr = sl->ScriptRecord;
|
|
|
|
for ( n = 0; n < count; n++ )
|
|
Free_Script( &sr[n].Script, memory );
|
|
|
|
FREE( sr );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*********************************
|
|
* Feature List related functions
|
|
*********************************/
|
|
|
|
|
|
/* Feature */
|
|
|
|
static FT_Error Load_Feature( TTO_Feature* f,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = stream->memory;
|
|
|
|
FT_UShort n, count;
|
|
|
|
FT_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, FT_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 TT_Err_Ok;
|
|
}
|
|
|
|
|
|
static void Free_Feature( TTO_Feature* f,
|
|
FT_Memory memory )
|
|
{
|
|
FREE( f->LookupListIndex );
|
|
}
|
|
|
|
|
|
/* FeatureList */
|
|
|
|
FT_Error Load_FeatureList( TTO_FeatureList* fl,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = stream->memory;
|
|
|
|
FT_UShort n, m, count;
|
|
FT_ULong cur_offset, new_offset, base_offset;
|
|
|
|
TTO_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, TTO_FeatureRecord ) )
|
|
return error;
|
|
|
|
fr = fl->FeatureRecord;
|
|
|
|
for ( n = 0; n < count; n++ )
|
|
{
|
|
if ( ACCESS_Frame( 6L ) )
|
|
goto Fail;
|
|
|
|
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 ) ) != TT_Err_Ok )
|
|
goto Fail;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail:
|
|
for ( m = 0; m < n; m++ )
|
|
Free_Feature( &fr[m].Feature, memory );
|
|
|
|
FREE( fl->FeatureRecord );
|
|
return error;
|
|
}
|
|
|
|
|
|
void Free_FeatureList( TTO_FeatureList* fl,
|
|
FT_Memory memory)
|
|
{
|
|
FT_UShort n, count;
|
|
|
|
TTO_FeatureRecord* fr;
|
|
|
|
|
|
if ( fl->FeatureRecord )
|
|
{
|
|
count = fl->FeatureCount;
|
|
fr = fl->FeatureRecord;
|
|
|
|
for ( n = 0; n < count; n++ )
|
|
Free_Feature( &fr[n].Feature, memory );
|
|
|
|
FREE( fr );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/********************************
|
|
* Lookup List related functions
|
|
********************************/
|
|
|
|
/* the subroutines of the following two functions are defined in
|
|
ftxgsub.c and ftxgpos.c respectively */
|
|
|
|
|
|
/* SubTable */
|
|
|
|
static FT_Error Load_SubTable( TTO_SubTable* st,
|
|
FT_Stream stream,
|
|
TTO_Type table_type,
|
|
FT_UShort lookup_type )
|
|
{
|
|
if ( table_type == GSUB )
|
|
switch ( lookup_type )
|
|
{
|
|
case GSUB_LOOKUP_SINGLE:
|
|
return Load_SingleSubst( &st->st.gsub.single, stream );
|
|
|
|
case GSUB_LOOKUP_MULTIPLE:
|
|
return Load_MultipleSubst( &st->st.gsub.multiple, stream );
|
|
|
|
case GSUB_LOOKUP_ALTERNATE:
|
|
return Load_AlternateSubst( &st->st.gsub.alternate, stream );
|
|
|
|
case GSUB_LOOKUP_LIGATURE:
|
|
return Load_LigatureSubst( &st->st.gsub.ligature, stream );
|
|
|
|
case GSUB_LOOKUP_CONTEXT:
|
|
return Load_ContextSubst( &st->st.gsub.context, stream );
|
|
|
|
case GSUB_LOOKUP_CHAIN:
|
|
return Load_ChainContextSubst( &st->st.gsub.chain, stream );
|
|
|
|
default:
|
|
return TTO_Err_Invalid_GSUB_SubTable_Format;
|
|
}
|
|
else
|
|
switch ( lookup_type )
|
|
{
|
|
case GPOS_LOOKUP_SINGLE:
|
|
return Load_SinglePos( &st->st.gpos.single, stream );
|
|
|
|
case GPOS_LOOKUP_PAIR:
|
|
return Load_PairPos( &st->st.gpos.pair, stream );
|
|
|
|
case GPOS_LOOKUP_CURSIVE:
|
|
return Load_CursivePos( &st->st.gpos.cursive, stream );
|
|
|
|
case GPOS_LOOKUP_MARKBASE:
|
|
return Load_MarkBasePos( &st->st.gpos.markbase, stream );
|
|
|
|
case GPOS_LOOKUP_MARKLIG:
|
|
return Load_MarkLigPos( &st->st.gpos.marklig, stream );
|
|
|
|
case GPOS_LOOKUP_MARKMARK:
|
|
return Load_MarkMarkPos( &st->st.gpos.markmark, stream );
|
|
|
|
case GPOS_LOOKUP_CONTEXT:
|
|
return Load_ContextPos( &st->st.gpos.context, stream );
|
|
|
|
case GPOS_LOOKUP_CHAIN:
|
|
return Load_ChainContextPos( &st->st.gpos.chain, stream );
|
|
|
|
default:
|
|
return TTO_Err_Invalid_GPOS_SubTable_Format;
|
|
}
|
|
|
|
return TT_Err_Ok; /* never reached */
|
|
}
|
|
|
|
|
|
static void Free_SubTable( TTO_SubTable* st,
|
|
TTO_Type table_type,
|
|
FT_UShort lookup_type,
|
|
FT_Memory memory )
|
|
{
|
|
if ( table_type == GSUB )
|
|
switch ( lookup_type )
|
|
{
|
|
case GSUB_LOOKUP_SINGLE:
|
|
Free_SingleSubst( &st->st.gsub.single, memory );
|
|
break;
|
|
|
|
case GSUB_LOOKUP_MULTIPLE:
|
|
Free_MultipleSubst( &st->st.gsub.multiple, memory );
|
|
break;
|
|
|
|
case GSUB_LOOKUP_ALTERNATE:
|
|
Free_AlternateSubst( &st->st.gsub.alternate, memory );
|
|
break;
|
|
|
|
case GSUB_LOOKUP_LIGATURE:
|
|
Free_LigatureSubst( &st->st.gsub.ligature, memory );
|
|
break;
|
|
|
|
case GSUB_LOOKUP_CONTEXT:
|
|
Free_ContextSubst( &st->st.gsub.context, memory );
|
|
break;
|
|
|
|
case GSUB_LOOKUP_CHAIN:
|
|
Free_ChainContextSubst( &st->st.gsub.chain, memory );
|
|
break;
|
|
}
|
|
else
|
|
switch ( lookup_type )
|
|
{
|
|
case GPOS_LOOKUP_SINGLE:
|
|
Free_SinglePos( &st->st.gpos.single, memory );
|
|
break;
|
|
|
|
case GPOS_LOOKUP_PAIR:
|
|
Free_PairPos( &st->st.gpos.pair, memory );
|
|
break;
|
|
|
|
case GPOS_LOOKUP_CURSIVE:
|
|
Free_CursivePos( &st->st.gpos.cursive, memory );
|
|
break;
|
|
|
|
case GPOS_LOOKUP_MARKBASE:
|
|
Free_MarkBasePos( &st->st.gpos.markbase, memory );
|
|
break;
|
|
|
|
case GPOS_LOOKUP_MARKLIG:
|
|
Free_MarkLigPos( &st->st.gpos.marklig, memory );
|
|
break;
|
|
|
|
case GPOS_LOOKUP_MARKMARK:
|
|
Free_MarkMarkPos( &st->st.gpos.markmark, memory );
|
|
break;
|
|
|
|
case GPOS_LOOKUP_CONTEXT:
|
|
Free_ContextPos( &st->st.gpos.context, memory );
|
|
break;
|
|
|
|
case GPOS_LOOKUP_CHAIN:
|
|
Free_ChainContextPos ( &st->st.gpos.chain, memory );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* Lookup */
|
|
|
|
static FT_Error Load_Lookup( TTO_Lookup* l,
|
|
FT_Stream stream,
|
|
TTO_Type type )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = stream->memory;
|
|
|
|
FT_UShort n, m, count;
|
|
FT_ULong cur_offset, new_offset, base_offset;
|
|
|
|
TTO_SubTable* st;
|
|
|
|
FT_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, TTO_SubTable ) )
|
|
return error;
|
|
|
|
st = l->SubTable;
|
|
|
|
if ( ( type == GSUB && l->LookupType == GSUB_LOOKUP_EXTENSION ) ||
|
|
( type == GPOS && l->LookupType == 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;
|
|
|
|
(void)GET_UShort(); /* format should be 1 */
|
|
l->LookupType = GET_UShort();
|
|
new_offset = GET_ULong() + base_offset;
|
|
|
|
FORGET_Frame();
|
|
}
|
|
|
|
if ( FILE_Seek( new_offset ) ||
|
|
( error = Load_SubTable( &st[n], stream,
|
|
type, l->LookupType ) ) != TT_Err_Ok )
|
|
goto Fail;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail:
|
|
for ( m = 0; m < n; m++ )
|
|
Free_SubTable( &st[m], type, l->LookupType, memory );
|
|
|
|
FREE( l->SubTable );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_Lookup( TTO_Lookup* l,
|
|
TTO_Type type,
|
|
FT_Memory memory)
|
|
{
|
|
FT_UShort n, count;
|
|
|
|
TTO_SubTable* st;
|
|
|
|
|
|
if ( l->SubTable )
|
|
{
|
|
count = l->SubTableCount;
|
|
st = l->SubTable;
|
|
|
|
for ( n = 0; n < count; n++ )
|
|
Free_SubTable( &st[n], type, l->LookupType, memory );
|
|
|
|
FREE( st );
|
|
}
|
|
}
|
|
|
|
|
|
/* LookupList */
|
|
|
|
FT_Error Load_LookupList( TTO_LookupList* ll,
|
|
FT_Stream stream,
|
|
TTO_Type type )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = stream->memory;
|
|
|
|
FT_UShort n, m, count;
|
|
FT_ULong cur_offset, new_offset, base_offset;
|
|
|
|
TTO_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, TTO_Lookup ) )
|
|
return error;
|
|
if ( ALLOC_ARRAY( ll->Properties, count, FT_UShort ) )
|
|
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 ) ) != TT_Err_Ok )
|
|
goto Fail1;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail1:
|
|
FREE( ll->Properties );
|
|
|
|
for ( m = 0; m < n; m++ )
|
|
Free_Lookup( &l[m], type, memory );
|
|
|
|
Fail2:
|
|
FREE( ll->Lookup );
|
|
return error;
|
|
}
|
|
|
|
|
|
void Free_LookupList( TTO_LookupList* ll,
|
|
TTO_Type type,
|
|
FT_Memory memory )
|
|
{
|
|
FT_UShort n, count;
|
|
|
|
TTO_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, memory );
|
|
|
|
FREE( l );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*****************************
|
|
* Coverage related functions
|
|
*****************************/
|
|
|
|
|
|
/* CoverageFormat1 */
|
|
|
|
static FT_Error Load_Coverage1( TTO_CoverageFormat1* cf1,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = stream->memory;
|
|
|
|
FT_UShort n, count;
|
|
|
|
FT_UShort* ga;
|
|
|
|
|
|
if ( ACCESS_Frame( 2L ) )
|
|
return error;
|
|
|
|
count = cf1->GlyphCount = GET_UShort();
|
|
|
|
FORGET_Frame();
|
|
|
|
cf1->GlyphArray = NULL;
|
|
|
|
if ( ALLOC_ARRAY( cf1->GlyphArray, count, FT_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 TT_Err_Ok;
|
|
}
|
|
|
|
|
|
static void Free_Coverage1( TTO_CoverageFormat1* cf1,
|
|
FT_Memory memory)
|
|
{
|
|
FREE( cf1->GlyphArray );
|
|
}
|
|
|
|
|
|
/* CoverageFormat2 */
|
|
|
|
static FT_Error Load_Coverage2( TTO_CoverageFormat2* cf2,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = stream->memory;
|
|
|
|
FT_UShort n, count;
|
|
|
|
TTO_RangeRecord* rr;
|
|
|
|
|
|
if ( ACCESS_Frame( 2L ) )
|
|
return error;
|
|
|
|
count = cf2->RangeCount = GET_UShort();
|
|
|
|
FORGET_Frame();
|
|
|
|
cf2->RangeRecord = NULL;
|
|
|
|
if ( ALLOC_ARRAY( cf2->RangeRecord, count, TTO_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 = TTO_Err_Invalid_SubTable;
|
|
goto Fail;
|
|
}
|
|
}
|
|
|
|
FORGET_Frame();
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail:
|
|
FREE( cf2->RangeRecord );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_Coverage2( TTO_CoverageFormat2* cf2,
|
|
FT_Memory memory )
|
|
{
|
|
FREE( cf2->RangeRecord );
|
|
}
|
|
|
|
|
|
FT_Error Load_Coverage( TTO_Coverage* c,
|
|
FT_Stream stream )
|
|
{
|
|
FT_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 TTO_Err_Invalid_SubTable_Format;
|
|
}
|
|
|
|
return TT_Err_Ok; /* never reached */
|
|
}
|
|
|
|
|
|
void Free_Coverage( TTO_Coverage* c,
|
|
FT_Memory memory )
|
|
{
|
|
switch ( c->CoverageFormat )
|
|
{
|
|
case 1:
|
|
Free_Coverage1( &c->cf.cf1, memory );
|
|
break;
|
|
|
|
case 2:
|
|
Free_Coverage2( &c->cf.cf2, memory );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static FT_Error Coverage_Index1( TTO_CoverageFormat1* cf1,
|
|
FT_UShort glyphID,
|
|
FT_UShort* index )
|
|
{
|
|
FT_UShort min, max, new_min, new_max, middle;
|
|
|
|
FT_UShort* array = cf1->GlyphArray;
|
|
|
|
|
|
/* binary search */
|
|
|
|
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 TT_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 TTO_Err_Not_Covered;
|
|
}
|
|
|
|
|
|
static FT_Error Coverage_Index2( TTO_CoverageFormat2* cf2,
|
|
FT_UShort glyphID,
|
|
FT_UShort* index )
|
|
{
|
|
FT_UShort min, max, new_min, new_max, middle;
|
|
|
|
TTO_RangeRecord* rr = cf2->RangeRecord;
|
|
|
|
|
|
/* binary search */
|
|
|
|
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 TT_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 TTO_Err_Not_Covered;
|
|
}
|
|
|
|
|
|
FT_Error Coverage_Index( TTO_Coverage* c,
|
|
FT_UShort glyphID,
|
|
FT_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 TTO_Err_Invalid_SubTable_Format;
|
|
}
|
|
|
|
return TT_Err_Ok; /* never reached */
|
|
}
|
|
|
|
|
|
|
|
/*************************************
|
|
* Class Definition related functions
|
|
*************************************/
|
|
|
|
|
|
/* ClassDefFormat1 */
|
|
|
|
static FT_Error Load_ClassDef1( TTO_ClassDefinition* cd,
|
|
FT_UShort limit,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = stream->memory;
|
|
|
|
FT_UShort n, count;
|
|
|
|
FT_UShort* cva;
|
|
FT_Bool* d;
|
|
|
|
TTO_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 TTO_Err_Invalid_SubTable;
|
|
|
|
cdf1->ClassValueArray = NULL;
|
|
|
|
if ( ALLOC_ARRAY( cdf1->ClassValueArray, count, FT_UShort ) )
|
|
return error;
|
|
|
|
d = cd->Defined;
|
|
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 = TTO_Err_Invalid_SubTable;
|
|
goto Fail;
|
|
}
|
|
d[cva[n]] = TRUE;
|
|
}
|
|
|
|
FORGET_Frame();
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail:
|
|
FREE( cva );
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_ClassDef1( TTO_ClassDefFormat1* cdf1,
|
|
FT_Memory memory )
|
|
{
|
|
FREE( cdf1->ClassValueArray );
|
|
}
|
|
|
|
|
|
/* ClassDefFormat2 */
|
|
|
|
static FT_Error Load_ClassDef2( TTO_ClassDefinition* cd,
|
|
FT_UShort limit,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = stream->memory;
|
|
|
|
FT_UShort n, count;
|
|
|
|
TTO_ClassRangeRecord* crr;
|
|
FT_Bool* d;
|
|
|
|
TTO_ClassDefFormat2* cdf2;
|
|
|
|
|
|
cdf2 = &cd->cd.cd2;
|
|
|
|
if ( ACCESS_Frame( 2L ) )
|
|
return error;
|
|
|
|
count = cdf2->ClassRangeCount = GET_UShort();
|
|
|
|
FORGET_Frame();
|
|
|
|
cdf2->ClassRangeRecord = NULL;
|
|
|
|
if ( ALLOC_ARRAY( cdf2->ClassRangeRecord, count, TTO_ClassRangeRecord ) )
|
|
return error;
|
|
|
|
d = cd->Defined;
|
|
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 )
|
|
{
|
|
error = TTO_Err_Invalid_SubTable;
|
|
goto Fail;
|
|
}
|
|
d[crr[n].Class] = TRUE;
|
|
}
|
|
|
|
FORGET_Frame();
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail:
|
|
FREE( crr );
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_ClassDef2( TTO_ClassDefFormat2* cdf2,
|
|
FT_Memory memory )
|
|
{
|
|
FREE( cdf2->ClassRangeRecord );
|
|
}
|
|
|
|
|
|
/* ClassDefinition */
|
|
|
|
FT_Error Load_ClassDefinition( TTO_ClassDefinition* cd,
|
|
FT_UShort limit,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = stream->memory;
|
|
|
|
|
|
if ( ALLOC_ARRAY( cd->Defined, limit, FT_Bool ) )
|
|
return error;
|
|
|
|
if ( ACCESS_Frame( 2L ) )
|
|
goto Fail;
|
|
|
|
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 = TTO_Err_Invalid_SubTable_Format;
|
|
break;
|
|
}
|
|
|
|
if ( error )
|
|
goto Fail;
|
|
|
|
cd->loaded = TRUE;
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail:
|
|
FREE( cd->Defined );
|
|
return error;
|
|
}
|
|
|
|
|
|
FT_Error Load_EmptyClassDefinition( TTO_ClassDefinition* cd,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = stream->memory;
|
|
|
|
|
|
if ( ALLOC_ARRAY( cd->Defined, 1, FT_Bool ) )
|
|
return error;
|
|
|
|
cd->ClassFormat = 1; /* Meaningless */
|
|
cd->Defined[0] = FALSE;
|
|
|
|
if ( ALLOC_ARRAY( cd->cd.cd1.ClassValueArray, 1, FT_UShort ) )
|
|
goto Fail;
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail:
|
|
FREE( cd->Defined );
|
|
return error;
|
|
}
|
|
|
|
void Free_ClassDefinition( TTO_ClassDefinition* cd,
|
|
FT_Memory memory )
|
|
{
|
|
if ( !cd->loaded )
|
|
return;
|
|
|
|
FREE( cd->Defined );
|
|
|
|
switch ( cd->ClassFormat )
|
|
{
|
|
case 1:
|
|
Free_ClassDef1( &cd->cd.cd1, memory );
|
|
break;
|
|
|
|
case 2:
|
|
Free_ClassDef2( &cd->cd.cd2, memory );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static FT_Error Get_Class1( TTO_ClassDefFormat1* cdf1,
|
|
FT_UShort glyphID,
|
|
FT_UShort* class,
|
|
FT_UShort* index )
|
|
{
|
|
FT_UShort* cva = cdf1->ClassValueArray;
|
|
|
|
|
|
if ( index )
|
|
*index = 0;
|
|
|
|
if ( glyphID >= cdf1->StartGlyph &&
|
|
glyphID <= cdf1->StartGlyph + cdf1->GlyphCount )
|
|
{
|
|
*class = cva[glyphID - cdf1->StartGlyph];
|
|
return TT_Err_Ok;
|
|
}
|
|
else
|
|
{
|
|
*class = 0;
|
|
return TTO_Err_Not_Covered;
|
|
}
|
|
}
|
|
|
|
|
|
/* we need the index value of the last searched class range record
|
|
in case of failure for constructed GDEF tables */
|
|
|
|
static FT_Error Get_Class2( TTO_ClassDefFormat2* cdf2,
|
|
FT_UShort glyphID,
|
|
FT_UShort* class,
|
|
FT_UShort* index )
|
|
{
|
|
FT_Error error = TT_Err_Ok;
|
|
FT_UShort min, max, new_min, new_max, middle;
|
|
|
|
TTO_ClassRangeRecord* crr = cdf2->ClassRangeRecord;
|
|
|
|
|
|
/* binary search */
|
|
|
|
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 )
|
|
{
|
|
*class = crr[middle].Class;
|
|
error = TT_Err_Ok;
|
|
break;
|
|
}
|
|
else if ( glyphID < crr[middle].Start )
|
|
{
|
|
if ( middle == min )
|
|
{
|
|
*class = 0;
|
|
error = TTO_Err_Not_Covered;
|
|
break;
|
|
}
|
|
new_max = middle - 1;
|
|
}
|
|
else
|
|
{
|
|
if ( middle == max )
|
|
{
|
|
*class = 0;
|
|
error = TTO_Err_Not_Covered;
|
|
break;
|
|
}
|
|
new_min = middle + 1;
|
|
}
|
|
} while ( min < max );
|
|
|
|
if ( index )
|
|
*index = middle;
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
FT_Error Get_Class( TTO_ClassDefinition* cd,
|
|
FT_UShort glyphID,
|
|
FT_UShort* class,
|
|
FT_UShort* index )
|
|
{
|
|
switch ( cd->ClassFormat )
|
|
{
|
|
case 1:
|
|
return Get_Class1( &cd->cd.cd1, glyphID, class, index );
|
|
|
|
case 2:
|
|
return Get_Class2( &cd->cd.cd2, glyphID, class, index );
|
|
|
|
default:
|
|
return TTO_Err_Invalid_SubTable_Format;
|
|
}
|
|
|
|
return TT_Err_Ok; /* never reached */
|
|
}
|
|
|
|
|
|
|
|
/***************************
|
|
* Device related functions
|
|
***************************/
|
|
|
|
|
|
FT_Error Load_Device( TTO_Device* d,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = stream->memory;
|
|
|
|
FT_UShort n, count;
|
|
|
|
FT_UShort* dv;
|
|
|
|
|
|
if ( ACCESS_Frame( 6L ) )
|
|
return error;
|
|
|
|
d->StartSize = GET_UShort();
|
|
d->EndSize = GET_UShort();
|
|
d->DeltaFormat = GET_UShort();
|
|
|
|
FORGET_Frame();
|
|
|
|
if ( d->StartSize > d->EndSize ||
|
|
d->DeltaFormat == 0 || d->DeltaFormat > 3 )
|
|
return TTO_Err_Invalid_SubTable;
|
|
|
|
d->DeltaValue = NULL;
|
|
|
|
count = ( ( d->EndSize - d->StartSize + 1 ) >>
|
|
( 4 - d->DeltaFormat ) ) + 1;
|
|
|
|
if ( ALLOC_ARRAY( d->DeltaValue, count, FT_UShort ) )
|
|
return error;
|
|
|
|
if ( ACCESS_Frame( count * 2L ) )
|
|
{
|
|
FREE( d->DeltaValue );
|
|
return error;
|
|
}
|
|
|
|
dv = d->DeltaValue;
|
|
|
|
for ( n = 0; n < count; n++ )
|
|
dv[n] = GET_UShort();
|
|
|
|
FORGET_Frame();
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
void Free_Device( TTO_Device* d,
|
|
FT_Memory memory )
|
|
{
|
|
FREE( d->DeltaValue );
|
|
}
|
|
|
|
|
|
/* 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 */
|
|
|
|
FT_Error Get_Device( TTO_Device* d,
|
|
FT_UShort size,
|
|
FT_Short* value )
|
|
{
|
|
FT_UShort byte, bits, mask, f, s;
|
|
|
|
|
|
f = d->DeltaFormat;
|
|
|
|
if ( d->DeltaValue && size >= d->StartSize && size <= d->EndSize )
|
|
{
|
|
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 = (FT_Short)( bits & mask );
|
|
|
|
/* conversion to a signed value */
|
|
|
|
if ( *value >= ( ( mask + 1 ) >> 1 ) )
|
|
*value -= mask + 1;
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
else
|
|
{
|
|
*value = 0;
|
|
return TTO_Err_Not_Covered;
|
|
}
|
|
}
|
|
|
|
|
|
/* END */
|