summaryrefslogtreecommitdiffstats
path: root/lib/silfont/scripts/psfcsv2comp.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/silfont/scripts/psfcsv2comp.py')
-rw-r--r--lib/silfont/scripts/psfcsv2comp.py129
1 files changed, 129 insertions, 0 deletions
diff --git a/lib/silfont/scripts/psfcsv2comp.py b/lib/silfont/scripts/psfcsv2comp.py
new file mode 100644
index 0000000..7a4a960
--- /dev/null
+++ b/lib/silfont/scripts/psfcsv2comp.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python
+__doc__ = '''generate composite definitions from csv file'''
+__url__ = 'http://github.com/silnrsi/pysilfont'
+__copyright__ = 'Copyright (c) 2018 SIL International (http://www.sil.org)'
+__license__ = 'Released under the MIT License (http://opensource.org/licenses/MIT)'
+__author__ = 'Bob Hallissy'
+
+import re
+from silfont.core import execute
+import re
+
+argspec = [
+ ('output',{'help': 'Output file containing composite definitions'}, {'type': 'outfile'}),
+ ('-i','--input',{'help': 'Glyph info csv file'}, {'type': 'incsv', 'def': 'glyph_data.csv'}),
+ ('-f','--fontcode',{'help': 'letter to filter for glyph_data'},{}),
+ ('--gname', {'help': 'Column header for glyph name', 'default': 'glyph_name'}, {}),
+ ('--base', {'help': 'Column header for name of base', 'default': 'base'}, {}),
+ ('--usv', {'help': 'Column header for USV'}, {}),
+ ('--anchors', {'help': 'Column header(s) for APs to compose', 'default': 'above,below'}, {}),
+ ('-r','--report',{'help': 'Set reporting level for log', 'type':str, 'choices':['X','S','E','P','W','I','V']},{}),
+ ('-l','--log',{'help': 'Set log file name'}, {'type': 'outfile', 'def': 'csv2comp.log'}),
+ ]
+
+def doit(args):
+ logger = args.logger
+ if args.report: logger.loglevel = args.report
+ # infont = args.ifont
+ incsv = args.input
+ output = args.output
+
+ def csvWarning(msg, exception = None):
+ m = "glyph_data warning: %s at line %d" % (msg, incsv.line_num)
+ if exception is not None:
+ m += '; ' + exception.message
+ logger.log(m, 'W')
+
+ if args.fontcode is not None:
+ whichfont = args.fontcode.strip().lower()
+ if len(whichfont) != 1:
+ logger.log('-f parameter must be a single letter', 'S')
+ else:
+ whichfont = None
+
+ # Which headers represent APs to use:
+ apList = args.anchors.split(',')
+ if len(apList) == 0:
+ logger.log('--anchors option value "%s" is invalid' % args.anchors, 'S')
+
+ # Get headings from csvfile:
+ fl = incsv.firstline
+ if fl is None: logger.log("Empty input file", "S")
+ # required columns:
+ try:
+ nameCol = fl.index(args.gname)
+ baseCol = fl.index(args.base)
+ apCols = [fl.index(ap) for ap in apList]
+ if args.usv is not None:
+ usvCol = fl.index(args.usv)
+ else:
+ usvCol = None
+ except ValueError as e:
+ logger.log('Missing csv input field: ' + e.message, 'S')
+ except Exception as e:
+ logger.log('Error reading csv input field: ' + e.message, 'S')
+
+ # Now make strip AP names; pair up with columns so easy to iterate:
+ apInfo = list(zip(apCols, [x.strip() for x in apList]))
+
+ # If -f specified, make sure we have the fonts column
+ if whichfont is not None:
+ if 'fonts' not in fl: logger.log('-f requires "fonts" column in glyph_data', 'S')
+ fontsCol = fl.index('fonts')
+
+ # RE that matches names of glyphs we don't care about
+ namesToSkipRE = re.compile('^(?:[._].*|null|cr|nonmarkingreturn|tab|glyph_name)$',re.IGNORECASE)
+
+ # keep track of glyph names we've seen to detect duplicates
+ namesSeen = set()
+
+ # OK, process all records in glyph_data
+ for line in incsv:
+ base = line[baseCol].strip()
+ if len(base) == 0:
+ # No composites specified
+ continue
+
+ gname = line[nameCol].strip()
+ # things to ignore:
+ if namesToSkipRE.match(gname): continue
+ if whichfont is not None and line[fontsCol] != '*' and line[fontsCol].lower().find(whichfont) < 0:
+ continue
+
+ if len(gname) == 0:
+ csvWarning('empty glyph name in glyph_data; ignored')
+ continue
+ if gname.startswith('#'): continue
+ if gname in namesSeen:
+ csvWarning('glyph name %s previously seen in glyph_data; ignored' % gname)
+ continue
+ namesSeen.add(gname)
+
+ # Ok, start building the composite
+ composite = '%s = %s' %(gname, base)
+
+ # The first component must *not* reference the base; all others *must*:
+ seenfirst = False
+ for apCol, apName in apInfo:
+ component = line[apCol].strip()
+ if len(component):
+ if not seenfirst:
+ composite += ' + %s@%s' % (component, apName)
+ seenfirst = True
+ else:
+ composite += ' + %s@%s:%s' % (component, base, apName)
+
+ # Add USV if present
+ if usvCol is not None:
+ usv = line[usvCol].strip()
+ if len(usv):
+ composite += ' | %s' % usv
+
+ # Output this one
+ output.write(composite + '\n')
+
+ output.close()
+
+def cmd() : execute("",doit,argspec)
+if __name__ == "__main__": cmd()
+