1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
#!/usr/bin/env python3
__doc__ = '''Run project-wide checks. Currently just checking glyph inventory and unicode values for ufo sources in
the designspace files supplied but maybe expanded to do more checks later'''
__url__ = 'https://github.com/silnrsi/pysilfont'
__copyright__ = 'Copyright (c) 2022 SIL International (https://www.sil.org)'
__license__ = 'Released under the MIT License (https://opensource.org/licenses/MIT)'
__author__ = 'David Raymond'
from silfont.core import execute, splitfn
import fontTools.designspaceLib as DSD
import glob, os
import silfont.ufo as UFO
import silfont.etutil as ETU
argspec = [
('ds', {'help': 'designspace files to check; wildcards allowed', 'nargs': "+"}, {'type': 'filename'})
]
## Quite a few things are being set and then not used at the moment - this is to allow for more checks to be added in the future.
# For example projectroot, psource
def doit(args):
logger = args.logger
# Open all the supplied DS files and ufos within them
dsinfos = []
failures = False
for pattern in args.ds:
cnt = 0
for fullpath in glob.glob(pattern):
cnt += 1
logger.log(f'Opening {fullpath}', 'P')
try:
ds = DSD.DesignSpaceDocument.fromfile(fullpath)
except Exception as e:
logger.log(f'Error opening {fullpath}: {e}', 'E')
failures = True
break
dsinfos.append({'dspath': fullpath, 'ds': ds})
if not cnt: logger.log(f'No files matched {pattern}', "S")
if failures: logger.log("Failed to open all the designspace files", "S")
# Find the project root based on first ds assuming the project root is one level above a source directory containing the DS files
path = dsinfos[0]['dspath']
(path, base, ext) = splitfn(path)
(parent,dir) = os.path.split(path)
projectroot = parent if dir == "source" else None
logger.log(f'Project root: {projectroot}', "V")
# Find and open all the unique UFO sources in the DSs
ufos = {}
refufo = None
for dsinfo in dsinfos:
logger.log(f'Processing {dsinfo["dspath"]}', "V")
ds = dsinfo['ds']
for source in ds.sources:
if source.path not in ufos:
ufos[source.path] = Ufo(source, logger)
if not refufo: refufo = source.path # For now use the first found. Need to work out how to choose the best one
refunicodes = ufos[refufo].unicodes
refglyphlist = set(refunicodes)
(path,refname) = os.path.split(refufo)
# Now compare with other UFOs
logger.log(f'Comparing glyph inventory and unicode values with those in {refname}', "P")
for ufopath in ufos:
if ufopath == refufo: continue
ufo = ufos[ufopath]
logger.log(f'Checking {ufo.name}', "I")
unicodes = ufo.unicodes
glyphlist = set(unicodes)
missing = refglyphlist - glyphlist
extras = glyphlist - refglyphlist
both = glyphlist - extras
if missing: logger.log(f'These glyphs are missing from {ufo.name}: {str(list(missing))}', 'E')
if extras: logger.log(f'These extra glyphs are in {ufo.name}: {", ".join(extras)}', 'E')
valdiff = [f'{g}: {str(unicodes[g])}/{str(refunicodes[g])}'
for g in both if refunicodes[g] != unicodes[g]]
if valdiff:
valdiff = "\n".join(valdiff)
logger.log(f'These glyphs in {ufo.name} have different unicode values to those in {refname}:\n'
f'{valdiff}', 'E')
class Ufo(object): # Read just the bits for UFO needed for current checks for efficientcy reasons
def __init__(self, source, logger):
self.source = source
(path, self.name) = os.path.split(source.path)
self.logger = logger
self.ufodir = source.path
self.unicodes = {}
if not os.path.isdir(self.ufodir): logger.log(self.ufodir + " in designspace doc does not exist", "S")
try:
self.layercontents = UFO.Uplist(font=None, dirn=self.ufodir, filen="layercontents.plist")
except Exception as e:
logger.log("Unable to open layercontents.plist in " + self.ufodir, "S")
for i in sorted(self.layercontents.keys()):
layername = self.layercontents[i][0].text
if layername != 'public.default': continue
layerdir = self.layercontents[i][1].text
fulldir = os.path.join(self.ufodir, layerdir)
self.contents = UFO.Uplist(font=None, dirn=fulldir, filen="contents.plist")
for glyphn in sorted(self.contents.keys()):
glifn = self.contents[glyphn][1].text
glyph = ETU.xmlitem(os.path.join(self.ufodir,layerdir), glifn, logger=logger)
unicode = None
for x in glyph.etree:
if x.tag == 'unicode':
unicode = x.attrib['hex']
break
self.unicodes[glyphn] = unicode
def cmd(): execute('', doit, argspec)
if __name__ == '__main__': cmd()
|