1175f04812343b1d149971dca7fe32ba49c623cc2jvr#! /usr/bin/env python
2a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
3a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr"""usage: ttroundtrip [options] font1 ... fontN
4a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
5a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr    Dump each TT/OT font as a TTX file, compile again to TTF or OTF
6a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr    and dump again. Then do a diff on the two TTX files. Append problems
7a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr    and diffs to a file called "report.txt" in the current directory.
8a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr    This is only for testing FontTools/TTX, the resulting files are
9a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr    deleted afterwards.
10a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
11848d25d97dd1d9d9ff6ea9ff8c5ae59374526488jvr    This tool supports some of ttx's command line options (-i, -t
12848d25d97dd1d9d9ff6ea9ff8c5ae59374526488jvr    and -x). Specifying -t or -x implies ttx -m <originalfile> on
13848d25d97dd1d9d9ff6ea9ff8c5ae59374526488jvr    the way back.
14a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr"""
15a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
16a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
17a0af6c7dffb109a2918332ff9eca7b7e439f2499jvrimport sys
18a0af6c7dffb109a2918332ff9eca7b7e439f2499jvrimport os
19a0af6c7dffb109a2918332ff9eca7b7e439f2499jvrimport tempfile
20a0af6c7dffb109a2918332ff9eca7b7e439f2499jvrimport getopt
21a0af6c7dffb109a2918332ff9eca7b7e439f2499jvrimport traceback
22a0af6c7dffb109a2918332ff9eca7b7e439f2499jvrfrom fontTools import ttx
23a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
24a0af6c7dffb109a2918332ff9eca7b7e439f2499jvrclass Error(Exception): pass
25a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
26a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
27a0af6c7dffb109a2918332ff9eca7b7e439f2499jvrdef usage():
283ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod	print(__doc__)
29a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr	sys.exit(2)
30a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
31a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
32a0af6c7dffb109a2918332ff9eca7b7e439f2499jvrdef roundTrip(ttFile1, options, report):
33a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr	fn = os.path.basename(ttFile1)
34a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr	xmlFile1 = tempfile.mktemp(".%s.ttx1" % fn)
35a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr	ttFile2 = tempfile.mktemp(".%s" % fn)
36a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr	xmlFile2 = tempfile.mktemp(".%s.ttx2" % fn)
37a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
38a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr	try:
39a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		ttx.ttDump(ttFile1, xmlFile1, options)
40a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		if options.onlyTables or options.skipTables:
41a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			options.mergeFile = ttFile1
42a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		ttx.ttCompile(xmlFile1, ttFile2, options)
43a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		options.mergeFile = None
44a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		ttx.ttDump(ttFile2, xmlFile2, options)
45a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
46044f8988d6f4ba4b956fae6799e2c81a86bf24c2pabs		diffcmd = 'diff -U2 -I ".*modified value\|checkSumAdjustment.*" "%s" "%s"' % (xmlFile1, xmlFile2)
47a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		output = os.popen(diffcmd, "r", 1)
48a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		lines = []
49ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		while True:
50a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			line = output.readline()
51a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			if not line:
52a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr				break
53a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			sys.stdout.write(line)
54a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			lines.append(line)
55a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		if lines:
56a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			report.write("=============================================================\n")
57a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			report.write("  \"%s\" differs after round tripping\n" % ttFile1)
58a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			report.write("-------------------------------------------------------------\n")
59a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			report.writelines(lines)
60a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		else:
613ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod			print("(TTX files are the same)")
62a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr	finally:
63a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		for tmpFile in (xmlFile1, ttFile2, xmlFile2):
64a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			if os.path.exists(tmpFile):
65a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr				os.remove(tmpFile)
66a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
67a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
68a0af6c7dffb109a2918332ff9eca7b7e439f2499jvrdef main(args):
69a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr	try:
70848d25d97dd1d9d9ff6ea9ff8c5ae59374526488jvr		rawOptions, files = getopt.getopt(args, "it:x:")
71a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr	except getopt.GetoptError:
72a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		usage()
73a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
74a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr	if not files:
75a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		usage()
76a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
77a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr	report = open("report.txt", "a+")
78a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr	options = ttx.Options(rawOptions, len(files))
79a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr	for ttFile in files:
80a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		try:
81a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			roundTrip(ttFile, options, report)
82a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		except KeyboardInterrupt:
833ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod			print("(Cancelled)")
841397f56eb3e871387203a15f71585afb69b91a92jvr			break
85a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr		except:
863ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod			print("*** round tripping aborted ***")
87a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			traceback.print_exc()
88a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			report.write("=============================================================\n")
89a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			report.write("  An exception occurred while round tripping")
90a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			report.write("  \"%s\"\n" % ttFile)
91a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			traceback.print_exc(file=report)
92a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr			report.write("-------------------------------------------------------------\n")
93a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr	report.close()
94a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
95a0af6c7dffb109a2918332ff9eca7b7e439f2499jvr
96a0af6c7dffb109a2918332ff9eca7b7e439f2499jvrmain(sys.argv[1:])
97