/* | |
* 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 */ |