fonttools 0.1.0

A library for reading, manipulating and writing OpenType font files
Documentation
from fontTools.ttLib import TTFont
from fontTools.misc.fixedTools import fixedToFloat
from fontTools.ttLib.tables._g_v_a_r import decompileGlyph_, table__g_v_a_r
from fontTools.ttLib.tables.TupleVariation import (
    TUPLES_SHARE_POINT_NUMBERS,
    TUPLE_COUNT_MASK,
    EMBEDDED_PEAK_TUPLE,
    INTERMEDIATE_REGION,
    PRIVATE_POINT_NUMBERS,
    TUPLE_INDEX_MASK,
    DELTA_RUN_COUNT_MASK,
    DELTAS_ARE_ZERO,
    DELTAS_ARE_WORDS,
    TupleVariation,
    decompileTupleVariation_,
)
import array
import fontTools.ttLib.tables.TupleVariation as tv
import struct
import sys


font = TTFont(sys.argv[1])
order = font.getGlyphOrder()
glyf = font["glyf"]
data = font.reader["gvar"]
ptr = 0


def read_short():
    global ptr
    global data
    val = struct.unpack(">h", data[ptr : ptr + 2])
    ptr += 2
    return val[0]


def read_long():
    global ptr
    global data
    val = struct.unpack(">l", data[ptr : ptr + 4])
    ptr += 4
    return val[0]


axis_order = [x.axisTag for x in font["fvar"].axes]


def read_tuple(axis_count):
    a_tuple = {}
    for j in range(0, axis_count):
        a_tuple[axis_order[j]] = fixedToFloat(read_short(), 14)
    return a_tuple


def decompileDeltas(numDeltas, data, offset):
    """(numDeltas, data, offset) --> ([delta, delta, ...], newOffset)"""
    result = []
    pos = offset
    while len(result) < numDeltas:
        runHeader = data[pos]
        pos += 1
        numDeltasInRun = (runHeader & DELTA_RUN_COUNT_MASK) + 1
        print("    Num deltas in run: %i " % numDeltasInRun)
        if (runHeader & DELTAS_ARE_ZERO) != 0:
            result.extend([0] * numDeltasInRun)
        else:
            if (runHeader & DELTAS_ARE_WORDS) != 0:
                deltas = array.array("h")
                deltasSize = numDeltasInRun * 2
            else:
                deltas = array.array("b")
                deltasSize = numDeltasInRun
            deltas.frombytes(data[pos : pos + deltasSize])
            if sys.byteorder != "big":
                deltas.byteswap()
            assert len(deltas) == numDeltasInRun
            pos += deltasSize
            result.extend(deltas)
    print(" Total length of deltas: %i" % len(result))
    return (result, pos)


major_version = read_short()
minor_version = read_short()
assert major_version == 1 and minor_version == 0

axis_count = read_short()
shared_tuple_count = read_short()
shared_tuples_offset = read_long()
print("Axis count: %i" % axis_count)
print("Shared tuple count: %i" % shared_tuple_count)
print("Shared tuples offset: %i" % shared_tuples_offset)

glyph_count = read_short()
flags = read_short()
data_array_offset = read_long()
print("Glyph count: %i" % glyph_count)
print("Flags: %i" % flags)
print("Data array offset: %i" % data_array_offset)
assert glyph_count == len(order)

data_offsets = []
for i in range(0, glyph_count + 1):
    if flags > 0:
        data_offsets.append(read_long())
    else:
        data_offsets.append(read_short() * 2)

if ptr != shared_tuples_offset:
    print("Warning: Expecting shared tuples")

shared_tuples = []
for i in range(0, shared_tuple_count):
    shared_tuples.append(read_tuple(axis_count))

print("\nShared tuples:")
for t in shared_tuples:
    print("  %s" % t)

if ptr != data_array_offset:
    print("Warning: Expecting glyphVariationDataArray")

for g in range(0, glyph_count):
    glyph_name = order[g]
    start_of_gvd = ptr = data_array_offset + data_offsets[g]
    end_of_gvd = data_array_offset + data_offsets[g + 1]
    print(
        "\n\nReading table for %s (%i-%i out of %i)"
        % (glyph_name, start_of_gvd, end_of_gvd, len(data))
    )
    if start_of_gvd == end_of_gvd:
        print("No data")
        continue

    tuple_variation_count = read_short()
    actual_tvh_count = tuple_variation_count & TUPLE_COUNT_MASK
    has_shared_points = (tuple_variation_count & TUPLES_SHARE_POINT_NUMBERS) > 0
    print("  Tuple variation count: %i" % actual_tvh_count)
    print("  Shared points?: %s" % has_shared_points)
    assert actual_tvh_count > 0 and actual_tvh_count < 12

    data_offset = read_short()
    num_glyph_points = table__g_v_a_r.getNumPoints_(glyf[glyph_name])

    serialized_block_ptr = start_of_gvd + data_offset
    # assert data[serialized_block_ptr] == 0
    if has_shared_points:
        (shared_points, serialized_block_ptr) = TupleVariation.decompilePoints_(
            num_glyph_points, data, serialized_block_ptr, "gvar"
        )
        print("Shared points: %s" % shared_points)

    for h_ix in range(0, tuple_variation_count & TUPLE_COUNT_MASK):
        print("  Tuple variation header: %i" % h_ix)
        # Read a TVH
        pos = ptr
        variation_data_size = read_short()
        tuple_index = read_short()

        tupleSize = TupleVariation.getTupleSize_(tuple_index, axis_count)
        tupleData = data[(pos) : (pos + tupleSize)]
        pointDeltaData = data[
            serialized_block_ptr : serialized_block_ptr + variation_data_size
        ]
        has_private_points = tuple_index & PRIVATE_POINT_NUMBERS
        if tuple_index & EMBEDDED_PEAK_TUPLE:
            peak_tuple = read_tuple(axis_count)
            print("     Embedded peak tuple: %s" % peak_tuple)
        else:
            print("   Shared tuple %i" % (tuple_index & TUPLE_INDEX_MASK))
            print(
                "     Shared tuple: %s" % shared_tuples[tuple_index & TUPLE_INDEX_MASK]
            )
        if tuple_index & INTERMEDIATE_REGION:
            start_tuple = read_tuple(axis_count)
            end_tuple = read_tuple(axis_count)
            print("     Embedded start tuple: %s" % start_tuple)
            print("     Embedded end tuple: %s" % end_tuple)

        deltaptr = 0
        if has_private_points:
            print("     Has private points")
            print(pointDeltaData[0:5])
            (points, deltaptr) = TupleVariation.decompilePoints_(
                num_glyph_points, pointDeltaData, deltaptr, "gvar"
            )
            num_points = len(points)
            print("     %s" % points)
        else:
            num_glyph_points = len(shared_points)

        print("Expecting %i deltas " % num_points)
        deltas_x, deltaptr = decompileDeltas(num_points, pointDeltaData, deltaptr)
        deltas_y, deltaptr = decompileDeltas(num_points, pointDeltaData, deltaptr)
        print(list(zip(deltas_x, deltas_y)))
        serialized_block_ptr += variation_data_size

    print("Decompiling with fontTools:")
    print(
        tv.decompileTupleVariationStore(
            "gvar",
            axis_order,
            tuple_variation_count,
            num_glyph_points,
            shared_tuples,
            data[start_of_gvd:end_of_gvd],
            4,
            data_offset,
        )
    )