From da875fcb62c801b8d19b3d4d984ad963574fb356 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 2 Mar 2023 21:01:10 +0100 Subject: Adding upstream version 1.6.0. Signed-off-by: Daniel Baumann --- lib/silfont/scripts/psftuneraliases.py | 121 +++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 lib/silfont/scripts/psftuneraliases.py (limited to 'lib/silfont/scripts/psftuneraliases.py') diff --git a/lib/silfont/scripts/psftuneraliases.py b/lib/silfont/scripts/psftuneraliases.py new file mode 100644 index 0000000..f71d097 --- /dev/null +++ b/lib/silfont/scripts/psftuneraliases.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +__doc__ = '''Merge lookup and feature aliases into TypeTuner feature file''' +__url__ = 'http://github.com/silnrsi/pysilfont' +__copyright__ = 'Copyright (c) 2019 SIL International (http://www.sil.org)' +__license__ = 'Released under the MIT License (http://opensource.org/licenses/MIT)' +__author__ = 'Bob Hallissy' + +from silfont.core import execute +from xml.etree import ElementTree as ET +from fontTools import ttLib +import csv +import struct + +argspec = [ + ('input', {'help': 'Input TypeTuner feature file'}, {'type': 'infile'}), + ('output', {'help': 'Output TypeTuner feature file'}, {}), + ('-m','--mapping', {'help': 'Input csv mapping file'}, {'type': 'incsv'}), + ('-f','--ttf', {'help': 'Compiled TTF file'}, {}), + ('-l','--log',{'help': 'Optional log file'}, {'type': 'outfile', 'def': '_tuneraliases.log', 'optlog': True}), + ] + +def doit(args) : + logger = args.logger + + if args.mapping is None and args.ttf is None: + logger.log("One or both of -m and -f must be provided", "S") + featdoc = ET.parse(args.input) + root = featdoc.getroot() + if root.tag != 'all_features': + logger.log("Invalid TypeTuner feature file: missing root element", "S") + + # Whitespace to add after each new alias: + tail = '\n\t\t' + + # Find or add alliaes element + aliases = root.find('aliases') + if aliases is None: + aliases = ET.SubElement(root,'aliases') + aliases.tail = '\n' + + added = set() + duplicates = set() + def setalias(name, value): + # detect duplicate names in input + if name in added: + duplicates.add(name) + else: + added.add(name) + # modify existing or add new alias + alias = aliases.find('alias[@name="{}"]'.format(name)) + if alias is None: + alias = ET.SubElement(aliases, 'alias', {'name': name, 'value': value}) + alias.tail = tail + else: + alias.set('value', value) + + # Process mapping file if present: + if args.mapping: + # Mapping file is assumed to come from psfbuildfea, and should look like: + # lookupname,table,index + # e.g. DigitAlternates,GSUB,51 + for (name,table,value) in args.mapping: + setalias(name, value) + + # Process the ttf file if present + if args.ttf: + # Generate aliases for features. + # In this code featureID means the key used in FontUtils for finding the feature, e.g., "calt _2" + def dotable(t): # Common routine for GPOS and GSUB + currtag = None + currtagindex = None + flist = [] # list, in order, of (featureTag, featureID), per Font::TTF + for i in range(0,t.FeatureList.FeatureCount): + newtag = str(t.FeatureList.FeatureRecord[i].FeatureTag) + if currtag is None or currtag != newtag: + flist.append((newtag, newtag)) + currtag = newtag + currtagindex = 0 + else: + flist.append( (currtag, '{} _{}'.format(currtag, currtagindex))) + currtagindex += 1 + fslList = {} # dictionary keyed by feature_script_lang values returning featureID + for s in t.ScriptList.ScriptRecord: + currtag = str(s.ScriptTag) + # At present only looking at the dflt lang entries + for findex in s.Script.DefaultLangSys.FeatureIndex: + fslList['{}_{}_dflt'.format(flist[findex][0],currtag)] = flist[findex][1] + # Now that we have them all, add them in sorted order. + for name, value in sorted(fslList.items()): + setalias(name,value) + + # Open the TTF for processing + try: + f = ttLib.TTFont(args.ttf) + except Exception as e: + logger.log("Couldn't open font '{}' for reading : {}".format(args.ttf, str(e)),"S") + # Grab features from GSUB and GPOS + for tag in ('GSUB', 'GPOS'): + try: + dotable(f[tag].table) + except Exception as e: + logger.log("Failed to process {} table: {}".format(tag, str(e)), "W") + # Grab features from Graphite: + try: + for tag in sorted(f['Feat'].features.keys()): + if tag == '1': + continue + name = 'gr_' + tag + value = str(struct.unpack('>L', tag.encode())[0]) + setalias(name,value) + except Exception as e: + logger.log("Failed to process Feat table: {}".format(str(e)), "W") + + if len(duplicates): + logger.log("The following aliases defined more than once in input: {}".format(", ".join(sorted(duplicates))), "S") + + # Success. Write the result + featdoc.write(args.output, encoding='UTF-8', xml_declaration=True) + +def cmd() : execute(None,doit,argspec) +if __name__ == "__main__": cmd() -- cgit v1.2.3