mirror of
https://github.com/freetype/freetype.git
synced 2026-10-23 11:08:54 +03:00
[truetype] Update the VARC variation-store binary format.
Spec: https://github.com/harfbuzz/boring-expansion-spec/pull/175 Offset-base erratum: https://github.com/harfbuzz/boring-expansion-spec/issues/177 * src/truetype/ttvarc.c (tt_varc_evaluate_region, tt_varc_get_item_deltas): Read the sparse-region count as uint32, resolve each sparse axis through a region-relative Offset32, and follow the Offset32 to the delta-set INDEX. Bound the new offset fields and count arrays before dereferencing them. * tests/varc/main.c (test_variation_store): New function to check translated outlines at default, midpoint, and peak coordinates. (main): Updated. * tests/data/varc-null-condition.ttf, tests/data/varc-value-conditions.ttf: Updated to migrate the existing affected fixture without changing its logical contents or non-VARC tables. * tests/data/varc-variation-store.ttf: New test file. Tests: full Meson regression suite (3 passed). Assisted-by: OpenAI Codex
This commit is contained in:
committed by
Werner Lemberg
parent
d1f916a65e
commit
42e28aa7ad
@@ -1070,9 +1070,10 @@
|
||||
if ( !coords || coord_count == 0 )
|
||||
return scalar; /* no coords available, return 1.0 */
|
||||
|
||||
/* `SparseVarRegionList` region format:
|
||||
/* `SparseVariationRegion` table:
|
||||
*
|
||||
* regionAxisCount (u16) - number of non-default axes
|
||||
* axisOffsets (u32[regionAxisCount]) - relative to this region
|
||||
* for each sparse axis:
|
||||
* axisIndex (u16)
|
||||
* startCoord (F2DOT14)
|
||||
@@ -1080,13 +1081,16 @@
|
||||
* endCoord (F2DOT14)
|
||||
*/
|
||||
|
||||
if ( p + 2 > limit )
|
||||
if ( limit - p < 2 )
|
||||
return 0;
|
||||
|
||||
/* read sparse axis count (u16) */
|
||||
axis_count = ( (FT_UInt)p[0] << 8 ) | p[1];
|
||||
p += 2;
|
||||
|
||||
if ( axis_count > (FT_ULong)( limit - p ) / 4 )
|
||||
return 0;
|
||||
|
||||
/* process each axis range */
|
||||
for ( i = 0; i < axis_count; i++ )
|
||||
{
|
||||
@@ -1094,32 +1098,38 @@
|
||||
FT_Short min_val, peak_val, max_val;
|
||||
FT_Fixed coord;
|
||||
FT_Fixed axis_scalar;
|
||||
FT_UInt32 axis_offset;
|
||||
FT_Byte* axis;
|
||||
|
||||
FT_Fixed min_16;
|
||||
FT_Fixed peak_16;
|
||||
FT_Fixed max_16;
|
||||
|
||||
|
||||
/* read axis index (u16) */
|
||||
if ( p + 8 > limit )
|
||||
axis_offset = ( (FT_UInt32)p[0] << 24 ) |
|
||||
( (FT_UInt32)p[1] << 16 ) |
|
||||
( (FT_UInt32)p[2] << 8 ) |
|
||||
(FT_UInt32)p[3];
|
||||
p += 4;
|
||||
|
||||
if ( axis_offset > (FT_ULong)( limit - region_data ) ||
|
||||
(FT_ULong)( limit - region_data ) - axis_offset < 8 )
|
||||
return 0;
|
||||
|
||||
axis_index = ( (FT_UInt16)p[0] << 8 ) | p[1];
|
||||
p += 2;
|
||||
axis = region_data + axis_offset;
|
||||
axis_index = ( (FT_UInt16)axis[0] << 8 ) | axis[1];
|
||||
axis += 2;
|
||||
|
||||
/* read min, peak, max (F2DOT14) - each is 2 bytes, */
|
||||
/* advance after each read */
|
||||
min_val = (FT_Short)( ( (FT_UShort)p[0] << 8 ) |
|
||||
(FT_UShort)p[1] );
|
||||
p += 2;
|
||||
/* fixed: read from p[0] after advancing */
|
||||
peak_val = (FT_Short)( ( (FT_UShort)p[0] << 8 ) |
|
||||
(FT_UShort)p[1] );
|
||||
p += 2;
|
||||
/* fixed: read from p[0] after advancing */
|
||||
max_val = (FT_Short)( ( (FT_UShort)p[0] << 8 ) |
|
||||
(FT_UShort)p[1] );
|
||||
p += 2;
|
||||
min_val = (FT_Short)( ( (FT_UShort)axis[0] << 8 ) |
|
||||
(FT_UShort)axis[1] );
|
||||
axis += 2;
|
||||
peak_val = (FT_Short)( ( (FT_UShort)axis[0] << 8 ) |
|
||||
(FT_UShort)axis[1] );
|
||||
axis += 2;
|
||||
max_val = (FT_Short)( ( (FT_UShort)axis[0] << 8 ) |
|
||||
(FT_UShort)axis[1] );
|
||||
|
||||
/* get coordinate for this axis */
|
||||
if ( axis_index >= coord_count )
|
||||
@@ -1385,15 +1395,18 @@
|
||||
FT_Byte mvd_format;
|
||||
FT_UInt16 region_count;
|
||||
FT_Byte* delta_sets;
|
||||
FT_UInt32 delta_sets_offset;
|
||||
FT_Byte* tuple_data;
|
||||
FT_UInt tuple_size;
|
||||
|
||||
FT_Int64 stack_accumulators[VARC_STACK_DELTA_COUNT];
|
||||
/* deltas * regions */
|
||||
FT_Int32 stack_all_deltas[VARC_STACK_DELTA_COUNT * 16];
|
||||
FT_Int64* accumulators = NULL;
|
||||
FT_Byte* regions_data = NULL;
|
||||
FT_Byte* regions_limit = NULL;
|
||||
FT_Int64* accumulators = NULL;
|
||||
FT_Byte* regions_data = NULL;
|
||||
FT_Byte* regions_limit = NULL;
|
||||
FT_UInt32 region_list_count = 0;
|
||||
|
||||
FT_UInt total_deltas;
|
||||
FT_Int32* all_deltas = NULL;
|
||||
FT_UInt bytes_consumed = 0;
|
||||
@@ -1439,8 +1452,6 @@
|
||||
(FT_UInt32)p[3];
|
||||
p += 4;
|
||||
|
||||
FT_UNUSED( regions_offset ); /* TODO: Use for region evaluation */
|
||||
|
||||
/* read dataSets count (u16) */
|
||||
data_sets_count = ( (FT_UInt16)p[0] << 8 ) | p[1];
|
||||
p += 2;
|
||||
@@ -1467,7 +1478,7 @@
|
||||
|
||||
mvd_data = mvs_data + data_set_offset;
|
||||
|
||||
if ( mvd_data + 3 > table_limit )
|
||||
if ( table_limit - mvd_data < 7 )
|
||||
return FT_THROW( Invalid_Table );
|
||||
|
||||
/* parse `MultiVarData` header */
|
||||
@@ -1482,14 +1493,23 @@
|
||||
/* read `regionIndices` count (u16) */
|
||||
region_count = ( (FT_UInt16)p[0] << 8 ) | p[1];
|
||||
p += 2;
|
||||
/* skip `regionIndices` array */
|
||||
|
||||
if ( region_count > (FT_ULong)( table_limit - p ) / 2 )
|
||||
return FT_THROW( Invalid_Table );
|
||||
p += region_count * 2;
|
||||
|
||||
if ( p > table_limit )
|
||||
if ( table_limit - p < 4 )
|
||||
return FT_THROW( Invalid_Table );
|
||||
|
||||
/* Now `p` points to `deltaSets` (INDEX2) */
|
||||
delta_sets = p;
|
||||
delta_sets_offset = ( (FT_UInt32)p[0] << 24 ) |
|
||||
( (FT_UInt32)p[1] << 16 ) |
|
||||
( (FT_UInt32)p[2] << 8 ) |
|
||||
(FT_UInt32)p[3];
|
||||
if ( !delta_sets_offset ||
|
||||
delta_sets_offset > (FT_ULong)( table_limit - mvd_data ) ||
|
||||
(FT_ULong)( table_limit - mvd_data ) - delta_sets_offset < 4 )
|
||||
return FT_THROW( Invalid_Table );
|
||||
delta_sets = mvd_data + delta_sets_offset;
|
||||
|
||||
/* read tuple from INDEX2 at inner index */
|
||||
error = tt_varc_read_cff2_index_entry( delta_sets, table_limit, inner,
|
||||
@@ -1525,18 +1545,37 @@
|
||||
}
|
||||
|
||||
/* parse `SparseVarRegionList` to get region definitions */
|
||||
if ( regions_offset > 0 && regions_offset < varc->table_size )
|
||||
if ( regions_offset > 0 &&
|
||||
regions_offset <= (FT_ULong)( table_limit - mvs_data ) )
|
||||
{
|
||||
regions_data = mvs_data + regions_offset;
|
||||
regions_limit = table_limit;
|
||||
|
||||
/* read and skip region count (u16) */
|
||||
if ( regions_data + 2 <= regions_limit )
|
||||
regions_data += 2;
|
||||
/* read region count (u32) */
|
||||
if ( regions_limit - regions_data >= 4 )
|
||||
{
|
||||
region_list_count = ( (FT_UInt32)regions_data[0] << 24 ) |
|
||||
( (FT_UInt32)regions_data[1] << 16 ) |
|
||||
( (FT_UInt32)regions_data[2] << 8 ) |
|
||||
(FT_UInt32)regions_data[3];
|
||||
regions_data += 4;
|
||||
|
||||
if ( region_list_count >
|
||||
(FT_ULong)( regions_limit - regions_data ) / 4 )
|
||||
regions_data = NULL;
|
||||
}
|
||||
else
|
||||
regions_data = NULL;
|
||||
}
|
||||
|
||||
if ( !regions_data )
|
||||
{
|
||||
if ( accumulators != stack_accumulators )
|
||||
FT_FREE( accumulators );
|
||||
error = FT_THROW( Invalid_Table );
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* read ALL deltas from the flat tuple */
|
||||
/* (region_count * num_deltas values) */
|
||||
|
||||
@@ -1604,23 +1643,27 @@
|
||||
{
|
||||
/* SparseVarRegionList format:
|
||||
*
|
||||
* u16: regionCount
|
||||
* u32: regionCount
|
||||
* u32[regionCount]: array of offsets (from start of list)
|
||||
* to each region
|
||||
*/
|
||||
|
||||
/* back to start (before count) */
|
||||
FT_Byte* region_list_start = regions_data - 2;
|
||||
FT_Byte* region_offset_ptr = regions_data + region_index * 4;
|
||||
FT_Byte* region_list_start = regions_data - 4;
|
||||
FT_Byte* region_offset_ptr;
|
||||
|
||||
|
||||
if ( region_offset_ptr + 4 <= regions_limit )
|
||||
if ( region_index < region_list_count &&
|
||||
region_index < (FT_ULong)( regions_limit - regions_data ) / 4 )
|
||||
{
|
||||
FT_UInt32 region_offset =
|
||||
( (FT_UInt32)region_offset_ptr[0] << 24 ) |
|
||||
( (FT_UInt32)region_offset_ptr[1] << 16 ) |
|
||||
( (FT_UInt32)region_offset_ptr[2] << 8 ) |
|
||||
(FT_UInt32)region_offset_ptr[3];
|
||||
FT_UInt32 region_offset;
|
||||
|
||||
|
||||
region_offset_ptr = regions_data + region_index * 4;
|
||||
region_offset = ( (FT_UInt32)region_offset_ptr[0] << 24 ) |
|
||||
( (FT_UInt32)region_offset_ptr[1] << 16 ) |
|
||||
( (FT_UInt32)region_offset_ptr[2] << 8 ) |
|
||||
(FT_UInt32)region_offset_ptr[3];
|
||||
|
||||
|
||||
if ( region_offset <
|
||||
|
||||
Binary file not shown.
Binary file not shown.
BIN
tests/data/varc-variation-store.ttf
Normal file
BIN
tests/data/varc-variation-store.ttf
Normal file
Binary file not shown.
@@ -198,6 +198,51 @@ Exit:
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test_variation_store( FT_Library library,
|
||||
const char* testdata_dir )
|
||||
{
|
||||
FT_Face face = NULL;
|
||||
char filepath[FILENAME_MAX];
|
||||
|
||||
FT_UInt i;
|
||||
int ret = 1;
|
||||
|
||||
|
||||
/* A triangle translated by 100, plus a TEST-axis delta of 100. */
|
||||
snprintf( filepath, sizeof ( filepath ), "%s/varc-variation-store.ttf",
|
||||
testdata_dir );
|
||||
if ( FT_New_Face( library, filepath, 0, &face ) )
|
||||
goto Exit;
|
||||
|
||||
for ( i = 0; i < 3; i++ )
|
||||
{
|
||||
FT_Fixed coordinate = (FT_Fixed)i * 0x8000L;
|
||||
FT_BBox box;
|
||||
|
||||
|
||||
if ( FT_Set_Var_Design_Coordinates( face, 1, &coordinate ) ||
|
||||
FT_Load_Glyph( face, 2, FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING ) )
|
||||
goto Exit;
|
||||
|
||||
FT_Outline_Get_CBox( &face->glyph->outline, &box );
|
||||
if ( box.xMin != 100 + (FT_Pos)i * 50 ||
|
||||
box.xMax != 200 + (FT_Pos)i * 50 ||
|
||||
box.yMin != 0 ||
|
||||
box.yMax != 100 )
|
||||
{
|
||||
fprintf( stderr, "Unexpected VARC variation-store outline\n" );
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
Exit:
|
||||
FT_Done_Face( face );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main( void )
|
||||
{
|
||||
@@ -233,6 +278,7 @@ main( void )
|
||||
|
||||
ret |= test_reset_without_axes( library, testdata_dir );
|
||||
ret |= test_value_conditions( library, testdata_dir );
|
||||
ret |= test_variation_store( library, testdata_dir );
|
||||
|
||||
FT_Done_FreeType( library );
|
||||
return ret;
|
||||
|
||||
Reference in New Issue
Block a user