/*
* 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-open-private.h"
/***************************
* Script related functions
***************************/
/* LangSys */
static HB_Error Load_LangSys( HB_LangSys* ls,
HB_Stream stream )
{
HB_Error error;
HB_UShort n, count;
HB_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, HB_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 HB_Err_Ok;
}
static void Free_LangSys( HB_LangSys* ls )
{
FREE( ls->FeatureIndex );
}
/* Script */
static HB_Error Load_Script( HB_ScriptTable* s,
HB_Stream stream )
{
HB_Error error;
HB_UShort n, m, count;
HB_UInt cur_offset, new_offset, base_offset;
HB_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 ) ) != HB_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 = HB_Err_Not_Covered;
goto Fail2;
}
FORGET_Frame();
s->LangSysRecord = NULL;
if ( ALLOC_ARRAY( s->LangSysRecord, count, HB_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 ) ) != HB_Err_Ok )
goto Fail1;
(void)FILE_Seek( cur_offset );
}
return HB_Err_Ok;
Fail1:
for ( m = 0; m < n; m++ )
Free_LangSys( &lsr[m].LangSys );
FREE( s->LangSysRecord );
Fail2:
Free_LangSys( &s->DefaultLangSys );
return error;
}
static void Free_Script( HB_ScriptTable* s )
{
HB_UShort n, count;
HB_LangSysRecord* lsr;
Free_LangSys( &s->DefaultLangSys );
if ( s->LangSysRecord )
{
count = s->LangSysCount;
lsr = s->LangSysRecord;
for ( n = 0; n < count; n++ )
Free_LangSys( &lsr[n].LangSys );
FREE( lsr );
}
}
/* ScriptList */
HB_INTERNAL HB_Error
_HB_OPEN_Load_ScriptList( HB_ScriptList* sl,
HB_Stream stream )
{
HB_Error error;
HB_UShort n, script_count;
HB_UInt cur_offset, new_offset, base_offset;
HB_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, HB_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 == HB_Err_Ok )
sl->ScriptCount += 1;
else if ( error != HB_Err_Not_Covered )
goto Fail;
(void)FILE_Seek( cur_offset );
}
/* Empty tables are harmless and generated by fontforge.
* See http://bugzilla.gnome.org/show_bug.cgi?id=347073
*/
#if 0
if ( sl->ScriptCount == 0 )
{
error = ERR(HB_Err_Invalid_SubTable);
goto Fail;
}
#endif
return HB_Err_Ok;
Fail:
for ( n = 0; n < sl->ScriptCount; n++ )
Free_Script( &sr[n].Script );
FREE( sl->ScriptRecord );
return error;
}
HB_INTERNAL void
_HB_OPEN_Free_ScriptList( HB_ScriptList* sl )
{
HB_UShort n, count;
HB_ScriptRecord* sr;
if ( sl->ScriptRecord )
{
count = sl->ScriptCount;
sr = sl->ScriptRecord;
for ( n = 0; n < count; n++ )
Free_Script( &sr[n].Script );
FREE( sr );
}
}
/*********************************
* Feature List related functions
*********************************/
/* Feature */
static HB_Error Load_Feature( HB_Feature* f,
HB_Stream stream )
{
HB_Error error;
HB_UShort n, count;
HB_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, HB_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 HB_Err_Ok;
}
static void Free_Feature( HB_Feature* f )
{
FREE( f->LookupListIndex );
}
/* FeatureList */
HB_INTERNAL HB_Error
_HB_OPEN_Load_FeatureList( HB_FeatureList* fl,
HB_Stream stream )
{
HB_Error error;
HB_UShort n, m, count;
HB_UInt cur_offset, new_offset, base_offset;
HB_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, HB_FeatureRecord ) )
return error;
if ( ALLOC_ARRAY( fl->ApplyOrder, count, HB_UShort ) )
goto Fail2;
fl->ApplyCount = 0;
fr = fl->FeatureRecord;
for ( n = 0; n < count; n++ )
{
if ( ACCESS_Frame( 6L ) )
goto Fail1;
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 ) ) != HB_Err_Ok )
goto Fail1;
(void)FILE_Seek( cur_offset );
}
return HB_Err_Ok;
Fail1:
for ( m = 0; m < n; m++ )
Free_Feature( &fr[m].Feature );
FREE( fl->ApplyOrder );
Fail2:
FREE( fl->FeatureRecord );
return error;
}
HB_INTERNAL void
_HB_OPEN_Free_FeatureList( HB_FeatureList* fl )
{
HB_UShort n, count;
HB_FeatureRecord* fr;
if ( fl->FeatureRecord )
{
count = fl->FeatureCount;
fr = fl->FeatureRecord;
for ( n = 0; n < count; n++ )
Free_Feature( &fr[n].Feature );
FREE( fr );
}
FREE( fl->ApplyOrder );
}
/********************************
* Lookup List related functions
********************************/
/* the subroutines of the following two functions are defined in
ftxgsub.c and ftxgpos.c respectively */
/* SubTable */
static HB_Error Load_SubTable( HB_SubTable* st,
HB_Stream stream,
HB_Type table_type,
HB_UShort lookup_type )
{
if ( table_type == HB_Type_GSUB )
return _HB_GSUB_Load_SubTable ( &st->st.gsub, stream, lookup_type );
else
return _HB_GPOS_Load_SubTable ( &st->st.gpos, stream, lookup_type );
}
static void Free_SubTable( HB_SubTable* st,
HB_Type table_type,
HB_UShort lookup_type )
{
if ( table_type == HB_Type_GSUB )
_HB_GSUB_Free_SubTable ( &st->st.gsub, lookup_type );
else
_HB_GPOS_Free_SubTable ( &st->st.gpos, lookup_type );
}
/* Lookup */
static HB_Error Load_Lookup( HB_Lookup* l,
HB_Stream stream,
HB_Type type )
{
HB_Error error;
HB_UShort n, m, count;
HB_UInt cur_offset, new_offset, base_offset;
HB_SubTable* st;
HB_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, HB_SubTable ) )
return error;
st = l->SubTable;
if ( ( type == HB_Type_GSUB && l->LookupType == HB_GSUB_LOOKUP_EXTENSION ) ||
( type == HB_Type_GPOS && l->LookupType == HB_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;
if (GET_UShort() != 1) /* format should be 1 */
goto Fail;
l->LookupType = GET_UShort();
new_offset += GET_ULong();
FORGET_Frame();
}
if ( FILE_Seek( new_offset ) ||
( error = Load_SubTable( &st[n], stream,
type, l->LookupType ) ) != HB_Err_Ok )
goto Fail;
(void)FILE_Seek( cur_offset );
}
return HB_Err_Ok;
Fail:
for ( m = 0; m < n; m++ )
Free_SubTable( &st[m], type, l->LookupType );
FREE( l->SubTable );
return error;
}
static void Free_Lookup( HB_Lookup* l,
HB_Type type)
{
HB_UShort n, count;
HB_SubTable* st;
if ( l->SubTable )
{
count = l->SubTableCount;
st = l->SubTable;
for ( n = 0; n < count; n++ )
Free_SubTable( &st[n], type, l->LookupType );
FREE( st );
}
}
/* LookupList */
HB_INTERNAL HB_Error
_HB_OPEN_Load_LookupList( HB_LookupList* ll,
HB_Stream stream,
HB_Type type )
{
HB_Error error;
HB_UShort n, m, count;
HB_UInt cur_offset, new_offset, base_offset;
HB_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, HB_Lookup ) )
return error;
if ( ALLOC_ARRAY( ll->Properties, count, HB_UInt ) )
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 ) ) != HB_Err_Ok )
goto Fail1;
(void)FILE_Seek( cur_offset );
}
return HB_Err_Ok;
Fail1:
FREE( ll->Properties );
for ( m = 0; m < n; m++ )
Free_Lookup( &l[m], type );
Fail2:
FREE( ll->Lookup );
return error;
}
HB_INTERNAL void
_HB_OPEN_Free_LookupList( HB_LookupList* ll,
HB_Type type )
{
HB_UShort n, count;
HB_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 );
FREE( l );
}
}
/*****************************
* Coverage related functions
*****************************/
/* CoverageFormat1 */
static HB_Error Load_Coverage1( HB_CoverageFormat1* cf1,
HB_Stream stream )
{
HB_Error error;
HB_UShort n, count;
HB_UShort* ga;
if ( ACCESS_Frame( 2L ) )
return error;
count = cf1->GlyphCount = GET_UShort();
FORGET_Frame();
cf1->GlyphArray = NULL;
if ( ALLOC_ARRAY( cf1->GlyphArray, count, HB_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 HB_Err_Ok;
}
static void Free_Coverage1( HB_CoverageFormat1* cf1)
{
FREE( cf1->GlyphArray );
}
/* CoverageFormat2 */
static HB_Error Load_Coverage2( HB_CoverageFormat2* cf2,
HB_Stream stream )
{
HB_Error error;
HB_UShort n, count;
HB_RangeRecord* rr;
if ( ACCESS_Frame( 2L ) )
return error;
count = cf2->RangeCount = GET_UShort();
FORGET_Frame();
cf2->RangeRecord = NULL;
if ( ALLOC_ARRAY( cf2->RangeRecord, count, HB_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 = ERR(HB_Err_Invalid_SubTable);
goto Fail;
}
}
FORGET_Frame();
return HB_Err_Ok;
Fail:
FREE( cf2->RangeRecord );
return error;
}
static void Free_Coverage2( HB_CoverageFormat2* cf2 )
{
FREE( cf2->RangeRecord );
}
HB_INTERNAL HB_Error
_HB_OPEN_Load_Coverage( HB_Coverage* c,
HB_Stream stream )
{
HB_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 ERR(HB_Err_Invalid_SubTable_Format);
}
return HB_Err_Ok; /* never reached */
}
HB_INTERNAL void
_HB_OPEN_Free_Coverage( HB_Coverage* c )
{
switch ( c->CoverageFormat )
{
case 1: Free_Coverage1( &c->cf.cf1 ); break;
case 2: Free_Coverage2( &c->cf.cf2 ); break;
default: break;
}
}
static HB_Error Coverage_Index1( HB_CoverageFormat1* cf1,
HB_UShort glyphID,
HB_UShort* index )
{
HB_UShort min, max, new_min, new_max, middle;
HB_UShort* array = cf1->GlyphArray;
/* binary search */
if ( cf1->GlyphCount == 0 )
return HB_Err_Not_Covered;
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 HB_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 HB_Err_Not_Covered;
}
static HB_Error Coverage_Index2( HB_CoverageFormat2* cf2,
HB_UShort glyphID,
HB_UShort* index )
{
HB_UShort min, max, new_min, new_max, middle;
HB_RangeRecord* rr = cf2->RangeRecord;
/* binary search */
if ( cf2->RangeCount == 0 )
return HB_Err_Not_Covered;
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 HB_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 HB_Err_Not_Covered;
}
HB_INTERNAL HB_Error
_HB_OPEN_Coverage_Index( HB_Coverage* c,
HB_UShort glyphID,
HB_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 ERR(HB_Err_Invalid_SubTable_Format);
}
return HB_Err_Ok; /* never reached */
}
/*************************************
* Class Definition related functions
*************************************/
/* ClassDefFormat1 */
static HB_Error Load_ClassDef1( HB_ClassDefinition* cd,
HB_UShort limit,
HB_Stream stream )
{
HB_Error error;
HB_UShort n, count;
HB_UShort* cva;
HB_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 ERR(HB_Err_Invalid_SubTable);
cdf1->ClassValueArray = NULL;
if ( ALLOC_ARRAY( cdf1->ClassValueArray, count, HB_UShort ) )
return error;
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 = ERR(HB_Err_Invalid_SubTable);
goto Fail;
}
}
FORGET_Frame();
return HB_Err_Ok;
Fail:
FREE( cva );
return error;
}
static void Free_ClassDef1( HB_ClassDefFormat1* cdf1 )
{
FREE( cdf1->ClassValueArray );
}
/* ClassDefFormat2 */
static HB_Error Load_ClassDef2( HB_ClassDefinition* cd,
HB_UShort limit,
HB_Stream stream )
{
HB_Error error;
HB_UShort n, count;
HB_ClassRangeRecord* crr;
HB_ClassDefFormat2* cdf2;
cdf2 = &cd->cd.cd2;
if ( ACCESS_Frame( 2L ) )
return error;
count = GET_UShort();
cdf2->ClassRangeCount = 0; /* zero for now. we fill with the number of good entries later */
FORGET_Frame();
cdf2->ClassRangeRecord = NULL;
if ( ALLOC_ARRAY( cdf2->ClassRangeRecord, count, HB_ClassRangeRecord ) )
return error;
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 )
{
/* XXX
* Corrupt entry. Skip it.
* This is hit by Nafees Nastaliq font for example
*/
n--;
count--;
}
}
FORGET_Frame();
cdf2->ClassRangeCount = count;
return HB_Err_Ok;
Fail:
FREE( crr );
return error;
}
static void Free_ClassDef2( HB_ClassDefFormat2* cdf2 )
{
FREE( cdf2->ClassRangeRecord );
}
/* ClassDefinition */
HB_INTERNAL HB_Error
_HB_OPEN_Load_ClassDefinition( HB_ClassDefinition* cd,
HB_UShort limit,
HB_Stream stream )
{
HB_Error error;
if ( ACCESS_Frame( 2L ) )
return error;
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 = ERR(HB_Err_Invalid_SubTable_Format); break;
}
if ( error )
return error;
cd->loaded = TRUE;
return HB_Err_Ok;
}
static HB_Error
_HB_OPEN_Load_EmptyClassDefinition( HB_ClassDefinition* cd )
{
HB_Error error;
cd->ClassFormat = 1; /* Meaningless */
if ( ALLOC_ARRAY( cd->cd.cd1.ClassValueArray, 1, HB_UShort ) )
return error;
cd->loaded = TRUE;
return HB_Err_Ok;
}
HB_INTERNAL HB_Error
_HB_OPEN_Load_EmptyOrClassDefinition( HB_ClassDefinition* cd,
HB_UShort limit,
HB_UInt class_offset,
HB_UInt base_offset,
HB_Stream stream )
{
HB_Error error;
HB_UInt cur_offset;
cur_offset = FILE_Pos();
if ( class_offset )
{
if ( !FILE_Seek( class_offset + base_offset ) )
error = _HB_OPEN_Load_ClassDefinition( cd, limit, stream );
}
else
error = _HB_OPEN_Load_EmptyClassDefinition ( cd );
if (error == HB_Err_Ok)
(void)FILE_Seek( cur_offset ); /* Changes error as a side-effect */
return error;
}
HB_INTERNAL void
_HB_OPEN_Free_ClassDefinition( HB_ClassDefinition* cd )
{
if ( !cd->loaded )
return;
switch ( cd->ClassFormat )
{
case 1: Free_ClassDef1( &cd->cd.cd1 ); break;
case 2: Free_ClassDef2( &cd->cd.cd2 ); break;
default: break;
}
}
static HB_Error Get_Class1( HB_ClassDefFormat1* cdf1,
HB_UShort glyphID,
HB_UShort* klass,
HB_UShort* index )
{
HB_UShort* cva = cdf1->ClassValueArray;
if ( index )
*index = 0;
if ( glyphID >= cdf1->StartGlyph &&
glyphID < cdf1->StartGlyph + cdf1->GlyphCount )
{
*klass = cva[glyphID - cdf1->StartGlyph];
return HB_Err_Ok;
}
else
{
*klass = 0;
return HB_Err_Not_Covered;
}
}
/* we need the index value of the last searched class range record
in case of failure for constructed GDEF tables */
static HB_Error Get_Class2( HB_ClassDefFormat2* cdf2,
HB_UShort glyphID,
HB_UShort* klass,
HB_UShort* index )
{
HB_Error error = HB_Err_Ok;
HB_UShort min, max, new_min, new_max, middle;
HB_ClassRangeRecord* crr = cdf2->ClassRangeRecord;
/* binary search */
if ( cdf2->ClassRangeCount == 0 )
{
*klass = 0;
if ( index )
*index = 0;
return HB_Err_Not_Covered;
}
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 )
{
*klass = crr[middle].Class;
error = HB_Err_Ok;
break;
}
else if ( glyphID < crr[middle].Start )
{
if ( middle == min )
{
*klass = 0;
error = HB_Err_Not_Covered;
break;
}
new_max = middle - 1;
}
else
{
if ( middle == max )
{
*klass = 0;
error = HB_Err_Not_Covered;
break;
}
new_min = middle + 1;
}
} while ( min < max );
if ( index )
*index = middle;
return error;
}
HB_INTERNAL HB_Error
_HB_OPEN_Get_Class( HB_ClassDefinition* cd,
HB_UShort glyphID,
HB_UShort* klass,
HB_UShort* index )
{
switch ( cd->ClassFormat )
{
case 1: return Get_Class1( &cd->cd.cd1, glyphID, klass, index );
case 2: return Get_Class2( &cd->cd.cd2, glyphID, klass, index );
default: return ERR(HB_Err_Invalid_SubTable_Format);
}
return HB_Err_Ok; /* never reached */
}
/***************************
* Device related functions
***************************/
HB_INTERNAL HB_Error
_HB_OPEN_Load_Device( HB_Device** device,
HB_Stream stream )
{
HB_Device* d;
HB_Error error;
HB_UShort n, count;
HB_UShort* dv;
if ( ACCESS_Frame( 6L ) )
return error;
if ( ALLOC( *device, sizeof(HB_Device)) )
{
*device = 0;
return error;
}
d = *device;
d->StartSize = GET_UShort();
d->EndSize = GET_UShort();
d->DeltaFormat = GET_UShort();
FORGET_Frame();
d->DeltaValue = NULL;
if ( d->StartSize > d->EndSize ||
d->DeltaFormat == 0 || d->DeltaFormat > 3 )
{
/* XXX
* I've seen fontforge generate DeltaFormat == 0.
* Just return Ok and let the NULL DeltaValue disable
* this table.
*/
return HB_Err_Ok;
}
count = ( ( d->EndSize - d->StartSize + 1 ) >>
( 4 - d->DeltaFormat ) ) + 1;
if ( ALLOC_ARRAY( d->DeltaValue, count, HB_UShort ) )
{
FREE( *device );
*device = 0;
return error;
}
if ( ACCESS_Frame( count * 2L ) )
{
FREE( d->DeltaValue );
FREE( *device );
*device = 0;
return error;
}
dv = d->DeltaValue;
for ( n = 0; n < count; n++ )
dv[n] = GET_UShort();
FORGET_Frame();
return HB_Err_Ok;
}
HB_INTERNAL void
_HB_OPEN_Free_Device( HB_Device* d )
{
if ( d )
{
FREE( d->DeltaValue );
FREE( d );
}
}
/* 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 */
HB_INTERNAL HB_Error
_HB_OPEN_Get_Device( HB_Device* d,
HB_UShort size,
HB_Short* value )
{
HB_UShort byte, bits, mask, s;
if ( d && d->DeltaValue && size >= d->StartSize && size <= d->EndSize )
{
HB_UShort f = d->DeltaFormat;
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 = (HB_Short)( bits & mask );
/* conversion to a signed value */
if ( *value >= ( ( mask + 1 ) >> 1 ) )
*value -= mask + 1;
return HB_Err_Ok;
}
else
{
*value = 0;
return HB_Err_Not_Covered;
}
}
/* END */