コンテンツへスキップ

TTCファイルの分割

タグ:

複数のTTF(TrueTypeFont)が格納されているTTCファイルから単一のTTFに分割するスクリプトです。

元は https://github.com/yhchen/ttc2ttf となります。

使用方法は、

$ python ttc_split.py [TTF Filename]

ttc_split.py

import sys
import argparse
import struct
import pathlib

# original: https://github.com/yhchen/ttc2ttf


def iter_ttf(buf: bytes, tpl_ttf_offset: tuple[int]):
    def bit_ceil(v: int, align: int) -> int:
        return (v + (align - 1)) & ~(align - 1)

    for i, table_header_offset in enumerate(tpl_ttf_offset):
        print("Extract TTF #{:d}".format(i + 1))
        print("\tOffset {:d}".format(table_header_offset))
        tpl_table_count = struct.unpack_from(
            "!H",
            buf,
            table_header_offset + 0x04,
        )

        header_length = 0x0C + tpl_table_count[0] * 0x10
        print("\tHeaderlänge: %s Byte" % header_length)

        table_length = 0
        for j in range(tpl_table_count[0]):
            length = struct.unpack_from(
                "!L", buf, table_header_offset + 0x0C + 0x0C + j * 0x10
            )[0]
            table_length += bit_ceil(length, 4)

        total_length = header_length + table_length
        new_buf = bytearray(total_length)
        header = struct.unpack_from(
            header_length * "c",
            buf,
            table_header_offset,
        )
        struct.pack_into(header_length * "c", new_buf, 0, *header)
        current_offset = header_length

        for j in range(tpl_table_count[0]):
            offset = struct.unpack_from(
                "!L", buf, table_header_offset + 0x0C + 0x08 + j * 0x10
            )[0]
            length = struct.unpack_from(
                "!L", buf, table_header_offset + 0x0C + 0x0C + j * 0x10
            )[0]
            struct.pack_into(
                "!L",
                new_buf,
                0x0C + 0x08 + j * 0x10,
                current_offset,
            )
            current_table = struct.unpack_from(length * "c", buf, offset)
            struct.pack_into(
                length * "c",
                new_buf,
                current_offset,
                *current_table,
            )

            current_offset += bit_ceil(length, 4)

        yield i, new_buf


def split_ttc(ttc_file: pathlib.Path, buf: bytes):

    tpl_ttf_count = struct.unpack_from("!L", buf, 0x08)
    print("TrueType count = {:d}".format(tpl_ttf_count[0]))

    tpl_ttf_offset = struct.unpack_from(
        "!" + tpl_ttf_count[0] * "L",
        buf,
        0x0C,
    )

    for i, ttfdata in iter_ttf(buf, tpl_ttf_offset):
        ttf_path = pathlib.Path("{:s}_{:02d}.ttf".format(ttc_file.stem, i))
        with ttf_path.open("wb") as wf:
            wf.write(ttfdata)
            print("\tSaved {:s} {:d} Bytes".format(ttf_path.name, len(ttfdata)))
            print()


def main():

    parser = argparse.ArgumentParser()
    parser.add_argument("ttc_filename")

    args = parser.parse_args()

    ttc_file = pathlib.Path(args.ttc_filename)

    if ttc_file.exists() is False:
        return -1

    if ttc_file.suffix.lower() != ".ttc":
        return -1

    with ttc_file.open("rb") as rf:
        buf = rf.read()
        rf.close()

        if buf[:4] != b"ttcf":
            return -1

        split_ttc(ttc_file, buf)

    return 0


if __name__ == "__main__":
    sys.exit(main())