941 lines
24 KiB
C
941 lines
24 KiB
C
/***************************************************************************/
|
|
/* */
|
|
/* otlcommn.c */
|
|
/* */
|
|
/* OpenType layout support, common tables (body). */
|
|
/* */
|
|
/* Copyright 2002 by */
|
|
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
|
|
/* */
|
|
/* This file is part of the FreeType project, and may only be used, */
|
|
/* modified, and distributed under the terms of the FreeType project */
|
|
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
|
|
/* this file you indicate that you have read the license and */
|
|
/* understand and accept it fully. */
|
|
/* */
|
|
/***************************************************************************/
|
|
|
|
|
|
#include "otlayout.h"
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/***** *****/
|
|
/***** COVERAGE TABLE *****/
|
|
/***** *****/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
OTL_LOCALDEF( void )
|
|
otl_coverage_validate( OTL_Bytes table,
|
|
OTL_Validator valid )
|
|
{
|
|
OTL_Bytes p;
|
|
OTL_UInt format;
|
|
|
|
|
|
if ( table + 4 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
format = OTL_NEXT_USHORT( p );
|
|
switch ( format )
|
|
{
|
|
case 1:
|
|
{
|
|
OTL_UInt count = OTL_NEXT_USHORT( p );
|
|
|
|
|
|
if ( p + count * 2 >= valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
/* XXX: check glyph indices */
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
{
|
|
OTL_UInt n, num_ranges = OTL_NEXT_USHORT( p );
|
|
OTL_UInt start, end, start_cover, total = 0, last = 0;
|
|
|
|
|
|
if ( p + num_ranges * 6 >= valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
for ( n = 0; n < num_ranges; n++ )
|
|
{
|
|
start = OTL_NEXT_USHORT( p );
|
|
end = OTL_NEXT_USHORT( p );
|
|
start_cover = OTL_NEXT_USHORT( p );
|
|
|
|
if ( start > end || start_cover != total )
|
|
OTL_INVALID_DATA;
|
|
|
|
if ( n > 0 && start <= last )
|
|
OTL_INVALID_DATA;
|
|
|
|
total += end - start + 1;
|
|
last = end;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
OTL_INVALID_FORMAT;
|
|
}
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_UInt )
|
|
otl_coverage_get_count( OTL_Bytes table )
|
|
{
|
|
OTL_Bytes p = table;
|
|
OTL_UInt format = OTL_NEXT_USHORT( p );
|
|
OTL_UInt count = OTL_NEXT_USHORT( p );
|
|
OTL_UInt result = 0;
|
|
|
|
|
|
switch ( format )
|
|
{
|
|
case 1:
|
|
return count;
|
|
|
|
case 2:
|
|
{
|
|
OTL_UInt start, end;
|
|
|
|
|
|
for ( ; count > 0; count-- )
|
|
{
|
|
start = OTL_NEXT_USHORT( p );
|
|
end = OTL_NEXT_USHORT( p );
|
|
p += 2; /* skip start_index */
|
|
|
|
result += end - start + 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_Int )
|
|
otl_coverage_get_index( OTL_Bytes table,
|
|
OTL_UInt glyph_index )
|
|
{
|
|
OTL_Bytes p = table;
|
|
OTL_UInt format = OTL_NEXT_USHORT( p );
|
|
OTL_UInt count = OTL_NEXT_USHORT( p );
|
|
|
|
|
|
switch ( format )
|
|
{
|
|
case 1:
|
|
{
|
|
OTL_UInt min = 0, max = count, mid, gindex;
|
|
|
|
|
|
table += 4;
|
|
while ( min < max )
|
|
{
|
|
mid = ( min + max ) >> 1;
|
|
p = table + 2 * mid;
|
|
gindex = OTL_PEEK_USHORT( p );
|
|
|
|
if ( glyph_index == gindex )
|
|
return (OTL_Int)mid;
|
|
|
|
if ( glyph_index < gindex )
|
|
max = mid;
|
|
else
|
|
min = mid + 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
{
|
|
OTL_UInt min = 0, max = count, mid;
|
|
OTL_UInt start, end, delta, start_cover;
|
|
|
|
|
|
table += 4;
|
|
while ( min < max )
|
|
{
|
|
mid = ( min + max ) >> 1;
|
|
p = table + 6 * mid;
|
|
start = OTL_NEXT_USHORT( p );
|
|
end = OTL_NEXT_USHORT( p );
|
|
|
|
if ( glyph_index < start )
|
|
max = mid;
|
|
else if ( glyph_index > end )
|
|
min = mid + 1;
|
|
else
|
|
return (OTL_Int)( glyph_index + OTL_NEXT_USHORT( p ) - start );
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/***** *****/
|
|
/***** CLASS DEFINITION TABLE *****/
|
|
/***** *****/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
OTL_LOCALDEF( void )
|
|
otl_class_definition_validate( OTL_Bytes table,
|
|
OTL_Validator valid )
|
|
{
|
|
OTL_Bytes p = table;
|
|
OTL_UInt format;
|
|
|
|
|
|
if ( p + 4 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
format = OTL_NEXT_USHORT( p );
|
|
switch ( format )
|
|
{
|
|
case 1:
|
|
{
|
|
OTL_UInt count, start = OTL_NEXT_USHORT( p );
|
|
|
|
|
|
if ( p + 2 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
count = OTL_NEXT_USHORT( p );
|
|
|
|
if ( p + count * 2 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
/* XXX: check glyph indices */
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
{
|
|
OTL_UInt n, num_ranges = OTL_NEXT_USHORT( p );
|
|
OTL_UInt start, end, value, last = 0;
|
|
|
|
|
|
if ( p + num_ranges * 6 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
for ( n = 0; n < num_ranges; n++ )
|
|
{
|
|
start = OTL_NEXT_USHORT( p );
|
|
end = OTL_NEXT_USHORT( p );
|
|
value = OTL_NEXT_USHORT( p ); /* ignored */
|
|
|
|
if ( start > end || ( n > 0 && start <= last ) )
|
|
OTL_INVALID_DATA;
|
|
|
|
last = end;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
OTL_INVALID_FORMAT;
|
|
}
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_UInt )
|
|
otl_class_definition_get_value( OTL_Bytes table,
|
|
OTL_UInt glyph_index )
|
|
{
|
|
OTL_Bytes p = table;
|
|
OTL_UInt format = OTL_NEXT_USHORT( p );
|
|
|
|
|
|
switch ( format )
|
|
{
|
|
case 1:
|
|
{
|
|
OTL_UInt start = OTL_NEXT_USHORT( p );
|
|
OTL_UInt count = OTL_NEXT_USHORT( p );
|
|
OTL_UInt idx = (OTL_UInt)( glyph_index - start );
|
|
|
|
|
|
if ( idx < count )
|
|
{
|
|
p += 2 * idx;
|
|
return OTL_PEEK_USHORT( p );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
{
|
|
OTL_UInt count = OTL_NEXT_USHORT( p );
|
|
OTL_UInt min = 0, max = count, mid, gindex;
|
|
|
|
|
|
table += 4;
|
|
while ( min < max )
|
|
{
|
|
mid = ( min + max ) >> 1;
|
|
p = table + 6 * mid;
|
|
start = OTL_NEXT_USHORT( p );
|
|
end = OTL_NEXT_USHORT( p );
|
|
|
|
if ( glyph_index < start )
|
|
max = mid;
|
|
else if ( glyph_index > end )
|
|
min = mid + 1;
|
|
else
|
|
return OTL_PEEK_USHORT( p );
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/***** *****/
|
|
/***** DEVICE TABLE *****/
|
|
/***** *****/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
OTL_LOCALDEF( void )
|
|
otl_device_table_validate( OTL_Bytes table,
|
|
OTL_Validator valid )
|
|
{
|
|
OTL_Bytes p = table;
|
|
OTL_UInt start, end, count, format, count;
|
|
|
|
|
|
if ( p + 8 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
start = OTL_NEXT_USHORT( p );
|
|
end = OTL_NEXT_USHORT( p );
|
|
format = OTL_NEXT_USHORT( p );
|
|
|
|
if ( format < 1 || format > 3 || end < start )
|
|
OTL_INVALID_DATA;
|
|
|
|
count = (OTL_UInt)( end - start + 1 );
|
|
|
|
if ( p + ( ( 1 << format ) * count ) / 8 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_UInt )
|
|
otl_device_table_get_start( OTL_Bytes table )
|
|
{
|
|
OTL_Bytes p = table;
|
|
|
|
|
|
return OTL_PEEK_USHORT( p );
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_UInt )
|
|
otl_device_table_get_end( OTL_Bytes table )
|
|
{
|
|
OTL_Bytes p = table + 2;
|
|
|
|
|
|
return OTL_PEEK_USHORT( p );
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_Int )
|
|
otl_device_table_get_delta( OTL_Bytes table,
|
|
OTL_UInt size )
|
|
{
|
|
OTL_Bytes p = table;
|
|
OTL_Int result = 0;
|
|
OTL_UInt start, end, format, idx, value;
|
|
|
|
|
|
start = OTL_NEXT_USHORT( p );
|
|
end = OTL_NEXT_USHORT( p );
|
|
format = OTL_NEXT_USHORT( p );
|
|
|
|
if ( size >= start && size <= end )
|
|
{
|
|
/* we could do that with clever bit operations, but a switch is */
|
|
/* much simpler to understand and maintain */
|
|
/* */
|
|
switch ( format )
|
|
{
|
|
case 1:
|
|
idx = (OTL_UInt)( ( size - start ) * 2 );
|
|
p += idx / 16;
|
|
value = OTL_PEEK_USHORT( p );
|
|
shift = idx & 15;
|
|
result = (OTL_Short)( value << shift ) >> ( 14 - shift );
|
|
|
|
break;
|
|
|
|
case 2:
|
|
idx = (OTL_UInt)( ( size - start ) * 4 );
|
|
p += idx / 16;
|
|
value = OTL_PEEK_USHORT( p );
|
|
shift = idx & 15;
|
|
result = (OTL_Short)( value << shift ) >> ( 12 - shift );
|
|
|
|
break;
|
|
|
|
case 3:
|
|
idx = (OTL_UInt)( ( size - start ) * 8 );
|
|
p += idx / 16;
|
|
value = OTL_PEEK_USHORT( p );
|
|
shift = idx & 15;
|
|
result = (OTL_Short)( value << shift ) >> ( 8 - shift );
|
|
|
|
break;
|
|
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/***** *****/
|
|
/***** LOOKUP LISTS *****/
|
|
/***** *****/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
OTL_LOCALDEF( void )
|
|
otl_lookup_validate( OTL_Bytes table,
|
|
OTL_Validator valid )
|
|
{
|
|
OTL_Bytes p = table;
|
|
OTL_UInt num_tables;
|
|
|
|
|
|
if ( table + 6 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
p += 4;
|
|
num_tables = OTL_NEXT_USHORT( p );
|
|
|
|
if ( p + num_tables * 2 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
for ( ; num_tables > 0; num_tables-- )
|
|
{
|
|
offset = OTL_NEXT_USHORT( p );
|
|
|
|
if ( table + offset >= valid->limit )
|
|
OTL_INVALID_OFFSET;
|
|
}
|
|
|
|
/* XXX: check sub-tables? */
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_UInt )
|
|
otl_lookup_get_count( OTL_Bytes table )
|
|
{
|
|
OTL_Bytes p = table + 4;
|
|
|
|
|
|
return OTL_PEEK_USHORT( p );
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_Bytes )
|
|
otl_lookup_get_table( OTL_Bytes table,
|
|
OTL_UInt idx )
|
|
{
|
|
OTL_Bytes p, result = NULL;
|
|
OTL_UInt count;
|
|
|
|
|
|
p = table + 4;
|
|
count = OTL_NEXT_USHORT( p );
|
|
if ( idx < count )
|
|
{
|
|
p += idx * 2;
|
|
result = table + OTL_PEEK_USHORT( p );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/***** *****/
|
|
/***** LOOKUP LISTS *****/
|
|
/***** *****/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
OTL_LOCALDEF( void )
|
|
otl_lookup_list_validate( OTL_Bytes table,
|
|
OTL_Validator valid )
|
|
{
|
|
OTL_Bytes p = table, q;
|
|
OTL_UInt num_lookups, offset;
|
|
|
|
|
|
if ( p + 2 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
num_lookups = OTL_NEXT_USHORT( p );
|
|
|
|
if ( p + num_lookups * 2 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
for ( ; num_lookups > 0; num_lookups-- )
|
|
{
|
|
offset = OTL_NEXT_USHORT( p );
|
|
|
|
otl_lookup_validate( table + offset, valid );
|
|
}
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_UInt )
|
|
otl_lookup_list_get_count( OTL_Bytes table )
|
|
{
|
|
OTL_Bytes p = table;
|
|
|
|
|
|
return OTL_PEEK_USHORT( p );
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_Bytes )
|
|
otl_lookup_list_get_lookup( OTL_Bytes table,
|
|
OTL_UInt idx )
|
|
{
|
|
OTL_Bytes p, result = 0;
|
|
OTL_UInt count;
|
|
|
|
|
|
p = table;
|
|
count = OTL_NEXT_USHORT( p );
|
|
if ( idx < count )
|
|
{
|
|
p += idx * 2;
|
|
result = table + OTL_PEEK_USHORT( p );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_Bytes )
|
|
otl_lookup_list_get_table( OTL_Bytes table,
|
|
OTL_UInt lookup_index,
|
|
OTL_UInt table_index )
|
|
{
|
|
OTL_Bytes result = NULL;
|
|
|
|
|
|
result = otl_lookup_list_get_lookup( table, lookup_index );
|
|
if ( result )
|
|
result = otl_lookup_get_table( result, table_index );
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( void )
|
|
otl_lookup_list_foreach( OTL_Bytes table,
|
|
OTL_ForeachFunc func,
|
|
OTL_Pointer func_data )
|
|
{
|
|
OTL_Bytes p = table;
|
|
OTL_UInt count = OTL_NEXT_USHORT( p );
|
|
|
|
|
|
for ( ; count > 0; count-- )
|
|
func( table + OTL_NEXT_USHORT( p ), func_data );
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/***** *****/
|
|
/***** FEATURES *****/
|
|
/***** *****/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
OTL_LOCALDEF( void )
|
|
otl_feature_validate( OTL_Bytes table,
|
|
OTL_Validator valid )
|
|
{
|
|
OTL_Bytes p = table;
|
|
OTL_UInt feat_params, num_lookups;
|
|
|
|
|
|
if ( p + 4 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
feat_params = OTL_NEXT_USHORT( p ); /* ignored */
|
|
num_lookups = OTL_NEXT_USHORT( p );
|
|
|
|
if ( p + num_lookups * 2 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
/* XXX: check lookup indices */
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_UInt )
|
|
otl_feature_get_count( OTL_Bytes table )
|
|
{
|
|
OTL_Bytes p = table + 4;
|
|
|
|
|
|
return OTL_PEEK_USHORT( p );
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_UInt )
|
|
otl_feature_get_lookups( OTL_Bytes table,
|
|
OTL_UInt start,
|
|
OTL_UInt count,
|
|
OTL_UInt *lookups )
|
|
{
|
|
OTL_Bytes p;
|
|
OTL_UInt num_features, result = 0;
|
|
|
|
|
|
p = table + 4;
|
|
num_features = OTL_NEXT_USHORT( p );
|
|
|
|
p += start * 2;
|
|
|
|
for ( ; count > 0 && start < num_features; count--, start++ )
|
|
{
|
|
lookups[0] = OTL_NEXT_USHORT(p);
|
|
lookups++;
|
|
result++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/***** *****/
|
|
/***** FEATURE LIST *****/
|
|
/***** *****/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
OTL_LOCALDEF( void )
|
|
otl_feature_list_validate( OTL_Bytes table,
|
|
OTL_Validator valid )
|
|
{
|
|
OTL_Bytes p = table;
|
|
OTL_UInt num_features, offset;
|
|
|
|
|
|
if ( table + 2 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
num_features = OTL_NEXT_USHORT( p );
|
|
|
|
if ( p + num_features * 2 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
for ( ; num_features > 0; num_features-- )
|
|
{
|
|
p += 4; /* skip tag */
|
|
offset = OTL_NEXT_USHORT( p );
|
|
|
|
otl_feature_table_validate( table + offset, valid );
|
|
}
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_UInt )
|
|
otl_feature_list_get_count( OTL_Bytes table )
|
|
{
|
|
OTL_Bytes p = table;
|
|
|
|
|
|
return OTL_PEEK_USHORT( p );
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_Bytes )
|
|
otl_feature_list_get_feature( OTL_Bytes table,
|
|
OTL_UInt idx )
|
|
{
|
|
OTL_Bytes p, result = NULL;
|
|
OTL_UInt count;
|
|
|
|
|
|
p = table;
|
|
count = OTL_NEXT_USHORT( p );
|
|
|
|
if ( idx < count )
|
|
{
|
|
p += idx * 2;
|
|
result = table + OTL_PEEK_USHORT( p );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( void )
|
|
otl_feature_list_foreach( OTL_Bytes table,
|
|
OTL_ForeachFunc func,
|
|
OTL_Pointer func_data )
|
|
{
|
|
OTL_Bytes p;
|
|
OTL_UInt count;
|
|
|
|
|
|
p = table;
|
|
count = OTL_NEXT_USHORT( p );
|
|
|
|
for ( ; count > 0; count-- )
|
|
func( table + OTL_NEXT_USHORT( p ), func_data );
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/***** *****/
|
|
/***** LANGUAGE SYSTEM *****/
|
|
/***** *****/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
|
|
OTL_LOCALDEF( void )
|
|
otl_lang_validate( OTL_Bytes table,
|
|
OTL_Validator valid )
|
|
{
|
|
OTL_Bytes p = table;
|
|
OTL_UInt lookup_order;
|
|
OTL_UInt req_feature;
|
|
OTL_UInt num_features;
|
|
|
|
|
|
if ( table + 6 >= valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
lookup_order = OTL_NEXT_USHORT( p );
|
|
req_feature = OTL_NEXT_USHORT( p );
|
|
num_features = OTL_NEXT_USHORT( p );
|
|
|
|
/* XXX: check req_feature if not 0xFFFFU */
|
|
|
|
if ( p + 2 * num_features >= valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
/* XXX: check features indices! */
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_UInt )
|
|
otl_lang_get_count( OTL_Bytes table )
|
|
{
|
|
OTL_Bytes p = table + 4;
|
|
|
|
return OTL_PEEK_USHORT( p );
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_UInt )
|
|
otl_lang_get_req_feature( OTL_Bytes table )
|
|
{
|
|
OTL_Bytes p = table + 2;
|
|
|
|
|
|
return OTL_PEEK_USHORT( p );
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( OTL_UInt )
|
|
otl_lang_get_features( OTL_Bytes table,
|
|
OTL_UInt start,
|
|
OTL_UInt count,
|
|
OTL_UInt *features )
|
|
{
|
|
OTL_Bytes p = table + 4;
|
|
OTL_UInt num_features = OTL_NEXT_USHORT( p );
|
|
OTL_UInt result = 0;
|
|
|
|
|
|
p += start * 2;
|
|
|
|
for ( ; count > 0 && start < num_features; start++, count-- )
|
|
{
|
|
features[0] = OTL_NEXT_USHORT( p );
|
|
features++;
|
|
result++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/***** *****/
|
|
/***** SCRIPTS *****/
|
|
/***** *****/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
|
|
OTL_LOCALDEF( void )
|
|
otl_script_validate( OTL_Bytes table,
|
|
OTL_Validator valid )
|
|
{
|
|
OTL_UInt default_lang;
|
|
OTL_Bytes p = table;
|
|
|
|
|
|
if ( table + 4 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
default_lang = OTL_NEXT_USHORT( p );
|
|
num_langs = OTL_NEXT_USHORT( p );
|
|
|
|
if ( default_lang != 0 )
|
|
{
|
|
if ( table + default_lang >= valid->limit )
|
|
OTL_INVALID_OFFSET;
|
|
}
|
|
|
|
if ( p + num_langs * 6 >= valid->limit )
|
|
OTL_INVALID_OFFSET;
|
|
|
|
for ( ; num_langs > 0; num_langs-- )
|
|
{
|
|
OTL_UInt offset;
|
|
|
|
|
|
p += 4; /* skip tag */
|
|
offset = OTL_NEXT_USHORT( p );
|
|
|
|
otl_lang_validate( table + offset, valid );
|
|
}
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( void )
|
|
otl_script_list_validate( OTL_Bytes list,
|
|
OTL_Validator valid )
|
|
{
|
|
OTL_UInt num_scripts;
|
|
OTL_Bytes p = list;
|
|
|
|
|
|
if ( list + 2 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
num_scripts = OTL_NEXT_USHORT( p );
|
|
|
|
if ( p + num_scripts * 6 > valid->limit )
|
|
OTL_INVALID_TOO_SHORT;
|
|
|
|
for ( ; num_scripts > 0; num_scripts-- )
|
|
{
|
|
OTL_UInt offset;
|
|
|
|
|
|
p += 4; /* skip tag */
|
|
offset = OTL_NEXT_USHORT( p );
|
|
|
|
otl_script_table_validate( list + offset, valid );
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/***** *****/
|
|
/***** LOOKUP LISTS *****/
|
|
/***** *****/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
static void
|
|
otl_lookup_table_validate( OTL_Bytes table,
|
|
OTL_UInt type_count,
|
|
OTL_ValidateFunc* type_funcs,
|
|
OTL_Validator valid )
|
|
{
|
|
OTL_Bytes p = table;
|
|
OTL_UInt lookup_type, lookup_flag, count;
|
|
OTL_ValidateFunc validate;
|
|
|
|
OTL_CHECK( 6 );
|
|
lookup_type = OTL_NEXT_USHORT( p );
|
|
lookup_flag = OTL_NEXT_USHORT( p );
|
|
count = OTL_NEXT_USHORT( p );
|
|
|
|
if ( lookup_type == 0 || lookup_type >= type_count )
|
|
OTL_INVALID_DATA;
|
|
|
|
validate = type_funcs[ lookup_type - 1 ];
|
|
|
|
OTL_CHECK( 2*count );
|
|
for ( ; count > 0; count-- )
|
|
validate( table + OTL_NEXT_USHORT( p ), valid );
|
|
}
|
|
|
|
|
|
OTL_LOCALDEF( void )
|
|
otl_lookup_list_validate( OTL_Bytes table,
|
|
OTL_UInt type_count,
|
|
OTL_ValidateFunc* type_funcs,
|
|
OTL_Validator valid )
|
|
{
|
|
OTL_Bytes p = table;
|
|
OTL_UInt count;
|
|
|
|
OTL_CHECK( 2 );
|
|
count = OTL_NEXT_USHORT( p );
|
|
|
|
OTL_CHECK( 2*count );
|
|
for ( ; count > 0; count-- )
|
|
otl_lookup_table_validate( table + OTL_NEXT_USHORT( p ),
|
|
type_count, type_funcs, valid );
|
|
}
|
|
|
|
/* END */
|