/*
* 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-gdef-private.h"
#include "harfbuzz-open-private.h"
static HB_Error Load_AttachList( HB_AttachList* al,
HB_Stream stream );
static HB_Error Load_LigCaretList( HB_LigCaretList* lcl,
HB_Stream stream );
static void Free_AttachList( HB_AttachList* al);
static void Free_LigCaretList( HB_LigCaretList* lcl);
static void Free_NewGlyphClasses( HB_GDEFHeader* gdef);
/* GDEF glyph classes */
#define UNCLASSIFIED_GLYPH 0
#define SIMPLE_GLYPH 1
#define LIGATURE_GLYPH 2
#define MARK_GLYPH 3
#define COMPONENT_GLYPH 4
HB_Error HB_New_GDEF_Table( HB_GDEFHeader** retptr )
{
HB_Error error;
HB_GDEFHeader* gdef;
if ( !retptr )
return ERR(HB_Err_Invalid_Argument);
if ( ALLOC( gdef, sizeof( *gdef ) ) )
return error;
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 HB_Err_Ok;
}
HB_Error HB_Load_GDEF_Table( HB_Stream stream,
HB_GDEFHeader** retptr )
{
HB_Error error;
HB_UInt cur_offset, new_offset, base_offset;
HB_GDEFHeader* gdef;
if ( !retptr )
return ERR(HB_Err_Invalid_Argument);
if ( GOTO_Table( TTAG_GDEF ) )
return error;
if (( error = HB_New_GDEF_Table ( &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 = _HB_OPEN_Load_ClassDefinition( &gdef->GlyphClassDef, 5,
stream ) ) != HB_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 ) ) != HB_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 ) ) != HB_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 HB_Err_Ok;
Fail3:
Free_LigCaretList( &gdef->LigCaretList );
Fail2:
Free_AttachList( &gdef->AttachList );
Fail1:
_HB_OPEN_Free_ClassDefinition( &gdef->GlyphClassDef );
Fail0:
FREE( gdef );
return error;
}
HB_Error HB_Done_GDEF_Table ( HB_GDEFHeader* gdef )
{
Free_LigCaretList( &gdef->LigCaretList );
Free_AttachList( &gdef->AttachList );
_HB_OPEN_Free_ClassDefinition( &gdef->GlyphClassDef );
_HB_OPEN_Free_ClassDefinition( &gdef->MarkAttachClassDef );
Free_NewGlyphClasses( gdef );
FREE( gdef );
return HB_Err_Ok;
}
/*******************************
* AttachList related functions
*******************************/
/* AttachPoint */
static HB_Error Load_AttachPoint( HB_AttachPoint* ap,
HB_Stream stream )
{
HB_Error error;
HB_UShort n, count;
HB_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, HB_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 HB_Err_Ok;
}
static void Free_AttachPoint( HB_AttachPoint* ap )
{
FREE( ap->PointIndex );
}
/* AttachList */
static HB_Error Load_AttachList( HB_AttachList* al,
HB_Stream stream )
{
HB_Error error;
HB_UShort n, m, count;
HB_UInt cur_offset, new_offset, base_offset;
HB_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 = _HB_OPEN_Load_Coverage( &al->Coverage, stream ) ) != HB_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, HB_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 ) ) != HB_Err_Ok )
goto Fail1;
(void)FILE_Seek( cur_offset );
}
al->loaded = TRUE;
return HB_Err_Ok;
Fail1:
for ( m = 0; m < n; m++ )
Free_AttachPoint( &ap[m] );
FREE( ap );
Fail2:
_HB_OPEN_Free_Coverage( &al->Coverage );
return error;
}
static void Free_AttachList( HB_AttachList* al)
{
HB_UShort n, count;
HB_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 );
}
_HB_OPEN_Free_Coverage( &al->Coverage );
}
/*********************************
* LigCaretList related functions
*********************************/
/* CaretValueFormat1 */
/* CaretValueFormat2 */
/* CaretValueFormat3 */
/* CaretValueFormat4 */
static HB_Error Load_CaretValue( HB_CaretValue* cv,
HB_Stream stream )
{
HB_Error error;
HB_UInt 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 = _HB_OPEN_Load_Device( &cv->cvf.cvf3.Device,
stream ) ) != HB_Err_Ok )
return error;
(void)FILE_Seek( cur_offset );
break;
case 4:
if ( ACCESS_Frame( 2L ) )
return error;
#ifdef HB_SUPPORT_MULTIPLE_MASTER
cv->cvf.cvf4.IdCaretValue = GET_UShort();
#else
(void) GET_UShort();
#endif
FORGET_Frame();
break;
default:
return ERR(HB_Err_Invalid_SubTable_Format);
}
return HB_Err_Ok;
}
static void Free_CaretValue( HB_CaretValue* cv)
{
if ( cv->CaretValueFormat == 3 )
_HB_OPEN_Free_Device( cv->cvf.cvf3.Device );
}
/* LigGlyph */
static HB_Error Load_LigGlyph( HB_LigGlyph* lg,
HB_Stream stream )
{
HB_Error error;
HB_UShort n, m, count;
HB_UInt cur_offset, new_offset, base_offset;
HB_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, HB_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 ) ) != HB_Err_Ok )
goto Fail;
(void)FILE_Seek( cur_offset );
}
return HB_Err_Ok;
Fail:
for ( m = 0; m < n; m++ )
Free_CaretValue( &cv[m] );
FREE( cv );
return error;
}
static void Free_LigGlyph( HB_LigGlyph* lg)
{
HB_UShort n, count;
HB_CaretValue* cv;
if ( lg->CaretValue )
{
count = lg->CaretCount;
cv = lg->CaretValue;
for ( n = 0; n < count; n++ )
Free_CaretValue( &cv[n] );
FREE( cv );
}
}
/* LigCaretList */
static HB_Error Load_LigCaretList( HB_LigCaretList* lcl,
HB_Stream stream )
{
HB_Error error;
HB_UShort m, n, count;
HB_UInt cur_offset, new_offset, base_offset;
HB_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 = _HB_OPEN_Load_Coverage( &lcl->Coverage, stream ) ) != HB_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, HB_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 ) ) != HB_Err_Ok )
goto Fail1;
(void)FILE_Seek( cur_offset );
}
lcl->loaded = TRUE;
return HB_Err_Ok;
Fail1:
for ( m = 0; m < n; m++ )
Free_LigGlyph( &lg[m] );
FREE( lg );
Fail2:
_HB_OPEN_Free_Coverage( &lcl->Coverage );
return error;
}
static void Free_LigCaretList( HB_LigCaretList* lcl )
{
HB_UShort n, count;
HB_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 );
}
_HB_OPEN_Free_Coverage( &lcl->Coverage );
}
/***********
* GDEF API
***********/
static HB_UShort Get_New_Class( HB_GDEFHeader* gdef,
HB_UShort glyphID,
HB_UShort index )
{
HB_UShort glyph_index, array_index, count;
HB_UShort byte, bits;
HB_ClassRangeRecord* gcrr;
HB_UShort** ngc;
if ( glyphID >= gdef->LastGlyph )
return 0;
count = gdef->GlyphClassDef.cd.cd2.ClassRangeCount;
gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord;
ngc = gdef->NewGlyphClasses;
if ( index < count && glyphID < gcrr[index].Start )
{
array_index = index;
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];
bits = byte >> ( 16 - ( glyph_index % 4 + 1 ) * 4 );
return bits & 0x000F;
}
HB_Error HB_GDEF_Get_Glyph_Property( HB_GDEFHeader* gdef,
HB_UShort glyphID,
HB_UShort* property )
{
HB_UShort class = 0, index = 0; /* shut compiler up */
HB_Error error;
if ( !gdef || !property )
return ERR(HB_Err_Invalid_Argument);
/* first, we check for mark attach classes */
if ( gdef->MarkAttachClassDef.loaded )
{
error = _HB_OPEN_Get_Class( &gdef->MarkAttachClassDef, glyphID, &class, &index );
if ( error && error != HB_Err_Not_Covered )
return error;
if ( !error )
{
*property = class << 8;
return HB_Err_Ok;
}
}
error = _HB_OPEN_Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index );
if ( error && error != HB_Err_Not_Covered )
return error;
/* if we have a constructed class table, check whether additional
values have been assigned */
if ( error == HB_Err_Not_Covered && gdef->NewGlyphClasses )
class = Get_New_Class( gdef, glyphID, index );
switch ( class )
{
default:
case UNCLASSIFIED_GLYPH:
*property = 0;
break;
case SIMPLE_GLYPH:
*property = HB_GDEF_BASE_GLYPH;
break;
case LIGATURE_GLYPH:
*property = HB_GDEF_LIGATURE;
break;
case MARK_GLYPH:
*property = HB_GDEF_MARK;
break;
case COMPONENT_GLYPH:
*property = HB_GDEF_COMPONENT;
break;
}
return HB_Err_Ok;
}
static HB_Error Make_ClassRange( HB_ClassDefinition* cd,
HB_UShort start,
HB_UShort end,
HB_UShort class )
{
HB_Error error;
HB_UShort index;
HB_ClassDefFormat2* cdf2;
HB_ClassRangeRecord* crr;
cdf2 = &cd->cd.cd2;
if ( REALLOC_ARRAY( cdf2->ClassRangeRecord,
cdf2->ClassRangeCount + 1 ,
HB_ClassRangeRecord ) )
return error;
cdf2->ClassRangeCount++;
crr = cdf2->ClassRangeRecord;
index = cdf2->ClassRangeCount - 1;
crr[index].Start = start;
crr[index].End = end;
crr[index].Class = class;
return HB_Err_Ok;
}
HB_Error HB_GDEF_Build_ClassDefinition( HB_GDEFHeader* gdef,
HB_UShort num_glyphs,
HB_UShort glyph_count,
HB_UShort* glyph_array,
HB_UShort* class_array )
{
HB_UShort start, curr_glyph, curr_class;
HB_UShort n, m, count;
HB_Error error;
HB_ClassDefinition* gcd;
HB_ClassRangeRecord* gcrr;
HB_UShort** ngc;
if ( !gdef || !glyph_array || !class_array )
return ERR(HB_Err_Invalid_Argument);
gcd = &gdef->GlyphClassDef;
/* We build a format 2 table */
gcd->ClassFormat = 2;
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 = ERR(HB_Err_Invalid_Argument);
goto Fail4;
}
glyph_count--;
for ( n = 0; n < glyph_count + 1; 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) ) != HB_Err_Ok )
goto Fail3;
}
else
{
if ( curr_glyph == 0xFFFF )
{
error = ERR(HB_Err_Invalid_Argument);
goto Fail3;
}
else
curr_glyph++;
}
}
else
{
if ( ( error = Make_ClassRange( gcd, start,
curr_glyph - 1,
curr_class) ) != HB_Err_Ok )
goto Fail3;
if ( curr_glyph > glyph_array[n] )
{
error = ERR(HB_Err_Invalid_Argument);
goto Fail3;
}
start = glyph_array[n];
curr_class = class_array[n];
curr_glyph = start;
if ( curr_class >= 5 )
{
error = ERR(HB_Err_Invalid_Argument);
goto Fail3;
}
if ( n == glyph_count )
{
if ( ( error = Make_ClassRange( gcd, start,
curr_glyph,
curr_class) ) != HB_Err_Ok )
goto Fail3;
}
else
{
if ( curr_glyph == 0xFFFF )
{
error = ERR(HB_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, HB_UShort* ) )
goto Fail3;
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 ( count > 0 )
{
if ( gcrr[0].Start )
{
if ( ALLOC_ARRAY( ngc[0], ( gcrr[0].Start + 3 ) / 4, HB_UShort ) )
goto Fail2;
}
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 + 2 ) / 4,
HB_UShort ) )
goto Fail1;
}
if ( gcrr[count - 1].End != num_glyphs - 1 )
{
if ( ALLOC_ARRAY( ngc[count],
( num_glyphs - gcrr[count - 1].End + 2 ) / 4,
HB_UShort ) )
goto Fail1;
}
}
else if ( num_glyphs > 0 )
{
if ( ALLOC_ARRAY( ngc[count],
( num_glyphs + 3 ) / 4,
HB_UShort ) )
goto Fail2;
}
gdef->LastGlyph = num_glyphs - 1;
gdef->MarkAttachClassDef_offset = 0L;
gdef->MarkAttachClassDef.loaded = FALSE;
gcd->loaded = TRUE;
return HB_Err_Ok;
Fail1:
for ( m = 0; m < n; m++ )
FREE( ngc[m] );
Fail2:
FREE( gdef->NewGlyphClasses );
Fail3:
FREE( gcd->cd.cd2.ClassRangeRecord );
Fail4:
return error;
}
static void Free_NewGlyphClasses( HB_GDEFHeader* gdef )
{
HB_UShort** ngc;
HB_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 );
}
}
HB_INTERNAL HB_Error
_HB_GDEF_Add_Glyph_Property( HB_GDEFHeader* gdef,
HB_UShort glyphID,
HB_UShort property )
{
HB_Error error;
HB_UShort class, new_class, index = 0; /* shut compiler up */
HB_UShort byte, bits, mask;
HB_UShort array_index, glyph_index, count;
HB_ClassRangeRecord* gcrr;
HB_UShort** ngc;
error = _HB_OPEN_Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index );
if ( error && error != HB_Err_Not_Covered )
return error;
/* we don't accept glyphs covered in `GlyphClassDef' */
if ( !error )
return HB_Err_Not_Covered;
switch ( property )
{
case 0:
new_class = UNCLASSIFIED_GLYPH;
break;
case HB_GDEF_BASE_GLYPH:
new_class = SIMPLE_GLYPH;
break;
case HB_GDEF_LIGATURE:
new_class = LIGATURE_GLYPH;
break;
case HB_GDEF_MARK:
new_class = MARK_GLYPH;
break;
case HB_GDEF_COMPONENT:
new_class = COMPONENT_GLYPH;
break;
default:
return ERR(HB_Err_Invalid_Argument);
}
count = gdef->GlyphClassDef.cd.cd2.ClassRangeCount;
gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord;
ngc = gdef->NewGlyphClasses;
if ( index < count && glyphID < gcrr[index].Start )
{
array_index = index;
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];
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] &= mask;
ngc[array_index][glyph_index / 4] |= bits;
}
return HB_Err_Ok;
}
HB_INTERNAL HB_Error
_HB_GDEF_Check_Property( HB_GDEFHeader* gdef,
HB_GlyphItem gitem,
HB_UShort flags,
HB_UShort* property )
{
HB_Error error;
if ( gdef )
{
HB_UShort basic_glyph_class;
HB_UShort desired_attachment_class;
if ( gitem->gproperties == HB_GLYPH_PROPERTIES_UNKNOWN )
{
error = HB_GDEF_Get_Glyph_Property( gdef, gitem->gindex, &gitem->gproperties );
if ( error )
return error;
}
*property = gitem->gproperties;
/* 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 & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS )
basic_glyph_class = HB_GDEF_MARK;
else
basic_glyph_class = *property;
/* Return Not_Covered, if, for example, basic_glyph_class
* is HB_GDEF_LIGATURE and LookFlags includes HB_LOOKUP_FLAG_IGNORE_LIGATURES
*/
if ( flags & basic_glyph_class )
return HB_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 & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS;
if ( desired_attachment_class )
{
if ( basic_glyph_class == HB_GDEF_MARK &&
*property != desired_attachment_class )
return HB_Err_Not_Covered;
}
} else {
*property = 0;
}
return HB_Err_Ok;
}
HB_INTERNAL HB_Error
_HB_GDEF_LoadMarkAttachClassDef_From_LookupFlags( HB_GDEFHeader* gdef,
HB_Stream stream,
HB_Lookup* lo,
HB_UShort num_lookups)
{
HB_Error error = HB_Err_Ok;
HB_UShort i;
/* We now check the LookupFlags for values larger than 0xFF to find
out whether we need to load the `MarkAttachClassDef' field of the
GDEF table -- this hack is necessary for OpenType 1.2 tables since
the version field of the GDEF table hasn't been incremented.
For constructed GDEF tables, we only load it if
`MarkAttachClassDef_offset' is not zero (nevertheless, a build of
a constructed mark attach table is not supported currently). */
if ( gdef &&
gdef->MarkAttachClassDef_offset && !gdef->MarkAttachClassDef.loaded )
{
for ( i = 0; i < num_lookups; i++ )
{
if ( lo[i].LookupFlag & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS )
{
if ( FILE_Seek( gdef->MarkAttachClassDef_offset ) ||
( error = _HB_OPEN_Load_ClassDefinition( &gdef->MarkAttachClassDef,
256, stream ) ) != HB_Err_Ok )
goto Done;
break;
}
}
}
Done:
return error;
}
/* END */