1156 lines
25 KiB
C
1156 lines
25 KiB
C
/*******************************************************************
|
|
*
|
|
* ftxgdef.c
|
|
*
|
|
* TrueType Open GDEF 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 <freetype/tttags.h>
|
|
|
|
#include <freetype/internal/ftstream.h>
|
|
#include <freetype/internal/ftmemory.h>
|
|
#include <freetype/internal/tttypes.h>
|
|
|
|
#include "fterrcompat.h"
|
|
|
|
#include "ftxopen.h"
|
|
#include "ftxopenf.h"
|
|
|
|
#define TTAG_GDEF FT_MAKE_TAG( 'G', 'D', 'E', 'F' )
|
|
|
|
static FT_Error Load_AttachList( TTO_AttachList* al,
|
|
FT_Stream stream );
|
|
static FT_Error Load_LigCaretList( TTO_LigCaretList* lcl,
|
|
FT_Stream stream );
|
|
|
|
static void Free_AttachList( TTO_AttachList* al,
|
|
FT_Memory memory );
|
|
static void Free_LigCaretList( TTO_LigCaretList* lcl,
|
|
FT_Memory memory );
|
|
|
|
static void Free_NewGlyphClasses( TTO_GDEFHeader* gdef,
|
|
FT_Memory memory );
|
|
|
|
|
|
|
|
/**********************
|
|
* Extension Functions
|
|
**********************/
|
|
|
|
#if 0
|
|
#define GDEF_ID Build_Extension_ID( 'G', 'D', 'E', 'F' )
|
|
|
|
|
|
static FT_Error GDEF_Create( void* ext,
|
|
PFace face )
|
|
{
|
|
DEFINE_LOAD_LOCALS( face->stream );
|
|
|
|
TTO_GDEFHeader* gdef = (TTO_GDEFHeader*)ext;
|
|
Long table;
|
|
|
|
|
|
/* by convention */
|
|
|
|
if ( !gdef )
|
|
return TT_Err_Ok;
|
|
|
|
/* a null offset indicates that there is no GDEF table */
|
|
|
|
gdef->offset = 0;
|
|
|
|
/* we store the start offset and the size of the subtable */
|
|
|
|
table = TT_LookUp_Table( face, TTAG_GDEF );
|
|
if ( table < 0 )
|
|
return TT_Err_Ok; /* The table is optional */
|
|
|
|
if ( FILE_Seek( face->dirTables[table].Offset ) ||
|
|
ACCESS_Frame( 4L ) )
|
|
return error;
|
|
|
|
gdef->offset = FILE_Pos() - 4L; /* undo ACCESS_Frame() */
|
|
gdef->Version = GET_ULong();
|
|
|
|
FORGET_Frame();
|
|
|
|
gdef->loaded = FALSE;
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
static FT_Error GDEF_Destroy( void* ext,
|
|
PFace face )
|
|
{
|
|
TTO_GDEFHeader* gdef = (TTO_GDEFHeader*)ext;
|
|
|
|
|
|
/* by convention */
|
|
|
|
if ( !gdef )
|
|
return TT_Err_Ok;
|
|
|
|
if ( gdef->loaded )
|
|
{
|
|
Free_LigCaretList( &gdef->LigCaretList, memory );
|
|
Free_AttachList( &gdef->AttachList, memory );
|
|
Free_ClassDefinition( &gdef->GlyphClassDef, memory );
|
|
Free_ClassDefinition( &gdef->MarkAttachClassDef, memory );
|
|
|
|
Free_NewGlyphClasses( gdef, memory );
|
|
}
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
EXPORT_FUNC
|
|
FT_Error TT_Init_GDEF_Extension( TT_Engine engine )
|
|
{
|
|
PEngine_Instance _engine = HANDLE_Engine( engine );
|
|
|
|
|
|
if ( !_engine )
|
|
return TT_Err_Invalid_Engine;
|
|
|
|
return TT_Register_Extension( _engine,
|
|
GDEF_ID,
|
|
sizeof ( TTO_GDEFHeader ),
|
|
GDEF_Create,
|
|
GDEF_Destroy );
|
|
}
|
|
#endif
|
|
|
|
EXPORT_FUNC
|
|
FT_Error TT_Load_GDEF_Table( FT_Face face,
|
|
TTO_GDEFHeader** retptr )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory = face->memory;
|
|
FT_Stream stream = face->stream;
|
|
TT_Face tt_face = (TT_Face)face;
|
|
FT_ULong cur_offset, new_offset, base_offset;
|
|
|
|
TTO_GDEFHeader* gdef;
|
|
|
|
|
|
if ( !retptr )
|
|
return TT_Err_Invalid_Argument;
|
|
|
|
if (( error = tt_face->goto_table( tt_face, TTAG_GDEF, stream, 0 ) ))
|
|
return error;
|
|
|
|
if ( ALLOC( gdef, sizeof( *gdef ) ) )
|
|
return error;
|
|
|
|
gdef->memory = face->memory;
|
|
|
|
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 = Load_ClassDefinition( &gdef->GlyphClassDef, 5,
|
|
stream ) ) != TT_Err_Ok )
|
|
goto Fail0;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
else
|
|
gdef->GlyphClassDef.loaded = FALSE;
|
|
|
|
if ( ACCESS_Frame( 2L ) )
|
|
goto Fail0;
|
|
|
|
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 ) ) != TT_Err_Ok )
|
|
goto Fail1;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
else
|
|
gdef->AttachList.loaded = FALSE;
|
|
|
|
if ( ACCESS_Frame( 2L ) )
|
|
return error;
|
|
|
|
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 ) ) != TT_Err_Ok )
|
|
goto Fail2;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
else
|
|
gdef->LigCaretList.loaded = FALSE;
|
|
|
|
/* 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 current file offset. */
|
|
|
|
gdef->MarkAttachClassDef_offset = FILE_Pos();
|
|
gdef->MarkAttachClassDef.loaded = FALSE;
|
|
|
|
gdef->LastGlyph = 0;
|
|
gdef->NewGlyphClasses = NULL;
|
|
|
|
*retptr = gdef;
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail2:
|
|
Free_AttachList( &gdef->AttachList, memory );
|
|
|
|
Fail1:
|
|
Free_ClassDefinition( &gdef->GlyphClassDef, memory );
|
|
|
|
Fail0:
|
|
FREE( gdef );
|
|
|
|
return error;
|
|
}
|
|
|
|
EXPORT_FUNC
|
|
FT_Error TT_Done_GDEF_Table ( TTO_GDEFHeader* gdef )
|
|
{
|
|
FT_Memory memory = gdef->memory;
|
|
|
|
Free_LigCaretList( &gdef->LigCaretList, memory );
|
|
Free_AttachList( &gdef->AttachList, memory );
|
|
Free_ClassDefinition( &gdef->GlyphClassDef, memory );
|
|
Free_ClassDefinition( &gdef->MarkAttachClassDef, memory );
|
|
|
|
Free_NewGlyphClasses( gdef, memory );
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*******************************
|
|
* AttachList related functions
|
|
*******************************/
|
|
|
|
|
|
/* AttachPoint */
|
|
|
|
static FT_Error Load_AttachPoint( TTO_AttachPoint* ap,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Memory memory = stream->memory;
|
|
FT_Error error;
|
|
|
|
FT_UShort n, count;
|
|
FT_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, FT_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 TT_Err_Ok;
|
|
}
|
|
|
|
|
|
static void Free_AttachPoint( TTO_AttachPoint* ap,
|
|
FT_Memory memory )
|
|
{
|
|
FREE( ap->PointIndex );
|
|
}
|
|
|
|
|
|
/* AttachList */
|
|
|
|
static FT_Error Load_AttachList( TTO_AttachList* al,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Memory memory = stream->memory;
|
|
FT_Error error;
|
|
|
|
FT_UShort n, count;
|
|
FT_ULong cur_offset, new_offset, base_offset;
|
|
|
|
TTO_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 = Load_Coverage( &al->Coverage, stream ) ) != TT_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, TTO_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 ) ) != TT_Err_Ok )
|
|
goto Fail1;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
al->loaded = TRUE;
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail1:
|
|
for ( n = 0; n < count; n++ )
|
|
Free_AttachPoint( &ap[n], memory );
|
|
|
|
FREE( ap );
|
|
|
|
Fail2:
|
|
Free_Coverage( &al->Coverage, memory );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_AttachList( TTO_AttachList* al,
|
|
FT_Memory memory )
|
|
{
|
|
FT_UShort n, count;
|
|
|
|
TTO_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], memory );
|
|
|
|
FREE( ap );
|
|
}
|
|
|
|
Free_Coverage( &al->Coverage, memory );
|
|
}
|
|
|
|
|
|
|
|
/*********************************
|
|
* LigCaretList related functions
|
|
*********************************/
|
|
|
|
|
|
/* CaretValueFormat1 */
|
|
/* CaretValueFormat2 */
|
|
/* CaretValueFormat3 */
|
|
/* CaretValueFormat4 */
|
|
|
|
static FT_Error Load_CaretValue( TTO_CaretValue* cv,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Error error;
|
|
|
|
FT_ULong 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 = Load_Device( &cv->cvf.cvf3.Device,
|
|
stream ) ) != TT_Err_Ok )
|
|
return error;
|
|
(void)FILE_Seek( cur_offset );
|
|
|
|
break;
|
|
|
|
case 4:
|
|
if ( ACCESS_Frame( 2L ) )
|
|
return error;
|
|
|
|
cv->cvf.cvf4.IdCaretValue = GET_UShort();
|
|
|
|
FORGET_Frame();
|
|
break;
|
|
|
|
default:
|
|
return TTO_Err_Invalid_GDEF_SubTable_Format;
|
|
}
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
static void Free_CaretValue( TTO_CaretValue* cv,
|
|
FT_Memory memory )
|
|
{
|
|
if ( cv->CaretValueFormat == 3 )
|
|
Free_Device( &cv->cvf.cvf3.Device, memory );
|
|
}
|
|
|
|
|
|
/* LigGlyph */
|
|
|
|
static FT_Error Load_LigGlyph( TTO_LigGlyph* lg,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Memory memory = stream->memory;
|
|
FT_Error error;
|
|
|
|
FT_UShort n, count;
|
|
FT_ULong cur_offset, new_offset, base_offset;
|
|
|
|
TTO_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, TTO_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 ) ) != TT_Err_Ok )
|
|
goto Fail;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail:
|
|
for ( n = 0; n < count; n++ )
|
|
Free_CaretValue( &cv[n], memory );
|
|
|
|
FREE( cv );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_LigGlyph( TTO_LigGlyph* lg,
|
|
FT_Memory memory )
|
|
{
|
|
FT_UShort n, count;
|
|
|
|
TTO_CaretValue* cv;
|
|
|
|
|
|
if ( lg->CaretValue )
|
|
{
|
|
count = lg->CaretCount;
|
|
cv = lg->CaretValue;
|
|
|
|
for ( n = 0; n < count; n++ )
|
|
Free_CaretValue( &cv[n], memory );
|
|
|
|
FREE( cv );
|
|
}
|
|
}
|
|
|
|
|
|
/* LigCaretList */
|
|
|
|
static FT_Error Load_LigCaretList( TTO_LigCaretList* lcl,
|
|
FT_Stream stream )
|
|
{
|
|
FT_Memory memory = stream->memory;
|
|
FT_Error error;
|
|
|
|
FT_UShort n, count;
|
|
FT_ULong cur_offset, new_offset, base_offset;
|
|
|
|
TTO_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 = Load_Coverage( &lcl->Coverage, stream ) ) != TT_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, TTO_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 ) ) != TT_Err_Ok )
|
|
goto Fail1;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
lcl->loaded = TRUE;
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail1:
|
|
for ( n = 0; n < count; n++ )
|
|
Free_LigGlyph( &lg[n], memory );
|
|
|
|
FREE( lg );
|
|
|
|
Fail2:
|
|
Free_Coverage( &lcl->Coverage, memory );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_LigCaretList( TTO_LigCaretList* lcl,
|
|
FT_Memory memory )
|
|
{
|
|
FT_UShort n, count;
|
|
|
|
TTO_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], memory );
|
|
|
|
FREE( lg );
|
|
}
|
|
|
|
Free_Coverage( &lcl->Coverage, memory );
|
|
}
|
|
|
|
|
|
|
|
/***********
|
|
* GDEF API
|
|
***********/
|
|
|
|
|
|
static FT_UShort Get_New_Class( TTO_GDEFHeader* gdef,
|
|
FT_UShort glyphID,
|
|
FT_UShort index )
|
|
{
|
|
FT_UShort glyph_index, array_index;
|
|
FT_UShort byte, bits;
|
|
|
|
TTO_ClassRangeRecord* gcrr;
|
|
FT_UShort** ngc;
|
|
|
|
|
|
if ( glyphID >= gdef->LastGlyph )
|
|
return 0;
|
|
|
|
gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord;
|
|
ngc = gdef->NewGlyphClasses;
|
|
|
|
if ( glyphID < gcrr[index].Start )
|
|
{
|
|
array_index = 0;
|
|
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 + 1];
|
|
bits = byte >> ( 16 - ( glyph_index % 4 + 1 ) * 4 );
|
|
|
|
return bits & 0x000F;
|
|
}
|
|
|
|
|
|
EXPORT_FUNC
|
|
FT_Error TT_GDEF_Get_Glyph_Property( TTO_GDEFHeader* gdef,
|
|
FT_UShort glyphID,
|
|
FT_UShort* property )
|
|
{
|
|
FT_UShort class, index;
|
|
|
|
FT_Error error;
|
|
|
|
|
|
if ( !gdef || !property )
|
|
return TT_Err_Invalid_Argument;
|
|
|
|
/* first, we check for mark attach classes */
|
|
|
|
if ( gdef->MarkAttachClassDef.loaded )
|
|
{
|
|
error = Get_Class( &gdef->MarkAttachClassDef, glyphID, &class, &index );
|
|
if ( error && error != TTO_Err_Not_Covered )
|
|
return error;
|
|
if ( !error )
|
|
{
|
|
*property = class << 8;
|
|
return TT_Err_Ok;
|
|
}
|
|
}
|
|
|
|
error = Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index );
|
|
if ( error && error != TTO_Err_Not_Covered )
|
|
return error;
|
|
|
|
/* if we have a constructed class table, check whether additional
|
|
values have been assigned */
|
|
|
|
if ( error == TTO_Err_Not_Covered && gdef->NewGlyphClasses )
|
|
class = Get_New_Class( gdef, glyphID, index );
|
|
|
|
switch ( class )
|
|
{
|
|
case UNCLASSIFIED_GLYPH:
|
|
*property = 0;
|
|
break;
|
|
|
|
case SIMPLE_GLYPH:
|
|
*property = TTO_BASE_GLYPH;
|
|
break;
|
|
|
|
case LIGATURE_GLYPH:
|
|
*property = TTO_LIGATURE;
|
|
break;
|
|
|
|
case MARK_GLYPH:
|
|
*property = TTO_MARK;
|
|
break;
|
|
|
|
case COMPONENT_GLYPH:
|
|
*property = TTO_COMPONENT;
|
|
break;
|
|
}
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
static FT_Error Make_ClassRange( TTO_ClassDefinition* cd,
|
|
FT_UShort start,
|
|
FT_UShort end,
|
|
FT_UShort class,
|
|
FT_Memory memory )
|
|
{
|
|
FT_Error error;
|
|
FT_UShort index;
|
|
|
|
TTO_ClassDefFormat2* cdf2;
|
|
TTO_ClassRangeRecord* crr;
|
|
|
|
|
|
cdf2 = &cd->cd.cd2;
|
|
|
|
if ( REALLOC_ARRAY( cdf2->ClassRangeRecord,
|
|
cdf2->ClassRangeCount,
|
|
cdf2->ClassRangeCount + 1 ,
|
|
TTO_ClassRangeRecord ) )
|
|
return error;
|
|
|
|
cdf2->ClassRangeCount++;
|
|
|
|
crr = cdf2->ClassRangeRecord;
|
|
index = cdf2->ClassRangeCount - 1;
|
|
|
|
crr[index].Start = start;
|
|
crr[index].End = end;
|
|
crr[index].Class = class;
|
|
|
|
cd->Defined[class] = TRUE;
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
EXPORT_FUNC
|
|
FT_Error TT_GDEF_Build_ClassDefinition( TTO_GDEFHeader* gdef,
|
|
FT_UShort num_glyphs,
|
|
FT_UShort glyph_count,
|
|
FT_UShort* glyph_array,
|
|
FT_UShort* class_array )
|
|
{
|
|
FT_UShort start, curr_glyph, curr_class;
|
|
FT_UShort n, count;
|
|
FT_Error error;
|
|
FT_Memory memory = gdef->memory;
|
|
|
|
TTO_ClassDefinition* gcd;
|
|
TTO_ClassRangeRecord* gcrr;
|
|
FT_UShort** ngc;
|
|
|
|
|
|
if ( !gdef || !glyph_array || !class_array )
|
|
return TT_Err_Invalid_Argument;
|
|
|
|
gcd = &gdef->GlyphClassDef;
|
|
|
|
/* We build a format 2 table */
|
|
|
|
gcd->ClassFormat = 2;
|
|
|
|
/* A GlyphClassDef table contains at most 5 different class values */
|
|
|
|
if ( ALLOC_ARRAY( gcd->Defined, 5, FT_Bool ) )
|
|
return error;
|
|
|
|
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 = TT_Err_Invalid_Argument;
|
|
goto Fail4;
|
|
}
|
|
|
|
glyph_count--;
|
|
|
|
for ( n = 0; n <= glyph_count; 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,
|
|
memory ) ) != TT_Err_Ok )
|
|
goto Fail3;
|
|
}
|
|
else
|
|
{
|
|
if ( curr_glyph == 0xFFFF )
|
|
{
|
|
error = TT_Err_Invalid_Argument;
|
|
goto Fail3;
|
|
}
|
|
else
|
|
curr_glyph++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ( error = Make_ClassRange( gcd, start,
|
|
curr_glyph - 1,
|
|
curr_class,
|
|
memory ) ) != TT_Err_Ok )
|
|
goto Fail3;
|
|
|
|
if ( curr_glyph > glyph_array[n] )
|
|
{
|
|
error = TT_Err_Invalid_Argument;
|
|
goto Fail3;
|
|
}
|
|
|
|
start = glyph_array[n];
|
|
curr_class = class_array[n];
|
|
curr_glyph = start;
|
|
|
|
if ( curr_class >= 5 )
|
|
{
|
|
error = TT_Err_Invalid_Argument;
|
|
goto Fail3;
|
|
}
|
|
|
|
if ( n == glyph_count )
|
|
{
|
|
if ( ( error = Make_ClassRange( gcd, start,
|
|
curr_glyph,
|
|
curr_class,
|
|
memory ) ) != TT_Err_Ok )
|
|
goto Fail3;
|
|
}
|
|
else
|
|
{
|
|
if ( curr_glyph == 0xFFFF )
|
|
{
|
|
error = TT_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, FT_UShort* ) )
|
|
goto Fail2;
|
|
|
|
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 ( gcrr[0].Start )
|
|
{
|
|
if ( ALLOC_ARRAY( ngc[0], gcrr[0].Start / 4 + 1, FT_UShort ) )
|
|
goto Fail1;
|
|
}
|
|
|
|
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 - 1 ) / 4 + 1,
|
|
FT_UShort ) )
|
|
goto Fail1;
|
|
}
|
|
|
|
if ( gcrr[count - 1].End != num_glyphs - 1 )
|
|
{
|
|
if ( ALLOC_ARRAY( ngc[count],
|
|
( num_glyphs - gcrr[count - 1].End - 1 ) / 4 + 1,
|
|
FT_UShort ) )
|
|
goto Fail1;
|
|
}
|
|
|
|
gdef->LastGlyph = num_glyphs - 1;
|
|
|
|
gdef->MarkAttachClassDef_offset = 0L;
|
|
gdef->MarkAttachClassDef.loaded = FALSE;
|
|
|
|
return TT_Err_Ok;
|
|
|
|
Fail1:
|
|
for ( n = 0; n < count; n++ )
|
|
FREE( ngc[n] );
|
|
|
|
Fail2:
|
|
FREE( gdef->NewGlyphClasses );
|
|
|
|
Fail3:
|
|
FREE( gcd->cd.cd2.ClassRangeRecord );
|
|
|
|
Fail4:
|
|
FREE( gcd->Defined );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_NewGlyphClasses( TTO_GDEFHeader* gdef,
|
|
FT_Memory memory )
|
|
{
|
|
FT_UShort** ngc;
|
|
FT_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 );
|
|
}
|
|
}
|
|
|
|
|
|
FT_Error Add_Glyph_Property( TTO_GDEFHeader* gdef,
|
|
FT_UShort glyphID,
|
|
FT_UShort property )
|
|
{
|
|
FT_Error error;
|
|
FT_UShort class, new_class, index;
|
|
FT_UShort byte, bits, mask;
|
|
FT_UShort array_index, glyph_index;
|
|
|
|
TTO_ClassRangeRecord* gcrr;
|
|
FT_UShort** ngc;
|
|
|
|
|
|
error = Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index );
|
|
if ( error && error != TTO_Err_Not_Covered )
|
|
return error;
|
|
|
|
/* we don't accept glyphs covered in `GlyphClassDef' */
|
|
|
|
if ( !error )
|
|
return TTO_Err_Not_Covered;
|
|
|
|
switch ( property )
|
|
{
|
|
case 0:
|
|
new_class = UNCLASSIFIED_GLYPH;
|
|
break;
|
|
|
|
case TTO_BASE_GLYPH:
|
|
new_class = SIMPLE_GLYPH;
|
|
break;
|
|
|
|
case TTO_LIGATURE:
|
|
new_class = LIGATURE_GLYPH;
|
|
break;
|
|
|
|
case TTO_MARK:
|
|
new_class = MARK_GLYPH;
|
|
break;
|
|
|
|
case TTO_COMPONENT:
|
|
new_class = COMPONENT_GLYPH;
|
|
break;
|
|
|
|
default:
|
|
return TT_Err_Invalid_Argument;
|
|
}
|
|
|
|
gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord;
|
|
ngc = gdef->NewGlyphClasses;
|
|
|
|
if ( glyphID < gcrr[index].Start )
|
|
{
|
|
array_index = 0;
|
|
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 + 1];
|
|
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 + 1] &= mask;
|
|
ngc[array_index][glyph_index / 4 + 1] |= bits;
|
|
}
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
FT_Error Check_Property( TTO_GDEFHeader* gdef,
|
|
FT_UShort index,
|
|
FT_UShort flags,
|
|
FT_UShort* property )
|
|
{
|
|
FT_Error error;
|
|
|
|
|
|
if ( gdef )
|
|
{
|
|
error = TT_GDEF_Get_Glyph_Property( gdef, index, property );
|
|
if ( error )
|
|
return error;
|
|
|
|
/* This is OpenType 1.2 */
|
|
|
|
if ( flags & IGNORE_SPECIAL_MARKS )
|
|
if ( (flags & 0xFF00) != *property )
|
|
return TTO_Err_Not_Covered;
|
|
|
|
if ( flags & *property )
|
|
return TTO_Err_Not_Covered;
|
|
}
|
|
|
|
return TT_Err_Ok;
|
|
}
|
|
|
|
|
|
/* END */
|