105 lines
2.9 KiB
Python
105 lines
2.9 KiB
Python
|
#!/usr/bin/env python3
|
||
|
|
||
|
from os import system as run
|
||
|
import os
|
||
|
from subprocess import check_output as run_and_read
|
||
|
import sys
|
||
|
from typing import Dict, Set
|
||
|
|
||
|
def get_vendor(file: str) -> str:
|
||
|
command = f"otfinfo -i {file} | grep 'Vendor ID' | awk '{{print $3}}'"
|
||
|
return run_and_read(command, shell=True).decode(encoding='ascii').rstrip('\n')
|
||
|
|
||
|
def get_tables(file: str) -> list[str]:
|
||
|
command = f"otfinfo -t {file} | awk '{{print $2}}'"
|
||
|
return run_and_read(command, shell=True).decode(encoding='ascii').rstrip('\n').split('\n')
|
||
|
|
||
|
FILENAME = os.path.join(os.path.dirname(__file__), "tables")
|
||
|
|
||
|
graph: Dict[str, Set[str]] = {}
|
||
|
|
||
|
def verify_node(tag: str):
|
||
|
if tag not in graph: graph[tag] = set()
|
||
|
|
||
|
def add_edge(tag1: str, tag2: str):
|
||
|
verify_node(tag1)
|
||
|
verify_node(tag2)
|
||
|
graph[tag1].add(tag2)
|
||
|
|
||
|
MODE = None
|
||
|
if not sys.stdin.isatty():
|
||
|
MODE = 'direct' if '--direct' in sys.argv else 'before'
|
||
|
accumulator: Dict[tuple[str, str], int] = {} # acc[(tag1, tag2)] is number of times tag1 occured before tag2
|
||
|
for file in sys.stdin:
|
||
|
file = file.rstrip('\n')
|
||
|
print(f"{get_vendor(file):<4} {file}")
|
||
|
tables = get_tables(file)
|
||
|
|
||
|
def add_to_acc(tag1: str, tag2: str):
|
||
|
accumulator[(tag1, tag2)] = accumulator.get((tag1, tag2), 0)+1
|
||
|
match MODE:
|
||
|
case 'before':
|
||
|
for i, tag1 in enumerate(tables):
|
||
|
for tag2 in tables[i+1:]:
|
||
|
add_to_acc(tag1, tag2)
|
||
|
case 'direct':
|
||
|
for i, tag1 in enumerate(tables[:-1]):
|
||
|
tag2 = tables[i+1]
|
||
|
add_to_acc(tag1, tag2)
|
||
|
case _:
|
||
|
assert False, f"Invalid mode: '{MODE}'"
|
||
|
|
||
|
with open(f"{FILENAME}.txt", 'w') as f:
|
||
|
f.write(f"mode: {MODE}\n")
|
||
|
for (tag1, tag2) in accumulator:
|
||
|
f.write(f"'{tag1:<4}', '{tag2:<4}', {accumulator[(tag1, tag2)]}\n")
|
||
|
|
||
|
for (tag1, tag2) in accumulator: add_edge(tag1, tag2)
|
||
|
|
||
|
else:
|
||
|
with open(f"{FILENAME}.txt", 'r') as f:
|
||
|
for i, line in enumerate(f.readlines()):
|
||
|
if i == 0:
|
||
|
MODE = line[6:-1]
|
||
|
continue
|
||
|
tag1, tag2 = line[1:5], line[9:13]
|
||
|
add_edge(tag1, tag2)
|
||
|
assert MODE, "Unreachable"
|
||
|
|
||
|
UNTRANSITIVITY = '--untrans' in sys.argv
|
||
|
def untransitivity():
|
||
|
to_remove: Dict[str, Set[str]] = {tag: set() for tag in graph}
|
||
|
for tag1 in graph:
|
||
|
for tag2 in graph[tag1]:
|
||
|
for tag3 in graph[tag2]:
|
||
|
if tag3 in graph[tag1]: # find a->b, b->c where a->c
|
||
|
to_remove[tag1].add(tag3)
|
||
|
|
||
|
# to_remove = {
|
||
|
# tag1: {tag3 for tag3 in graph[tag2] for tag2 in graph[tag1] if tag3 in graph[tag1]}
|
||
|
# for tag1 in graph
|
||
|
# }
|
||
|
|
||
|
for tag1 in to_remove:
|
||
|
for tag3 in to_remove[tag1]:
|
||
|
graph[tag1].remove(tag3)
|
||
|
|
||
|
if UNTRANSITIVITY: untransitivity()
|
||
|
|
||
|
GENERATE = '--svg' in sys.argv
|
||
|
def generate_svg() -> int:
|
||
|
def node_name(tag: str) -> str:
|
||
|
return '"'+tag+'"'
|
||
|
|
||
|
with open(f"{FILENAME}.dot", 'w') as f:
|
||
|
f.write("digraph {\n")
|
||
|
for node in graph:
|
||
|
for neighbour in graph[node]:
|
||
|
f.write(f"\t{node_name(node)} -> {node_name(neighbour)};\n")
|
||
|
f.write("}")
|
||
|
|
||
|
return run(f"dot -Tsvg {FILENAME}.dot > {FILENAME}.svg")
|
||
|
|
||
|
if GENERATE: generate_svg()
|
||
|
|
||
|
print(f"{MODE = }\n{UNTRANSITIVITY = }\n{GENERATE = }")
|