/* * 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" #include "harfbuzz-shaper.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 ); #ifdef HB_SUPPORT_MULTIPLE_MASTER /* 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 */ } #endif HB_Error HB_Load_GPOS_Table( HB_Stream stream, HB_GPOSHeader** retptr, HB_GDEFHeader* gdef, HB_Stream gdefStream ) { HB_UInt cur_offset, new_offset, base_offset; HB_GPOSHeader* gpos; HB_Error error = HB_Err_Ok; 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; #ifdef HB_SUPPORT_MULTIPLE_MASTER gpos->mmfunc = default_mmfunc; #endif /* 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, gdefStream, 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; } static HB_UInt Calculate_Class2RecordSize(HB_UShort format1, HB_UShort format2) { // Return number of 16 bit values in two value records with given formats return (format1 & 0x01) + (format2 & 0x01) + ((format1 & 0x02) >> 1) + ((format2 & 0x02) >> 1) + ((format1 & 0x04) >> 2) + ((format2 & 0x04) >> 2) + ((format1 & 0x08) >> 3) + ((format2 & 0x08) >> 3) + ((format1 & 0x10) >> 4) + ((format2 & 0x10) >> 4) + ((format1 & 0x20) >> 5) + ((format2 & 0x20) >> 5) + ((format1 & 0x40) >> 6) + ((format2 & 0x40) >> 6) + ((format1 & 0x80) >> 7) + ((format2 & 0x80) >> 7); } /***************************** * SubTable related functions *****************************/ /* shared tables */ /* ValueRecord */ static HB_Error Get_FlexibleValueRecord( GPOS_Instance* gpi, HB_Short* vr, HB_UShort format, HB_Position gd ) { HB_Error error = HB_Err_Ok; HB_16Dot16 x_scale, y_scale; if ( !format ) return HB_Err_Ok; x_scale = gpi->font->x_scale; y_scale = gpi->font->y_scale; /* design units -> fractional pixel */ if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT ) { gd->x_pos += *vr * x_scale / 0x10000; vr++; } if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT ) { gd->y_pos += *vr * y_scale / 0x10000; vr++; } if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE ) { gd->x_advance += *vr * x_scale / 0x10000; vr++; } if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE ) { gd->y_advance += *vr * y_scale / 0x10000; vr++; } return error; } /* 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_DEVICE_TABLES ) { if ( ALLOC_ARRAY( vr->DeviceTables, 4, HB_Device ) ) return error; vr->DeviceTables[VR_X_ADVANCE_DEVICE] = 0; vr->DeviceTables[VR_Y_ADVANCE_DEVICE] = 0; vr->DeviceTables[VR_X_PLACEMENT_DEVICE] = 0; vr->DeviceTables[VR_Y_PLACEMENT_DEVICE] = 0; } else { vr->DeviceTables = 0; } if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE ) { if ( ACCESS_Frame( 2L ) ) goto Fail4; 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->DeviceTables[VR_X_PLACEMENT_DEVICE], stream ) ) != HB_Err_Ok ) goto Fail4; (void)FILE_Seek( cur_offset ); } } 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->DeviceTables[VR_Y_PLACEMENT_DEVICE], stream ) ) != HB_Err_Ok ) goto Fail3; (void)FILE_Seek( cur_offset ); } } 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->DeviceTables[VR_X_ADVANCE_DEVICE], stream ) ) != HB_Err_Ok ) goto Fail2; (void)FILE_Seek( cur_offset ); } } 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->DeviceTables[VR_Y_ADVANCE_DEVICE], stream ) ) != HB_Err_Ok ) goto Fail1; (void)FILE_Seek( cur_offset ); } } if ( format & HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT ) { if ( ACCESS_Frame( 2L ) ) goto Fail1; #ifdef HB_SUPPORT_MULTIPLE_MASTER vr->XIdPlacement = GET_UShort(); #else (void) GET_UShort(); #endif FORGET_Frame(); } #ifdef HB_SUPPORT_MULTIPLE_MASTER else vr->XIdPlacement = 0; #endif if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT ) { if ( ACCESS_Frame( 2L ) ) goto Fail1; #ifdef HB_SUPPORT_MULTIPLE_MASTER vr->YIdPlacement = GET_UShort(); #else (void) GET_UShort(); #endif FORGET_Frame(); } #ifdef HB_SUPPORT_MULTIPLE_MASTER else vr->YIdPlacement = 0; #endif if ( format & HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE ) { if ( ACCESS_Frame( 2L ) ) goto Fail1; #ifdef HB_SUPPORT_MULTIPLE_MASTER vr->XIdAdvance = GET_UShort(); #else (void) GET_UShort(); #endif FORGET_Frame(); } #ifdef HB_SUPPORT_MULTIPLE_MASTER else vr->XIdAdvance = 0; #endif if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE ) { if ( ACCESS_Frame( 2L ) ) goto Fail1; #ifdef HB_SUPPORT_MULTIPLE_MASTER vr->YIdAdvance = GET_UShort(); #else (void) GET_UShort(); #endif FORGET_Frame(); } #ifdef HB_SUPPORT_MULTIPLE_MASTER else vr->YIdAdvance = 0; #endif return HB_Err_Ok; Fail1: if ( vr->DeviceTables ) _HB_OPEN_Free_Device( vr->DeviceTables[VR_Y_ADVANCE_DEVICE] ); Fail2: if ( vr->DeviceTables ) _HB_OPEN_Free_Device( vr->DeviceTables[VR_X_ADVANCE_DEVICE] ); Fail3: if ( vr->DeviceTables ) _HB_OPEN_Free_Device( vr->DeviceTables[VR_Y_PLACEMENT_DEVICE] ); Fail4: FREE( vr->DeviceTables ); 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->DeviceTables[VR_Y_ADVANCE_DEVICE] ); if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE ) _HB_OPEN_Free_Device( vr->DeviceTables[VR_X_ADVANCE_DEVICE] ); if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE ) _HB_OPEN_Free_Device( vr->DeviceTables[VR_Y_PLACEMENT_DEVICE] ); if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE ) _HB_OPEN_Free_Device( vr->DeviceTables[VR_X_PLACEMENT_DEVICE] ); FREE( vr->DeviceTables ); } static HB_Error Get_ValueRecord( GPOS_Instance* gpi, HB_ValueRecord* vr, HB_UShort format, HB_Position gd ) { HB_Short pixel_value; HB_Error error = HB_Err_Ok; #ifdef HB_SUPPORT_MULTIPLE_MASTER HB_GPOSHeader* gpos = gpi->gpos; HB_Fixed value; #endif HB_UShort x_ppem, y_ppem; HB_16Dot16 x_scale, y_scale; if ( !format ) return HB_Err_Ok; x_ppem = gpi->font->x_ppem; y_ppem = gpi->font->y_ppem; x_scale = gpi->font->x_scale; y_scale = gpi->font->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->DeviceTables[VR_X_PLACEMENT_DEVICE], x_ppem, &pixel_value ); gd->x_pos += pixel_value << 6; } if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE ) { _HB_OPEN_Get_Device( vr->DeviceTables[VR_Y_PLACEMENT_DEVICE], y_ppem, &pixel_value ); gd->y_pos += pixel_value << 6; } if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE ) { _HB_OPEN_Get_Device( vr->DeviceTables[VR_X_ADVANCE_DEVICE], x_ppem, &pixel_value ); gd->x_advance += pixel_value << 6; } if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE ) { _HB_OPEN_Get_Device( vr->DeviceTables[VR_Y_ADVANCE_DEVICE], y_ppem, &pixel_value ); gd->y_advance += pixel_value << 6; } } #ifdef HB_SUPPORT_MULTIPLE_MASTER /* 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; } #endif 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 ) { if ( ALLOC_ARRAY( an->af.af3.DeviceTables, 2, HB_Device ) ) return error; an->af.af3.DeviceTables[AF3_X_DEVICE_TABLE] = 0; an->af.af3.DeviceTables[AF3_Y_DEVICE_TABLE] = 0; new_offset += base_offset; cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = _HB_OPEN_Load_Device( &an->af.af3.DeviceTables[AF3_X_DEVICE_TABLE], stream ) ) != HB_Err_Ok ) goto Fail2; (void)FILE_Seek( cur_offset ); } if ( ACCESS_Frame( 2L ) ) goto Fail; new_offset = GET_UShort(); FORGET_Frame(); if ( new_offset ) { if ( !an->af.af3.DeviceTables ) { if ( ALLOC_ARRAY( an->af.af3.DeviceTables, 2, HB_Device ) ) return error; an->af.af3.DeviceTables[AF3_X_DEVICE_TABLE] = 0; an->af.af3.DeviceTables[AF3_Y_DEVICE_TABLE] = 0; } new_offset += base_offset; cur_offset = FILE_Pos(); if ( FILE_Seek( new_offset ) || ( error = _HB_OPEN_Load_Device( &an->af.af3.DeviceTables[AF3_Y_DEVICE_TABLE], stream ) ) != HB_Err_Ok ) goto Fail; (void)FILE_Seek( cur_offset ); } break; case 4: if ( ACCESS_Frame( 4L ) ) return error; #ifdef HB_SUPPORT_MULTIPLE_MASTER an->af.af4.XIdAnchor = GET_UShort(); an->af.af4.YIdAnchor = GET_UShort(); #else (void) GET_UShort(); (void) GET_UShort(); #endif FORGET_Frame(); break; default: return ERR(HB_Err_Invalid_SubTable_Format); } return HB_Err_Ok; Fail: if ( an->af.af3.DeviceTables ) _HB_OPEN_Free_Device( an->af.af3.DeviceTables[AF3_X_DEVICE_TABLE] ); Fail2: FREE( an->af.af3.DeviceTables ); return error; } static void Free_Anchor( HB_Anchor* an) { if ( an->PosFormat == 3 && an->af.af3.DeviceTables ) { _HB_OPEN_Free_Device( an->af.af3.DeviceTables[AF3_X_DEVICE_TABLE] ); _HB_OPEN_Free_Device( an->af.af3.DeviceTables[AF3_Y_DEVICE_TABLE] ); FREE( an->af.af3.DeviceTables ); } } 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; #ifdef HB_SUPPORT_MULTIPLE_MASTER HB_GPOSHeader* gpos = gpi->gpos; #endif HB_UShort ap; HB_Short pixel_value; HB_UShort x_ppem, y_ppem; HB_16Dot16 x_scale, y_scale; x_ppem = gpi->font->x_ppem; y_ppem = gpi->font->y_ppem; x_scale = gpi->font->x_scale; y_scale = gpi->font->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: if ( !gpi->dvi ) { hb_uint32 n_points = 0; ap = an->af.af2.AnchorPoint; if (!gpi->font->klass->getPointInOutline) goto no_contour_point; error = gpi->font->klass->getPointInOutline(gpi->font, glyph_index, gpi->load_flags, ap, x_value, y_value, &n_points); if (error) return error; /* if n_points is set to zero, we use the design coordinate value pair. * This can happen e.g. for sbit glyphs. */ if (!n_points) goto no_contour_point; } 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.DeviceTables[AF3_X_DEVICE_TABLE], x_ppem, &pixel_value ); *x_value = pixel_value << 6; _HB_OPEN_Get_Device( an->af.af3.DeviceTables[AF3_Y_DEVICE_TABLE], 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: #ifdef HB_SUPPORT_MULTIPLE_MASTER 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; #else return ERR(HB_Err_Not_Covered); #endif } 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; #ifdef HB_USE_FLEXIBLE_VALUE_RECORD HB_UInt record_size = 0; HB_Short *vr; #else HB_UInt base_offset; HB_PairValueRecord* pvr; base_offset = FILE_Pos(); #endif if ( ACCESS_Frame( 2L ) ) return error; count = ps->PairValueCount = GET_UShort(); FORGET_Frame(); #ifndef HB_USE_FLEXIBLE_VALUE_RECORD 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; } } } #else ps->ValueRecords = 0; // Add one for the SecondGlyph part of each record record_size = Calculate_Class2RecordSize( format1, format2 ) + 1; if ( ALLOC_ARRAY( ps->ValueRecords, record_size * count, HB_Short ) ) return error; vr = ps->ValueRecords; for ( n = 0; n < count; n++ ) { for ( m = 0; m < record_size; m++ ) { if ( ACCESS_Frame( 2L ) ) goto Fail; *(vr++) = GET_Short(); FORGET_Frame(); } } #endif return HB_Err_Ok; Fail: #ifndef HB_USE_FLEXIBLE_VALUE_RECORD for ( m = 0; m < n; m++ ) { if ( format1 ) Free_ValueRecord( &pvr[m].Value1, format1 ); if ( format2 ) Free_ValueRecord( &pvr[m].Value2, format2 ); } FREE( pvr ); #else FREE ( ps->ValueRecords ); #endif return error; } static void Free_PairSet( HB_PairSet* ps, HB_UShort format1, HB_UShort format2) { #ifndef HB_USE_FLEXIBLE_VALUE_RECORD 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 ); } #else (void)format1; // unused (void)format2; // unused if ( ps->ValueRecords ) { FREE( ps->ValueRecords ); } #endif } /* 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, cls2_record_size = 0; HB_Class1Record* c1r; HB_Class2Record* c2r; HB_Short* vr; hb_uint8 use_flexible_value_records; 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(); #ifndef HB_USE_FLEXIBLE_VALUE_RECORD use_flexible_value_records = 0; #else use_flexible_value_records = !((format1 & HB_GPOS_FORMAT_HAVE_DEVICE_TABLES) || (format2 & HB_GPOS_FORMAT_HAVE_DEVICE_TABLES)); #endif 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; if ( use_flexible_value_records ) cls2_record_size = Calculate_Class2RecordSize(format1, format2); for ( m = 0; m < count1; m++ ) { c1r[m].IsFlexible = use_flexible_value_records; if ( use_flexible_value_records ) { c1r[m].c2r.ValueRecords = NULL; if ( ALLOC_ARRAY( c1r[m].c2r.ValueRecords, count2 * cls2_record_size, HB_UShort ) ) goto Fail1; vr = c1r[m].c2r.ValueRecords; if ( ACCESS_Frame( count2 * cls2_record_size * 2L )) goto Fail1; for ( n = 0; n < count2 * cls2_record_size; n++ ) vr[n] = GET_Short(); FORGET_Frame(); } else { c1r[m].c2r.Class2Record = NULL; if ( ALLOC_ARRAY( c1r[m].c2r.Class2Record, count2, HB_Class2Record ) ) goto Fail1; c2r = c1r[m].c2r.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++ ) { if ( !use_flexible_value_records ) { c2r = c1r[k].c2r.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 ); } else { FREE( c1r[k].c2r.ValueRecords ); } } 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++ ) { if ( !c1r[m].IsFlexible ) { c2r = c1r[m].c2r.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 ); } else { FREE( c1r[m].c2r.ValueRecords ); } } 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; #ifndef HB_USE_FLEXIBLE_VALUE_RECORD HB_PairValueRecord* pvr; #else HB_Short *vr; HB_UShort second_glyph; HB_UInt record_size1, record_size2; #endif if ( index >= ppf1->PairSetCount ) return ERR(HB_Err_Invalid_SubTable); if (!ppf1->PairSet[index].PairValueCount) return HB_Err_Not_Covered; glyph2 = IN_CURGLYPH(); #ifndef HB_USE_FLEXIBLE_VALUE_RECORD pvr = ppf1->PairSet[index].PairValueRecord; if ( !pvr ) return ERR(HB_Err_Invalid_SubTable); 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 ) ); } } #else vr = ppf1->PairSet[index].ValueRecords; if ( !vr ) return ERR(HB_Err_Invalid_SubTable); record_size1 = Calculate_Class2RecordSize( format1, 0 ); record_size2 = Calculate_Class2RecordSize( format2, 0 ); for ( numpvr = ppf1->PairSet[index].PairValueCount; numpvr; numpvr-- ) { second_glyph = *((HB_UShort *)vr); vr++; if ( glyph2 == second_glyph ) { error = Get_FlexibleValueRecord( gpi, vr, format1, POSITION( first_pos ) ); if ( error ) return error; vr += record_size1; return Get_FlexibleValueRecord( gpi, vr, format2, POSITION( buffer->in_pos ) ); } else { vr += record_size1 + record_size2; } } #endif 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; HB_Short* vr; HB_UShort vr1_size; HB_UShort vr2_size; 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); if ( !c1r->IsFlexible ) { c2r = &c1r->c2r.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 ) ); } else { vr1_size = Calculate_Class2RecordSize( format1, 0 ); vr2_size = Calculate_Class2RecordSize( format2, 0 ); vr = c1r->c2r.ValueRecords + (cl2 * ( vr1_size + vr2_size )); error = Get_FlexibleValueRecord( gpi, vr, format1, POSITION( first_pos ) ); if ( error ) return error; vr += vr1_size; // Skip to second record return Get_FlexibleValueRecord( gpi, vr, 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 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; if ( ba->BaseRecord ) { br = ba->BaseRecord; if ( ba->BaseCount ) { HB_UShort i, count; count = num_classes * ba->BaseCount; bans = br[0].BaseAnchor; for (i = 0; i < count; i++) Free_Anchor (&bans[i]); 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 m, n, count; HB_UInt cur_offset, new_offset, base_offset; HB_Mark2Record *m2r; HB_Anchor *m2an, *m2ans; 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; m2ans = NULL; if ( ALLOC_ARRAY( m2ans, count * num_classes, HB_Anchor ) ) goto Fail; for ( m = 0; m < count; m++ ) { m2an = m2r[m].Mark2Anchor = m2ans + 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) { /* 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 Fail; (void)FILE_Seek( cur_offset ); } } return HB_Err_Ok; Fail: FREE( m2ans ); FREE( m2r ); return error; } static void Free_Mark2Array( HB_Mark2Array* m2a, HB_UShort num_classes ) { HB_Mark2Record *m2r; HB_Anchor *m2ans; HB_UNUSED(num_classes); if ( m2a->Mark2Record ) { m2r = m2a->Mark2Record; if ( m2a->Mark2Count ) { m2ans = m2r[0].Mark2Anchor; FREE( m2ans ); } 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--; } if ( i > buffer->in_pos ) return HB_Err_Not_Covered; error = _HB_OPEN_Coverage_Index( &mmp->Mark2Coverage, IN_GLYPH( j ), &mark2_index ); if ( error ) return error; ma1 = &mmp->Mark1Array; if ( mark1_index >= ma1->MarkCount ) return 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; 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; if ( ACCESS_Frame( count * 2L ) ) goto Fail2; for ( n = 0; n < count; n++ ) c[n] = GET_UShort(); 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; m++ ) 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 (cpf2->MaxContextLength < 1) return HB_Err_Not_Covered; 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; 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; 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; 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; 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; 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; 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; 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 (ccpf2->MaxInputLength < 1) return HB_Err_Not_Covered; 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; } #ifdef HB_SUPPORT_MULTIPLE_MASTER 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; } #endif /* 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 */