blob: 8143eacec8a2aabdc8427f4c93c60bc17f3d1d4a [file] [log] [blame]
/*******************************************************************
*
* Copyright 1996-2000 by
* David Turner, Robert Wilhelm, and Werner Lemberg.
*
* Copyright 2006 Behdad Esfahbod
*
* This is part of HarfBuzz, an OpenType Layout engine library.
*
* See the file name COPYING for licensing information.
*
******************************************************************/
#include "harfbuzz-impl.h"
#include "harfbuzz-gpos-private.h"
#include "harfbuzz-open-private.h"
#include "harfbuzz-gdef-private.h"
#include FT_TRUETYPE_TAGS_H
struct GPOS_Instance_
{
HB_GPOSHeader* gpos;
FT_Face face;
FT_Bool dvi;
FT_UShort load_flags; /* how the glyph should be loaded */
FT_Bool r2l;
FT_UShort last; /* the last valid glyph -- used
with cursive positioning */
FT_Pos anchor_x; /* the coordinates of the anchor point */
FT_Pos anchor_y; /* of the last valid glyph */
};
typedef struct GPOS_Instance_ GPOS_Instance;
static FT_Error GPOS_Do_Glyph_Lookup( GPOS_Instance* gpi,
FT_UShort lookup_index,
HB_Buffer buffer,
FT_UShort context_length,
int nesting_level );
/* the client application must replace this with something more
meaningful if multiple master fonts are to be supported. */
static FT_Error default_mmfunc( FT_Face face,
FT_UShort metric_id,
FT_Pos* metric_value,
void* data )
{
return HB_Err_No_MM_Interpreter;
}
FT_Error HB_Load_GPOS_Table( FT_Face face,
HB_GPOSHeader** retptr,
HB_GDEFHeader* gdef )
{
FT_ULong cur_offset, new_offset, base_offset;
FT_UShort i, num_lookups;
HB_GPOSHeader* gpos;
HB_Lookup* lo;
FT_Stream stream = face->stream;
FT_Error error;
FT_Memory memory = face->memory;
if ( !retptr )
return FT_Err_Invalid_Argument;
if ( !stream )
return FT_Err_Invalid_Face_Handle;
if (( error = _hb_ftglue_face_goto_table( face, TTAG_GPOS, stream ) ))
return error;
base_offset = FILE_Pos();
if ( ALLOC ( gpos, sizeof( *gpos ) ) )
return error;
gpos->memory = memory;
gpos->gfunc = FT_Load_Glyph;
gpos->mmfunc = default_mmfunc;
/* skip version */
if ( FILE_Seek( base_offset + 4L ) ||
ACCESS_Frame( 2L ) )
goto Fail4;
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_ScriptList( &gpos->ScriptList,
stream ) ) != FT_Err_Ok )
goto Fail4;
(void)FILE_Seek( cur_offset );
if ( ACCESS_Frame( 2L ) )
goto Fail3;
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_FeatureList( &gpos->FeatureList,
stream ) ) != FT_Err_Ok )
goto Fail3;
(void)FILE_Seek( cur_offset );
if ( ACCESS_Frame( 2L ) )
goto Fail2;
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_LookupList( &gpos->LookupList,
stream, HB_Type_GPOS ) ) != FT_Err_Ok )
goto Fail2;
gpos->gdef = gdef; /* can be NULL */
/* 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 )
{
lo = gpos->LookupList.Lookup;
num_lookups = gpos->LookupList.LookupCount;
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 ) ) != FT_Err_Ok )
goto Fail1;
break;
}
}
}
*retptr = gpos;
return FT_Err_Ok;
Fail1:
_HB_OPEN_Free_LookupList( &gpos->LookupList, HB_Type_GPOS, memory );
Fail2:
_HB_OPEN_Free_FeatureList( &gpos->FeatureList, memory );
Fail3:
_HB_OPEN_Free_ScriptList( &gpos->ScriptList, memory );
Fail4:
FREE( gpos );
return error;
}
FT_Error HB_Done_GPOS_Table( HB_GPOSHeader* gpos )
{
FT_Memory memory = gpos->memory;
_HB_OPEN_Free_LookupList( &gpos->LookupList, HB_Type_GPOS, memory );
_HB_OPEN_Free_FeatureList( &gpos->FeatureList, memory );
_HB_OPEN_Free_ScriptList( &gpos->ScriptList, memory );
return FT_Err_Ok;
}
/*****************************
* SubTable related functions
*****************************/
/* shared tables */
/* ValueRecord */
/* There is a subtle difference in the specs between a `table' and a
`record' -- offsets for device tables in ValueRecords are taken from
the parent table and not the parent record. */
static FT_Error Load_ValueRecord( HB_ValueRecord* vr,
FT_UShort format,
FT_ULong base_offset,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_ULong cur_offset, new_offset;
if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT )
{
if ( ACCESS_Frame( 2L ) )
return error;
vr->XPlacement = GET_Short();
FORGET_Frame();
}
else
vr->XPlacement = 0;
if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT )
{
if ( ACCESS_Frame( 2L ) )
return error;
vr->YPlacement = GET_Short();
FORGET_Frame();
}
else
vr->YPlacement = 0;
if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE )
{
if ( ACCESS_Frame( 2L ) )
return error;
vr->XAdvance = GET_Short();
FORGET_Frame();
}
else
vr->XAdvance = 0;
if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE )
{
if ( ACCESS_Frame( 2L ) )
return error;
vr->YAdvance = GET_Short();
FORGET_Frame();
}
else
vr->YAdvance = 0;
if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
{
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 = _HB_OPEN_Load_Device( &vr->XPlacementDevice,
stream ) ) != FT_Err_Ok )
return error;
(void)FILE_Seek( cur_offset );
}
else
goto empty1;
}
else
{
empty1:
vr->XPlacementDevice.StartSize = 0;
vr->XPlacementDevice.EndSize = 0;
vr->XPlacementDevice.DeltaValue = NULL;
}
if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
{
if ( ACCESS_Frame( 2L ) )
goto Fail3;
new_offset = GET_UShort();
FORGET_Frame();
if ( new_offset )
{
new_offset += base_offset;
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Device( &vr->YPlacementDevice,
stream ) ) != FT_Err_Ok )
goto Fail3;
(void)FILE_Seek( cur_offset );
}
else
goto empty2;
}
else
{
empty2:
vr->YPlacementDevice.StartSize = 0;
vr->YPlacementDevice.EndSize = 0;
vr->YPlacementDevice.DeltaValue = NULL;
}
if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
{
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 = _HB_OPEN_Load_Device( &vr->XAdvanceDevice,
stream ) ) != FT_Err_Ok )
goto Fail2;
(void)FILE_Seek( cur_offset );
}
else
goto empty3;
}
else
{
empty3:
vr->XAdvanceDevice.StartSize = 0;
vr->XAdvanceDevice.EndSize = 0;
vr->XAdvanceDevice.DeltaValue = NULL;
}
if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
{
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 = _HB_OPEN_Load_Device( &vr->YAdvanceDevice,
stream ) ) != FT_Err_Ok )
goto Fail1;
(void)FILE_Seek( cur_offset );
}
else
goto empty4;
}
else
{
empty4:
vr->YAdvanceDevice.StartSize = 0;
vr->YAdvanceDevice.EndSize = 0;
vr->YAdvanceDevice.DeltaValue = NULL;
}
if ( format & HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT )
{
if ( ACCESS_Frame( 2L ) )
goto Fail1;
vr->XIdPlacement = GET_UShort();
FORGET_Frame();
}
else
vr->XIdPlacement = 0;
if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT )
{
if ( ACCESS_Frame( 2L ) )
goto Fail1;
vr->YIdPlacement = GET_UShort();
FORGET_Frame();
}
else
vr->YIdPlacement = 0;
if ( format & HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE )
{
if ( ACCESS_Frame( 2L ) )
goto Fail1;
vr->XIdAdvance = GET_UShort();
FORGET_Frame();
}
else
vr->XIdAdvance = 0;
if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE )
{
if ( ACCESS_Frame( 2L ) )
goto Fail1;
vr->YIdAdvance = GET_UShort();
FORGET_Frame();
}
else
vr->YIdAdvance = 0;
return FT_Err_Ok;
Fail1:
_HB_OPEN_Free_Device( &vr->YAdvanceDevice, memory );
Fail2:
_HB_OPEN_Free_Device( &vr->XAdvanceDevice, memory );
Fail3:
_HB_OPEN_Free_Device( &vr->YPlacementDevice, memory );
return error;
}
static void Free_ValueRecord( HB_ValueRecord* vr,
FT_UShort format,
FT_Memory memory )
{
if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
_HB_OPEN_Free_Device( &vr->YAdvanceDevice, memory );
if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
_HB_OPEN_Free_Device( &vr->XAdvanceDevice, memory );
if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
_HB_OPEN_Free_Device( &vr->YPlacementDevice, memory );
if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
_HB_OPEN_Free_Device( &vr->XPlacementDevice, memory );
}
static FT_Error Get_ValueRecord( GPOS_Instance* gpi,
HB_ValueRecord* vr,
FT_UShort format,
HB_Position gd )
{
FT_Pos value;
FT_Short pixel_value;
FT_Error error = FT_Err_Ok;
HB_GPOSHeader* gpos = gpi->gpos;
FT_UShort x_ppem, y_ppem;
FT_Fixed x_scale, y_scale;
if ( !format )
return FT_Err_Ok;
x_ppem = gpi->face->size->metrics.x_ppem;
y_ppem = gpi->face->size->metrics.y_ppem;
x_scale = gpi->face->size->metrics.x_scale;
y_scale = gpi->face->size->metrics.y_scale;
/* design units -> fractional pixel */
if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT )
gd->x_pos += x_scale * vr->XPlacement / 0x10000;
if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT )
gd->y_pos += y_scale * vr->YPlacement / 0x10000;
if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE )
gd->x_advance += x_scale * vr->XAdvance / 0x10000;
if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE )
gd->y_advance += y_scale * vr->YAdvance / 0x10000;
if ( !gpi->dvi )
{
/* pixel -> fractional pixel */
if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
{
_HB_OPEN_Get_Device( &vr->XPlacementDevice, x_ppem, &pixel_value );
gd->x_pos += pixel_value << 6;
}
if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
{
_HB_OPEN_Get_Device( &vr->YPlacementDevice, y_ppem, &pixel_value );
gd->y_pos += pixel_value << 6;
}
if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
{
_HB_OPEN_Get_Device( &vr->XAdvanceDevice, x_ppem, &pixel_value );
gd->x_advance += pixel_value << 6;
}
if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
{
_HB_OPEN_Get_Device( &vr->YAdvanceDevice, y_ppem, &pixel_value );
gd->y_advance += pixel_value << 6;
}
}
/* values returned from mmfunc() are already in fractional pixels */
if ( format & HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT )
{
error = (gpos->mmfunc)( gpi->face, vr->XIdPlacement,
&value, gpos->data );
if ( error )
return error;
gd->x_pos += value;
}
if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT )
{
error = (gpos->mmfunc)( gpi->face, vr->YIdPlacement,
&value, gpos->data );
if ( error )
return error;
gd->y_pos += value;
}
if ( format & HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE )
{
error = (gpos->mmfunc)( gpi->face, vr->XIdAdvance,
&value, gpos->data );
if ( error )
return error;
gd->x_advance += value;
}
if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE )
{
error = (gpos->mmfunc)( gpi->face, vr->YIdAdvance,
&value, gpos->data );
if ( error )
return error;
gd->y_advance += value;
}
return error;
}
/* AnchorFormat1 */
/* AnchorFormat2 */
/* AnchorFormat3 */
/* AnchorFormat4 */
static FT_Error Load_Anchor( HB_Anchor* an,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_ULong cur_offset, new_offset, base_offset;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 2L ) )
return error;
an->PosFormat = GET_UShort();
FORGET_Frame();
switch ( an->PosFormat )
{
case 1:
if ( ACCESS_Frame( 4L ) )
return error;
an->af.af1.XCoordinate = GET_Short();
an->af.af1.YCoordinate = GET_Short();
FORGET_Frame();
break;
case 2:
if ( ACCESS_Frame( 6L ) )
return error;
an->af.af2.XCoordinate = GET_Short();
an->af.af2.YCoordinate = GET_Short();
an->af.af2.AnchorPoint = GET_UShort();
FORGET_Frame();
break;
case 3:
if ( ACCESS_Frame( 6L ) )
return error;
an->af.af3.XCoordinate = GET_Short();
an->af.af3.YCoordinate = GET_Short();
new_offset = GET_UShort();
FORGET_Frame();
if ( new_offset )
{
new_offset += base_offset;
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Device( &an->af.af3.XDeviceTable,
stream ) ) != FT_Err_Ok )
return error;
(void)FILE_Seek( cur_offset );
}
else
{
an->af.af3.XDeviceTable.StartSize = 0;
an->af.af3.XDeviceTable.EndSize = 0;
an->af.af3.XDeviceTable.DeltaValue = NULL;
}
if ( ACCESS_Frame( 2L ) )
goto Fail;
new_offset = GET_UShort();
FORGET_Frame();
if ( new_offset )
{
new_offset += base_offset;
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Device( &an->af.af3.YDeviceTable,
stream ) ) != FT_Err_Ok )
goto Fail;
(void)FILE_Seek( cur_offset );
}
else
{
an->af.af3.YDeviceTable.StartSize = 0;
an->af.af3.YDeviceTable.EndSize = 0;
an->af.af3.YDeviceTable.DeltaValue = NULL;
}
break;
case 4:
if ( ACCESS_Frame( 4L ) )
return error;
an->af.af4.XIdAnchor = GET_UShort();
an->af.af4.YIdAnchor = GET_UShort();
FORGET_Frame();
break;
default:
return HB_Err_Invalid_GPOS_SubTable_Format;
}
return FT_Err_Ok;
Fail:
_HB_OPEN_Free_Device( &an->af.af3.XDeviceTable, memory );
return error;
}
static void Free_Anchor( HB_Anchor* an,
FT_Memory memory)
{
if ( an->PosFormat == 3 )
{
_HB_OPEN_Free_Device( &an->af.af3.YDeviceTable, memory );
_HB_OPEN_Free_Device( &an->af.af3.XDeviceTable, memory );
}
}
static FT_Error Get_Anchor( GPOS_Instance* gpi,
HB_Anchor* an,
FT_UShort glyph_index,
FT_Pos* x_value,
FT_Pos* y_value )
{
FT_Error error = FT_Err_Ok;
FT_Outline outline;
HB_GPOSHeader* gpos = gpi->gpos;
FT_UShort ap;
FT_Short pixel_value;
FT_UShort load_flags;
FT_UShort x_ppem, y_ppem;
FT_Fixed x_scale, y_scale;
x_ppem = gpi->face->size->metrics.x_ppem;
y_ppem = gpi->face->size->metrics.y_ppem;
x_scale = gpi->face->size->metrics.x_scale;
y_scale = gpi->face->size->metrics.y_scale;
switch ( an->PosFormat )
{
case 0:
/* The special case of an empty AnchorTable */
return HB_Err_Not_Covered;
case 1:
*x_value = x_scale * an->af.af1.XCoordinate / 0x10000;
*y_value = y_scale * an->af.af1.YCoordinate / 0x10000;
break;
case 2:
/* glyphs must be scaled */
load_flags = gpi->load_flags & ~FT_LOAD_NO_SCALE;
if ( !gpi->dvi )
{
error = (gpos->gfunc)( gpi->face, glyph_index, load_flags );
if ( error )
return error;
if ( gpi->face->glyph->format != ft_glyph_format_outline )
return HB_Err_Invalid_GPOS_SubTable;
ap = an->af.af2.AnchorPoint;
outline = gpi->face->glyph->outline;
/* if outline.n_points is set to zero by gfunc(), we use the
design coordinate value pair. This can happen e.g. for
sbit glyphs */
if ( !outline.n_points )
goto no_contour_point;
if ( ap >= outline.n_points )
return HB_Err_Invalid_GPOS_SubTable;
*x_value = outline.points[ap].x;
*y_value = outline.points[ap].y;
}
else
{
no_contour_point:
*x_value = x_scale * an->af.af3.XCoordinate / 0x10000;
*y_value = y_scale * an->af.af3.YCoordinate / 0x10000;
}
break;
case 3:
if ( !gpi->dvi )
{
_HB_OPEN_Get_Device( &an->af.af3.XDeviceTable, x_ppem, &pixel_value );
*x_value = pixel_value << 6;
_HB_OPEN_Get_Device( &an->af.af3.YDeviceTable, y_ppem, &pixel_value );
*y_value = pixel_value << 6;
}
else
*x_value = *y_value = 0;
*x_value += x_scale * an->af.af3.XCoordinate / 0x10000;
*y_value += y_scale * an->af.af3.YCoordinate / 0x10000;
break;
case 4:
error = (gpos->mmfunc)( gpi->face, an->af.af4.XIdAnchor,
x_value, gpos->data );
if ( error )
return error;
error = (gpos->mmfunc)( gpi->face, an->af.af4.YIdAnchor,
y_value, gpos->data );
if ( error )
return error;
break;
}
return error;
}
/* MarkArray */
static FT_Error Load_MarkArray ( HB_MarkArray* ma,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, m, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_MarkRecord* mr;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 2L ) )
return error;
count = ma->MarkCount = GET_UShort();
FORGET_Frame();
ma->MarkRecord = NULL;
if ( ALLOC_ARRAY( ma->MarkRecord, count, HB_MarkRecord ) )
return error;
mr = ma->MarkRecord;
for ( n = 0; n < count; n++ )
{
if ( ACCESS_Frame( 4L ) )
goto Fail;
mr[n].Class = GET_UShort();
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = Load_Anchor( &mr[n].MarkAnchor, stream ) ) != FT_Err_Ok )
goto Fail;
(void)FILE_Seek( cur_offset );
}
return FT_Err_Ok;
Fail:
for ( m = 0; m < n; m++ )
Free_Anchor( &mr[m].MarkAnchor, memory );
FREE( mr );
return error;
}
static void Free_MarkArray( HB_MarkArray* ma,
FT_Memory memory )
{
FT_UShort n, count;
HB_MarkRecord* mr;
if ( ma->MarkRecord )
{
count = ma->MarkCount;
mr = ma->MarkRecord;
for ( n = 0; n < count; n++ )
Free_Anchor( &mr[n].MarkAnchor, memory );
FREE( mr );
}
}
/* LookupType 1 */
/* SinglePosFormat1 */
/* SinglePosFormat2 */
static FT_Error Load_SinglePos( HB_GPOS_SubTable* st,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
HB_SinglePos* sp = &st->single;
FT_UShort n, m, count, format;
FT_ULong cur_offset, new_offset, base_offset;
HB_ValueRecord* vr;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 6L ) )
return error;
sp->PosFormat = GET_UShort();
new_offset = GET_UShort() + base_offset;
format = sp->ValueFormat = GET_UShort();
FORGET_Frame();
if ( !format )
return HB_Err_Invalid_GPOS_SubTable;
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Coverage( &sp->Coverage, stream ) ) != FT_Err_Ok )
return error;
(void)FILE_Seek( cur_offset );
switch ( sp->PosFormat )
{
case 1:
error = Load_ValueRecord( &sp->spf.spf1.Value, format,
base_offset, stream );
if ( error )
goto Fail2;
break;
case 2:
if ( ACCESS_Frame( 2L ) )
goto Fail2;
count = sp->spf.spf2.ValueCount = GET_UShort();
FORGET_Frame();
sp->spf.spf2.Value = NULL;
if ( ALLOC_ARRAY( sp->spf.spf2.Value, count, HB_ValueRecord ) )
goto Fail2;
vr = sp->spf.spf2.Value;
for ( n = 0; n < count; n++ )
{
error = Load_ValueRecord( &vr[n], format, base_offset, stream );
if ( error )
goto Fail1;
}
break;
default:
return HB_Err_Invalid_GPOS_SubTable_Format;
}
return FT_Err_Ok;
Fail1:
for ( m = 0; m < n; m++ )
Free_ValueRecord( &vr[m], format, memory );
FREE( vr );
Fail2:
_HB_OPEN_Free_Coverage( &sp->Coverage, memory );
return error;
}
static void Free_SinglePos( HB_GPOS_SubTable* st,
FT_Memory memory )
{
FT_UShort n, count, format;
HB_SinglePos* sp = &st->single;
HB_ValueRecord* v;
format = sp->ValueFormat;
switch ( sp->PosFormat )
{
case 1:
Free_ValueRecord( &sp->spf.spf1.Value, format, memory );
break;
case 2:
if ( sp->spf.spf2.Value )
{
count = sp->spf.spf2.ValueCount;
v = sp->spf.spf2.Value;
for ( n = 0; n < count; n++ )
Free_ValueRecord( &v[n], format, memory );
FREE( v );
}
break;
}
_HB_OPEN_Free_Coverage( &sp->Coverage, memory );
}
static FT_Error Lookup_DefaultPos( GPOS_Instance* gpi,
HB_GPOS_SubTable* st,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
return HB_Err_Not_Covered;
}
static FT_Error Lookup_SinglePos( GPOS_Instance* gpi,
HB_GPOS_SubTable* st,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
FT_UShort index, property;
FT_Error error;
HB_GPOSHeader* gpos = gpi->gpos;
HB_SinglePos* sp = &st->single;
if ( context_length != 0xFFFF && context_length < 1 )
return HB_Err_Not_Covered;
if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
return error;
error = _HB_OPEN_Coverage_Index( &sp->Coverage, IN_CURGLYPH(), &index );
if ( error )
return error;
switch ( sp->PosFormat )
{
case 1:
error = Get_ValueRecord( gpi, &sp->spf.spf1.Value,
sp->ValueFormat, POSITION( buffer->in_pos ) );
if ( error )
return error;
break;
case 2:
if ( index >= sp->spf.spf2.ValueCount )
return HB_Err_Invalid_GPOS_SubTable;
error = Get_ValueRecord( gpi, &sp->spf.spf2.Value[index],
sp->ValueFormat, POSITION( buffer->in_pos ) );
if ( error )
return error;
break;
default:
return HB_Err_Invalid_GPOS_SubTable;
}
(buffer->in_pos)++;
return FT_Err_Ok;
}
/* LookupType 2 */
/* PairSet */
static FT_Error Load_PairSet ( HB_PairSet* ps,
FT_UShort format1,
FT_UShort format2,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, m, count;
FT_ULong base_offset;
HB_PairValueRecord* pvr;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 2L ) )
return error;
count = ps->PairValueCount = GET_UShort();
FORGET_Frame();
ps->PairValueRecord = NULL;
if ( ALLOC_ARRAY( ps->PairValueRecord, count, HB_PairValueRecord ) )
return error;
pvr = ps->PairValueRecord;
for ( n = 0; n < count; n++ )
{
if ( ACCESS_Frame( 2L ) )
goto Fail;
pvr[n].SecondGlyph = GET_UShort();
FORGET_Frame();
if ( format1 )
{
error = Load_ValueRecord( &pvr[n].Value1, format1,
base_offset, stream );
if ( error )
goto Fail;
}
if ( format2 )
{
error = Load_ValueRecord( &pvr[n].Value2, format2,
base_offset, stream );
if ( error )
{
if ( format1 )
Free_ValueRecord( &pvr[n].Value1, format1, memory );
goto Fail;
}
}
}
return FT_Err_Ok;
Fail:
for ( m = 0; m < n; m++ )
{
if ( format1 )
Free_ValueRecord( &pvr[m].Value1, format1, memory );
if ( format2 )
Free_ValueRecord( &pvr[m].Value2, format2, memory );
}
FREE( pvr );
return error;
}
static void Free_PairSet( HB_PairSet* ps,
FT_UShort format1,
FT_UShort format2,
FT_Memory memory )
{
FT_UShort n, count;
HB_PairValueRecord* pvr;
if ( ps->PairValueRecord )
{
count = ps->PairValueCount;
pvr = ps->PairValueRecord;
for ( n = 0; n < count; n++ )
{
if ( format1 )
Free_ValueRecord( &pvr[n].Value1, format1, memory );
if ( format2 )
Free_ValueRecord( &pvr[n].Value2, format2, memory );
}
FREE( pvr );
}
}
/* PairPosFormat1 */
static FT_Error Load_PairPos1( HB_PairPosFormat1* ppf1,
FT_UShort format1,
FT_UShort format2,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, m, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_PairSet* ps;
base_offset = FILE_Pos() - 8L;
if ( ACCESS_Frame( 2L ) )
return error;
count = ppf1->PairSetCount = GET_UShort();
FORGET_Frame();
ppf1->PairSet = NULL;
if ( ALLOC_ARRAY( ppf1->PairSet, count, HB_PairSet ) )
return error;
ps = ppf1->PairSet;
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_PairSet( &ps[n], format1,
format2, stream ) ) != FT_Err_Ok )
goto Fail;
(void)FILE_Seek( cur_offset );
}
return FT_Err_Ok;
Fail:
for ( m = 0; m < n; m++ )
Free_PairSet( &ps[m], format1, format2, memory );
FREE( ps );
return error;
}
static void Free_PairPos1( HB_PairPosFormat1* ppf1,
FT_UShort format1,
FT_UShort format2,
FT_Memory memory )
{
FT_UShort n, count;
HB_PairSet* ps;
if ( ppf1->PairSet )
{
count = ppf1->PairSetCount;
ps = ppf1->PairSet;
for ( n = 0; n < count; n++ )
Free_PairSet( &ps[n], format1, format2, memory );
FREE( ps );
}
}
/* PairPosFormat2 */
static FT_Error Load_PairPos2( HB_PairPosFormat2* ppf2,
FT_UShort format1,
FT_UShort format2,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort m, n, k, count1, count2;
FT_ULong cur_offset, new_offset1, new_offset2, base_offset;
HB_Class1Record* c1r;
HB_Class2Record* c2r;
base_offset = FILE_Pos() - 8L;
if ( ACCESS_Frame( 8L ) )
return error;
new_offset1 = GET_UShort() + base_offset;
new_offset2 = GET_UShort() + base_offset;
/* `Class1Count' and `Class2Count' are the upper limits for class
values, thus we read it now to make additional safety checks. */
count1 = ppf2->Class1Count = GET_UShort();
count2 = ppf2->Class2Count = GET_UShort();
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset1 ) ||
( error = _HB_OPEN_Load_ClassDefinition( &ppf2->ClassDef1, count1,
stream ) ) != FT_Err_Ok )
return error;
if ( FILE_Seek( new_offset2 ) ||
( error = _HB_OPEN_Load_ClassDefinition( &ppf2->ClassDef2, count2,
stream ) ) != FT_Err_Ok )
goto Fail3;
(void)FILE_Seek( cur_offset );
ppf2->Class1Record = NULL;
if ( ALLOC_ARRAY( ppf2->Class1Record, count1, HB_Class1Record ) )
goto Fail2;
c1r = ppf2->Class1Record;
for ( m = 0; m < count1; m++ )
{
c1r[m].Class2Record = NULL;
if ( ALLOC_ARRAY( c1r[m].Class2Record, count2, HB_Class2Record ) )
goto Fail1;
c2r = c1r[m].Class2Record;
for ( n = 0; n < count2; n++ )
{
if ( format1 )
{
error = Load_ValueRecord( &c2r[n].Value1, format1,
base_offset, stream );
if ( error )
goto Fail0;
}
if ( format2 )
{
error = Load_ValueRecord( &c2r[n].Value2, format2,
base_offset, stream );
if ( error )
{
if ( format1 )
Free_ValueRecord( &c2r[n].Value1, format1, memory );
goto Fail0;
}
}
}
continue;
Fail0:
for ( k = 0; k < n; k++ )
{
if ( format1 )
Free_ValueRecord( &c2r[k].Value1, format1, memory );
if ( format2 )
Free_ValueRecord( &c2r[k].Value2, format2, memory );
}
goto Fail1;
}
return FT_Err_Ok;
Fail1:
for ( k = 0; k < m; k++ )
{
c2r = c1r[k].Class2Record;
for ( n = 0; n < count2; n++ )
{
if ( format1 )
Free_ValueRecord( &c2r[n].Value1, format1, memory );
if ( format2 )
Free_ValueRecord( &c2r[n].Value2, format2, memory );
}
FREE( c2r );
}
FREE( c1r );
Fail2:
_HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef2, memory );
Fail3:
_HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef1, memory );
return error;
}
static void Free_PairPos2( HB_PairPosFormat2* ppf2,
FT_UShort format1,
FT_UShort format2,
FT_Memory memory )
{
FT_UShort m, n, count1, count2;
HB_Class1Record* c1r;
HB_Class2Record* c2r;
if ( ppf2->Class1Record )
{
c1r = ppf2->Class1Record;
count1 = ppf2->Class1Count;
count2 = ppf2->Class2Count;
for ( m = 0; m < count1; m++ )
{
c2r = c1r[m].Class2Record;
for ( n = 0; n < count2; n++ )
{
if ( format1 )
Free_ValueRecord( &c2r[n].Value1, format1, memory );
if ( format2 )
Free_ValueRecord( &c2r[n].Value2, format2, memory );
}
FREE( c2r );
}
FREE( c1r );
_HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef2, memory );
_HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef1, memory );
}
}
static FT_Error Load_PairPos( HB_GPOS_SubTable* st,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
HB_PairPos* pp = &st->pair;
FT_UShort format1, format2;
FT_ULong cur_offset, new_offset, base_offset;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 8L ) )
return error;
pp->PosFormat = GET_UShort();
new_offset = GET_UShort() + base_offset;
format1 = pp->ValueFormat1 = GET_UShort();
format2 = pp->ValueFormat2 = GET_UShort();
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Coverage( &pp->Coverage, stream ) ) != FT_Err_Ok )
return error;
(void)FILE_Seek( cur_offset );
switch ( pp->PosFormat )
{
case 1:
error = Load_PairPos1( &pp->ppf.ppf1, format1, format2, stream );
if ( error )
goto Fail;
break;
case 2:
error = Load_PairPos2( &pp->ppf.ppf2, format1, format2, stream );
if ( error )
goto Fail;
break;
default:
return HB_Err_Invalid_GPOS_SubTable_Format;
}
return FT_Err_Ok;
Fail:
_HB_OPEN_Free_Coverage( &pp->Coverage, memory );
return error;
}
static void Free_PairPos( HB_GPOS_SubTable* st,
FT_Memory memory )
{
FT_UShort format1, format2;
HB_PairPos* pp = &st->pair;
format1 = pp->ValueFormat1;
format2 = pp->ValueFormat2;
switch ( pp->PosFormat )
{
case 1:
Free_PairPos1( &pp->ppf.ppf1, format1, format2, memory );
break;
case 2:
Free_PairPos2( &pp->ppf.ppf2, format1, format2, memory );
break;
}
_HB_OPEN_Free_Coverage( &pp->Coverage, memory );
}
static FT_Error Lookup_PairPos1( GPOS_Instance* gpi,
HB_PairPosFormat1* ppf1,
HB_Buffer buffer,
FT_UShort first_pos,
FT_UShort index,
FT_UShort format1,
FT_UShort format2 )
{
FT_Error error;
FT_UShort numpvr, glyph2;
HB_PairValueRecord* pvr;
if ( index >= ppf1->PairSetCount )
return HB_Err_Invalid_GPOS_SubTable;
pvr = ppf1->PairSet[index].PairValueRecord;
if ( !pvr )
return HB_Err_Invalid_GPOS_SubTable;
glyph2 = IN_CURGLYPH();
for ( numpvr = ppf1->PairSet[index].PairValueCount;
numpvr;
numpvr--, pvr++ )
{
if ( glyph2 == pvr->SecondGlyph )
{
error = Get_ValueRecord( gpi, &pvr->Value1, format1,
POSITION( first_pos ) );
if ( error )
return error;
return Get_ValueRecord( gpi, &pvr->Value2, format2,
POSITION( buffer->in_pos ) );
}
}
return HB_Err_Not_Covered;
}
static FT_Error Lookup_PairPos2( GPOS_Instance* gpi,
HB_PairPosFormat2* ppf2,
HB_Buffer buffer,
FT_UShort first_pos,
FT_UShort format1,
FT_UShort format2 )
{
FT_Error error;
FT_UShort cl1, cl2;
HB_Class1Record* c1r;
HB_Class2Record* c2r;
error = _HB_OPEN_Get_Class( &ppf2->ClassDef1, IN_GLYPH( first_pos ),
&cl1, NULL );
if ( error && error != HB_Err_Not_Covered )
return error;
error = _HB_OPEN_Get_Class( &ppf2->ClassDef2, IN_CURGLYPH(),
&cl2, NULL );
if ( error && error != HB_Err_Not_Covered )
return error;
c1r = &ppf2->Class1Record[cl1];
if ( !c1r )
return HB_Err_Invalid_GPOS_SubTable;
c2r = &c1r->Class2Record[cl2];
error = Get_ValueRecord( gpi, &c2r->Value1, format1, POSITION( first_pos ) );
if ( error )
return error;
return Get_ValueRecord( gpi, &c2r->Value2, format2, POSITION( buffer->in_pos ) );
}
static FT_Error Lookup_PairPos( GPOS_Instance* gpi,
HB_GPOS_SubTable* st,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
FT_Error error;
FT_UShort index, property, first_pos;
HB_GPOSHeader* gpos = gpi->gpos;
HB_PairPos* pp = &st->pair;
if ( buffer->in_pos >= buffer->in_length - 1 )
return HB_Err_Not_Covered; /* Not enough glyphs in stream */
if ( context_length != 0xFFFF && context_length < 2 )
return HB_Err_Not_Covered;
if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
return error;
error = _HB_OPEN_Coverage_Index( &pp->Coverage, IN_CURGLYPH(), &index );
if ( error )
return error;
/* second glyph */
first_pos = buffer->in_pos;
(buffer->in_pos)++;
while ( CHECK_Property( gpos->gdef, IN_CURITEM(),
flags, &property ) )
{
if ( error && error != HB_Err_Not_Covered )
return error;
if ( buffer->in_pos == buffer->in_length )
return HB_Err_Not_Covered;
(buffer->in_pos)++;
}
switch ( pp->PosFormat )
{
case 1:
error = Lookup_PairPos1( gpi, &pp->ppf.ppf1, buffer,
first_pos, index,
pp->ValueFormat1, pp->ValueFormat2 );
break;
case 2:
error = Lookup_PairPos2( gpi, &pp->ppf.ppf2, buffer, first_pos,
pp->ValueFormat1, pp->ValueFormat2 );
break;
default:
return HB_Err_Invalid_GPOS_SubTable_Format;
}
/* adjusting the `next' glyph */
if ( pp->ValueFormat2 )
(buffer->in_pos)++;
return error;
}
/* LookupType 3 */
/* CursivePosFormat1 */
static FT_Error Load_CursivePos( HB_GPOS_SubTable* st,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
HB_CursivePos* cp = &st->cursive;
FT_UShort n, m, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_EntryExitRecord* eer;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 4L ) )
return error;
cp->PosFormat = GET_UShort();
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Coverage( &cp->Coverage, stream ) ) != FT_Err_Ok )
return error;
(void)FILE_Seek( cur_offset );
if ( ACCESS_Frame( 2L ) )
goto Fail2;
count = cp->EntryExitCount = GET_UShort();
FORGET_Frame();
cp->EntryExitRecord = NULL;
if ( ALLOC_ARRAY( cp->EntryExitRecord, count, HB_EntryExitRecord ) )
goto Fail2;
eer = cp->EntryExitRecord;
for ( n = 0; n < count; n++ )
{
FT_ULong entry_offset;
if ( ACCESS_Frame( 2L ) )
return error;
entry_offset = new_offset = GET_UShort();
FORGET_Frame();
if ( new_offset )
{
new_offset += base_offset;
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = Load_Anchor( &eer[n].EntryAnchor,
stream ) ) != FT_Err_Ok )
goto Fail1;
(void)FILE_Seek( cur_offset );
}
else
eer[n].EntryAnchor.PosFormat = 0;
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_Anchor( &eer[n].ExitAnchor,
stream ) ) != FT_Err_Ok )
{
if ( entry_offset )
Free_Anchor( &eer[n].EntryAnchor, memory );
goto Fail1;
}
(void)FILE_Seek( cur_offset );
}
else
eer[n].ExitAnchor.PosFormat = 0;
}
return FT_Err_Ok;
Fail1:
for ( m = 0; m < n; m++ )
{
Free_Anchor( &eer[m].EntryAnchor, memory );
Free_Anchor( &eer[m].ExitAnchor, memory );
}
FREE( eer );
Fail2:
_HB_OPEN_Free_Coverage( &cp->Coverage, memory );
return error;
}
static void Free_CursivePos( HB_GPOS_SubTable* st,
FT_Memory memory )
{
FT_UShort n, count;
HB_CursivePos* cp = &st->cursive;
HB_EntryExitRecord* eer;
if ( cp->EntryExitRecord )
{
count = cp->EntryExitCount;
eer = cp->EntryExitRecord;
for ( n = 0; n < count; n++ )
{
Free_Anchor( &eer[n].EntryAnchor, memory );
Free_Anchor( &eer[n].ExitAnchor, memory );
}
FREE( eer );
}
_HB_OPEN_Free_Coverage( &cp->Coverage, memory );
}
static FT_Error Lookup_CursivePos( GPOS_Instance* gpi,
HB_GPOS_SubTable* st,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
FT_UShort index, property;
FT_Error error;
HB_GPOSHeader* gpos = gpi->gpos;
HB_CursivePos* cp = &st->cursive;
HB_EntryExitRecord* eer;
FT_Pos entry_x, entry_y;
FT_Pos exit_x, exit_y;
if ( context_length != 0xFFFF && context_length < 1 )
{
gpi->last = 0xFFFF;
return HB_Err_Not_Covered;
}
/* Glyphs not having the right GDEF properties will be ignored, i.e.,
gpi->last won't be reset (contrary to user defined properties). */
if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
return error;
/* We don't handle mark glyphs here. According to Andrei, this isn't
possible, but who knows... */
if ( property == HB_GDEF_MARK )
{
gpi->last = 0xFFFF;
return HB_Err_Not_Covered;
}
error = _HB_OPEN_Coverage_Index( &cp->Coverage, IN_CURGLYPH(), &index );
if ( error )
{
gpi->last = 0xFFFF;
return error;
}
if ( index >= cp->EntryExitCount )
return HB_Err_Invalid_GPOS_SubTable;
eer = &cp->EntryExitRecord[index];
/* Now comes the messiest part of the whole OpenType
specification. At first glance, cursive connections seem easy
to understand, but there are pitfalls! The reason is that
the specs don't mention how to compute the advance values
resp. glyph offsets. I was told it would be an omission, to
be fixed in the next OpenType version... Again many thanks to
Andrei Burago <andreib@microsoft.com> for clarifications.
Consider the following example:
| xadv1 |
+---------+
| |
+-----+--+ 1 |
| | .| |
| 0+--+------+
| 2 |
| |
0+--------+
| xadv2 |
glyph1: advance width = 12
anchor point = (3,1)
glyph2: advance width = 11
anchor point = (9,4)
LSB is 1 for both glyphs (so the boxes drawn above are glyph
bboxes). Writing direction is R2L; `0' denotes the glyph's
coordinate origin.
Now the surprising part: The advance width of the *left* glyph
(resp. of the *bottom* glyph) will be modified, no matter
whether the writing direction is L2R or R2L (resp. T2B or
B2T)! This assymetry is caused by the fact that the glyph's
coordinate origin is always the lower left corner for all
writing directions.
Continuing the above example, we can compute the new
(horizontal) advance width of glyph2 as
9 - 3 = 6 ,
and the new vertical offset of glyph2 as
1 - 4 = -3 .
Vertical writing direction is far more complicated:
a) Assuming that we recompute the advance height of the lower glyph:
--
+---------+
-- | |
+-----+--+ 1 | yadv1
| | .| |
yadv2 | 0+--+------+ -- BSB1 --
| 2 | -- -- y_offset
| |
BSB2 -- 0+--------+ --
-- --
glyph1: advance height = 6
anchor point = (3,1)
glyph2: advance height = 7
anchor point = (9,4)
TSB is 1 for both glyphs; writing direction is T2B.
BSB1 = yadv1 - (TSB1 + ymax1)
BSB2 = yadv2 - (TSB2 + ymax2)
y_offset = y2 - y1
vertical advance width of glyph2
= y_offset + BSB2 - BSB1
= (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
= y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
= y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
b) Assuming that we recompute the advance height of the upper glyph:
-- --
+---------+ -- TSB1
-- -- | |
TSB2 -- +-----+--+ 1 | yadv1 ymax1
| | .| |
yadv2 | 0+--+------+ -- --
ymax2 | 2 | -- y_offset
| |
-- 0+--------+ --
--
glyph1: advance height = 6
anchor point = (3,1)
glyph2: advance height = 7
anchor point = (9,4)
TSB is 1 for both glyphs; writing direction is T2B.
y_offset = y2 - y1
vertical advance width of glyph2
= TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
= TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
Comparing a) with b) shows that b) is easier to compute. I'll wait
for a reply from Andrei to see what should really be implemented...
Since horizontal advance widths or vertical advance heights
can be used alone but not together, no ambiguity occurs. */
if ( gpi->last == 0xFFFF )
goto end;
/* Get_Anchor() returns HB_Err_Not_Covered if there is no anchor
table. */
error = Get_Anchor( gpi, &eer->EntryAnchor, IN_CURGLYPH(),
&entry_x, &entry_y );
if ( error == HB_Err_Not_Covered )
goto end;
if ( error )
return error;
if ( gpi->r2l )
{
POSITION( buffer->in_pos )->x_advance = entry_x - gpi->anchor_x;
POSITION( buffer->in_pos )->new_advance = TRUE;
}
else
{
POSITION( gpi->last )->x_advance = gpi->anchor_x - entry_x;
POSITION( gpi->last )->new_advance = TRUE;
}
if ( flags & HB_LOOKUP_FLAG_RIGHT_TO_LEFT )
{
POSITION( gpi->last )->cursive_chain = gpi->last - buffer->in_pos;
POSITION( gpi->last )->y_pos = entry_y - gpi->anchor_y;
}
else
{
POSITION( buffer->in_pos )->cursive_chain = buffer->in_pos - gpi->last;
POSITION( buffer->in_pos )->y_pos = gpi->anchor_y - entry_y;
}
end:
error = Get_Anchor( gpi, &eer->ExitAnchor, IN_CURGLYPH(),
&exit_x, &exit_y );
if ( error == HB_Err_Not_Covered )
gpi->last = 0xFFFF;
else
{
gpi->last = buffer->in_pos;
gpi->anchor_x = exit_x;
gpi->anchor_y = exit_y;
}
if ( error )
return error;
(buffer->in_pos)++;
return FT_Err_Ok;
}
/* LookupType 4 */
/* BaseArray */
static FT_Error Load_BaseArray( HB_BaseArray* ba,
FT_UShort num_classes,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort m, n, k, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_BaseRecord* br;
HB_Anchor* ban;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 2L ) )
return error;
count = ba->BaseCount = GET_UShort();
FORGET_Frame();
ba->BaseRecord = NULL;
if ( ALLOC_ARRAY( ba->BaseRecord, count, HB_BaseRecord ) )
return error;
br = ba->BaseRecord;
for ( m = 0; m < count; m++ )
{
br[m].BaseAnchor = NULL;
if ( ALLOC_ARRAY( br[m].BaseAnchor, num_classes, HB_Anchor ) )
goto Fail;
ban = br[m].BaseAnchor;
for ( n = 0; n < num_classes; n++ )
{
if ( ACCESS_Frame( 2L ) )
goto Fail0;
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
if (new_offset == base_offset) {
/* Doulos SIL Regular is buggy and has zer offsets here. Skip */
ban[n].PosFormat = 0;
continue;
}
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = Load_Anchor( &ban[n], stream ) ) != FT_Err_Ok )
goto Fail0;
(void)FILE_Seek( cur_offset );
}
continue;
Fail0:
for ( k = 0; k < n; k++ )
Free_Anchor( &ban[k], memory );
goto Fail;
}
return FT_Err_Ok;
Fail:
for ( k = 0; k < m; k++ )
{
ban = br[k].BaseAnchor;
for ( n = 0; n < num_classes; n++ )
Free_Anchor( &ban[n], memory );
FREE( ban );
}
FREE( br );
return error;
}
static void Free_BaseArray( HB_BaseArray* ba,
FT_UShort num_classes,
FT_Memory memory )
{
FT_UShort m, n, count;
HB_BaseRecord* br;
HB_Anchor* ban;
if ( ba->BaseRecord )
{
count = ba->BaseCount;
br = ba->BaseRecord;
for ( m = 0; m < count; m++ )
{
ban = br[m].BaseAnchor;
for ( n = 0; n < num_classes; n++ )
Free_Anchor( &ban[n], memory );
FREE( ban );
}
FREE( br );
}
}
/* MarkBasePosFormat1 */
static FT_Error Load_MarkBasePos( HB_GPOS_SubTable* st,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
HB_MarkBasePos* mbp = &st->markbase;
FT_ULong cur_offset, new_offset, base_offset;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 4L ) )
return error;
mbp->PosFormat = GET_UShort();
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
if (mbp->PosFormat != 1)
return HB_Err_Invalid_SubTable_Format;
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Coverage( &mbp->MarkCoverage, stream ) ) != FT_Err_Ok )
return error;
(void)FILE_Seek( cur_offset );
if ( ACCESS_Frame( 2L ) )
goto Fail3;
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Coverage( &mbp->BaseCoverage, stream ) ) != FT_Err_Ok )
goto Fail3;
(void)FILE_Seek( cur_offset );
if ( ACCESS_Frame( 4L ) )
goto Fail2;
mbp->ClassCount = GET_UShort();
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = Load_MarkArray( &mbp->MarkArray, stream ) ) != FT_Err_Ok )
goto Fail2;
(void)FILE_Seek( cur_offset );
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_BaseArray( &mbp->BaseArray, mbp->ClassCount,
stream ) ) != FT_Err_Ok )
goto Fail1;
return FT_Err_Ok;
Fail1:
Free_MarkArray( &mbp->MarkArray, memory );
Fail2:
_HB_OPEN_Free_Coverage( &mbp->BaseCoverage, memory );
Fail3:
_HB_OPEN_Free_Coverage( &mbp->MarkCoverage, memory );
return error;
}
static void Free_MarkBasePos( HB_GPOS_SubTable* st,
FT_Memory memory )
{
HB_MarkBasePos* mbp = &st->markbase;
Free_BaseArray( &mbp->BaseArray, mbp->ClassCount, memory );
Free_MarkArray( &mbp->MarkArray, memory );
_HB_OPEN_Free_Coverage( &mbp->BaseCoverage, memory );
_HB_OPEN_Free_Coverage( &mbp->MarkCoverage, memory );
}
static FT_Error Lookup_MarkBasePos( GPOS_Instance* gpi,
HB_GPOS_SubTable* st,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
FT_UShort i, j, mark_index, base_index, property, class;
FT_Pos x_mark_value, y_mark_value, x_base_value, y_base_value;
FT_Error error;
HB_GPOSHeader* gpos = gpi->gpos;
HB_MarkBasePos* mbp = &st->markbase;
HB_MarkArray* ma;
HB_BaseArray* ba;
HB_BaseRecord* br;
HB_Anchor* mark_anchor;
HB_Anchor* base_anchor;
HB_Position o;
if ( context_length != 0xFFFF && context_length < 1 )
return HB_Err_Not_Covered;
if ( flags & HB_LOOKUP_FLAG_IGNORE_BASE_GLYPHS )
return HB_Err_Not_Covered;
if ( CHECK_Property( gpos->gdef, IN_CURITEM(),
flags, &property ) )
return error;
error = _HB_OPEN_Coverage_Index( &mbp->MarkCoverage, IN_CURGLYPH(),
&mark_index );
if ( error )
return error;
/* now we search backwards for a non-mark glyph */
i = 1;
j = buffer->in_pos - 1;
while ( i <= buffer->in_pos )
{
error = HB_GDEF_Get_Glyph_Property( gpos->gdef, IN_GLYPH( j ),
&property );
if ( error )
return error;
if ( !( property == HB_GDEF_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
break;
i++;
j--;
}
/* The following assertion is too strong -- at least for mangal.ttf. */
#if 0
if ( property != HB_GDEF_BASE_GLYPH )
return HB_Err_Not_Covered;
#endif
if ( i > buffer->in_pos )
return HB_Err_Not_Covered;
error = _HB_OPEN_Coverage_Index( &mbp->BaseCoverage, IN_GLYPH( j ),
&base_index );
if ( error )
return error;
ma = &mbp->MarkArray;
if ( mark_index >= ma->MarkCount )
return HB_Err_Invalid_GPOS_SubTable;
class = ma->MarkRecord[mark_index].Class;
mark_anchor = &ma->MarkRecord[mark_index].MarkAnchor;
if ( class >= mbp->ClassCount )
return HB_Err_Invalid_GPOS_SubTable;
ba = &mbp->BaseArray;
if ( base_index >= ba->BaseCount )
return HB_Err_Invalid_GPOS_SubTable;
br = &ba->BaseRecord[base_index];
base_anchor = &br->BaseAnchor[class];
error = Get_Anchor( gpi, mark_anchor, IN_CURGLYPH(),
&x_mark_value, &y_mark_value );
if ( error )
return error;
error = Get_Anchor( gpi, base_anchor, IN_GLYPH( j ),
&x_base_value, &y_base_value );
if ( error )
return error;
/* anchor points are not cumulative */
o = POSITION( buffer->in_pos );
o->x_pos = x_base_value - x_mark_value;
o->y_pos = y_base_value - y_mark_value;
o->x_advance = 0;
o->y_advance = 0;
o->back = i;
(buffer->in_pos)++;
return FT_Err_Ok;
}
/* LookupType 5 */
/* LigatureAttach */
static FT_Error Load_LigatureAttach( HB_LigatureAttach* lat,
FT_UShort num_classes,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort m, n, k, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_ComponentRecord* cr;
HB_Anchor* lan;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 2L ) )
return error;
count = lat->ComponentCount = GET_UShort();
FORGET_Frame();
lat->ComponentRecord = NULL;
if ( ALLOC_ARRAY( lat->ComponentRecord, count, HB_ComponentRecord ) )
return error;
cr = lat->ComponentRecord;
for ( m = 0; m < count; m++ )
{
cr[m].LigatureAnchor = NULL;
if ( ALLOC_ARRAY( cr[m].LigatureAnchor, num_classes, HB_Anchor ) )
goto Fail;
lan = cr[m].LigatureAnchor;
for ( n = 0; n < num_classes; n++ )
{
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_Anchor( &lan[n], stream ) ) != FT_Err_Ok )
goto Fail0;
(void)FILE_Seek( cur_offset );
}
else
lan[n].PosFormat = 0;
}
continue;
Fail0:
for ( k = 0; k < n; k++ )
Free_Anchor( &lan[k], memory );
goto Fail;
}
return FT_Err_Ok;
Fail:
for ( k = 0; k < m; k++ )
{
lan = cr[k].LigatureAnchor;
for ( n = 0; n < num_classes; n++ )
Free_Anchor( &lan[n], memory );
FREE( lan );
}
FREE( cr );
return error;
}
static void Free_LigatureAttach( HB_LigatureAttach* lat,
FT_UShort num_classes,
FT_Memory memory )
{
FT_UShort m, n, count;
HB_ComponentRecord* cr;
HB_Anchor* lan;
if ( lat->ComponentRecord )
{
count = lat->ComponentCount;
cr = lat->ComponentRecord;
for ( m = 0; m < count; m++ )
{
lan = cr[m].LigatureAnchor;
for ( n = 0; n < num_classes; n++ )
Free_Anchor( &lan[n], memory );
FREE( lan );
}
FREE( cr );
}
}
/* LigatureArray */
static FT_Error Load_LigatureArray( HB_LigatureArray* la,
FT_UShort num_classes,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, m, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_LigatureAttach* lat;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 2L ) )
return error;
count = la->LigatureCount = GET_UShort();
FORGET_Frame();
la->LigatureAttach = NULL;
if ( ALLOC_ARRAY( la->LigatureAttach, count, HB_LigatureAttach ) )
return error;
lat = la->LigatureAttach;
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_LigatureAttach( &lat[n], num_classes,
stream ) ) != FT_Err_Ok )
goto Fail;
(void)FILE_Seek( cur_offset );
}
return FT_Err_Ok;
Fail:
for ( m = 0; m < n; m++ )
Free_LigatureAttach( &lat[m], num_classes, memory );
FREE( lat );
return error;
}
static void Free_LigatureArray( HB_LigatureArray* la,
FT_UShort num_classes,
FT_Memory memory )
{
FT_UShort n, count;
HB_LigatureAttach* lat;
if ( la->LigatureAttach )
{
count = la->LigatureCount;
lat = la->LigatureAttach;
for ( n = 0; n < count; n++ )
Free_LigatureAttach( &lat[n], num_classes, memory );
FREE( lat );
}
}
/* MarkLigPosFormat1 */
static FT_Error Load_MarkLigPos( HB_GPOS_SubTable* st,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
HB_MarkLigPos* mlp = &st->marklig;
FT_ULong cur_offset, new_offset, base_offset;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 4L ) )
return error;
mlp->PosFormat = GET_UShort();
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Coverage( &mlp->MarkCoverage, stream ) ) != FT_Err_Ok )
return error;
(void)FILE_Seek( cur_offset );
if ( ACCESS_Frame( 2L ) )
goto Fail3;
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Coverage( &mlp->LigatureCoverage,
stream ) ) != FT_Err_Ok )
goto Fail3;
(void)FILE_Seek( cur_offset );
if ( ACCESS_Frame( 4L ) )
goto Fail2;
mlp->ClassCount = GET_UShort();
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = Load_MarkArray( &mlp->MarkArray, stream ) ) != FT_Err_Ok )
goto Fail2;
(void)FILE_Seek( cur_offset );
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_LigatureArray( &mlp->LigatureArray, mlp->ClassCount,
stream ) ) != FT_Err_Ok )
goto Fail1;
return FT_Err_Ok;
Fail1:
Free_MarkArray( &mlp->MarkArray, memory );
Fail2:
_HB_OPEN_Free_Coverage( &mlp->LigatureCoverage, memory );
Fail3:
_HB_OPEN_Free_Coverage( &mlp->MarkCoverage, memory );
return error;
}
static void Free_MarkLigPos( HB_GPOS_SubTable* st,
FT_Memory memory)
{
HB_MarkLigPos* mlp = &st->marklig;
Free_LigatureArray( &mlp->LigatureArray, mlp->ClassCount, memory );
Free_MarkArray( &mlp->MarkArray, memory );
_HB_OPEN_Free_Coverage( &mlp->LigatureCoverage, memory );
_HB_OPEN_Free_Coverage( &mlp->MarkCoverage, memory );
}
static FT_Error Lookup_MarkLigPos( GPOS_Instance* gpi,
HB_GPOS_SubTable* st,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
FT_UShort i, j, mark_index, lig_index, property, class;
FT_UShort mark_glyph;
FT_Pos x_mark_value, y_mark_value, x_lig_value, y_lig_value;
FT_Error error;
HB_GPOSHeader* gpos = gpi->gpos;
HB_MarkLigPos* mlp = &st->marklig;
HB_MarkArray* ma;
HB_LigatureArray* la;
HB_LigatureAttach* lat;
HB_ComponentRecord* cr;
FT_UShort comp_index;
HB_Anchor* mark_anchor;
HB_Anchor* lig_anchor;
HB_Position o;
if ( context_length != 0xFFFF && context_length < 1 )
return HB_Err_Not_Covered;
if ( flags & HB_LOOKUP_FLAG_IGNORE_LIGATURES )
return HB_Err_Not_Covered;
mark_glyph = IN_CURGLYPH();
if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
return error;
error = _HB_OPEN_Coverage_Index( &mlp->MarkCoverage, mark_glyph, &mark_index );
if ( error )
return error;
/* now we search backwards for a non-mark glyph */
i = 1;
j = buffer->in_pos - 1;
while ( i <= buffer->in_pos )
{
error = HB_GDEF_Get_Glyph_Property( gpos->gdef, IN_GLYPH( j ),
&property );
if ( error )
return error;
if ( !( property == HB_GDEF_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
break;
i++;
j--;
}
/* Similar to Lookup_MarkBasePos(), I suspect that this assertion is
too strong, thus it is commented out. */
#if 0
if ( property != HB_GDEF_LIGATURE )
return HB_Err_Not_Covered;
#endif
if ( i > buffer->in_pos )
return HB_Err_Not_Covered;
error = _HB_OPEN_Coverage_Index( &mlp->LigatureCoverage, IN_GLYPH( j ),
&lig_index );
if ( error )
return error;
ma = &mlp->MarkArray;
if ( mark_index >= ma->MarkCount )
return HB_Err_Invalid_GPOS_SubTable;
class = ma->MarkRecord[mark_index].Class;
mark_anchor = &ma->MarkRecord[mark_index].MarkAnchor;
if ( class >= mlp->ClassCount )
return HB_Err_Invalid_GPOS_SubTable;
la = &mlp->LigatureArray;
if ( lig_index >= la->LigatureCount )
return HB_Err_Invalid_GPOS_SubTable;
lat = &la->LigatureAttach[lig_index];
/* We must now check whether the ligature ID of the current mark glyph
is identical to the ligature ID of the found ligature. If yes, we
can directly use the component index. If not, we attach the mark
glyph to the last component of the ligature. */
if ( IN_LIGID( j ) == IN_LIGID( buffer->in_pos) )
{
comp_index = IN_COMPONENT( buffer->in_pos );
if ( comp_index >= lat->ComponentCount )
return HB_Err_Not_Covered;
}
else
comp_index = lat->ComponentCount - 1;
cr = &lat->ComponentRecord[comp_index];
lig_anchor = &cr->LigatureAnchor[class];
error = Get_Anchor( gpi, mark_anchor, IN_CURGLYPH(),
&x_mark_value, &y_mark_value );
if ( error )
return error;
error = Get_Anchor( gpi, lig_anchor, IN_GLYPH( j ),
&x_lig_value, &y_lig_value );
if ( error )
return error;
/* anchor points are not cumulative */
o = POSITION( buffer->in_pos );
o->x_pos = x_lig_value - x_mark_value;
o->y_pos = y_lig_value - y_mark_value;
o->x_advance = 0;
o->y_advance = 0;
o->back = i;
(buffer->in_pos)++;
return FT_Err_Ok;
}
/* LookupType 6 */
/* Mark2Array */
static FT_Error Load_Mark2Array( HB_Mark2Array* m2a,
FT_UShort num_classes,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort k, m, n, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_Mark2Record* m2r;
HB_Anchor* m2an;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 2L ) )
return error;
count = m2a->Mark2Count = GET_UShort();
FORGET_Frame();
m2a->Mark2Record = NULL;
if ( ALLOC_ARRAY( m2a->Mark2Record, count, HB_Mark2Record ) )
return error;
m2r = m2a->Mark2Record;
for ( m = 0; m < count; m++ )
{
m2r[m].Mark2Anchor = NULL;
if ( ALLOC_ARRAY( m2r[m].Mark2Anchor, num_classes, HB_Anchor ) )
goto Fail;
m2an = m2r[m].Mark2Anchor;
for ( n = 0; n < num_classes; n++ )
{
if ( ACCESS_Frame( 2L ) )
goto Fail0;
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = Load_Anchor( &m2an[n], stream ) ) != FT_Err_Ok )
goto Fail0;
(void)FILE_Seek( cur_offset );
}
continue;
Fail0:
for ( k = 0; k < n; k++ )
Free_Anchor( &m2an[k], memory );
goto Fail;
}
return FT_Err_Ok;
Fail:
for ( k = 0; k < m; k++ )
{
m2an = m2r[k].Mark2Anchor;
for ( n = 0; n < num_classes; n++ )
Free_Anchor( &m2an[n], memory );
FREE( m2an );
}
FREE( m2r );
return error;
}
static void Free_Mark2Array( HB_Mark2Array* m2a,
FT_UShort num_classes,
FT_Memory memory )
{
FT_UShort m, n, count;
HB_Mark2Record* m2r;
HB_Anchor* m2an;
if ( m2a->Mark2Record )
{
count = m2a->Mark2Count;
m2r = m2a->Mark2Record;
for ( m = 0; m < count; m++ )
{
m2an = m2r[m].Mark2Anchor;
for ( n = 0; n < num_classes; n++ )
Free_Anchor( &m2an[n], memory );
FREE( m2an );
}
FREE( m2r );
}
}
/* MarkMarkPosFormat1 */
static FT_Error Load_MarkMarkPos( HB_GPOS_SubTable* st,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
HB_MarkMarkPos* mmp = &st->markmark;
FT_ULong cur_offset, new_offset, base_offset;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 4L ) )
return error;
mmp->PosFormat = GET_UShort();
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Coverage( &mmp->Mark1Coverage,
stream ) ) != FT_Err_Ok )
return error;
(void)FILE_Seek( cur_offset );
if ( ACCESS_Frame( 2L ) )
goto Fail3;
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Coverage( &mmp->Mark2Coverage,
stream ) ) != FT_Err_Ok )
goto Fail3;
(void)FILE_Seek( cur_offset );
if ( ACCESS_Frame( 4L ) )
goto Fail2;
mmp->ClassCount = GET_UShort();
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = Load_MarkArray( &mmp->Mark1Array, stream ) ) != FT_Err_Ok )
goto Fail2;
(void)FILE_Seek( cur_offset );
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_Mark2Array( &mmp->Mark2Array, mmp->ClassCount,
stream ) ) != FT_Err_Ok )
goto Fail1;
return FT_Err_Ok;
Fail1:
Free_MarkArray( &mmp->Mark1Array, memory );
Fail2:
_HB_OPEN_Free_Coverage( &mmp->Mark2Coverage, memory );
Fail3:
_HB_OPEN_Free_Coverage( &mmp->Mark1Coverage, memory );
return error;
}
static void Free_MarkMarkPos( HB_GPOS_SubTable* st,
FT_Memory memory)
{
HB_MarkMarkPos* mmp = &st->markmark;
Free_Mark2Array( &mmp->Mark2Array, mmp->ClassCount, memory );
Free_MarkArray( &mmp->Mark1Array, memory );
_HB_OPEN_Free_Coverage( &mmp->Mark2Coverage, memory );
_HB_OPEN_Free_Coverage( &mmp->Mark1Coverage, memory );
}
static FT_Error Lookup_MarkMarkPos( GPOS_Instance* gpi,
HB_GPOS_SubTable* st,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
FT_UShort j, mark1_index, mark2_index, property, class;
FT_Pos x_mark1_value, y_mark1_value,
x_mark2_value, y_mark2_value;
FT_Error error;
HB_GPOSHeader* gpos = gpi->gpos;
HB_MarkMarkPos* mmp = &st->markmark;
HB_MarkArray* ma1;
HB_Mark2Array* ma2;
HB_Mark2Record* m2r;
HB_Anchor* mark1_anchor;
HB_Anchor* mark2_anchor;
HB_Position o;
if ( context_length != 0xFFFF && context_length < 1 )
return HB_Err_Not_Covered;
if ( flags & HB_LOOKUP_FLAG_IGNORE_MARKS )
return HB_Err_Not_Covered;
if ( CHECK_Property( gpos->gdef, IN_CURITEM(),
flags, &property ) )
return error;
error = _HB_OPEN_Coverage_Index( &mmp->Mark1Coverage, IN_CURGLYPH(),
&mark1_index );
if ( error )
return error;
/* now we check the preceding glyph whether it is a suitable
mark glyph */
if ( buffer->in_pos == 0 )
return HB_Err_Not_Covered;
j = buffer->in_pos - 1;
error = HB_GDEF_Get_Glyph_Property( gpos->gdef, IN_GLYPH( j ),
&property );
if ( error )
return error;
if ( flags & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS )
{
if ( property != (flags & 0xFF00) )
return HB_Err_Not_Covered;
}
else
{
if ( property != HB_GDEF_MARK )
return HB_Err_Not_Covered;
}
error = _HB_OPEN_Coverage_Index( &mmp->Mark2Coverage, IN_GLYPH( j ),
&mark2_index );
if ( error )
return error;
ma1 = &mmp->Mark1Array;
if ( mark1_index >= ma1->MarkCount )
return HB_Err_Invalid_GPOS_SubTable;
class = ma1->MarkRecord[mark1_index].Class;
mark1_anchor = &ma1->MarkRecord[mark1_index].MarkAnchor;
if ( class >= mmp->ClassCount )
return HB_Err_Invalid_GPOS_SubTable;
ma2 = &mmp->Mark2Array;
if ( mark2_index >= ma2->Mark2Count )
return HB_Err_Invalid_GPOS_SubTable;
m2r = &ma2->Mark2Record[mark2_index];
mark2_anchor = &m2r->Mark2Anchor[class];
error = Get_Anchor( gpi, mark1_anchor, IN_CURGLYPH(),
&x_mark1_value, &y_mark1_value );
if ( error )
return error;
error = Get_Anchor( gpi, mark2_anchor, IN_GLYPH( j ),
&x_mark2_value, &y_mark2_value );
if ( error )
return error;
/* anchor points are not cumulative */
o = POSITION( buffer->in_pos );
o->x_pos = x_mark2_value - x_mark1_value;
o->y_pos = y_mark2_value - y_mark1_value;
o->x_advance = 0;
o->y_advance = 0;
o->back = 1;
(buffer->in_pos)++;
return FT_Err_Ok;
}
/* Do the actual positioning for a context positioning (either format
7 or 8). This is only called after we've determined that the stream
matches the subrule. */
static FT_Error Do_ContextPos( GPOS_Instance* gpi,
FT_UShort GlyphCount,
FT_UShort PosCount,
HB_PosLookupRecord* pos,
HB_Buffer buffer,
int nesting_level )
{
FT_Error error;
FT_UShort i, old_pos;
i = 0;
while ( i < GlyphCount )
{
if ( PosCount && i == pos->SequenceIndex )
{
old_pos = buffer->in_pos;
/* Do a positioning */
error = GPOS_Do_Glyph_Lookup( gpi, pos->LookupListIndex, buffer,
GlyphCount, nesting_level );
if ( error )
return error;
pos++;
PosCount--;
i += buffer->in_pos - old_pos;
}
else
{
i++;
(buffer->in_pos)++;
}
}
return FT_Err_Ok;
}
/* LookupType 7 */
/* PosRule */
static FT_Error Load_PosRule( HB_PosRule* pr,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, count;
FT_UShort* i;
HB_PosLookupRecord* plr;
if ( ACCESS_Frame( 4L ) )
return error;
pr->GlyphCount = GET_UShort();
pr->PosCount = GET_UShort();
FORGET_Frame();
pr->Input = NULL;
count = pr->GlyphCount - 1; /* only GlyphCount - 1 elements */
if ( ALLOC_ARRAY( pr->Input, count, FT_UShort ) )
return error;
i = pr->Input;
if ( ACCESS_Frame( count * 2L ) )
goto Fail2;
for ( n = 0; n < count; n++ )
i[n] = GET_UShort();
FORGET_Frame();
pr->PosLookupRecord = NULL;
count = pr->PosCount;
if ( ALLOC_ARRAY( pr->PosLookupRecord, count, HB_PosLookupRecord ) )
goto Fail2;
plr = pr->PosLookupRecord;
if ( ACCESS_Frame( count * 4L ) )
goto Fail1;
for ( n = 0; n < count; n++ )
{
plr[n].SequenceIndex = GET_UShort();
plr[n].LookupListIndex = GET_UShort();
}
FORGET_Frame();
return FT_Err_Ok;
Fail1:
FREE( plr );
Fail2:
FREE( i );
return error;
}
static void Free_PosRule( HB_PosRule* pr,
FT_Memory memory )
{
FREE( pr->PosLookupRecord );
FREE( pr->Input );
}
/* PosRuleSet */
static FT_Error Load_PosRuleSet( HB_PosRuleSet* prs,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, m, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_PosRule* pr;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 2L ) )
return error;
count = prs->PosRuleCount = GET_UShort();
FORGET_Frame();
prs->PosRule = NULL;
if ( ALLOC_ARRAY( prs->PosRule, count, HB_PosRule ) )
return error;
pr = prs->PosRule;
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_PosRule( &pr[n], stream ) ) != FT_Err_Ok )
goto Fail;
(void)FILE_Seek( cur_offset );
}
return FT_Err_Ok;
Fail:
for ( m = 0; m < n; m++ )
Free_PosRule( &pr[m], memory );
FREE( pr );
return error;
}
static void Free_PosRuleSet( HB_PosRuleSet* prs,
FT_Memory memory )
{
FT_UShort n, count;
HB_PosRule* pr;
if ( prs->PosRule )
{
count = prs->PosRuleCount;
pr = prs->PosRule;
for ( n = 0; n < count; n++ )
Free_PosRule( &pr[n], memory );
FREE( pr );
}
}
/* ContextPosFormat1 */
static FT_Error Load_ContextPos1( HB_ContextPosFormat1* cpf1,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, m, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_PosRuleSet* prs;
base_offset = FILE_Pos() - 2L;
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( &cpf1->Coverage, stream ) ) != FT_Err_Ok )
return error;
(void)FILE_Seek( cur_offset );
if ( ACCESS_Frame( 2L ) )
goto Fail2;
count = cpf1->PosRuleSetCount = GET_UShort();
FORGET_Frame();
cpf1->PosRuleSet = NULL;
if ( ALLOC_ARRAY( cpf1->PosRuleSet, count, HB_PosRuleSet ) )
goto Fail2;
prs = cpf1->PosRuleSet;
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_PosRuleSet( &prs[n], stream ) ) != FT_Err_Ok )
goto Fail1;
(void)FILE_Seek( cur_offset );
}
return FT_Err_Ok;
Fail1:
for ( m = 0; m < n; m++ )
Free_PosRuleSet( &prs[m], memory );
FREE( prs );
Fail2:
_HB_OPEN_Free_Coverage( &cpf1->Coverage, memory );
return error;
}
static void Free_ContextPos1( HB_ContextPosFormat1* cpf1,
FT_Memory memory )
{
FT_UShort n, count;
HB_PosRuleSet* prs;
if ( cpf1->PosRuleSet )
{
count = cpf1->PosRuleSetCount;
prs = cpf1->PosRuleSet;
for ( n = 0; n < count; n++ )
Free_PosRuleSet( &prs[n], memory );
FREE( prs );
}
_HB_OPEN_Free_Coverage( &cpf1->Coverage, memory );
}
/* PosClassRule */
static FT_Error Load_PosClassRule( HB_ContextPosFormat2* cpf2,
HB_PosClassRule* pcr,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, count;
FT_UShort* c;
HB_PosLookupRecord* plr;
FT_Bool* d;
if ( ACCESS_Frame( 4L ) )
return error;
pcr->GlyphCount = GET_UShort();
pcr->PosCount = GET_UShort();
FORGET_Frame();
if ( pcr->GlyphCount > cpf2->MaxContextLength )
cpf2->MaxContextLength = pcr->GlyphCount;
pcr->Class = NULL;
count = pcr->GlyphCount - 1; /* only GlyphCount - 1 elements */
if ( ALLOC_ARRAY( pcr->Class, count, FT_UShort ) )
return error;
c = pcr->Class;
d = cpf2->ClassDef.Defined;
if ( ACCESS_Frame( count * 2L ) )
goto Fail2;
for ( n = 0; n < count; n++ )
{
c[n] = GET_UShort();
/* We check whether the specific class is used at all. If not,
class 0 is used instead. */
if ( !d[c[n]] )
c[n] = 0;
}
FORGET_Frame();
pcr->PosLookupRecord = NULL;
count = pcr->PosCount;
if ( ALLOC_ARRAY( pcr->PosLookupRecord, count, HB_PosLookupRecord ) )
goto Fail2;
plr = pcr->PosLookupRecord;
if ( ACCESS_Frame( count * 4L ) )
goto Fail1;
for ( n = 0; n < count; n++ )
{
plr[n].SequenceIndex = GET_UShort();
plr[n].LookupListIndex = GET_UShort();
}
FORGET_Frame();
return FT_Err_Ok;
Fail1:
FREE( plr );
Fail2:
FREE( c );
return error;
}
static void Free_PosClassRule( HB_PosClassRule* pcr,
FT_Memory memory )
{
FREE( pcr->PosLookupRecord );
FREE( pcr->Class );
}
/* PosClassSet */
static FT_Error Load_PosClassSet( HB_ContextPosFormat2* cpf2,
HB_PosClassSet* pcs,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, m, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_PosClassRule* pcr;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 2L ) )
return error;
count = pcs->PosClassRuleCount = GET_UShort();
FORGET_Frame();
pcs->PosClassRule = NULL;
if ( ALLOC_ARRAY( pcs->PosClassRule, count, HB_PosClassRule ) )
return error;
pcr = pcs->PosClassRule;
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_PosClassRule( cpf2, &pcr[n],
stream ) ) != FT_Err_Ok )
goto Fail;
(void)FILE_Seek( cur_offset );
}
return FT_Err_Ok;
Fail:
for ( m = 0; m < n; m++ )
Free_PosClassRule( &pcr[m], memory );
FREE( pcr );
return error;
}
static void Free_PosClassSet( HB_PosClassSet* pcs,
FT_Memory memory )
{
FT_UShort n, count;
HB_PosClassRule* pcr;
if ( pcs->PosClassRule )
{
count = pcs->PosClassRuleCount;
pcr = pcs->PosClassRule;
for ( n = 0; n < count; n++ )
Free_PosClassRule( &pcr[n], memory );
FREE( pcr );
}
}
/* ContextPosFormat2 */
static FT_Error Load_ContextPos2( HB_ContextPosFormat2* cpf2,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, m, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_PosClassSet* pcs;
base_offset = FILE_Pos() - 2;
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( &cpf2->Coverage, stream ) ) != FT_Err_Ok )
return error;
(void)FILE_Seek( cur_offset );
if ( ACCESS_Frame( 4L ) )
goto Fail3;
new_offset = GET_UShort() + base_offset;
/* `PosClassSetCount' is the upper limit for class values, thus we
read it now to make an additional safety check. */
count = cpf2->PosClassSetCount = GET_UShort();
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_ClassDefinition( &cpf2->ClassDef, count,
stream ) ) != FT_Err_Ok )
goto Fail3;
(void)FILE_Seek( cur_offset );
cpf2->PosClassSet = NULL;
cpf2->MaxContextLength = 0;
if ( ALLOC_ARRAY( cpf2->PosClassSet, count, HB_PosClassSet ) )
goto Fail2;
pcs = cpf2->PosClassSet;
for ( n = 0; n < count; n++ )
{
if ( ACCESS_Frame( 2L ) )
goto Fail1;
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_PosClassSet( cpf2, &pcs[n],
stream ) ) != FT_Err_Ok )
goto Fail1;
(void)FILE_Seek( cur_offset );
}
else
{
/* we create a PosClassSet table with no entries */
cpf2->PosClassSet[n].PosClassRuleCount = 0;
cpf2->PosClassSet[n].PosClassRule = NULL;
}
}
return FT_Err_Ok;
Fail1:
for ( m = 0; m < n; n++ )
Free_PosClassSet( &pcs[m], memory );
FREE( pcs );
Fail2:
_HB_OPEN_Free_ClassDefinition( &cpf2->ClassDef, memory );
Fail3:
_HB_OPEN_Free_Coverage( &cpf2->Coverage, memory );
return error;
}
static void Free_ContextPos2( HB_ContextPosFormat2* cpf2,
FT_Memory memory )
{
FT_UShort n, count;
HB_PosClassSet* pcs;
if ( cpf2->PosClassSet )
{
count = cpf2->PosClassSetCount;
pcs = cpf2->PosClassSet;
for ( n = 0; n < count; n++ )
Free_PosClassSet( &pcs[n], memory );
FREE( pcs );
}
_HB_OPEN_Free_ClassDefinition( &cpf2->ClassDef, memory );
_HB_OPEN_Free_Coverage( &cpf2->Coverage, memory );
}
/* ContextPosFormat3 */
static FT_Error Load_ContextPos3( HB_ContextPosFormat3* cpf3,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_Coverage* c;
HB_PosLookupRecord* plr;
base_offset = FILE_Pos() - 2L;
if ( ACCESS_Frame( 4L ) )
return error;
cpf3->GlyphCount = GET_UShort();
cpf3->PosCount = GET_UShort();
FORGET_Frame();
cpf3->Coverage = NULL;
count = cpf3->GlyphCount;
if ( ALLOC_ARRAY( cpf3->Coverage, count, HB_Coverage ) )
return error;
c = cpf3->Coverage;
for ( n = 0; n < count; n++ )
{
if ( ACCESS_Frame( 2L ) )
goto Fail2;
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Coverage( &c[n], stream ) ) != FT_Err_Ok )
goto Fail2;
(void)FILE_Seek( cur_offset );
}
cpf3->PosLookupRecord = NULL;
count = cpf3->PosCount;
if ( ALLOC_ARRAY( cpf3->PosLookupRecord, count, HB_PosLookupRecord ) )
goto Fail2;
plr = cpf3->PosLookupRecord;
if ( ACCESS_Frame( count * 4L ) )
goto Fail1;
for ( n = 0; n < count; n++ )
{
plr[n].SequenceIndex = GET_UShort();
plr[n].LookupListIndex = GET_UShort();
}
FORGET_Frame();
return FT_Err_Ok;
Fail1:
FREE( plr );
Fail2:
for ( n = 0; n < count; n++ )
_HB_OPEN_Free_Coverage( &c[n], memory );
FREE( c );
return error;
}
static void Free_ContextPos3( HB_ContextPosFormat3* cpf3,
FT_Memory memory )
{
FT_UShort n, count;
HB_Coverage* c;
FREE( cpf3->PosLookupRecord );
if ( cpf3->Coverage )
{
count = cpf3->GlyphCount;
c = cpf3->Coverage;
for ( n = 0; n < count; n++ )
_HB_OPEN_Free_Coverage( &c[n], memory );
FREE( c );
}
}
/* ContextPos */
static FT_Error Load_ContextPos( HB_GPOS_SubTable* st,
FT_Stream stream )
{
FT_Error error;
HB_ContextPos* cp = &st->context;
if ( ACCESS_Frame( 2L ) )
return error;
cp->PosFormat = GET_UShort();
FORGET_Frame();
switch ( cp->PosFormat )
{
case 1:
return Load_ContextPos1( &cp->cpf.cpf1, stream );
case 2:
return Load_ContextPos2( &cp->cpf.cpf2, stream );
case 3:
return Load_ContextPos3( &cp->cpf.cpf3, stream );
default:
return HB_Err_Invalid_GPOS_SubTable_Format;
}
return FT_Err_Ok; /* never reached */
}
static void Free_ContextPos( HB_GPOS_SubTable* st,
FT_Memory memory )
{
HB_ContextPos* cp = &st->context;
switch ( cp->PosFormat )
{
case 1:
Free_ContextPos1( &cp->cpf.cpf1, memory );
break;
case 2:
Free_ContextPos2( &cp->cpf.cpf2, memory );
break;
case 3:
Free_ContextPos3( &cp->cpf.cpf3, memory );
break;
}
}
static FT_Error Lookup_ContextPos1( GPOS_Instance* gpi,
HB_ContextPosFormat1* cpf1,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
FT_UShort index, property;
FT_UShort i, j, k, numpr;
FT_Error error;
HB_GPOSHeader* gpos = gpi->gpos;
HB_PosRule* pr;
HB_GDEFHeader* gdef;
gdef = gpos->gdef;
if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
return error;
error = _HB_OPEN_Coverage_Index( &cpf1->Coverage, IN_CURGLYPH(), &index );
if ( error )
return error;
pr = cpf1->PosRuleSet[index].PosRule;
numpr = cpf1->PosRuleSet[index].PosRuleCount;
for ( k = 0; k < numpr; k++ )
{
if ( context_length != 0xFFFF && context_length < pr[k].GlyphCount )
goto next_posrule;
if ( buffer->in_pos + pr[k].GlyphCount > buffer->in_length )
goto next_posrule; /* context is too long */
for ( i = 1, j = buffer->in_pos + 1; i < pr[k].GlyphCount; i++, j++ )
{
while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
{
if ( error && error != HB_Err_Not_Covered )
return error;
if ( j + pr[k].GlyphCount - i == buffer->in_length )
goto next_posrule;
j++;
}
if ( IN_GLYPH( j ) != pr[k].Input[i - 1] )
goto next_posrule;
}
return Do_ContextPos( gpi, pr[k].GlyphCount,
pr[k].PosCount, pr[k].PosLookupRecord,
buffer,
nesting_level );
next_posrule:
;
}
return HB_Err_Not_Covered;
}
static FT_Error Lookup_ContextPos2( GPOS_Instance* gpi,
HB_ContextPosFormat2* cpf2,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
FT_UShort index, property;
FT_Error error;
FT_Memory memory = gpi->face->memory;
FT_UShort i, j, k, known_classes;
FT_UShort* classes;
FT_UShort* cl;
HB_GPOSHeader* gpos = gpi->gpos;
HB_PosClassSet* pcs;
HB_PosClassRule* pr;
HB_GDEFHeader* gdef;
gdef = gpos->gdef;
if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
return error;
/* Note: The coverage table in format 2 doesn't give an index into
anything. It just lets us know whether or not we need to
do any lookup at all. */
error = _HB_OPEN_Coverage_Index( &cpf2->Coverage, IN_CURGLYPH(), &index );
if ( error )
return error;
if ( ALLOC_ARRAY( classes, cpf2->MaxContextLength, FT_UShort ) )
return error;
error = _HB_OPEN_Get_Class( &cpf2->ClassDef, IN_CURGLYPH(),
&classes[0], NULL );
if ( error && error != HB_Err_Not_Covered )
goto End;
known_classes = 0;
pcs = &cpf2->PosClassSet[classes[0]];
if ( !pcs )
{
error = HB_Err_Invalid_GPOS_SubTable;
goto End;
}
for ( k = 0; k < pcs->PosClassRuleCount; k++ )
{
pr = &pcs->PosClassRule[k];
if ( context_length != 0xFFFF && context_length < pr->GlyphCount )
goto next_posclassrule;
if ( buffer->in_pos + pr->GlyphCount > buffer->in_length )
goto next_posclassrule; /* context is too long */
cl = pr->Class;
/* Start at 1 because [0] is implied */
for ( i = 1, j = buffer->in_pos + 1; i < pr->GlyphCount; i++, j++ )
{
while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
{
if ( error && error != HB_Err_Not_Covered )
goto End;
if ( j + pr->GlyphCount - i == buffer->in_length )
goto next_posclassrule;
j++;
}
if ( i > known_classes )
{
/* Keeps us from having to do this for each rule */
error = _HB_OPEN_Get_Class( &cpf2->ClassDef, IN_GLYPH( j ), &classes[i], NULL );
if ( error && error != HB_Err_Not_Covered )
goto End;
known_classes = i;
}
if ( cl[i - 1] != classes[i] )
goto next_posclassrule;
}
error = Do_ContextPos( gpi, pr->GlyphCount,
pr->PosCount, pr->PosLookupRecord,
buffer,
nesting_level );
goto End;
next_posclassrule:
;
}
error = HB_Err_Not_Covered;
End:
FREE( classes );
return error;
}
static FT_Error Lookup_ContextPos3( GPOS_Instance* gpi,
HB_ContextPosFormat3* cpf3,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
FT_Error error;
FT_UShort index, i, j, property;
HB_GPOSHeader* gpos = gpi->gpos;
HB_Coverage* c;
HB_GDEFHeader* gdef;
gdef = gpos->gdef;
if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
return error;
if ( context_length != 0xFFFF && context_length < cpf3->GlyphCount )
return HB_Err_Not_Covered;
if ( buffer->in_pos + cpf3->GlyphCount > buffer->in_length )
return HB_Err_Not_Covered; /* context is too long */
c = cpf3->Coverage;
for ( i = 1, j = 1; i < cpf3->GlyphCount; i++, j++ )
{
while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
{
if ( error && error != HB_Err_Not_Covered )
return error;
if ( j + cpf3->GlyphCount - i == buffer->in_length )
return HB_Err_Not_Covered;
j++;
}
error = _HB_OPEN_Coverage_Index( &c[i], IN_GLYPH( j ), &index );
if ( error )
return error;
}
return Do_ContextPos( gpi, cpf3->GlyphCount,
cpf3->PosCount, cpf3->PosLookupRecord,
buffer,
nesting_level );
}
static FT_Error Lookup_ContextPos( GPOS_Instance* gpi,
HB_GPOS_SubTable* st,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
HB_ContextPos* cp = &st->context;
switch ( cp->PosFormat )
{
case 1:
return Lookup_ContextPos1( gpi, &cp->cpf.cpf1, buffer,
flags, context_length, nesting_level );
case 2:
return Lookup_ContextPos2( gpi, &cp->cpf.cpf2, buffer,
flags, context_length, nesting_level );
case 3:
return Lookup_ContextPos3( gpi, &cp->cpf.cpf3, buffer,
flags, context_length, nesting_level );
default:
return HB_Err_Invalid_GPOS_SubTable_Format;
}
return FT_Err_Ok; /* never reached */
}
/* LookupType 8 */
/* ChainPosRule */
static FT_Error Load_ChainPosRule( HB_ChainPosRule* cpr,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, count;
FT_UShort* b;
FT_UShort* i;
FT_UShort* l;
HB_PosLookupRecord* plr;
if ( ACCESS_Frame( 2L ) )
return error;
cpr->BacktrackGlyphCount = GET_UShort();
FORGET_Frame();
cpr->Backtrack = NULL;
count = cpr->BacktrackGlyphCount;
if ( ALLOC_ARRAY( cpr->Backtrack, count, FT_UShort ) )
return error;
b = cpr->Backtrack;
if ( ACCESS_Frame( count * 2L ) )
goto Fail4;
for ( n = 0; n < count; n++ )
b[n] = GET_UShort();
FORGET_Frame();
if ( ACCESS_Frame( 2L ) )
goto Fail4;
cpr->InputGlyphCount = GET_UShort();
FORGET_Frame();
cpr->Input = NULL;
count = cpr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */
if ( ALLOC_ARRAY( cpr->Input, count, FT_UShort ) )
goto Fail4;
i = cpr->Input;
if ( ACCESS_Frame( count * 2L ) )
goto Fail3;
for ( n = 0; n < count; n++ )
i[n] = GET_UShort();
FORGET_Frame();
if ( ACCESS_Frame( 2L ) )
goto Fail3;
cpr->LookaheadGlyphCount = GET_UShort();
FORGET_Frame();
cpr->Lookahead = NULL;
count = cpr->LookaheadGlyphCount;
if ( ALLOC_ARRAY( cpr->Lookahead, count, FT_UShort ) )
goto Fail3;
l = cpr->Lookahead;
if ( ACCESS_Frame( count * 2L ) )
goto Fail2;
for ( n = 0; n < count; n++ )
l[n] = GET_UShort();
FORGET_Frame();
if ( ACCESS_Frame( 2L ) )
goto Fail2;
cpr->PosCount = GET_UShort();
FORGET_Frame();
cpr->PosLookupRecord = NULL;
count = cpr->PosCount;
if ( ALLOC_ARRAY( cpr->PosLookupRecord, count, HB_PosLookupRecord ) )
goto Fail2;
plr = cpr->PosLookupRecord;
if ( ACCESS_Frame( count * 4L ) )
goto Fail1;
for ( n = 0; n < count; n++ )
{
plr[n].SequenceIndex = GET_UShort();
plr[n].LookupListIndex = GET_UShort();
}
FORGET_Frame();
return FT_Err_Ok;
Fail1:
FREE( plr );
Fail2:
FREE( l );
Fail3:
FREE( i );
Fail4:
FREE( b );
return error;
}
static void Free_ChainPosRule( HB_ChainPosRule* cpr,
FT_Memory memory )
{
FREE( cpr->PosLookupRecord );
FREE( cpr->Lookahead );
FREE( cpr->Input );
FREE( cpr->Backtrack );
}
/* ChainPosRuleSet */
static FT_Error Load_ChainPosRuleSet( HB_ChainPosRuleSet* cprs,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, m, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_ChainPosRule* cpr;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 2L ) )
return error;
count = cprs->ChainPosRuleCount = GET_UShort();
FORGET_Frame();
cprs->ChainPosRule = NULL;
if ( ALLOC_ARRAY( cprs->ChainPosRule, count, HB_ChainPosRule ) )
return error;
cpr = cprs->ChainPosRule;
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_ChainPosRule( &cpr[n], stream ) ) != FT_Err_Ok )
goto Fail;
(void)FILE_Seek( cur_offset );
}
return FT_Err_Ok;
Fail:
for ( m = 0; m < n; m++ )
Free_ChainPosRule( &cpr[m], memory );
FREE( cpr );
return error;
}
static void Free_ChainPosRuleSet( HB_ChainPosRuleSet* cprs,
FT_Memory memory )
{
FT_UShort n, count;
HB_ChainPosRule* cpr;
if ( cprs->ChainPosRule )
{
count = cprs->ChainPosRuleCount;
cpr = cprs->ChainPosRule;
for ( n = 0; n < count; n++ )
Free_ChainPosRule( &cpr[n], memory );
FREE( cpr );
}
}
/* ChainContextPosFormat1 */
static FT_Error Load_ChainContextPos1( HB_ChainContextPosFormat1* ccpf1,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, m, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_ChainPosRuleSet* cprs;
base_offset = FILE_Pos() - 2L;
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( &ccpf1->Coverage, stream ) ) != FT_Err_Ok )
return error;
(void)FILE_Seek( cur_offset );
if ( ACCESS_Frame( 2L ) )
goto Fail2;
count = ccpf1->ChainPosRuleSetCount = GET_UShort();
FORGET_Frame();
ccpf1->ChainPosRuleSet = NULL;
if ( ALLOC_ARRAY( ccpf1->ChainPosRuleSet, count, HB_ChainPosRuleSet ) )
goto Fail2;
cprs = ccpf1->ChainPosRuleSet;
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_ChainPosRuleSet( &cprs[n], stream ) ) != FT_Err_Ok )
goto Fail1;
(void)FILE_Seek( cur_offset );
}
return FT_Err_Ok;
Fail1:
for ( m = 0; m < n; m++ )
Free_ChainPosRuleSet( &cprs[m], memory );
FREE( cprs );
Fail2:
_HB_OPEN_Free_Coverage( &ccpf1->Coverage, memory );
return error;
}
static void Free_ChainContextPos1( HB_ChainContextPosFormat1* ccpf1,
FT_Memory memory )
{
FT_UShort n, count;
HB_ChainPosRuleSet* cprs;
if ( ccpf1->ChainPosRuleSet )
{
count = ccpf1->ChainPosRuleSetCount;
cprs = ccpf1->ChainPosRuleSet;
for ( n = 0; n < count; n++ )
Free_ChainPosRuleSet( &cprs[n], memory );
FREE( cprs );
}
_HB_OPEN_Free_Coverage( &ccpf1->Coverage, memory );
}
/* ChainPosClassRule */
static FT_Error Load_ChainPosClassRule(
HB_ChainContextPosFormat2* ccpf2,
HB_ChainPosClassRule* cpcr,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, count;
FT_UShort* b;
FT_UShort* i;
FT_UShort* l;
HB_PosLookupRecord* plr;
FT_Bool* d;
if ( ACCESS_Frame( 2L ) )
return error;
cpcr->BacktrackGlyphCount = GET_UShort();
FORGET_Frame();
if ( cpcr->BacktrackGlyphCount > ccpf2->MaxBacktrackLength )
ccpf2->MaxBacktrackLength = cpcr->BacktrackGlyphCount;
cpcr->Backtrack = NULL;
count = cpcr->BacktrackGlyphCount;
if ( ALLOC_ARRAY( cpcr->Backtrack, count, FT_UShort ) )
return error;
b = cpcr->Backtrack;
d = ccpf2->BacktrackClassDef.Defined;
if ( ACCESS_Frame( count * 2L ) )
goto Fail4;
for ( n = 0; n < count; n++ )
{
b[n] = GET_UShort();
/* We check whether the specific class is used at all. If not,
class 0 is used instead. */
if ( !d[b[n]] )
b[n] = 0;
}
FORGET_Frame();
if ( ACCESS_Frame( 2L ) )
goto Fail4;
cpcr->InputGlyphCount = GET_UShort();
if ( cpcr->InputGlyphCount > ccpf2->MaxInputLength )
ccpf2->MaxInputLength = cpcr->InputGlyphCount;
FORGET_Frame();
cpcr->Input = NULL;
count = cpcr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */
if ( ALLOC_ARRAY( cpcr->Input, count, FT_UShort ) )
goto Fail4;
i = cpcr->Input;
d = ccpf2->InputClassDef.Defined;
if ( ACCESS_Frame( count * 2L ) )
goto Fail3;
for ( n = 0; n < count; n++ )
{
i[n] = GET_UShort();
if ( !d[i[n]] )
i[n] = 0;
}
FORGET_Frame();
if ( ACCESS_Frame( 2L ) )
goto Fail3;
cpcr->LookaheadGlyphCount = GET_UShort();
FORGET_Frame();
if ( cpcr->LookaheadGlyphCount > ccpf2->MaxLookaheadLength )
ccpf2->MaxLookaheadLength = cpcr->LookaheadGlyphCount;
cpcr->Lookahead = NULL;
count = cpcr->LookaheadGlyphCount;
if ( ALLOC_ARRAY( cpcr->Lookahead, count, FT_UShort ) )
goto Fail3;
l = cpcr->Lookahead;
d = ccpf2->LookaheadClassDef.Defined;
if ( ACCESS_Frame( count * 2L ) )
goto Fail2;
for ( n = 0; n < count; n++ )
{
l[n] = GET_UShort();
if ( !d[l[n]] )
l[n] = 0;
}
FORGET_Frame();
if ( ACCESS_Frame( 2L ) )
goto Fail2;
cpcr->PosCount = GET_UShort();
FORGET_Frame();
cpcr->PosLookupRecord = NULL;
count = cpcr->PosCount;
if ( ALLOC_ARRAY( cpcr->PosLookupRecord, count, HB_PosLookupRecord ) )
goto Fail2;
plr = cpcr->PosLookupRecord;
if ( ACCESS_Frame( count * 4L ) )
goto Fail1;
for ( n = 0; n < count; n++ )
{
plr[n].SequenceIndex = GET_UShort();
plr[n].LookupListIndex = GET_UShort();
}
FORGET_Frame();
return FT_Err_Ok;
Fail1:
FREE( plr );
Fail2:
FREE( l );
Fail3:
FREE( i );
Fail4:
FREE( b );
return error;
}
static void Free_ChainPosClassRule( HB_ChainPosClassRule* cpcr,
FT_Memory memory )
{
FREE( cpcr->PosLookupRecord );
FREE( cpcr->Lookahead );
FREE( cpcr->Input );
FREE( cpcr->Backtrack );
}
/* PosClassSet */
static FT_Error Load_ChainPosClassSet(
HB_ChainContextPosFormat2* ccpf2,
HB_ChainPosClassSet* cpcs,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, m, count;
FT_ULong cur_offset, new_offset, base_offset;
HB_ChainPosClassRule* cpcr;
base_offset = FILE_Pos();
if ( ACCESS_Frame( 2L ) )
return error;
count = cpcs->ChainPosClassRuleCount = GET_UShort();
FORGET_Frame();
cpcs->ChainPosClassRule = NULL;
if ( ALLOC_ARRAY( cpcs->ChainPosClassRule, count,
HB_ChainPosClassRule ) )
return error;
cpcr = cpcs->ChainPosClassRule;
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_ChainPosClassRule( ccpf2, &cpcr[n],
stream ) ) != FT_Err_Ok )
goto Fail;
(void)FILE_Seek( cur_offset );
}
return FT_Err_Ok;
Fail:
for ( m = 0; m < n; m++ )
Free_ChainPosClassRule( &cpcr[m], memory );
FREE( cpcr );
return error;
}
static void Free_ChainPosClassSet( HB_ChainPosClassSet* cpcs,
FT_Memory memory )
{
FT_UShort n, count;
HB_ChainPosClassRule* cpcr;
if ( cpcs->ChainPosClassRule )
{
count = cpcs->ChainPosClassRuleCount;
cpcr = cpcs->ChainPosClassRule;
for ( n = 0; n < count; n++ )
Free_ChainPosClassRule( &cpcr[n], memory );
FREE( cpcr );
}
}
static FT_Error GPOS_Load_EmptyOrClassDefinition( HB_ClassDefinition* cd,
FT_UShort limit,
FT_ULong class_offset,
FT_ULong base_offset,
FT_Stream stream )
{
FT_Error error;
FT_ULong 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, stream );
if (error == FT_Err_Ok)
(void)FILE_Seek( cur_offset ); /* Changes error as a side-effect */
return error;
}
/* ChainContextPosFormat2 */
static FT_Error Load_ChainContextPos2( HB_ChainContextPosFormat2* ccpf2,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, m, count;
FT_ULong cur_offset, new_offset, base_offset;
FT_ULong backtrack_offset, input_offset, lookahead_offset;
HB_ChainPosClassSet* cpcs;
base_offset = FILE_Pos() - 2;
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( &ccpf2->Coverage, stream ) ) != FT_Err_Ok )
return error;
(void)FILE_Seek( cur_offset );
if ( ACCESS_Frame( 8L ) )
goto Fail5;
backtrack_offset = GET_UShort();
input_offset = GET_UShort();
lookahead_offset = GET_UShort();
/* `ChainPosClassSetCount' is the upper limit for input class values,
thus we read it now to make an additional safety check. No limit
is known or needed for the other two class definitions */
count = ccpf2->ChainPosClassSetCount = GET_UShort();
FORGET_Frame();
if ( ( error = GPOS_Load_EmptyOrClassDefinition( &ccpf2->BacktrackClassDef, 65535,
backtrack_offset, base_offset,
stream ) ) != FT_Err_Ok )
goto Fail5;
if ( ( error = GPOS_Load_EmptyOrClassDefinition( &ccpf2->InputClassDef, count,
input_offset, base_offset,
stream ) ) != FT_Err_Ok )
goto Fail4;
if ( ( error = GPOS_Load_EmptyOrClassDefinition( &ccpf2->LookaheadClassDef, 65535,
lookahead_offset, base_offset,
stream ) ) != FT_Err_Ok )
goto Fail3;
ccpf2->ChainPosClassSet = NULL;
ccpf2->MaxBacktrackLength = 0;
ccpf2->MaxInputLength = 0;
ccpf2->MaxLookaheadLength = 0;
if ( ALLOC_ARRAY( ccpf2->ChainPosClassSet, count, HB_ChainPosClassSet ) )
goto Fail2;
cpcs = ccpf2->ChainPosClassSet;
for ( n = 0; n < count; n++ )
{
if ( ACCESS_Frame( 2L ) )
goto Fail1;
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_ChainPosClassSet( ccpf2, &cpcs[n],
stream ) ) != FT_Err_Ok )
goto Fail1;
(void)FILE_Seek( cur_offset );
}
else
{
/* we create a ChainPosClassSet table with no entries */
ccpf2->ChainPosClassSet[n].ChainPosClassRuleCount = 0;
ccpf2->ChainPosClassSet[n].ChainPosClassRule = NULL;
}
}
return FT_Err_Ok;
Fail1:
for ( m = 0; m < n; m++ )
Free_ChainPosClassSet( &cpcs[m], memory );
FREE( cpcs );
Fail2:
_HB_OPEN_Free_ClassDefinition( &ccpf2->LookaheadClassDef, memory );
Fail3:
_HB_OPEN_Free_ClassDefinition( &ccpf2->InputClassDef, memory );
Fail4:
_HB_OPEN_Free_ClassDefinition( &ccpf2->BacktrackClassDef, memory );
Fail5:
_HB_OPEN_Free_Coverage( &ccpf2->Coverage, memory );
return error;
}
static void Free_ChainContextPos2( HB_ChainContextPosFormat2* ccpf2,
FT_Memory memory )
{
FT_UShort n, count;
HB_ChainPosClassSet* cpcs;
if ( ccpf2->ChainPosClassSet )
{
count = ccpf2->ChainPosClassSetCount;
cpcs = ccpf2->ChainPosClassSet;
for ( n = 0; n < count; n++ )
Free_ChainPosClassSet( &cpcs[n], memory );
FREE( cpcs );
}
_HB_OPEN_Free_ClassDefinition( &ccpf2->LookaheadClassDef, memory );
_HB_OPEN_Free_ClassDefinition( &ccpf2->InputClassDef, memory );
_HB_OPEN_Free_ClassDefinition( &ccpf2->BacktrackClassDef, memory );
_HB_OPEN_Free_Coverage( &ccpf2->Coverage, memory );
}
/* ChainContextPosFormat3 */
static FT_Error Load_ChainContextPos3( HB_ChainContextPosFormat3* ccpf3,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_UShort n, nb, ni, nl, m, count;
FT_UShort backtrack_count, input_count, lookahead_count;
FT_ULong cur_offset, new_offset, base_offset;
HB_Coverage* b;
HB_Coverage* i;
HB_Coverage* l;
HB_PosLookupRecord* plr;
base_offset = FILE_Pos() - 2L;
if ( ACCESS_Frame( 2L ) )
return error;
ccpf3->BacktrackGlyphCount = GET_UShort();
FORGET_Frame();
ccpf3->BacktrackCoverage = NULL;
backtrack_count = ccpf3->BacktrackGlyphCount;
if ( ALLOC_ARRAY( ccpf3->BacktrackCoverage, backtrack_count,
HB_Coverage ) )
return error;
b = ccpf3->BacktrackCoverage;
for ( nb = 0; nb < backtrack_count; nb++ )
{
if ( ACCESS_Frame( 2L ) )
goto Fail4;
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Coverage( &b[nb], stream ) ) != FT_Err_Ok )
goto Fail4;
(void)FILE_Seek( cur_offset );
}
if ( ACCESS_Frame( 2L ) )
goto Fail4;
ccpf3->InputGlyphCount = GET_UShort();
FORGET_Frame();
ccpf3->InputCoverage = NULL;
input_count = ccpf3->InputGlyphCount;
if ( ALLOC_ARRAY( ccpf3->InputCoverage, input_count, HB_Coverage ) )
goto Fail4;
i = ccpf3->InputCoverage;
for ( ni = 0; ni < input_count; ni++ )
{
if ( ACCESS_Frame( 2L ) )
goto Fail3;
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Coverage( &i[ni], stream ) ) != FT_Err_Ok )
goto Fail3;
(void)FILE_Seek( cur_offset );
}
if ( ACCESS_Frame( 2L ) )
goto Fail3;
ccpf3->LookaheadGlyphCount = GET_UShort();
FORGET_Frame();
ccpf3->LookaheadCoverage = NULL;
lookahead_count = ccpf3->LookaheadGlyphCount;
if ( ALLOC_ARRAY( ccpf3->LookaheadCoverage, lookahead_count,
HB_Coverage ) )
goto Fail3;
l = ccpf3->LookaheadCoverage;
for ( nl = 0; nl < lookahead_count; nl++ )
{
if ( ACCESS_Frame( 2L ) )
goto Fail2;
new_offset = GET_UShort() + base_offset;
FORGET_Frame();
cur_offset = FILE_Pos();
if ( FILE_Seek( new_offset ) ||
( error = _HB_OPEN_Load_Coverage( &l[nl], stream ) ) != FT_Err_Ok )
goto Fail2;
(void)FILE_Seek( cur_offset );
}
if ( ACCESS_Frame( 2L ) )
goto Fail2;
ccpf3->PosCount = GET_UShort();
FORGET_Frame();
ccpf3->PosLookupRecord = NULL;
count = ccpf3->PosCount;
if ( ALLOC_ARRAY( ccpf3->PosLookupRecord, count, HB_PosLookupRecord ) )
goto Fail2;
plr = ccpf3->PosLookupRecord;
if ( ACCESS_Frame( count * 4L ) )
goto Fail1;
for ( n = 0; n < count; n++ )
{
plr[n].SequenceIndex = GET_UShort();
plr[n].LookupListIndex = GET_UShort();
}
FORGET_Frame();
return FT_Err_Ok;
Fail1:
FREE( plr );
Fail2:
for ( m = 0; m < nl; nl++ )
_HB_OPEN_Free_Coverage( &l[m], memory );
FREE( l );
Fail3:
for ( m = 0; m < ni; n++ )
_HB_OPEN_Free_Coverage( &i[m], memory );
FREE( i );
Fail4:
for ( m = 0; m < nb; n++ )
_HB_OPEN_Free_Coverage( &b[m], memory );
FREE( b );
return error;
}
static void Free_ChainContextPos3( HB_ChainContextPosFormat3* ccpf3,
FT_Memory memory )
{
FT_UShort n, count;
HB_Coverage* c;
FREE( ccpf3->PosLookupRecord );
if ( ccpf3->LookaheadCoverage )
{
count = ccpf3->LookaheadGlyphCount;
c = ccpf3->LookaheadCoverage;
for ( n = 0; n < count; n++ )
_HB_OPEN_Free_Coverage( &c[n], memory );
FREE( c );
}
if ( ccpf3->InputCoverage )
{
count = ccpf3->InputGlyphCount;
c = ccpf3->InputCoverage;
for ( n = 0; n < count; n++ )
_HB_OPEN_Free_Coverage( &c[n], memory );
FREE( c );
}
if ( ccpf3->BacktrackCoverage )
{
count = ccpf3->BacktrackGlyphCount;
c = ccpf3->BacktrackCoverage;
for ( n = 0; n < count; n++ )
_HB_OPEN_Free_Coverage( &c[n], memory );
FREE( c );
}
}
/* ChainContextPos */
static FT_Error Load_ChainContextPos( HB_GPOS_SubTable* st,
FT_Stream stream )
{
FT_Error error;
HB_ChainContextPos* ccp = &st->chain;
if ( ACCESS_Frame( 2L ) )
return error;
ccp->PosFormat = GET_UShort();
FORGET_Frame();
switch ( ccp->PosFormat )
{
case 1:
return Load_ChainContextPos1( &ccp->ccpf.ccpf1, stream );
case 2:
return Load_ChainContextPos2( &ccp->ccpf.ccpf2, stream );
case 3:
return Load_ChainContextPos3( &ccp->ccpf.ccpf3, stream );
default:
return HB_Err_Invalid_GPOS_SubTable_Format;
}
return FT_Err_Ok; /* never reached */
}
static void Free_ChainContextPos( HB_GPOS_SubTable* st,
FT_Memory memory )
{
HB_ChainContextPos* ccp = &st->chain;
switch ( ccp->PosFormat )
{
case 1:
Free_ChainContextPos1( &ccp->ccpf.ccpf1, memory );
break;
case 2:
Free_ChainContextPos2( &ccp->ccpf.ccpf2, memory );
break;
case 3:
Free_ChainContextPos3( &ccp->ccpf.ccpf3, memory );
break;
}
}
static FT_Error Lookup_ChainContextPos1(
GPOS_Instance* gpi,
HB_ChainContextPosFormat1* ccpf1,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
FT_UShort index, property;
FT_UShort i, j, k, num_cpr;
FT_UShort bgc, igc, lgc;
FT_Error error;
HB_GPOSHeader* gpos = gpi->gpos;
HB_ChainPosRule* cpr;
HB_ChainPosRule curr_cpr;
HB_GDEFHeader* gdef;
gdef = gpos->gdef;
if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
return error;
error = _HB_OPEN_Coverage_Index( &ccpf1->Coverage, IN_CURGLYPH(), &index );
if ( error )
return error;
cpr = ccpf1->ChainPosRuleSet[index].ChainPosRule;
num_cpr = ccpf1->ChainPosRuleSet[index].ChainPosRuleCount;
for ( k = 0; k < num_cpr; k++ )
{
curr_cpr = cpr[k];
bgc = curr_cpr.BacktrackGlyphCount;
igc = curr_cpr.InputGlyphCount;
lgc = curr_cpr.LookaheadGlyphCount;
if ( context_length != 0xFFFF && context_length < igc )
goto next_chainposrule;
/* check whether context is too long; it is a first guess only */
if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
goto next_chainposrule;
if ( bgc )
{
/* Since we don't know in advance the number of glyphs to inspect,
we search backwards for matches in the backtrack glyph array */
for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
{
while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
{
if ( error && error != HB_Err_Not_Covered )
return error;
if ( j + 1 == bgc - i )
goto next_chainposrule;
j--;
}
/* In OpenType 1.3, it is undefined whether the offsets of
backtrack glyphs is in logical order or not. Version 1.4
will clarify this:
Logical order - a b c d e f g h i j
i
Input offsets - 0 1
Backtrack offsets - 3 2 1 0
Lookahead offsets - 0 1 2 3 */
if ( IN_GLYPH( j ) != curr_cpr.Backtrack[i] )
goto next_chainposrule;
}
}
/* Start at 1 because [0] is implied */
for ( i = 1, j = buffer->in_pos + 1; i < igc; i++, j++ )
{
while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
{
if ( error && error != HB_Err_Not_Covered )
return error;
if ( j + igc - i + lgc == buffer->in_length )
goto next_chainposrule;
j++;
}
if ( IN_GLYPH( j ) != curr_cpr.Input[i - 1] )
goto next_chainposrule;
}
/* we are starting to check for lookahead glyphs right after the
last context glyph */
for ( i = 0; i < lgc; i++, j++ )
{
while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
{
if ( error && error != HB_Err_Not_Covered )
return error;
if ( j + lgc - i == buffer->in_length )
goto next_chainposrule;
j++;
}
if ( IN_GLYPH( j ) != curr_cpr.Lookahead[i] )
goto next_chainposrule;
}
return Do_ContextPos( gpi, igc,
curr_cpr.PosCount,
curr_cpr.PosLookupRecord,
buffer,
nesting_level );
next_chainposrule:
;
}
return HB_Err_Not_Covered;
}
static FT_Error Lookup_ChainContextPos2(
GPOS_Instance* gpi,
HB_ChainContextPosFormat2* ccpf2,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
FT_UShort index, property;
FT_Memory memory = gpi->face->memory;
FT_Error error;
FT_UShort i, j, k;
FT_UShort bgc, igc, lgc;
FT_UShort known_backtrack_classes,
known_input_classes,
known_lookahead_classes;
FT_UShort* backtrack_classes;
FT_UShort* input_classes;
FT_UShort* lookahead_classes;
FT_UShort* bc;
FT_UShort* ic;
FT_UShort* lc;
HB_GPOSHeader* gpos = gpi->gpos;
HB_ChainPosClassSet* cpcs;
HB_ChainPosClassRule cpcr;
HB_GDEFHeader* gdef;
gdef = gpos->gdef;
if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
return error;
/* Note: The coverage table in format 2 doesn't give an index into
anything. It just lets us know whether or not we need to
do any lookup at all. */
error = _HB_OPEN_Coverage_Index( &ccpf2->Coverage, IN_CURGLYPH(), &index );
if ( error )
return error;
if ( ALLOC_ARRAY( backtrack_classes, ccpf2->MaxBacktrackLength, FT_UShort ) )
return error;
known_backtrack_classes = 0;
if ( ALLOC_ARRAY( input_classes, ccpf2->MaxInputLength, FT_UShort ) )
goto End3;
known_input_classes = 1;
if ( ALLOC_ARRAY( lookahead_classes, ccpf2->MaxLookaheadLength, FT_UShort ) )
goto End2;
known_lookahead_classes = 0;
error = _HB_OPEN_Get_Class( &ccpf2->InputClassDef, IN_CURGLYPH(),
&input_classes[0], NULL );
if ( error && error != HB_Err_Not_Covered )
goto End1;
cpcs = &ccpf2->ChainPosClassSet[input_classes[0]];
if ( !cpcs )
{
error = HB_Err_Invalid_GPOS_SubTable;
goto End1;
}
for ( k = 0; k < cpcs->ChainPosClassRuleCount; k++ )
{
cpcr = cpcs->ChainPosClassRule[k];
bgc = cpcr.BacktrackGlyphCount;
igc = cpcr.InputGlyphCount;
lgc = cpcr.LookaheadGlyphCount;
if ( context_length != 0xFFFF && context_length < igc )
goto next_chainposclassrule;
/* check whether context is too long; it is a first guess only */
if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
goto next_chainposclassrule;
if ( bgc )
{
/* Since we don't know in advance the number of glyphs to inspect,
we search backwards for matches in the backtrack glyph array.
Note that `known_backtrack_classes' starts at index 0. */
bc = cpcr.Backtrack;
for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
{
while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
{
if ( error && error != HB_Err_Not_Covered )
goto End1;
if ( j + 1 == bgc - i )
goto next_chainposclassrule;
j++;
}
if ( i >= known_backtrack_classes )
{
/* Keeps us from having to do this for each rule */
error = _HB_OPEN_Get_Class( &ccpf2->BacktrackClassDef, IN_GLYPH( j ),
&backtrack_classes[i], NULL );
if ( error && error != HB_Err_Not_Covered )
goto End1;
known_backtrack_classes = i;
}
if ( bc[i] != backtrack_classes[i] )
goto next_chainposclassrule;
}
}
ic = cpcr.Input;
/* Start at 1 because [0] is implied */
for ( i = 1, j = buffer->in_pos + 1; i < igc; i++, j++ )
{
while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
{
if ( error && error != HB_Err_Not_Covered )
goto End1;
if ( j + igc - i + lgc == buffer->in_length )
goto next_chainposclassrule;
j++;
}
if ( i >= known_input_classes )
{
error = _HB_OPEN_Get_Class( &ccpf2->InputClassDef, IN_GLYPH( j ),
&input_classes[i], NULL );
if ( error && error != HB_Err_Not_Covered )
goto End1;
known_input_classes = i;
}
if ( ic[i - 1] != input_classes[i] )
goto next_chainposclassrule;
}
/* we are starting to check for lookahead glyphs right after the
last context glyph */
lc = cpcr.Lookahead;
for ( i = 0; i < lgc; i++, j++ )
{
while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
{
if ( error && error != HB_Err_Not_Covered )
goto End1;
if ( j + lgc - i == buffer->in_length )
goto next_chainposclassrule;
j++;
}
if ( i >= known_lookahead_classes )
{
error = _HB_OPEN_Get_Class( &ccpf2->LookaheadClassDef, IN_GLYPH( j ),
&lookahead_classes[i], NULL );
if ( error && error != HB_Err_Not_Covered )
goto End1;
known_lookahead_classes = i;
}
if ( lc[i] != lookahead_classes[i] )
goto next_chainposclassrule;
}
error = Do_ContextPos( gpi, igc,
cpcr.PosCount,
cpcr.PosLookupRecord,
buffer,
nesting_level );
goto End1;
next_chainposclassrule:
;
}
error = HB_Err_Not_Covered;
End1:
FREE( lookahead_classes );
End2:
FREE( input_classes );
End3:
FREE( backtrack_classes );
return error;
}
static FT_Error Lookup_ChainContextPos3(
GPOS_Instance* gpi,
HB_ChainContextPosFormat3* ccpf3,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
FT_UShort index, i, j, property;
FT_UShort bgc, igc, lgc;
FT_Error error;
HB_GPOSHeader* gpos = gpi->gpos;
HB_Coverage* bc;
HB_Coverage* ic;
HB_Coverage* lc;
HB_GDEFHeader* gdef;
gdef = gpos->gdef;
if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
return error;
bgc = ccpf3->BacktrackGlyphCount;
igc = ccpf3->InputGlyphCount;
lgc = ccpf3->LookaheadGlyphCount;
if ( context_length != 0xFFFF && context_length < igc )
return HB_Err_Not_Covered;
/* check whether context is too long; it is a first guess only */
if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
return HB_Err_Not_Covered;
if ( bgc )
{
/* Since we don't know in advance the number of glyphs to inspect,
we search backwards for matches in the backtrack glyph array */
bc = ccpf3->BacktrackCoverage;
for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
{
while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
{
if ( error && error != HB_Err_Not_Covered )
return error;
if ( j + 1 == bgc - i )
return HB_Err_Not_Covered;
j--;
}
error = _HB_OPEN_Coverage_Index( &bc[i], IN_GLYPH( j ), &index );
if ( error )
return error;
}
}
ic = ccpf3->InputCoverage;
for ( i = 0, j = buffer->in_pos; i < igc; i++, j++ )
{
/* We already called CHECK_Property for IN_GLYPH ( buffer->in_pos ) */
while ( j > buffer->in_pos && CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
{
if ( error && error != HB_Err_Not_Covered )
return error;
if ( j + igc - i + lgc == buffer->in_length )
return HB_Err_Not_Covered;
j++;
}
error = _HB_OPEN_Coverage_Index( &ic[i], IN_GLYPH( j ), &index );
if ( error )
return error;
}
/* we are starting to check for lookahead glyphs right after the
last context glyph */
lc = ccpf3->LookaheadCoverage;
for ( i = 0; i < lgc; i++, j++ )
{
while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
{
if ( error && error != HB_Err_Not_Covered )
return error;
if ( j + lgc - i == buffer->in_length )
return HB_Err_Not_Covered;
j++;
}
error = _HB_OPEN_Coverage_Index( &lc[i], IN_GLYPH( j ), &index );
if ( error )
return error;
}
return Do_ContextPos( gpi, igc,
ccpf3->PosCount,
ccpf3->PosLookupRecord,
buffer,
nesting_level );
}
static FT_Error Lookup_ChainContextPos(
GPOS_Instance* gpi,
HB_GPOS_SubTable* st,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level )
{
HB_ChainContextPos* ccp = &st->chain;
switch ( ccp->PosFormat )
{
case 1:
return Lookup_ChainContextPos1( gpi, &ccp->ccpf.ccpf1, buffer,
flags, context_length,
nesting_level );
case 2:
return Lookup_ChainContextPos2( gpi, &ccp->ccpf.ccpf2, buffer,
flags, context_length,
nesting_level );
case 3:
return Lookup_ChainContextPos3( gpi, &ccp->ccpf.ccpf3, buffer,
flags, context_length,
nesting_level );
default:
return HB_Err_Invalid_GPOS_SubTable_Format;
}
return FT_Err_Ok; /* never reached */
}
/***********
* GPOS API
***********/
FT_Error HB_GPOS_Select_Script( HB_GPOSHeader* gpos,
FT_ULong script_tag,
FT_UShort* script_index )
{
FT_UShort n;
HB_ScriptList* sl;
HB_ScriptRecord* sr;
if ( !gpos || !script_index )
return FT_Err_Invalid_Argument;
sl = &gpos->ScriptList;
sr = sl->ScriptRecord;
for ( n = 0; n < sl->ScriptCount; n++ )
if ( script_tag == sr[n].ScriptTag )
{
*script_index = n;
return FT_Err_Ok;
}
return HB_Err_Not_Covered;
}
FT_Error HB_GPOS_Select_Language( HB_GPOSHeader* gpos,
FT_ULong language_tag,
FT_UShort script_index,
FT_UShort* language_index,
FT_UShort* req_feature_index )
{
FT_UShort n;
HB_ScriptList* sl;
HB_ScriptRecord* sr;
HB_Script* s;
HB_LangSysRecord* lsr;
if ( !gpos || !language_index || !req_feature_index )
return FT_Err_Invalid_Argument;
sl = &gpos->ScriptList;
sr = sl->ScriptRecord;
if ( script_index >= sl->ScriptCount )
return FT_Err_Invalid_Argument;
s = &sr[script_index].Script;
lsr = s->LangSysRecord;
for ( n = 0; n < s->LangSysCount; n++ )
if ( language_tag == lsr[n].LangSysTag )
{
*language_index = n;
*req_feature_index = lsr[n].LangSys.ReqFeatureIndex;
return FT_Err_Ok;
}
return HB_Err_Not_Covered;
}
/* selecting 0xFFFF for language_index asks for the values of the
default language (DefaultLangSys) */
FT_Error HB_GPOS_Select_Feature( HB_GPOSHeader* gpos,
FT_ULong feature_tag,
FT_UShort script_index,
FT_UShort language_index,
FT_UShort* feature_index )
{
FT_UShort n;
HB_ScriptList* sl;
HB_ScriptRecord* sr;
HB_Script* s;
HB_LangSysRecord* lsr;
HB_LangSys* ls;
FT_UShort* fi;
HB_FeatureList* fl;
HB_FeatureRecord* fr;
if ( !gpos || !feature_index )
return FT_Err_Invalid_Argument;
sl = &gpos->ScriptList;
sr = sl->ScriptRecord;
fl = &gpos->FeatureList;
fr = fl->FeatureRecord;
if ( script_index >= sl->ScriptCount )
return FT_Err_Invalid_Argument;
s = &sr[script_index].Script;
lsr = s->LangSysRecord;
if ( language_index == 0xFFFF )
ls = &s->DefaultLangSys;
else
{
if ( language_index >= s->LangSysCount )
return FT_Err_Invalid_Argument;
ls = &lsr[language_index].LangSys;
}
fi = ls->FeatureIndex;
for ( n = 0; n < ls->FeatureCount; n++ )
{
if ( fi[n] >= fl->FeatureCount )
return HB_Err_Invalid_GPOS_SubTable_Format;
if ( feature_tag == fr[fi[n]].FeatureTag )
{
*feature_index = fi[n];
return FT_Err_Ok;
}
}
return HB_Err_Not_Covered;
}
/* The next three functions return a null-terminated list */
FT_Error HB_GPOS_Query_Scripts( HB_GPOSHeader* gpos,
FT_ULong** script_tag_list )
{
FT_Error error;
FT_Memory memory = gpos->memory;
FT_UShort n;
FT_ULong* stl;
HB_ScriptList* sl;
HB_ScriptRecord* sr;
if ( !gpos || !script_tag_list )
return FT_Err_Invalid_Argument;
sl = &gpos->ScriptList;
sr = sl->ScriptRecord;
if ( ALLOC_ARRAY( stl, sl->ScriptCount + 1, FT_ULong ) )
return error;
for ( n = 0; n < sl->ScriptCount; n++ )
stl[n] = sr[n].ScriptTag;
stl[n] = 0;
*script_tag_list = stl;
return FT_Err_Ok;
}
FT_Error HB_GPOS_Query_Languages( HB_GPOSHeader* gpos,
FT_UShort script_index,
FT_ULong** language_tag_list )
{
FT_Error error;
FT_Memory memory = gpos->memory;
FT_UShort n;
FT_ULong* ltl;
HB_ScriptList* sl;
HB_ScriptRecord* sr;
HB_Script* s;
HB_LangSysRecord* lsr;
if ( !gpos || !language_tag_list )
return FT_Err_Invalid_Argument;
sl = &gpos->ScriptList;
sr = sl->ScriptRecord;
if ( script_index >= sl->ScriptCount )
return FT_Err_Invalid_Argument;
s = &sr[script_index].Script;
lsr = s->LangSysRecord;
if ( ALLOC_ARRAY( ltl, s->LangSysCount + 1, FT_ULong ) )
return error;
for ( n = 0; n < s->LangSysCount; n++ )
ltl[n] = lsr[n].LangSysTag;
ltl[n] = 0;
*language_tag_list = ltl;
return FT_Err_Ok;
}
/* selecting 0xFFFF for language_index asks for the values of the
default language (DefaultLangSys) */
FT_Error HB_GPOS_Query_Features( HB_GPOSHeader* gpos,
FT_UShort script_index,
FT_UShort language_index,
FT_ULong** feature_tag_list )
{
FT_UShort n;
FT_Error error;
FT_Memory memory = gpos->memory;
FT_ULong* ftl;
HB_ScriptList* sl;
HB_ScriptRecord* sr;
HB_Script* s;
HB_LangSysRecord* lsr;
HB_LangSys* ls;
FT_UShort* fi;
HB_FeatureList* fl;
HB_FeatureRecord* fr;
if ( !gpos || !feature_tag_list )
return FT_Err_Invalid_Argument;
sl = &gpos->ScriptList;
sr = sl->ScriptRecord;
fl = &gpos->FeatureList;
fr = fl->FeatureRecord;
if ( script_index >= sl->ScriptCount )
return FT_Err_Invalid_Argument;
s = &sr[script_index].Script;
lsr = s->LangSysRecord;
if ( language_index == 0xFFFF )
ls = &s->DefaultLangSys;
else
{
if ( language_index >= s->LangSysCount )
return FT_Err_Invalid_Argument;
ls = &lsr[language_index].LangSys;
}
fi = ls->FeatureIndex;
if ( ALLOC_ARRAY( ftl, ls->FeatureCount + 1, FT_ULong ) )
return error;
for ( n = 0; n < ls->FeatureCount; n++ )
{
if ( fi[n] >= fl->FeatureCount )
{
FREE( ftl );
return HB_Err_Invalid_GPOS_SubTable_Format;
}
ftl[n] = fr[fi[n]].FeatureTag;
}
ftl[n] = 0;
*feature_tag_list = ftl;
return FT_Err_Ok;
}
typedef FT_Error (*Lookup_Pos_Func_Type) ( GPOS_Instance* gpi,
HB_GPOS_SubTable* st,
HB_Buffer buffer,
FT_UShort flags,
FT_UShort context_length,
int nesting_level );
static const Lookup_Pos_Func_Type Lookup_Pos_Call_Table[] = {
Lookup_DefaultPos,
Lookup_SinglePos, /* HB_GPOS_LOOKUP_SINGLE 1 */
Lookup_PairPos, /* HB_GPOS_LOOKUP_PAIR 2 */
Lookup_CursivePos, /* HB_GPOS_LOOKUP_CURSIVE 3 */
Lookup_MarkBasePos, /* HB_GPOS_LOOKUP_MARKBASE 4 */
Lookup_MarkLigPos, /* HB_GPOS_LOOKUP_MARKLIG 5 */
Lookup_MarkMarkPos, /* HB_GPOS_LOOKUP_MARKMARK 6 */
Lookup_ContextPos, /* HB_GPOS_LOOKUP_CONTEXT 7 */
Lookup_ChainContextPos, /* HB_GPOS_LOOKUP_CHAIN 8 */
Lookup_DefaultPos, /* HB_GPOS_LOOKUP_EXTENSION 9 */
};
/* Do an individual subtable lookup. Returns FT_Err_Ok if positioning
has been done, or HB_Err_Not_Covered if not. */
static FT_Error GPOS_Do_Glyph_Lookup( GPOS_Instance* gpi,
FT_UShort lookup_index,
HB_Buffer buffer,
FT_UShort context_length,
int nesting_level )
{
FT_Error error = HB_Err_Not_Covered;
FT_UShort i, flags, lookup_count;
HB_GPOSHeader* gpos = gpi->gpos;
HB_Lookup* lo;
int lookup_type;
Lookup_Pos_Func_Type Func;
nesting_level++;
if ( nesting_level > HB_MAX_NESTING_LEVEL )
return HB_Err_Too_Many_Nested_Contexts;
lookup_count = gpos->LookupList.LookupCount;
if (lookup_index >= lookup_count)
return error;
lo = &gpos->LookupList.Lookup[lookup_index];
flags = lo->LookupFlag;
lookup_type = lo->LookupType;
if (lookup_type >= ARRAY_LEN (Lookup_Pos_Call_Table))
lookup_type = 0;
Func = Lookup_Pos_Call_Table[lookup_type];
for ( i = 0; i < lo->SubTableCount; i++ )
{
error = Func ( gpi,
&lo->SubTable[i].st.gpos,
buffer,
flags, context_length,
nesting_level );
/* Check whether we have a successful positioning or an error other
than HB_Err_Not_Covered */
if ( error != HB_Err_Not_Covered )
return error;
}
return HB_Err_Not_Covered;
}
static FT_Error Load_DefaultPos( HB_GPOS_SubTable* st,
FT_Stream stream )
{
return HB_Err_Invalid_GPOS_SubTable_Format;
}
typedef FT_Error (*Load_Pos_Func_Type)( HB_GPOS_SubTable* st,
FT_Stream stream );
static const Load_Pos_Func_Type Load_Pos_Call_Table[] = {
Load_DefaultPos,
Load_SinglePos, /* HB_GPOS_LOOKUP_SINGLE 1 */
Load_PairPos, /* HB_GPOS_LOOKUP_PAIR 2 */
Load_CursivePos, /* HB_GPOS_LOOKUP_CURSIVE 3 */
Load_MarkBasePos, /* HB_GPOS_LOOKUP_MARKBASE 4 */
Load_MarkLigPos, /* HB_GPOS_LOOKUP_MARKLIG 5 */
Load_MarkMarkPos, /* HB_GPOS_LOOKUP_MARKMARK 6 */
Load_ContextPos, /* HB_GPOS_LOOKUP_CONTEXT 7 */
Load_ChainContextPos, /* HB_GPOS_LOOKUP_CHAIN 8 */
Load_DefaultPos, /* HB_GPOS_LOOKUP_EXTENSION 9 */
};
FT_Error _HB_GPOS_Load_SubTable( HB_GPOS_SubTable* st,
FT_Stream stream,
FT_UShort lookup_type )
{
Load_Pos_Func_Type Func;
if (lookup_type >= ARRAY_LEN (Load_Pos_Call_Table))
lookup_type = 0;
Func = Load_Pos_Call_Table[lookup_type];
return Func ( st, stream );
}
static void Free_DefaultPos( HB_GPOS_SubTable* st,
FT_Memory memory )
{
}
typedef void (*Free_Pos_Func_Type)( HB_GPOS_SubTable* st,
FT_Memory memory );
static const Free_Pos_Func_Type Free_Pos_Call_Table[] = {
Free_DefaultPos,
Free_SinglePos, /* HB_GPOS_LOOKUP_SINGLE 1 */
Free_PairPos, /* HB_GPOS_LOOKUP_PAIR 2 */
Free_CursivePos, /* HB_GPOS_LOOKUP_CURSIVE 3 */
Free_MarkBasePos, /* HB_GPOS_LOOKUP_MARKBASE 4 */
Free_MarkLigPos, /* HB_GPOS_LOOKUP_MARKLIG 5 */
Free_MarkMarkPos, /* HB_GPOS_LOOKUP_MARKMARK 6 */
Free_ContextPos, /* HB_GPOS_LOOKUP_CONTEXT 7 */
Free_ChainContextPos, /* HB_GPOS_LOOKUP_CHAIN 8 */
Free_DefaultPos, /* HB_GPOS_LOOKUP_EXTENSION 9 */
};
void _HB_GPOS_Free_SubTable( HB_GPOS_SubTable* st,
FT_Memory memory,
FT_UShort lookup_type )
{
Free_Pos_Func_Type Func;
if (lookup_type >= ARRAY_LEN (Free_Pos_Call_Table))
lookup_type = 0;
Func = Free_Pos_Call_Table[lookup_type];
Func ( st, memory );
}
/* apply one lookup to the input string object */
static FT_Error GPOS_Do_String_Lookup( GPOS_Instance* gpi,
FT_UShort lookup_index,
HB_Buffer buffer )
{
FT_Error error, retError = HB_Err_Not_Covered;
HB_GPOSHeader* gpos = gpi->gpos;
FT_UInt* properties = gpos->LookupList.Properties;
int nesting_level = 0;
gpi->last = 0xFFFF; /* no last valid glyph for cursive pos. */
buffer->in_pos = 0;
while ( buffer->in_pos < buffer->in_length )
{
if ( ~IN_PROPERTIES( buffer->in_pos ) & properties[lookup_index] )
{
/* 0xFFFF indicates that we don't have a context length yet. */
/* Note that the connection between mark and base glyphs hold
exactly one (string) lookup. For example, it would be possible
that in the first lookup, mark glyph X is attached to base
glyph A, and in the next lookup it is attached to base glyph B.
It is up to the font designer to provide meaningful lookups and
lookup order. */
error = GPOS_Do_Glyph_Lookup( gpi, lookup_index, buffer,
0xFFFF, nesting_level );
if ( error && error != HB_Err_Not_Covered )
return error;
}
else
{
/* Contrary to properties defined in GDEF, user-defined properties
will always stop a possible cursive positioning. */
gpi->last = 0xFFFF;
error = HB_Err_Not_Covered;
}
if ( error == HB_Err_Not_Covered )
(buffer->in_pos)++;
else
retError = error;
}
return retError;
}
static FT_Error Position_CursiveChain ( HB_Buffer buffer )
{
FT_ULong i, j;
HB_Position positions = buffer->positions;
/* First handle all left-to-right connections */
for (j = 0; j < buffer->in_length; j--)
{
if (positions[j].cursive_chain > 0)
positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
}
/* Then handle all right-to-left connections */
for (i = buffer->in_length; i > 0; i--)
{
j = i - 1;
if (positions[j].cursive_chain < 0)
positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
}
return FT_Err_Ok;
}
FT_Error HB_GPOS_Add_Feature( HB_GPOSHeader* gpos,
FT_UShort feature_index,
FT_UInt property )
{
FT_UShort i;
HB_Feature feature;
FT_UInt* properties;
FT_UShort* index;
FT_UShort lookup_count;
/* Each feature can only be added once */
if ( !gpos ||
feature_index >= gpos->FeatureList.FeatureCount ||
gpos->FeatureList.ApplyCount == gpos->FeatureList.FeatureCount )
return FT_Err_Invalid_Argument;
gpos->FeatureList.ApplyOrder[gpos->FeatureList.ApplyCount++] = feature_index;
properties = gpos->LookupList.Properties;
feature = gpos->FeatureList.FeatureRecord[feature_index].Feature;
index = feature.LookupListIndex;
lookup_count = gpos->LookupList.LookupCount;
for ( i = 0; i < feature.LookupListCount; i++ )
{
FT_UShort lookup_index = index[i];
if (lookup_index < lookup_count)
properties[lookup_index] |= property;
}
return FT_Err_Ok;
}
FT_Error HB_GPOS_Clear_Features( HB_GPOSHeader* gpos )
{
FT_UShort i;
FT_UInt* properties;
if ( !gpos )
return FT_Err_Invalid_Argument;
gpos->FeatureList.ApplyCount = 0;
properties = gpos->LookupList.Properties;
for ( i = 0; i < gpos->LookupList.LookupCount; i++ )
properties[i] = 0;
return FT_Err_Ok;
}
FT_Error HB_GPOS_Register_Glyph_Function( HB_GPOSHeader* gpos,
HB_GlyphFunction gfunc )
{
if ( !gpos )
return FT_Err_Invalid_Argument;
gpos->gfunc = gfunc;
return FT_Err_Ok;
}
FT_Error HB_GPOS_Register_MM_Function( HB_GPOSHeader* gpos,
HB_MMFunction mmfunc,
void* data )
{
if ( !gpos )
return FT_Err_Invalid_Argument;
gpos->mmfunc = mmfunc;
gpos->data = data;
return FT_Err_Ok;
}
/* If `dvi' is TRUE, glyph contour points for anchor points and device
tables are ignored -- you will get device independent values. */
FT_Error HB_GPOS_Apply_String( FT_Face face,
HB_GPOSHeader* gpos,
FT_UShort load_flags,
HB_Buffer buffer,
FT_Bool dvi,
FT_Bool r2l )
{
FT_Error error, retError = HB_Err_Not_Covered;
GPOS_Instance gpi;
FT_UShort i, j, feature_index, lookup_count;
HB_Feature feature;
if ( !face || !gpos ||
!buffer || buffer->in_length == 0 || buffer->in_pos >= buffer->in_length )
return FT_Err_Invalid_Argument;
gpi.face = face;
gpi.gpos = gpos;
gpi.load_flags = load_flags;
gpi.r2l = r2l;
gpi.dvi = dvi;
lookup_count = gpos->LookupList.LookupCount;
for ( i = 0; i < gpos->FeatureList.ApplyCount; i++ )
{
/* index of i'th feature */
feature_index = gpos->FeatureList.ApplyOrder[i];
feature = gpos->FeatureList.FeatureRecord[feature_index].Feature;
for ( j = 0; j < feature.LookupListCount; j++ )
{
FT_UShort lookup_index = feature.LookupListIndex[j];
/* Skip nonexistant lookups */
if (lookup_index >= lookup_count)
continue;
error = GPOS_Do_String_Lookup( &gpi, lookup_index, buffer );
if ( error )
{
if ( error != HB_Err_Not_Covered )
return error;
}
else
retError = error;
}
}
error = Position_CursiveChain ( buffer );
if ( error )
return error;
return retError;
}
/* END */