6126 lines
130 KiB
C
6126 lines
130 KiB
C
/*
|
|
* Copyright (C) 1998-2004 David Turner and Werner Lemberg
|
|
* Copyright (C) 2006 Behdad Esfahbod
|
|
* Copyright (C) 2007 Red Hat, Inc.
|
|
*
|
|
* This is part of HarfBuzz, an OpenType Layout engine library.
|
|
*
|
|
* Permission is hereby granted, without written agreement and without
|
|
* license or royalty fees, to use, copy, modify, and distribute this
|
|
* software and its documentation for any purpose, provided that the
|
|
* above copyright notice and the following two paragraphs appear in
|
|
* all copies of this software.
|
|
*
|
|
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
|
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
|
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
|
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
* DAMAGE.
|
|
*
|
|
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
|
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
|
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
|
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|
*
|
|
* Red Hat Author(s): Behdad Esfahbod
|
|
*/
|
|
|
|
#include "harfbuzz-impl.h"
|
|
#include "harfbuzz-gpos-private.h"
|
|
#include "harfbuzz-open-private.h"
|
|
#include "harfbuzz-gdef-private.h"
|
|
|
|
struct GPOS_Instance_
|
|
{
|
|
HB_GPOSHeader* gpos;
|
|
HB_Font font;
|
|
HB_Bool dvi;
|
|
HB_UShort load_flags; /* how the glyph should be loaded */
|
|
HB_Bool r2l;
|
|
|
|
HB_UShort last; /* the last valid glyph -- used
|
|
with cursive positioning */
|
|
HB_Fixed anchor_x; /* the coordinates of the anchor point */
|
|
HB_Fixed anchor_y; /* of the last valid glyph */
|
|
};
|
|
|
|
typedef struct GPOS_Instance_ GPOS_Instance;
|
|
|
|
|
|
static HB_Error GPOS_Do_Glyph_Lookup( GPOS_Instance* gpi,
|
|
HB_UShort lookup_index,
|
|
HB_Buffer buffer,
|
|
HB_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 HB_Error default_mmfunc( HB_Font font,
|
|
HB_UShort metric_id,
|
|
HB_Fixed* metric_value,
|
|
void* data )
|
|
{
|
|
HB_UNUSED(font);
|
|
HB_UNUSED(metric_id);
|
|
HB_UNUSED(metric_value);
|
|
HB_UNUSED(data);
|
|
return ERR(HB_Err_Not_Covered); /* ERR() call intended */
|
|
}
|
|
|
|
|
|
|
|
HB_Error HB_Load_GPOS_Table( HB_Font font,
|
|
HB_GPOSHeader** retptr,
|
|
HB_GDEFHeader* gdef )
|
|
{
|
|
HB_UInt cur_offset, new_offset, base_offset;
|
|
|
|
HB_GPOSHeader* gpos;
|
|
|
|
HB_Stream stream = font->stream;
|
|
HB_Error error;
|
|
|
|
|
|
if ( !retptr )
|
|
return ERR(HB_Err_Invalid_Argument);
|
|
|
|
if ( GOTO_Table( TTAG_GPOS ) )
|
|
return error;
|
|
|
|
base_offset = FILE_Pos();
|
|
|
|
if ( ALLOC ( gpos, sizeof( *gpos ) ) )
|
|
return error;
|
|
|
|
gpos->gfunc = (HB_GlyphFunction) 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 ) ) != HB_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 ) ) != HB_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 ) ) != HB_Err_Ok )
|
|
goto Fail2;
|
|
|
|
gpos->gdef = gdef; /* can be NULL */
|
|
|
|
if ( ( error = _HB_GDEF_LoadMarkAttachClassDef_From_LookupFlags( gdef, stream,
|
|
gpos->LookupList.Lookup,
|
|
gpos->LookupList.LookupCount ) ) )
|
|
goto Fail1;
|
|
|
|
*retptr = gpos;
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail1:
|
|
_HB_OPEN_Free_LookupList( &gpos->LookupList, HB_Type_GPOS );
|
|
|
|
Fail2:
|
|
_HB_OPEN_Free_FeatureList( &gpos->FeatureList );
|
|
|
|
Fail3:
|
|
_HB_OPEN_Free_ScriptList( &gpos->ScriptList );
|
|
|
|
Fail4:
|
|
FREE( gpos );
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
HB_Error HB_Done_GPOS_Table( HB_GPOSHeader* gpos )
|
|
{
|
|
_HB_OPEN_Free_LookupList( &gpos->LookupList, HB_Type_GPOS );
|
|
_HB_OPEN_Free_FeatureList( &gpos->FeatureList );
|
|
_HB_OPEN_Free_ScriptList( &gpos->ScriptList );
|
|
|
|
FREE( gpos );
|
|
|
|
return HB_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 HB_Error Load_ValueRecord( HB_ValueRecord* vr,
|
|
HB_UShort format,
|
|
HB_UInt base_offset,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UInt 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 ) ) != HB_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 ) ) != HB_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 ) ) != HB_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 ) ) != HB_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 HB_Err_Ok;
|
|
|
|
Fail1:
|
|
_HB_OPEN_Free_Device( &vr->YAdvanceDevice );
|
|
|
|
Fail2:
|
|
_HB_OPEN_Free_Device( &vr->XAdvanceDevice );
|
|
|
|
Fail3:
|
|
_HB_OPEN_Free_Device( &vr->YPlacementDevice );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_ValueRecord( HB_ValueRecord* vr,
|
|
HB_UShort format )
|
|
{
|
|
if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
|
|
_HB_OPEN_Free_Device( &vr->YAdvanceDevice );
|
|
if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
|
|
_HB_OPEN_Free_Device( &vr->XAdvanceDevice );
|
|
if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
|
|
_HB_OPEN_Free_Device( &vr->YPlacementDevice );
|
|
if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
|
|
_HB_OPEN_Free_Device( &vr->XPlacementDevice );
|
|
}
|
|
|
|
|
|
static HB_Error Get_ValueRecord( GPOS_Instance* gpi,
|
|
HB_ValueRecord* vr,
|
|
HB_UShort format,
|
|
HB_Position gd )
|
|
{
|
|
HB_Fixed value;
|
|
HB_Short pixel_value;
|
|
HB_Error error = HB_Err_Ok;
|
|
HB_GPOSHeader* gpos = gpi->gpos;
|
|
|
|
HB_UShort x_ppem, y_ppem;
|
|
HB_16Dot16 x_scale, y_scale;
|
|
|
|
|
|
if ( !format )
|
|
return HB_Err_Ok;
|
|
|
|
x_ppem = gpi->font->size->metrics.x_ppem;
|
|
y_ppem = gpi->font->size->metrics.y_ppem;
|
|
x_scale = gpi->font->size->metrics.x_scale;
|
|
y_scale = gpi->font->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->font, 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->font, 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->font, 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->font, vr->YIdAdvance,
|
|
&value, gpos->data );
|
|
if ( error )
|
|
return error;
|
|
gd->y_advance += value;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/* AnchorFormat1 */
|
|
/* AnchorFormat2 */
|
|
/* AnchorFormat3 */
|
|
/* AnchorFormat4 */
|
|
|
|
static HB_Error Load_Anchor( HB_Anchor* an,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UInt 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 ) ) != HB_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 ) ) != HB_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 ERR(HB_Err_Invalid_SubTable_Format);
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail:
|
|
_HB_OPEN_Free_Device( &an->af.af3.XDeviceTable );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_Anchor( HB_Anchor* an )
|
|
{
|
|
if ( an->PosFormat == 3 )
|
|
{
|
|
_HB_OPEN_Free_Device( &an->af.af3.YDeviceTable );
|
|
_HB_OPEN_Free_Device( &an->af.af3.XDeviceTable );
|
|
}
|
|
}
|
|
|
|
|
|
static HB_Error Get_Anchor( GPOS_Instance* gpi,
|
|
HB_Anchor* an,
|
|
HB_UShort glyph_index,
|
|
HB_Fixed* x_value,
|
|
HB_Fixed* y_value )
|
|
{
|
|
HB_Error error = HB_Err_Ok;
|
|
|
|
FT_Outline outline;
|
|
HB_GPOSHeader* gpos = gpi->gpos;
|
|
HB_UShort ap;
|
|
|
|
HB_Short pixel_value;
|
|
HB_UShort load_flags;
|
|
|
|
HB_UShort x_ppem, y_ppem;
|
|
HB_16Dot16 x_scale, y_scale;
|
|
|
|
|
|
x_ppem = gpi->font->size->metrics.x_ppem;
|
|
y_ppem = gpi->font->size->metrics.y_ppem;
|
|
x_scale = gpi->font->size->metrics.x_scale;
|
|
y_scale = gpi->font->size->metrics.y_scale;
|
|
|
|
switch ( an->PosFormat )
|
|
{
|
|
case 0:
|
|
/* The special case of an empty AnchorTable */
|
|
default:
|
|
|
|
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->font, glyph_index, load_flags );
|
|
if ( error )
|
|
return error;
|
|
|
|
if ( gpi->font->glyph->format != ft_glyph_format_outline )
|
|
return ERR(HB_Err_Invalid_SubTable);
|
|
|
|
ap = an->af.af2.AnchorPoint;
|
|
|
|
outline = gpi->font->glyph->outline;
|
|
|
|
/* if n_points is set to zero, 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 ERR(HB_Err_Invalid_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->font, an->af.af4.XIdAnchor,
|
|
x_value, gpos->data );
|
|
if ( error )
|
|
return error;
|
|
|
|
error = (gpos->mmfunc)( gpi->font, an->af.af4.YIdAnchor,
|
|
y_value, gpos->data );
|
|
if ( error )
|
|
return error;
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/* MarkArray */
|
|
|
|
static HB_Error Load_MarkArray ( HB_MarkArray* ma,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, m, count;
|
|
HB_UInt 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 ) ) != HB_Err_Ok )
|
|
goto Fail;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail:
|
|
for ( m = 0; m < n; m++ )
|
|
Free_Anchor( &mr[m].MarkAnchor );
|
|
|
|
FREE( mr );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_MarkArray( HB_MarkArray* ma )
|
|
{
|
|
HB_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 );
|
|
|
|
FREE( mr );
|
|
}
|
|
}
|
|
|
|
|
|
/* LookupType 1 */
|
|
|
|
/* SinglePosFormat1 */
|
|
/* SinglePosFormat2 */
|
|
|
|
static HB_Error Load_SinglePos( HB_GPOS_SubTable* st,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
HB_SinglePos* sp = &st->single;
|
|
|
|
HB_UShort n, m, count, format;
|
|
HB_UInt 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 ERR(HB_Err_Invalid_SubTable);
|
|
|
|
cur_offset = FILE_Pos();
|
|
if ( FILE_Seek( new_offset ) ||
|
|
( error = _HB_OPEN_Load_Coverage( &sp->Coverage, stream ) ) != HB_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 ERR(HB_Err_Invalid_SubTable_Format);
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail1:
|
|
for ( m = 0; m < n; m++ )
|
|
Free_ValueRecord( &vr[m], format );
|
|
|
|
FREE( vr );
|
|
|
|
Fail2:
|
|
_HB_OPEN_Free_Coverage( &sp->Coverage );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_SinglePos( HB_GPOS_SubTable* st )
|
|
{
|
|
HB_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 );
|
|
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 );
|
|
|
|
FREE( v );
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
_HB_OPEN_Free_Coverage( &sp->Coverage );
|
|
}
|
|
|
|
static HB_Error Lookup_SinglePos( GPOS_Instance* gpi,
|
|
HB_GPOS_SubTable* st,
|
|
HB_Buffer buffer,
|
|
HB_UShort flags,
|
|
HB_UShort context_length,
|
|
int nesting_level )
|
|
{
|
|
HB_UShort index, property;
|
|
HB_Error error;
|
|
HB_GPOSHeader* gpos = gpi->gpos;
|
|
HB_SinglePos* sp = &st->single;
|
|
|
|
HB_UNUSED(nesting_level);
|
|
|
|
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 ERR(HB_Err_Invalid_SubTable);
|
|
error = Get_ValueRecord( gpi, &sp->spf.spf2.Value[index],
|
|
sp->ValueFormat, POSITION( buffer->in_pos ) );
|
|
if ( error )
|
|
return error;
|
|
break;
|
|
|
|
default:
|
|
return ERR(HB_Err_Invalid_SubTable);
|
|
}
|
|
|
|
(buffer->in_pos)++;
|
|
|
|
return HB_Err_Ok;
|
|
}
|
|
|
|
|
|
/* LookupType 2 */
|
|
|
|
/* PairSet */
|
|
|
|
static HB_Error Load_PairSet ( HB_PairSet* ps,
|
|
HB_UShort format1,
|
|
HB_UShort format2,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, m, count;
|
|
HB_UInt 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 );
|
|
goto Fail;
|
|
}
|
|
}
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail:
|
|
for ( m = 0; m < n; m++ )
|
|
{
|
|
if ( format1 )
|
|
Free_ValueRecord( &pvr[m].Value1, format1 );
|
|
if ( format2 )
|
|
Free_ValueRecord( &pvr[m].Value2, format2 );
|
|
}
|
|
|
|
FREE( pvr );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_PairSet( HB_PairSet* ps,
|
|
HB_UShort format1,
|
|
HB_UShort format2 )
|
|
{
|
|
HB_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 );
|
|
if ( format2 )
|
|
Free_ValueRecord( &pvr[n].Value2, format2 );
|
|
}
|
|
|
|
FREE( pvr );
|
|
}
|
|
}
|
|
|
|
|
|
/* PairPosFormat1 */
|
|
|
|
static HB_Error Load_PairPos1( HB_PairPosFormat1* ppf1,
|
|
HB_UShort format1,
|
|
HB_UShort format2,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, m, count;
|
|
HB_UInt 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 ) ) != HB_Err_Ok )
|
|
goto Fail;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail:
|
|
for ( m = 0; m < n; m++ )
|
|
Free_PairSet( &ps[m], format1, format2 );
|
|
|
|
FREE( ps );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_PairPos1( HB_PairPosFormat1* ppf1,
|
|
HB_UShort format1,
|
|
HB_UShort format2 )
|
|
{
|
|
HB_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 );
|
|
|
|
FREE( ps );
|
|
}
|
|
}
|
|
|
|
|
|
/* PairPosFormat2 */
|
|
|
|
static HB_Error Load_PairPos2( HB_PairPosFormat2* ppf2,
|
|
HB_UShort format1,
|
|
HB_UShort format2,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort m, n, k, count1, count2;
|
|
HB_UInt 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 ) ) != HB_Err_Ok )
|
|
return error;
|
|
if ( FILE_Seek( new_offset2 ) ||
|
|
( error = _HB_OPEN_Load_ClassDefinition( &ppf2->ClassDef2, count2,
|
|
stream ) ) != HB_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 );
|
|
goto Fail0;
|
|
}
|
|
}
|
|
}
|
|
|
|
continue;
|
|
|
|
Fail0:
|
|
for ( k = 0; k < n; k++ )
|
|
{
|
|
if ( format1 )
|
|
Free_ValueRecord( &c2r[k].Value1, format1 );
|
|
if ( format2 )
|
|
Free_ValueRecord( &c2r[k].Value2, format2 );
|
|
}
|
|
goto Fail1;
|
|
}
|
|
|
|
return HB_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 );
|
|
if ( format2 )
|
|
Free_ValueRecord( &c2r[n].Value2, format2 );
|
|
}
|
|
|
|
FREE( c2r );
|
|
}
|
|
|
|
FREE( c1r );
|
|
Fail2:
|
|
|
|
_HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef2 );
|
|
|
|
Fail3:
|
|
_HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef1 );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_PairPos2( HB_PairPosFormat2* ppf2,
|
|
HB_UShort format1,
|
|
HB_UShort format2 )
|
|
{
|
|
HB_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 );
|
|
if ( format2 )
|
|
Free_ValueRecord( &c2r[n].Value2, format2 );
|
|
}
|
|
|
|
FREE( c2r );
|
|
}
|
|
|
|
FREE( c1r );
|
|
|
|
_HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef2 );
|
|
_HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef1 );
|
|
}
|
|
}
|
|
|
|
|
|
static HB_Error Load_PairPos( HB_GPOS_SubTable* st,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
HB_PairPos* pp = &st->pair;
|
|
|
|
HB_UShort format1, format2;
|
|
HB_UInt 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 ) ) != HB_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 ERR(HB_Err_Invalid_SubTable_Format);
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail:
|
|
_HB_OPEN_Free_Coverage( &pp->Coverage );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_PairPos( HB_GPOS_SubTable* st )
|
|
{
|
|
HB_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 );
|
|
break;
|
|
|
|
case 2:
|
|
Free_PairPos2( &pp->ppf.ppf2, format1, format2 );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
_HB_OPEN_Free_Coverage( &pp->Coverage );
|
|
}
|
|
|
|
|
|
static HB_Error Lookup_PairPos1( GPOS_Instance* gpi,
|
|
HB_PairPosFormat1* ppf1,
|
|
HB_Buffer buffer,
|
|
HB_UInt first_pos,
|
|
HB_UShort index,
|
|
HB_UShort format1,
|
|
HB_UShort format2 )
|
|
{
|
|
HB_Error error;
|
|
HB_UShort numpvr, glyph2;
|
|
|
|
HB_PairValueRecord* pvr;
|
|
|
|
|
|
if ( index >= ppf1->PairSetCount )
|
|
return ERR(HB_Err_Invalid_SubTable);
|
|
|
|
pvr = ppf1->PairSet[index].PairValueRecord;
|
|
if ( !pvr )
|
|
return ERR(HB_Err_Invalid_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 HB_Error Lookup_PairPos2( GPOS_Instance* gpi,
|
|
HB_PairPosFormat2* ppf2,
|
|
HB_Buffer buffer,
|
|
HB_UInt first_pos,
|
|
HB_UShort format1,
|
|
HB_UShort format2 )
|
|
{
|
|
HB_Error error;
|
|
HB_UShort cl1 = 0, cl2 = 0; /* shut compiler up */
|
|
|
|
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 ERR(HB_Err_Invalid_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 HB_Error Lookup_PairPos( GPOS_Instance* gpi,
|
|
HB_GPOS_SubTable* st,
|
|
HB_Buffer buffer,
|
|
HB_UShort flags,
|
|
HB_UShort context_length,
|
|
int nesting_level )
|
|
{
|
|
HB_Error error;
|
|
HB_UShort index, property;
|
|
HB_UInt first_pos;
|
|
HB_GPOSHeader* gpos = gpi->gpos;
|
|
HB_PairPos* pp = &st->pair;
|
|
|
|
HB_UNUSED(nesting_level);
|
|
|
|
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 )
|
|
{
|
|
buffer->in_pos = first_pos;
|
|
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 ERR(HB_Err_Invalid_SubTable_Format);
|
|
}
|
|
|
|
/* if we don't have coverage for the second glyph don't skip it for
|
|
further lookups but reset in_pos back to the first_glyph and let
|
|
the caller in Do_String_Lookup increment in_pos */
|
|
if ( error == HB_Err_Not_Covered )
|
|
buffer->in_pos = first_pos;
|
|
|
|
/* adjusting the `next' glyph */
|
|
|
|
if ( pp->ValueFormat2 )
|
|
(buffer->in_pos)++;
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/* LookupType 3 */
|
|
|
|
/* CursivePosFormat1 */
|
|
|
|
static HB_Error Load_CursivePos( HB_GPOS_SubTable* st,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
HB_CursivePos* cp = &st->cursive;
|
|
|
|
HB_UShort n, m, count;
|
|
HB_UInt 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 ) ) != HB_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++ )
|
|
{
|
|
HB_UInt 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 ) ) != HB_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 ) ) != HB_Err_Ok )
|
|
{
|
|
if ( entry_offset )
|
|
Free_Anchor( &eer[n].EntryAnchor );
|
|
goto Fail1;
|
|
}
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
else
|
|
eer[n].ExitAnchor.PosFormat = 0;
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail1:
|
|
for ( m = 0; m < n; m++ )
|
|
{
|
|
Free_Anchor( &eer[m].EntryAnchor );
|
|
Free_Anchor( &eer[m].ExitAnchor );
|
|
}
|
|
|
|
FREE( eer );
|
|
|
|
Fail2:
|
|
_HB_OPEN_Free_Coverage( &cp->Coverage );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_CursivePos( HB_GPOS_SubTable* st )
|
|
{
|
|
HB_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 );
|
|
Free_Anchor( &eer[n].ExitAnchor );
|
|
}
|
|
|
|
FREE( eer );
|
|
}
|
|
|
|
_HB_OPEN_Free_Coverage( &cp->Coverage );
|
|
}
|
|
|
|
|
|
static HB_Error Lookup_CursivePos( GPOS_Instance* gpi,
|
|
HB_GPOS_SubTable* st,
|
|
HB_Buffer buffer,
|
|
HB_UShort flags,
|
|
HB_UShort context_length,
|
|
int nesting_level )
|
|
{
|
|
HB_UShort index, property;
|
|
HB_Error error;
|
|
HB_GPOSHeader* gpos = gpi->gpos;
|
|
HB_CursivePos* cp = &st->cursive;
|
|
|
|
HB_EntryExitRecord* eer;
|
|
HB_Fixed entry_x, entry_y;
|
|
HB_Fixed exit_x, exit_y;
|
|
|
|
HB_UNUSED(nesting_level);
|
|
|
|
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 ERR(HB_Err_Invalid_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 HB_Err_Ok;
|
|
}
|
|
|
|
|
|
/* LookupType 4 */
|
|
|
|
/* BaseArray */
|
|
|
|
static HB_Error Load_BaseArray( HB_BaseArray* ba,
|
|
HB_UShort num_classes,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort m, n, count;
|
|
HB_UInt cur_offset, new_offset, base_offset;
|
|
|
|
HB_BaseRecord *br;
|
|
HB_Anchor *ban, *bans;
|
|
|
|
|
|
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;
|
|
|
|
bans = NULL;
|
|
|
|
if ( ALLOC_ARRAY( bans, count * num_classes, HB_Anchor ) )
|
|
goto Fail;
|
|
|
|
for ( m = 0; m < count; m++ )
|
|
{
|
|
br[m].BaseAnchor = NULL;
|
|
|
|
ban = br[m].BaseAnchor = bans + m * num_classes;
|
|
|
|
for ( n = 0; n < num_classes; n++ )
|
|
{
|
|
if ( ACCESS_Frame( 2L ) )
|
|
goto Fail;
|
|
|
|
new_offset = GET_UShort() + base_offset;
|
|
|
|
FORGET_Frame();
|
|
|
|
if (new_offset == base_offset) {
|
|
/* XXX
|
|
* Doulos SIL Regular is buggy and has zero offsets here.
|
|
* Skip it
|
|
*/
|
|
ban[n].PosFormat = 0;
|
|
continue;
|
|
}
|
|
|
|
cur_offset = FILE_Pos();
|
|
if ( FILE_Seek( new_offset ) ||
|
|
( error = Load_Anchor( &ban[n], stream ) ) != HB_Err_Ok )
|
|
goto Fail;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail:
|
|
FREE( bans );
|
|
FREE( br );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_BaseArray( HB_BaseArray* ba,
|
|
HB_UShort num_classes )
|
|
{
|
|
HB_BaseRecord *br;
|
|
HB_Anchor *bans;
|
|
|
|
HB_UNUSED(num_classes);
|
|
|
|
if ( ba->BaseRecord )
|
|
{
|
|
br = ba->BaseRecord;
|
|
bans = br[0].BaseAnchor;
|
|
|
|
FREE( bans );
|
|
FREE( br );
|
|
}
|
|
}
|
|
|
|
|
|
/* MarkBasePosFormat1 */
|
|
|
|
static HB_Error Load_MarkBasePos( HB_GPOS_SubTable* st,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
HB_MarkBasePos* mbp = &st->markbase;
|
|
|
|
HB_UInt 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 ERR(HB_Err_Invalid_SubTable_Format);
|
|
|
|
cur_offset = FILE_Pos();
|
|
if ( FILE_Seek( new_offset ) ||
|
|
( error = _HB_OPEN_Load_Coverage( &mbp->MarkCoverage, stream ) ) != HB_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 ) ) != HB_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 ) ) != HB_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 ) ) != HB_Err_Ok )
|
|
goto Fail1;
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail1:
|
|
Free_MarkArray( &mbp->MarkArray );
|
|
|
|
Fail2:
|
|
_HB_OPEN_Free_Coverage( &mbp->BaseCoverage );
|
|
|
|
Fail3:
|
|
_HB_OPEN_Free_Coverage( &mbp->MarkCoverage );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_MarkBasePos( HB_GPOS_SubTable* st )
|
|
{
|
|
HB_MarkBasePos* mbp = &st->markbase;
|
|
|
|
Free_BaseArray( &mbp->BaseArray, mbp->ClassCount );
|
|
Free_MarkArray( &mbp->MarkArray );
|
|
_HB_OPEN_Free_Coverage( &mbp->BaseCoverage );
|
|
_HB_OPEN_Free_Coverage( &mbp->MarkCoverage );
|
|
}
|
|
|
|
|
|
static HB_Error Lookup_MarkBasePos( GPOS_Instance* gpi,
|
|
HB_GPOS_SubTable* st,
|
|
HB_Buffer buffer,
|
|
HB_UShort flags,
|
|
HB_UShort context_length,
|
|
int nesting_level )
|
|
{
|
|
HB_UShort i, j, mark_index, base_index, property, class;
|
|
HB_Fixed x_mark_value, y_mark_value, x_base_value, y_base_value;
|
|
HB_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;
|
|
|
|
HB_UNUSED(nesting_level);
|
|
|
|
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 ERR(HB_Err_Invalid_SubTable);
|
|
|
|
class = ma->MarkRecord[mark_index].Class;
|
|
mark_anchor = &ma->MarkRecord[mark_index].MarkAnchor;
|
|
|
|
if ( class >= mbp->ClassCount )
|
|
return ERR(HB_Err_Invalid_SubTable);
|
|
|
|
ba = &mbp->BaseArray;
|
|
|
|
if ( base_index >= ba->BaseCount )
|
|
return ERR(HB_Err_Invalid_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 HB_Err_Ok;
|
|
}
|
|
|
|
|
|
/* LookupType 5 */
|
|
|
|
/* LigatureAttach */
|
|
|
|
static HB_Error Load_LigatureAttach( HB_LigatureAttach* lat,
|
|
HB_UShort num_classes,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort m, n, k, count;
|
|
HB_UInt 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 ) ) != HB_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] );
|
|
goto Fail;
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail:
|
|
for ( k = 0; k < m; k++ )
|
|
{
|
|
lan = cr[k].LigatureAnchor;
|
|
|
|
for ( n = 0; n < num_classes; n++ )
|
|
Free_Anchor( &lan[n] );
|
|
|
|
FREE( lan );
|
|
}
|
|
|
|
FREE( cr );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_LigatureAttach( HB_LigatureAttach* lat,
|
|
HB_UShort num_classes )
|
|
{
|
|
HB_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] );
|
|
|
|
FREE( lan );
|
|
}
|
|
|
|
FREE( cr );
|
|
}
|
|
}
|
|
|
|
|
|
/* LigatureArray */
|
|
|
|
static HB_Error Load_LigatureArray( HB_LigatureArray* la,
|
|
HB_UShort num_classes,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, m, count;
|
|
HB_UInt 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 ) ) != HB_Err_Ok )
|
|
goto Fail;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail:
|
|
for ( m = 0; m < n; m++ )
|
|
Free_LigatureAttach( &lat[m], num_classes );
|
|
|
|
FREE( lat );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_LigatureArray( HB_LigatureArray* la,
|
|
HB_UShort num_classes )
|
|
{
|
|
HB_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 );
|
|
|
|
FREE( lat );
|
|
}
|
|
}
|
|
|
|
|
|
/* MarkLigPosFormat1 */
|
|
|
|
static HB_Error Load_MarkLigPos( HB_GPOS_SubTable* st,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
HB_MarkLigPos* mlp = &st->marklig;
|
|
|
|
HB_UInt 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 ) ) != HB_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 ) ) != HB_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 ) ) != HB_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 ) ) != HB_Err_Ok )
|
|
goto Fail1;
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail1:
|
|
Free_MarkArray( &mlp->MarkArray );
|
|
|
|
Fail2:
|
|
_HB_OPEN_Free_Coverage( &mlp->LigatureCoverage );
|
|
|
|
Fail3:
|
|
_HB_OPEN_Free_Coverage( &mlp->MarkCoverage );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_MarkLigPos( HB_GPOS_SubTable* st )
|
|
{
|
|
HB_MarkLigPos* mlp = &st->marklig;
|
|
|
|
Free_LigatureArray( &mlp->LigatureArray, mlp->ClassCount );
|
|
Free_MarkArray( &mlp->MarkArray );
|
|
_HB_OPEN_Free_Coverage( &mlp->LigatureCoverage );
|
|
_HB_OPEN_Free_Coverage( &mlp->MarkCoverage );
|
|
}
|
|
|
|
|
|
static HB_Error Lookup_MarkLigPos( GPOS_Instance* gpi,
|
|
HB_GPOS_SubTable* st,
|
|
HB_Buffer buffer,
|
|
HB_UShort flags,
|
|
HB_UShort context_length,
|
|
int nesting_level )
|
|
{
|
|
HB_UShort i, j, mark_index, lig_index, property, class;
|
|
HB_UShort mark_glyph;
|
|
HB_Fixed x_mark_value, y_mark_value, x_lig_value, y_lig_value;
|
|
HB_Error error;
|
|
HB_GPOSHeader* gpos = gpi->gpos;
|
|
HB_MarkLigPos* mlp = &st->marklig;
|
|
|
|
HB_MarkArray* ma;
|
|
HB_LigatureArray* la;
|
|
HB_LigatureAttach* lat;
|
|
HB_ComponentRecord* cr;
|
|
HB_UShort comp_index;
|
|
HB_Anchor* mark_anchor;
|
|
HB_Anchor* lig_anchor;
|
|
|
|
HB_Position o;
|
|
|
|
HB_UNUSED(nesting_level);
|
|
|
|
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 ERR(HB_Err_Invalid_SubTable);
|
|
|
|
class = ma->MarkRecord[mark_index].Class;
|
|
mark_anchor = &ma->MarkRecord[mark_index].MarkAnchor;
|
|
|
|
if ( class >= mlp->ClassCount )
|
|
return ERR(HB_Err_Invalid_SubTable);
|
|
|
|
la = &mlp->LigatureArray;
|
|
|
|
if ( lig_index >= la->LigatureCount )
|
|
return ERR(HB_Err_Invalid_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 HB_Err_Ok;
|
|
}
|
|
|
|
|
|
/* LookupType 6 */
|
|
|
|
/* Mark2Array */
|
|
|
|
static HB_Error Load_Mark2Array( HB_Mark2Array* m2a,
|
|
HB_UShort num_classes,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort k, m, n, count;
|
|
HB_UInt 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();
|
|
|
|
if (new_offset == base_offset) {
|
|
/* Anchor table not provided. Skip loading.
|
|
* Some versions of FreeSans hit this. */
|
|
m2an[n].PosFormat = 0;
|
|
continue;
|
|
}
|
|
|
|
cur_offset = FILE_Pos();
|
|
if ( FILE_Seek( new_offset ) ||
|
|
( error = Load_Anchor( &m2an[n], stream ) ) != HB_Err_Ok )
|
|
goto Fail0;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
continue;
|
|
Fail0:
|
|
for ( k = 0; k < n; k++ )
|
|
Free_Anchor( &m2an[k] );
|
|
goto Fail;
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail:
|
|
for ( k = 0; k < m; k++ )
|
|
{
|
|
m2an = m2r[k].Mark2Anchor;
|
|
|
|
for ( n = 0; n < num_classes; n++ )
|
|
Free_Anchor( &m2an[n] );
|
|
|
|
FREE( m2an );
|
|
}
|
|
|
|
FREE( m2r );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_Mark2Array( HB_Mark2Array* m2a,
|
|
HB_UShort num_classes )
|
|
{
|
|
HB_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] );
|
|
|
|
FREE( m2an );
|
|
}
|
|
|
|
FREE( m2r );
|
|
}
|
|
}
|
|
|
|
|
|
/* MarkMarkPosFormat1 */
|
|
|
|
static HB_Error Load_MarkMarkPos( HB_GPOS_SubTable* st,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
HB_MarkMarkPos* mmp = &st->markmark;
|
|
|
|
HB_UInt 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 ) ) != HB_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 ) ) != HB_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 ) ) != HB_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 ) ) != HB_Err_Ok )
|
|
goto Fail1;
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail1:
|
|
Free_MarkArray( &mmp->Mark1Array );
|
|
|
|
Fail2:
|
|
_HB_OPEN_Free_Coverage( &mmp->Mark2Coverage );
|
|
|
|
Fail3:
|
|
_HB_OPEN_Free_Coverage( &mmp->Mark1Coverage );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_MarkMarkPos( HB_GPOS_SubTable* st )
|
|
{
|
|
HB_MarkMarkPos* mmp = &st->markmark;
|
|
|
|
Free_Mark2Array( &mmp->Mark2Array, mmp->ClassCount );
|
|
Free_MarkArray( &mmp->Mark1Array );
|
|
_HB_OPEN_Free_Coverage( &mmp->Mark2Coverage );
|
|
_HB_OPEN_Free_Coverage( &mmp->Mark1Coverage );
|
|
}
|
|
|
|
|
|
static HB_Error Lookup_MarkMarkPos( GPOS_Instance* gpi,
|
|
HB_GPOS_SubTable* st,
|
|
HB_Buffer buffer,
|
|
HB_UShort flags,
|
|
HB_UShort context_length,
|
|
int nesting_level )
|
|
{
|
|
HB_UShort i, j, mark1_index, mark2_index, property, class;
|
|
HB_Fixed x_mark1_value, y_mark1_value,
|
|
x_mark2_value, y_mark2_value;
|
|
HB_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;
|
|
|
|
HB_UNUSED(nesting_level);
|
|
|
|
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 search backwards for a suitable mark glyph until a non-mark
|
|
glyph */
|
|
|
|
if ( buffer->in_pos == 0 )
|
|
return HB_Err_Not_Covered;
|
|
|
|
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 ) )
|
|
return HB_Err_Not_Covered;
|
|
|
|
if ( flags & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS )
|
|
{
|
|
if ( property == (flags & 0xFF00) )
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
|
|
i++;
|
|
j--;
|
|
}
|
|
|
|
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 ERR(HB_Err_Invalid_SubTable);
|
|
|
|
class = ma1->MarkRecord[mark1_index].Class;
|
|
mark1_anchor = &ma1->MarkRecord[mark1_index].MarkAnchor;
|
|
|
|
if ( class >= mmp->ClassCount )
|
|
return ERR(HB_Err_Invalid_SubTable);
|
|
|
|
ma2 = &mmp->Mark2Array;
|
|
|
|
if ( mark2_index >= ma2->Mark2Count )
|
|
return ERR(HB_Err_Invalid_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 HB_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 HB_Error Do_ContextPos( GPOS_Instance* gpi,
|
|
HB_UShort GlyphCount,
|
|
HB_UShort PosCount,
|
|
HB_PosLookupRecord* pos,
|
|
HB_Buffer buffer,
|
|
int nesting_level )
|
|
{
|
|
HB_Error error;
|
|
HB_UInt 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 HB_Err_Ok;
|
|
}
|
|
|
|
|
|
/* LookupType 7 */
|
|
|
|
/* PosRule */
|
|
|
|
static HB_Error Load_PosRule( HB_PosRule* pr,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, count;
|
|
HB_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, HB_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 HB_Err_Ok;
|
|
|
|
Fail1:
|
|
FREE( plr );
|
|
|
|
Fail2:
|
|
FREE( i );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_PosRule( HB_PosRule* pr )
|
|
{
|
|
FREE( pr->PosLookupRecord );
|
|
FREE( pr->Input );
|
|
}
|
|
|
|
|
|
/* PosRuleSet */
|
|
|
|
static HB_Error Load_PosRuleSet( HB_PosRuleSet* prs,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, m, count;
|
|
HB_UInt 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 ) ) != HB_Err_Ok )
|
|
goto Fail;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail:
|
|
for ( m = 0; m < n; m++ )
|
|
Free_PosRule( &pr[m] );
|
|
|
|
FREE( pr );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_PosRuleSet( HB_PosRuleSet* prs )
|
|
{
|
|
HB_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] );
|
|
|
|
FREE( pr );
|
|
}
|
|
}
|
|
|
|
|
|
/* ContextPosFormat1 */
|
|
|
|
static HB_Error Load_ContextPos1( HB_ContextPosFormat1* cpf1,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, m, count;
|
|
HB_UInt 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 ) ) != HB_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 ) ) != HB_Err_Ok )
|
|
goto Fail1;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail1:
|
|
for ( m = 0; m < n; m++ )
|
|
Free_PosRuleSet( &prs[m] );
|
|
|
|
FREE( prs );
|
|
|
|
Fail2:
|
|
_HB_OPEN_Free_Coverage( &cpf1->Coverage );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_ContextPos1( HB_ContextPosFormat1* cpf1 )
|
|
{
|
|
HB_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] );
|
|
|
|
FREE( prs );
|
|
}
|
|
|
|
_HB_OPEN_Free_Coverage( &cpf1->Coverage );
|
|
}
|
|
|
|
|
|
/* PosClassRule */
|
|
|
|
static HB_Error Load_PosClassRule( HB_ContextPosFormat2* cpf2,
|
|
HB_PosClassRule* pcr,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, count;
|
|
|
|
HB_UShort* c;
|
|
HB_PosLookupRecord* plr;
|
|
HB_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, HB_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 HB_Err_Ok;
|
|
|
|
Fail1:
|
|
FREE( plr );
|
|
|
|
Fail2:
|
|
FREE( c );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_PosClassRule( HB_PosClassRule* pcr )
|
|
{
|
|
FREE( pcr->PosLookupRecord );
|
|
FREE( pcr->Class );
|
|
}
|
|
|
|
|
|
/* PosClassSet */
|
|
|
|
static HB_Error Load_PosClassSet( HB_ContextPosFormat2* cpf2,
|
|
HB_PosClassSet* pcs,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, m, count;
|
|
HB_UInt 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 ) ) != HB_Err_Ok )
|
|
goto Fail;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail:
|
|
for ( m = 0; m < n; m++ )
|
|
Free_PosClassRule( &pcr[m] );
|
|
|
|
FREE( pcr );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_PosClassSet( HB_PosClassSet* pcs )
|
|
{
|
|
HB_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] );
|
|
|
|
FREE( pcr );
|
|
}
|
|
}
|
|
|
|
|
|
/* ContextPosFormat2 */
|
|
|
|
static HB_Error Load_ContextPos2( HB_ContextPosFormat2* cpf2,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, m, count;
|
|
HB_UInt 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 ) ) != HB_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 ) ) != HB_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 ) ) != HB_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 HB_Err_Ok;
|
|
|
|
Fail1:
|
|
for ( m = 0; m < n; n++ )
|
|
Free_PosClassSet( &pcs[m] );
|
|
|
|
FREE( pcs );
|
|
|
|
Fail2:
|
|
_HB_OPEN_Free_ClassDefinition( &cpf2->ClassDef );
|
|
|
|
Fail3:
|
|
_HB_OPEN_Free_Coverage( &cpf2->Coverage );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_ContextPos2( HB_ContextPosFormat2* cpf2 )
|
|
{
|
|
HB_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] );
|
|
|
|
FREE( pcs );
|
|
}
|
|
|
|
_HB_OPEN_Free_ClassDefinition( &cpf2->ClassDef );
|
|
_HB_OPEN_Free_Coverage( &cpf2->Coverage );
|
|
}
|
|
|
|
|
|
/* ContextPosFormat3 */
|
|
|
|
static HB_Error Load_ContextPos3( HB_ContextPosFormat3* cpf3,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, count;
|
|
HB_UInt 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 ) ) != HB_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 HB_Err_Ok;
|
|
|
|
Fail1:
|
|
FREE( plr );
|
|
|
|
Fail2:
|
|
for ( n = 0; n < count; n++ )
|
|
_HB_OPEN_Free_Coverage( &c[n] );
|
|
|
|
FREE( c );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_ContextPos3( HB_ContextPosFormat3* cpf3 )
|
|
{
|
|
HB_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] );
|
|
|
|
FREE( c );
|
|
}
|
|
}
|
|
|
|
|
|
/* ContextPos */
|
|
|
|
static HB_Error Load_ContextPos( HB_GPOS_SubTable* st,
|
|
HB_Stream stream )
|
|
{
|
|
HB_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 ERR(HB_Err_Invalid_SubTable_Format);
|
|
}
|
|
|
|
return HB_Err_Ok; /* never reached */
|
|
}
|
|
|
|
|
|
static void Free_ContextPos( HB_GPOS_SubTable* st )
|
|
{
|
|
HB_ContextPos* cp = &st->context;
|
|
|
|
switch ( cp->PosFormat )
|
|
{
|
|
case 1: Free_ContextPos1( &cp->cpf.cpf1 ); break;
|
|
case 2: Free_ContextPos2( &cp->cpf.cpf2 ); break;
|
|
case 3: Free_ContextPos3( &cp->cpf.cpf3 ); break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
|
|
static HB_Error Lookup_ContextPos1( GPOS_Instance* gpi,
|
|
HB_ContextPosFormat1* cpf1,
|
|
HB_Buffer buffer,
|
|
HB_UShort flags,
|
|
HB_UShort context_length,
|
|
int nesting_level )
|
|
{
|
|
HB_UShort index, property;
|
|
HB_UShort i, j, k, numpr;
|
|
HB_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 == (HB_Int)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 HB_Error Lookup_ContextPos2( GPOS_Instance* gpi,
|
|
HB_ContextPosFormat2* cpf2,
|
|
HB_Buffer buffer,
|
|
HB_UShort flags,
|
|
HB_UShort context_length,
|
|
int nesting_level )
|
|
{
|
|
HB_UShort index, property;
|
|
HB_Error error;
|
|
HB_UShort i, j, k, known_classes;
|
|
|
|
HB_UShort* classes;
|
|
HB_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, HB_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 = ERR(HB_Err_Invalid_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 == (HB_Int)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 HB_Error Lookup_ContextPos3( GPOS_Instance* gpi,
|
|
HB_ContextPosFormat3* cpf3,
|
|
HB_Buffer buffer,
|
|
HB_UShort flags,
|
|
HB_UShort context_length,
|
|
int nesting_level )
|
|
{
|
|
HB_Error error;
|
|
HB_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 == (HB_Int)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 HB_Error Lookup_ContextPos( GPOS_Instance* gpi,
|
|
HB_GPOS_SubTable* st,
|
|
HB_Buffer buffer,
|
|
HB_UShort flags,
|
|
HB_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 ERR(HB_Err_Invalid_SubTable_Format);
|
|
}
|
|
|
|
return HB_Err_Ok; /* never reached */
|
|
}
|
|
|
|
|
|
/* LookupType 8 */
|
|
|
|
/* ChainPosRule */
|
|
|
|
static HB_Error Load_ChainPosRule( HB_ChainPosRule* cpr,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, count;
|
|
HB_UShort* b;
|
|
HB_UShort* i;
|
|
HB_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, HB_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, HB_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, HB_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 HB_Err_Ok;
|
|
|
|
Fail1:
|
|
FREE( plr );
|
|
|
|
Fail2:
|
|
FREE( l );
|
|
|
|
Fail3:
|
|
FREE( i );
|
|
|
|
Fail4:
|
|
FREE( b );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_ChainPosRule( HB_ChainPosRule* cpr )
|
|
{
|
|
FREE( cpr->PosLookupRecord );
|
|
FREE( cpr->Lookahead );
|
|
FREE( cpr->Input );
|
|
FREE( cpr->Backtrack );
|
|
}
|
|
|
|
|
|
/* ChainPosRuleSet */
|
|
|
|
static HB_Error Load_ChainPosRuleSet( HB_ChainPosRuleSet* cprs,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, m, count;
|
|
HB_UInt 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 ) ) != HB_Err_Ok )
|
|
goto Fail;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail:
|
|
for ( m = 0; m < n; m++ )
|
|
Free_ChainPosRule( &cpr[m] );
|
|
|
|
FREE( cpr );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_ChainPosRuleSet( HB_ChainPosRuleSet* cprs )
|
|
{
|
|
HB_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] );
|
|
|
|
FREE( cpr );
|
|
}
|
|
}
|
|
|
|
|
|
/* ChainContextPosFormat1 */
|
|
|
|
static HB_Error Load_ChainContextPos1( HB_ChainContextPosFormat1* ccpf1,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, m, count;
|
|
HB_UInt 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 ) ) != HB_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 ) ) != HB_Err_Ok )
|
|
goto Fail1;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail1:
|
|
for ( m = 0; m < n; m++ )
|
|
Free_ChainPosRuleSet( &cprs[m] );
|
|
|
|
FREE( cprs );
|
|
|
|
Fail2:
|
|
_HB_OPEN_Free_Coverage( &ccpf1->Coverage );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_ChainContextPos1( HB_ChainContextPosFormat1* ccpf1 )
|
|
{
|
|
HB_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] );
|
|
|
|
FREE( cprs );
|
|
}
|
|
|
|
_HB_OPEN_Free_Coverage( &ccpf1->Coverage );
|
|
}
|
|
|
|
|
|
/* ChainPosClassRule */
|
|
|
|
static HB_Error Load_ChainPosClassRule(
|
|
HB_ChainContextPosFormat2* ccpf2,
|
|
HB_ChainPosClassRule* cpcr,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, count;
|
|
|
|
HB_UShort* b;
|
|
HB_UShort* i;
|
|
HB_UShort* l;
|
|
HB_PosLookupRecord* plr;
|
|
HB_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, HB_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, HB_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, HB_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 HB_Err_Ok;
|
|
|
|
Fail1:
|
|
FREE( plr );
|
|
|
|
Fail2:
|
|
FREE( l );
|
|
|
|
Fail3:
|
|
FREE( i );
|
|
|
|
Fail4:
|
|
FREE( b );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_ChainPosClassRule( HB_ChainPosClassRule* cpcr )
|
|
{
|
|
FREE( cpcr->PosLookupRecord );
|
|
FREE( cpcr->Lookahead );
|
|
FREE( cpcr->Input );
|
|
FREE( cpcr->Backtrack );
|
|
}
|
|
|
|
|
|
/* PosClassSet */
|
|
|
|
static HB_Error Load_ChainPosClassSet(
|
|
HB_ChainContextPosFormat2* ccpf2,
|
|
HB_ChainPosClassSet* cpcs,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, m, count;
|
|
HB_UInt 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 ) ) != HB_Err_Ok )
|
|
goto Fail;
|
|
(void)FILE_Seek( cur_offset );
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
|
|
Fail:
|
|
for ( m = 0; m < n; m++ )
|
|
Free_ChainPosClassRule( &cpcr[m] );
|
|
|
|
FREE( cpcr );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_ChainPosClassSet( HB_ChainPosClassSet* cpcs )
|
|
{
|
|
HB_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] );
|
|
|
|
FREE( cpcr );
|
|
}
|
|
}
|
|
|
|
|
|
/* ChainContextPosFormat2 */
|
|
|
|
static HB_Error Load_ChainContextPos2( HB_ChainContextPosFormat2* ccpf2,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, m, count;
|
|
HB_UInt cur_offset, new_offset, base_offset;
|
|
HB_UInt 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 ) ) != HB_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 = _HB_OPEN_Load_EmptyOrClassDefinition( &ccpf2->BacktrackClassDef, 65535,
|
|
backtrack_offset, base_offset,
|
|
stream ) ) != HB_Err_Ok )
|
|
goto Fail5;
|
|
if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccpf2->InputClassDef, count,
|
|
input_offset, base_offset,
|
|
stream ) ) != HB_Err_Ok )
|
|
goto Fail4;
|
|
if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccpf2->LookaheadClassDef, 65535,
|
|
lookahead_offset, base_offset,
|
|
stream ) ) != HB_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 ) ) != HB_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 HB_Err_Ok;
|
|
|
|
Fail1:
|
|
for ( m = 0; m < n; m++ )
|
|
Free_ChainPosClassSet( &cpcs[m] );
|
|
|
|
FREE( cpcs );
|
|
|
|
Fail2:
|
|
_HB_OPEN_Free_ClassDefinition( &ccpf2->LookaheadClassDef );
|
|
|
|
Fail3:
|
|
_HB_OPEN_Free_ClassDefinition( &ccpf2->InputClassDef );
|
|
|
|
Fail4:
|
|
_HB_OPEN_Free_ClassDefinition( &ccpf2->BacktrackClassDef );
|
|
|
|
Fail5:
|
|
_HB_OPEN_Free_Coverage( &ccpf2->Coverage );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_ChainContextPos2( HB_ChainContextPosFormat2* ccpf2 )
|
|
{
|
|
HB_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] );
|
|
|
|
FREE( cpcs );
|
|
}
|
|
|
|
_HB_OPEN_Free_ClassDefinition( &ccpf2->LookaheadClassDef );
|
|
_HB_OPEN_Free_ClassDefinition( &ccpf2->InputClassDef );
|
|
_HB_OPEN_Free_ClassDefinition( &ccpf2->BacktrackClassDef );
|
|
|
|
_HB_OPEN_Free_Coverage( &ccpf2->Coverage );
|
|
}
|
|
|
|
|
|
/* ChainContextPosFormat3 */
|
|
|
|
static HB_Error Load_ChainContextPos3( HB_ChainContextPosFormat3* ccpf3,
|
|
HB_Stream stream )
|
|
{
|
|
HB_Error error;
|
|
|
|
HB_UShort n, nb, ni, nl, m, count;
|
|
HB_UShort backtrack_count, input_count, lookahead_count;
|
|
HB_UInt 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 ) ) != HB_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 ) ) != HB_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 ) ) != HB_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 HB_Err_Ok;
|
|
|
|
Fail1:
|
|
FREE( plr );
|
|
|
|
Fail2:
|
|
for ( m = 0; m < nl; m++ )
|
|
_HB_OPEN_Free_Coverage( &l[m] );
|
|
|
|
FREE( l );
|
|
|
|
Fail3:
|
|
for ( m = 0; m < ni; m++ )
|
|
_HB_OPEN_Free_Coverage( &i[m] );
|
|
|
|
FREE( i );
|
|
|
|
Fail4:
|
|
for ( m = 0; m < nb; m++ )
|
|
_HB_OPEN_Free_Coverage( &b[m] );
|
|
|
|
FREE( b );
|
|
return error;
|
|
}
|
|
|
|
|
|
static void Free_ChainContextPos3( HB_ChainContextPosFormat3* ccpf3 )
|
|
{
|
|
HB_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] );
|
|
|
|
FREE( c );
|
|
}
|
|
|
|
if ( ccpf3->InputCoverage )
|
|
{
|
|
count = ccpf3->InputGlyphCount;
|
|
c = ccpf3->InputCoverage;
|
|
|
|
for ( n = 0; n < count; n++ )
|
|
_HB_OPEN_Free_Coverage( &c[n] );
|
|
|
|
FREE( c );
|
|
}
|
|
|
|
if ( ccpf3->BacktrackCoverage )
|
|
{
|
|
count = ccpf3->BacktrackGlyphCount;
|
|
c = ccpf3->BacktrackCoverage;
|
|
|
|
for ( n = 0; n < count; n++ )
|
|
_HB_OPEN_Free_Coverage( &c[n] );
|
|
|
|
FREE( c );
|
|
}
|
|
}
|
|
|
|
|
|
/* ChainContextPos */
|
|
|
|
static HB_Error Load_ChainContextPos( HB_GPOS_SubTable* st,
|
|
HB_Stream stream )
|
|
{
|
|
HB_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 ERR(HB_Err_Invalid_SubTable_Format);
|
|
}
|
|
|
|
return HB_Err_Ok; /* never reached */
|
|
}
|
|
|
|
|
|
static void Free_ChainContextPos( HB_GPOS_SubTable* st )
|
|
{
|
|
HB_ChainContextPos* ccp = &st->chain;
|
|
|
|
switch ( ccp->PosFormat )
|
|
{
|
|
case 1: Free_ChainContextPos1( &ccp->ccpf.ccpf1 ); break;
|
|
case 2: Free_ChainContextPos2( &ccp->ccpf.ccpf2 ); break;
|
|
case 3: Free_ChainContextPos3( &ccp->ccpf.ccpf3 ); break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
|
|
static HB_Error Lookup_ChainContextPos1(
|
|
GPOS_Instance* gpi,
|
|
HB_ChainContextPosFormat1* ccpf1,
|
|
HB_Buffer buffer,
|
|
HB_UShort flags,
|
|
HB_UShort context_length,
|
|
int nesting_level )
|
|
{
|
|
HB_UShort index, property;
|
|
HB_UShort i, j, k, num_cpr;
|
|
HB_UShort bgc, igc, lgc;
|
|
HB_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 == (HB_Int)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 == (HB_Int)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 HB_Error Lookup_ChainContextPos2(
|
|
GPOS_Instance* gpi,
|
|
HB_ChainContextPosFormat2* ccpf2,
|
|
HB_Buffer buffer,
|
|
HB_UShort flags,
|
|
HB_UShort context_length,
|
|
int nesting_level )
|
|
{
|
|
HB_UShort index, property;
|
|
HB_Error error;
|
|
HB_UShort i, j, k;
|
|
HB_UShort bgc, igc, lgc;
|
|
HB_UShort known_backtrack_classes,
|
|
known_input_classes,
|
|
known_lookahead_classes;
|
|
|
|
HB_UShort* backtrack_classes;
|
|
HB_UShort* input_classes;
|
|
HB_UShort* lookahead_classes;
|
|
|
|
HB_UShort* bc;
|
|
HB_UShort* ic;
|
|
HB_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, HB_UShort ) )
|
|
return error;
|
|
known_backtrack_classes = 0;
|
|
|
|
if ( ALLOC_ARRAY( input_classes, ccpf2->MaxInputLength, HB_UShort ) )
|
|
goto End3;
|
|
known_input_classes = 1;
|
|
|
|
if ( ALLOC_ARRAY( lookahead_classes, ccpf2->MaxLookaheadLength, HB_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 = ERR(HB_Err_Invalid_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 == (HB_Int)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 == (HB_Int)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 HB_Error Lookup_ChainContextPos3(
|
|
GPOS_Instance* gpi,
|
|
HB_ChainContextPosFormat3* ccpf3,
|
|
HB_Buffer buffer,
|
|
HB_UShort flags,
|
|
HB_UShort context_length,
|
|
int nesting_level )
|
|
{
|
|
HB_UShort index, i, j, property;
|
|
HB_UShort bgc, igc, lgc;
|
|
HB_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 == (HB_Int)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 == (HB_Int)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 HB_Error Lookup_ChainContextPos(
|
|
GPOS_Instance* gpi,
|
|
HB_GPOS_SubTable* st,
|
|
HB_Buffer buffer,
|
|
HB_UShort flags,
|
|
HB_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 ERR(HB_Err_Invalid_SubTable_Format);
|
|
}
|
|
|
|
return HB_Err_Ok; /* never reached */
|
|
}
|
|
|
|
|
|
|
|
/***********
|
|
* GPOS API
|
|
***********/
|
|
|
|
|
|
|
|
HB_Error HB_GPOS_Select_Script( HB_GPOSHeader* gpos,
|
|
HB_UInt script_tag,
|
|
HB_UShort* script_index )
|
|
{
|
|
HB_UShort n;
|
|
|
|
HB_ScriptList* sl;
|
|
HB_ScriptRecord* sr;
|
|
|
|
|
|
if ( !gpos || !script_index )
|
|
return ERR(HB_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 HB_Err_Ok;
|
|
}
|
|
|
|
return HB_Err_Not_Covered;
|
|
}
|
|
|
|
|
|
|
|
HB_Error HB_GPOS_Select_Language( HB_GPOSHeader* gpos,
|
|
HB_UInt language_tag,
|
|
HB_UShort script_index,
|
|
HB_UShort* language_index,
|
|
HB_UShort* req_feature_index )
|
|
{
|
|
HB_UShort n;
|
|
|
|
HB_ScriptList* sl;
|
|
HB_ScriptRecord* sr;
|
|
HB_ScriptTable* s;
|
|
HB_LangSysRecord* lsr;
|
|
|
|
|
|
if ( !gpos || !language_index || !req_feature_index )
|
|
return ERR(HB_Err_Invalid_Argument);
|
|
|
|
sl = &gpos->ScriptList;
|
|
sr = sl->ScriptRecord;
|
|
|
|
if ( script_index >= sl->ScriptCount )
|
|
return ERR(HB_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 HB_Err_Ok;
|
|
}
|
|
|
|
return HB_Err_Not_Covered;
|
|
}
|
|
|
|
|
|
/* selecting 0xFFFF for language_index asks for the values of the
|
|
default language (DefaultLangSys) */
|
|
|
|
|
|
HB_Error HB_GPOS_Select_Feature( HB_GPOSHeader* gpos,
|
|
HB_UInt feature_tag,
|
|
HB_UShort script_index,
|
|
HB_UShort language_index,
|
|
HB_UShort* feature_index )
|
|
{
|
|
HB_UShort n;
|
|
|
|
HB_ScriptList* sl;
|
|
HB_ScriptRecord* sr;
|
|
HB_ScriptTable* s;
|
|
HB_LangSysRecord* lsr;
|
|
HB_LangSys* ls;
|
|
HB_UShort* fi;
|
|
|
|
HB_FeatureList* fl;
|
|
HB_FeatureRecord* fr;
|
|
|
|
|
|
if ( !gpos || !feature_index )
|
|
return ERR(HB_Err_Invalid_Argument);
|
|
|
|
sl = &gpos->ScriptList;
|
|
sr = sl->ScriptRecord;
|
|
|
|
fl = &gpos->FeatureList;
|
|
fr = fl->FeatureRecord;
|
|
|
|
if ( script_index >= sl->ScriptCount )
|
|
return ERR(HB_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 ERR(HB_Err_Invalid_Argument);
|
|
|
|
ls = &lsr[language_index].LangSys;
|
|
}
|
|
|
|
fi = ls->FeatureIndex;
|
|
|
|
for ( n = 0; n < ls->FeatureCount; n++ )
|
|
{
|
|
if ( fi[n] >= fl->FeatureCount )
|
|
return ERR(HB_Err_Invalid_SubTable_Format);
|
|
|
|
if ( feature_tag == fr[fi[n]].FeatureTag )
|
|
{
|
|
*feature_index = fi[n];
|
|
|
|
return HB_Err_Ok;
|
|
}
|
|
}
|
|
|
|
return HB_Err_Not_Covered;
|
|
}
|
|
|
|
|
|
/* The next three functions return a null-terminated list */
|
|
|
|
|
|
HB_Error HB_GPOS_Query_Scripts( HB_GPOSHeader* gpos,
|
|
HB_UInt** script_tag_list )
|
|
{
|
|
HB_Error error;
|
|
HB_UShort n;
|
|
HB_UInt* stl;
|
|
|
|
HB_ScriptList* sl;
|
|
HB_ScriptRecord* sr;
|
|
|
|
|
|
if ( !gpos || !script_tag_list )
|
|
return ERR(HB_Err_Invalid_Argument);
|
|
|
|
sl = &gpos->ScriptList;
|
|
sr = sl->ScriptRecord;
|
|
|
|
if ( ALLOC_ARRAY( stl, sl->ScriptCount + 1, HB_UInt ) )
|
|
return error;
|
|
|
|
for ( n = 0; n < sl->ScriptCount; n++ )
|
|
stl[n] = sr[n].ScriptTag;
|
|
stl[n] = 0;
|
|
|
|
*script_tag_list = stl;
|
|
|
|
return HB_Err_Ok;
|
|
}
|
|
|
|
|
|
|
|
HB_Error HB_GPOS_Query_Languages( HB_GPOSHeader* gpos,
|
|
HB_UShort script_index,
|
|
HB_UInt** language_tag_list )
|
|
{
|
|
HB_Error error;
|
|
HB_UShort n;
|
|
HB_UInt* ltl;
|
|
|
|
HB_ScriptList* sl;
|
|
HB_ScriptRecord* sr;
|
|
HB_ScriptTable* s;
|
|
HB_LangSysRecord* lsr;
|
|
|
|
|
|
if ( !gpos || !language_tag_list )
|
|
return ERR(HB_Err_Invalid_Argument);
|
|
|
|
sl = &gpos->ScriptList;
|
|
sr = sl->ScriptRecord;
|
|
|
|
if ( script_index >= sl->ScriptCount )
|
|
return ERR(HB_Err_Invalid_Argument);
|
|
|
|
s = &sr[script_index].Script;
|
|
lsr = s->LangSysRecord;
|
|
|
|
if ( ALLOC_ARRAY( ltl, s->LangSysCount + 1, HB_UInt ) )
|
|
return error;
|
|
|
|
for ( n = 0; n < s->LangSysCount; n++ )
|
|
ltl[n] = lsr[n].LangSysTag;
|
|
ltl[n] = 0;
|
|
|
|
*language_tag_list = ltl;
|
|
|
|
return HB_Err_Ok;
|
|
}
|
|
|
|
|
|
/* selecting 0xFFFF for language_index asks for the values of the
|
|
default language (DefaultLangSys) */
|
|
|
|
|
|
HB_Error HB_GPOS_Query_Features( HB_GPOSHeader* gpos,
|
|
HB_UShort script_index,
|
|
HB_UShort language_index,
|
|
HB_UInt** feature_tag_list )
|
|
{
|
|
HB_UShort n;
|
|
HB_Error error;
|
|
HB_UInt* ftl;
|
|
|
|
HB_ScriptList* sl;
|
|
HB_ScriptRecord* sr;
|
|
HB_ScriptTable* s;
|
|
HB_LangSysRecord* lsr;
|
|
HB_LangSys* ls;
|
|
HB_UShort* fi;
|
|
|
|
HB_FeatureList* fl;
|
|
HB_FeatureRecord* fr;
|
|
|
|
|
|
if ( !gpos || !feature_tag_list )
|
|
return ERR(HB_Err_Invalid_Argument);
|
|
|
|
sl = &gpos->ScriptList;
|
|
sr = sl->ScriptRecord;
|
|
|
|
fl = &gpos->FeatureList;
|
|
fr = fl->FeatureRecord;
|
|
|
|
if ( script_index >= sl->ScriptCount )
|
|
return ERR(HB_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 ERR(HB_Err_Invalid_Argument);
|
|
|
|
ls = &lsr[language_index].LangSys;
|
|
}
|
|
|
|
fi = ls->FeatureIndex;
|
|
|
|
if ( ALLOC_ARRAY( ftl, ls->FeatureCount + 1, HB_UInt ) )
|
|
return error;
|
|
|
|
for ( n = 0; n < ls->FeatureCount; n++ )
|
|
{
|
|
if ( fi[n] >= fl->FeatureCount )
|
|
{
|
|
FREE( ftl );
|
|
return ERR(HB_Err_Invalid_SubTable_Format);
|
|
}
|
|
ftl[n] = fr[fi[n]].FeatureTag;
|
|
}
|
|
ftl[n] = 0;
|
|
|
|
*feature_tag_list = ftl;
|
|
|
|
return HB_Err_Ok;
|
|
}
|
|
|
|
|
|
/* Do an individual subtable lookup. Returns HB_Err_Ok if positioning
|
|
has been done, or HB_Err_Not_Covered if not. */
|
|
static HB_Error GPOS_Do_Glyph_Lookup( GPOS_Instance* gpi,
|
|
HB_UShort lookup_index,
|
|
HB_Buffer buffer,
|
|
HB_UShort context_length,
|
|
int nesting_level )
|
|
{
|
|
HB_Error error = HB_Err_Not_Covered;
|
|
HB_UShort i, flags, lookup_count;
|
|
HB_GPOSHeader* gpos = gpi->gpos;
|
|
HB_Lookup* lo;
|
|
int lookup_type;
|
|
|
|
|
|
nesting_level++;
|
|
|
|
if ( nesting_level > HB_MAX_NESTING_LEVEL )
|
|
return ERR(HB_Err_Not_Covered); /* ERR() call intended */
|
|
|
|
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;
|
|
|
|
for ( i = 0; i < lo->SubTableCount; i++ )
|
|
{
|
|
HB_GPOS_SubTable *st = &lo->SubTable[i].st.gpos;
|
|
|
|
switch (lookup_type) {
|
|
case HB_GPOS_LOOKUP_SINGLE:
|
|
error = Lookup_SinglePos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
|
|
case HB_GPOS_LOOKUP_PAIR:
|
|
error = Lookup_PairPos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
|
|
case HB_GPOS_LOOKUP_CURSIVE:
|
|
error = Lookup_CursivePos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
|
|
case HB_GPOS_LOOKUP_MARKBASE:
|
|
error = Lookup_MarkBasePos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
|
|
case HB_GPOS_LOOKUP_MARKLIG:
|
|
error = Lookup_MarkLigPos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
|
|
case HB_GPOS_LOOKUP_MARKMARK:
|
|
error = Lookup_MarkMarkPos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
|
|
case HB_GPOS_LOOKUP_CONTEXT:
|
|
error = Lookup_ContextPos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
|
|
case HB_GPOS_LOOKUP_CHAIN:
|
|
error = Lookup_ChainContextPos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
|
|
/*case HB_GPOS_LOOKUP_EXTENSION:
|
|
error = Lookup_ExtensionPos ( gpi, st, buffer, flags, context_length, nesting_level ); break;*/
|
|
default:
|
|
error = HB_Err_Not_Covered;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
|
|
HB_INTERNAL HB_Error
|
|
_HB_GPOS_Load_SubTable( HB_GPOS_SubTable* st,
|
|
HB_Stream stream,
|
|
HB_UShort lookup_type )
|
|
{
|
|
switch ( lookup_type ) {
|
|
case HB_GPOS_LOOKUP_SINGLE: return Load_SinglePos ( st, stream );
|
|
case HB_GPOS_LOOKUP_PAIR: return Load_PairPos ( st, stream );
|
|
case HB_GPOS_LOOKUP_CURSIVE: return Load_CursivePos ( st, stream );
|
|
case HB_GPOS_LOOKUP_MARKBASE: return Load_MarkBasePos ( st, stream );
|
|
case HB_GPOS_LOOKUP_MARKLIG: return Load_MarkLigPos ( st, stream );
|
|
case HB_GPOS_LOOKUP_MARKMARK: return Load_MarkMarkPos ( st, stream );
|
|
case HB_GPOS_LOOKUP_CONTEXT: return Load_ContextPos ( st, stream );
|
|
case HB_GPOS_LOOKUP_CHAIN: return Load_ChainContextPos ( st, stream );
|
|
/*case HB_GPOS_LOOKUP_EXTENSION: return Load_ExtensionPos ( st, stream );*/
|
|
default: return ERR(HB_Err_Invalid_SubTable_Format);
|
|
}
|
|
}
|
|
|
|
|
|
HB_INTERNAL void
|
|
_HB_GPOS_Free_SubTable( HB_GPOS_SubTable* st,
|
|
HB_UShort lookup_type )
|
|
{
|
|
switch ( lookup_type ) {
|
|
case HB_GPOS_LOOKUP_SINGLE: Free_SinglePos ( st ); return;
|
|
case HB_GPOS_LOOKUP_PAIR: Free_PairPos ( st ); return;
|
|
case HB_GPOS_LOOKUP_CURSIVE: Free_CursivePos ( st ); return;
|
|
case HB_GPOS_LOOKUP_MARKBASE: Free_MarkBasePos ( st ); return;
|
|
case HB_GPOS_LOOKUP_MARKLIG: Free_MarkLigPos ( st ); return;
|
|
case HB_GPOS_LOOKUP_MARKMARK: Free_MarkMarkPos ( st ); return;
|
|
case HB_GPOS_LOOKUP_CONTEXT: Free_ContextPos ( st ); return;
|
|
case HB_GPOS_LOOKUP_CHAIN: Free_ChainContextPos ( st ); return;
|
|
/*case HB_GPOS_LOOKUP_EXTENSION: Free_ExtensionPos ( st ); return;*/
|
|
default: return;
|
|
}
|
|
}
|
|
|
|
|
|
/* apply one lookup to the input string object */
|
|
|
|
static HB_Error GPOS_Do_String_Lookup( GPOS_Instance* gpi,
|
|
HB_UShort lookup_index,
|
|
HB_Buffer buffer )
|
|
{
|
|
HB_Error error, retError = HB_Err_Not_Covered;
|
|
HB_GPOSHeader* gpos = gpi->gpos;
|
|
|
|
HB_UInt* properties = gpos->LookupList.Properties;
|
|
|
|
const int nesting_level = 0;
|
|
/* 0xFFFF indicates that we don't have a context length yet */
|
|
const HB_UShort context_length = 0xFFFF;
|
|
|
|
|
|
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] )
|
|
{
|
|
/* 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, context_length, 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 HB_Error Position_CursiveChain ( HB_Buffer buffer )
|
|
{
|
|
HB_UInt 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 HB_Err_Ok;
|
|
}
|
|
|
|
|
|
HB_Error HB_GPOS_Add_Feature( HB_GPOSHeader* gpos,
|
|
HB_UShort feature_index,
|
|
HB_UInt property )
|
|
{
|
|
HB_UShort i;
|
|
|
|
HB_Feature feature;
|
|
HB_UInt* properties;
|
|
HB_UShort* index;
|
|
HB_UShort lookup_count;
|
|
|
|
/* Each feature can only be added once */
|
|
|
|
if ( !gpos ||
|
|
feature_index >= gpos->FeatureList.FeatureCount ||
|
|
gpos->FeatureList.ApplyCount == gpos->FeatureList.FeatureCount )
|
|
return ERR(HB_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++ )
|
|
{
|
|
HB_UShort lookup_index = index[i];
|
|
if (lookup_index < lookup_count)
|
|
properties[lookup_index] |= property;
|
|
}
|
|
|
|
return HB_Err_Ok;
|
|
}
|
|
|
|
|
|
|
|
HB_Error HB_GPOS_Clear_Features( HB_GPOSHeader* gpos )
|
|
{
|
|
HB_UShort i;
|
|
|
|
HB_UInt* properties;
|
|
|
|
|
|
if ( !gpos )
|
|
return ERR(HB_Err_Invalid_Argument);
|
|
|
|
gpos->FeatureList.ApplyCount = 0;
|
|
|
|
properties = gpos->LookupList.Properties;
|
|
|
|
for ( i = 0; i < gpos->LookupList.LookupCount; i++ )
|
|
properties[i] = 0;
|
|
|
|
return HB_Err_Ok;
|
|
}
|
|
|
|
|
|
|
|
HB_Error HB_GPOS_Register_Glyph_Function( HB_GPOSHeader* gpos,
|
|
HB_GlyphFunction gfunc )
|
|
{
|
|
if ( !gpos )
|
|
return ERR(HB_Err_Invalid_Argument);
|
|
|
|
gpos->gfunc = gfunc;
|
|
|
|
return HB_Err_Ok;
|
|
}
|
|
|
|
|
|
|
|
HB_Error HB_GPOS_Register_MM_Function( HB_GPOSHeader* gpos,
|
|
HB_MMFunction mmfunc,
|
|
void* data )
|
|
{
|
|
if ( !gpos )
|
|
return ERR(HB_Err_Invalid_Argument);
|
|
|
|
gpos->mmfunc = mmfunc;
|
|
gpos->data = data;
|
|
|
|
return HB_Err_Ok;
|
|
}
|
|
|
|
/* If `dvi' is TRUE, glyph contour points for anchor points and device
|
|
tables are ignored -- you will get device independent values. */
|
|
|
|
|
|
HB_Error HB_GPOS_Apply_String( HB_Font font,
|
|
HB_GPOSHeader* gpos,
|
|
HB_UShort load_flags,
|
|
HB_Buffer buffer,
|
|
HB_Bool dvi,
|
|
HB_Bool r2l )
|
|
{
|
|
HB_Error error, retError = HB_Err_Not_Covered;
|
|
GPOS_Instance gpi;
|
|
int i, j, lookup_count, num_features;
|
|
|
|
if ( !font || !gpos || !buffer )
|
|
return ERR(HB_Err_Invalid_Argument);
|
|
|
|
if ( buffer->in_length == 0 )
|
|
return HB_Err_Not_Covered;
|
|
|
|
gpi.font = font;
|
|
gpi.gpos = gpos;
|
|
gpi.load_flags = load_flags;
|
|
gpi.r2l = r2l;
|
|
gpi.dvi = dvi;
|
|
|
|
lookup_count = gpos->LookupList.LookupCount;
|
|
num_features = gpos->FeatureList.ApplyCount;
|
|
|
|
if ( num_features )
|
|
{
|
|
error = _hb_buffer_clear_positions( buffer );
|
|
if ( error )
|
|
return error;
|
|
}
|
|
|
|
for ( i = 0; i < num_features; i++ )
|
|
{
|
|
HB_UShort feature_index = gpos->FeatureList.ApplyOrder[i];
|
|
HB_Feature feature = gpos->FeatureList.FeatureRecord[feature_index].Feature;
|
|
|
|
for ( j = 0; j < feature.LookupListCount; j++ )
|
|
{
|
|
HB_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;
|
|
}
|
|
}
|
|
|
|
if ( num_features )
|
|
{
|
|
error = Position_CursiveChain ( buffer );
|
|
if ( error )
|
|
return error;
|
|
}
|
|
|
|
return retError;
|
|
}
|
|
|
|
/* END */
|