mirror of
https://github.com/freetype/freetype.git
synced 2026-10-23 11:08:54 +03:00
[truetype] Use the blend axis count for private VARC axes.
Static VARC fonts can have private variation axes without an 'fvar' table or public MM metadata. Use the blend's axis count when validating 'avar' and item variation stores, avoiding a null 'mmvar' dereference when loading HVAR or VVAR for a VARC component. * src/truetype/ttgxvar.c (ft_var_load_avar, tt_var_load_item_variation_store): Use blend->num_axis. * tests/varc/make-hvvar-fonts.py: Generate HVAR and VVAR fixtures from the static VARC/gvar font using only Python's standard library. * tests/meson.build: Generate the fixtures and pass them to the VARC test. * tests/varc/main.c (test_font, main): Test generated fixture paths. Tests: all five Meson regression tests pass. Fixes issue #1474. Assisted-by: OpenAI Codex
This commit is contained in:
committed by
Werner Lemberg
parent
a5877f48ff
commit
0eca44e6e8
@@ -413,7 +413,7 @@
|
||||
|
||||
FT_TRACE2(( "loaded\n" ));
|
||||
|
||||
if ( axisCount != (FT_Long)blend->mmvar->num_axis )
|
||||
if ( axisCount != (FT_Long)blend->num_axis )
|
||||
{
|
||||
FT_TRACE2(( "ft_var_load_avar:"
|
||||
" number of axes in `avar' and `fvar'\n" ));
|
||||
@@ -571,7 +571,7 @@
|
||||
FT_READ_USHORT( region_count ) )
|
||||
goto Exit;
|
||||
|
||||
if ( axis_count != (FT_Long)blend->mmvar->num_axis )
|
||||
if ( axis_count != (FT_Long)blend->num_axis )
|
||||
{
|
||||
FT_TRACE2(( "tt_var_load_item_variation_store:"
|
||||
" number of axes in item variation store\n" ));
|
||||
|
||||
@@ -15,6 +15,12 @@ test('issue-1063',
|
||||
|
||||
###
|
||||
|
||||
varc_hvvar_fonts = custom_target('varc-hvvar-fonts',
|
||||
input: files('data/varc-static-gvar.ttf'),
|
||||
output: ['varc-static-hvar.ttf', 'varc-static-vvar.ttf'],
|
||||
command: [python_exe, files('varc/make-hvvar-fonts.py'), '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
|
||||
test_varc = executable('varc',
|
||||
files([ 'varc/main.c' ]),
|
||||
dependencies: freetype_dep,
|
||||
@@ -23,6 +29,8 @@ test_varc = executable('varc',
|
||||
test('varc',
|
||||
test_varc,
|
||||
env: test_env,
|
||||
depends: varc_hvvar_fonts,
|
||||
args: [varc_hvvar_fonts[0], varc_hvvar_fonts[1]],
|
||||
suite: 'regression')
|
||||
|
||||
###
|
||||
|
||||
@@ -29,8 +29,11 @@ test_font( FT_Library library,
|
||||
int ret = 1;
|
||||
|
||||
|
||||
snprintf( filepath, sizeof ( filepath ), "%s/%s",
|
||||
testdata_dir, test_font->filename );
|
||||
if ( testdata_dir )
|
||||
snprintf( filepath, sizeof ( filepath ), "%s/%s",
|
||||
testdata_dir, test_font->filename );
|
||||
else
|
||||
snprintf( filepath, sizeof ( filepath ), "%s", test_font->filename );
|
||||
|
||||
if ( FT_New_Face( library, filepath, 0, &face ) )
|
||||
{
|
||||
@@ -244,7 +247,8 @@ Exit:
|
||||
|
||||
|
||||
int
|
||||
main( void )
|
||||
main( int argc,
|
||||
char** argv )
|
||||
{
|
||||
static const TestFont test_fonts[] =
|
||||
{
|
||||
@@ -276,6 +280,16 @@ main( void )
|
||||
i++ )
|
||||
ret |= test_font( library, testdata_dir, &test_fonts[i] );
|
||||
|
||||
/* Generated HVAR/VVAR fixtures retain the static gvar font's outline. */
|
||||
for ( i = 1; i < (FT_UInt)argc; i++ )
|
||||
{
|
||||
TestFont font = { NULL, 1, 3, 50, 3, 0 };
|
||||
|
||||
|
||||
font.filename = argv[i];
|
||||
ret |= test_font( library, NULL, &font );
|
||||
}
|
||||
|
||||
ret |= test_reset_without_axes( library, testdata_dir );
|
||||
ret |= test_value_conditions( library, testdata_dir );
|
||||
ret |= test_variation_store( library, testdata_dir );
|
||||
|
||||
76
tests/varc/make-hvvar-fonts.py
Normal file
76
tests/varc/make-hvvar-fonts.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Add HVAR or VVAR to the static VARC/gvar fixture using only stdlib.
|
||||
|
||||
The component sets the private axis to a nonzero coordinate. Loading its
|
||||
metrics must therefore parse an item variation store without an 'fvar'.
|
||||
Distributed under the FreeType project license, LICENSE.TXT.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import struct
|
||||
import sys
|
||||
|
||||
|
||||
def checksum(data):
|
||||
data += bytes(-len(data) % 4)
|
||||
return sum(struct.unpack(">" + "I" * (len(data) // 4), data)) & 0xFFFFFFFF
|
||||
|
||||
|
||||
def read_tables(data):
|
||||
count, = struct.unpack_from(">H", data, 4)
|
||||
tables = {}
|
||||
for index in range(count):
|
||||
tag, _, offset, size = struct.unpack_from(">4sIII", data, 12 + 16 * index)
|
||||
tables[tag] = data[offset:offset + size]
|
||||
return tables
|
||||
|
||||
|
||||
def make_font(tables, vertical):
|
||||
tables = tables.copy()
|
||||
axis_count, = struct.unpack_from(">H", tables[b"gvar"], 4)
|
||||
glyph_count, = struct.unpack_from(">H", tables[b"maxp"], 4)
|
||||
assert b"fvar" not in tables and axis_count == 1
|
||||
|
||||
# One positive-axis region and one delta set per glyph, with implicit
|
||||
# glyph-to-delta mapping. The component is glyph 2.
|
||||
store = struct.pack(">HIHI", 1, 12, 1, 22)
|
||||
store += struct.pack(">HHhhh", 1, 1, 0, 16384, 16384)
|
||||
store += struct.pack(">4H", glyph_count, 1, 1, 0)
|
||||
store += struct.pack(">" + "h" * glyph_count,
|
||||
*(25 if i == 2 else 0 for i in range(glyph_count)))
|
||||
if vertical:
|
||||
tables[b"VVAR"] = struct.pack(">HH5I", 1, 0, 24, 0, 0, 0, 0) + store
|
||||
vhea = bytearray(tables[b"hhea"])
|
||||
struct.pack_into(">H", vhea, 10, 1000) # advanceHeightMax
|
||||
struct.pack_into(">H", vhea, 34, glyph_count)
|
||||
tables[b"vhea"] = bytes(vhea)
|
||||
tables[b"vmtx"] = struct.pack(">Hh", 1000, 0) * glyph_count
|
||||
else:
|
||||
tables[b"HVAR"] = struct.pack(">HH4I", 1, 0, 20, 0, 0, 0) + store
|
||||
|
||||
head = bytearray(tables[b"head"])
|
||||
struct.pack_into(">I", head, 8, 0)
|
||||
tables[b"head"] = bytes(head)
|
||||
count = len(tables)
|
||||
power = 1 << (count.bit_length() - 1)
|
||||
font = bytearray(struct.pack(">I4H", 0x10000, count, power * 16,
|
||||
power.bit_length() - 1, count * 16 - power * 16))
|
||||
font += bytes(16 * count)
|
||||
for index, (tag, data) in enumerate(sorted(tables.items())):
|
||||
offset = len(font)
|
||||
struct.pack_into(">4sIII", font, 12 + 16 * index,
|
||||
tag, checksum(data), offset, len(data))
|
||||
font += data + bytes(-len(data) % 4)
|
||||
if tag == b"head":
|
||||
head_offset = offset
|
||||
struct.pack_into(">I", font, head_offset + 8,
|
||||
(0xB1B0AFBA - checksum(font)) & 0xFFFFFFFF)
|
||||
return font
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 4:
|
||||
raise SystemExit("usage: make-hvvar-fonts.py input.ttf hvar.ttf vvar.ttf")
|
||||
tables = read_tables(Path(sys.argv[1]).read_bytes())
|
||||
for vertical, output in enumerate(sys.argv[2:]):
|
||||
Path(output).write_bytes(make_font(tables, vertical))
|
||||
Reference in New Issue
Block a user