diff --git a/metrics/README.md b/metrics/README.md index 8f8ed45..4531461 100644 --- a/metrics/README.md +++ b/metrics/README.md @@ -7,11 +7,11 @@ There are several requirements for generating the metrics used by KaTeX. this by running `tex --version`, and seeing if it has a line that looks like > kpathsea version 6.2.0 -- You need the JSON module for perl. You can install this either from CPAN or with - your package manager. +- You need the JSON module for perl. You can install this either from CPAN + (possibly using the `cpan` command line tool) or with your package manager. -- You need the python fontforge module. This is probably either installed with - fontforge or can be installed from your package manager. +- You need the python module fonttools. You can install this either from PyPi + (using `easy_install` or `pip`) or with your package manager. Once you have these things, run diff --git a/metrics/extract_ttfs.py b/metrics/extract_ttfs.py index edcf0da..05b70bf 100755 --- a/metrics/extract_ttfs.py +++ b/metrics/extract_ttfs.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -import fontforge +from fontTools.ttLib import TTFont import sys import json @@ -60,36 +60,50 @@ def main(): start_json = json.load(sys.stdin) for font, chars in metrics_to_extract.iteritems(): - fontInfo = fontforge.open("../static/fonts/KaTeX_" + font + ".ttf") + fontInfo = TTFont("../static/fonts/KaTeX_" + font + ".ttf") + glyf = fontInfo["glyf"] + unitsPerEm = float(fontInfo["head"].unitsPerEm) - for glyph in fontInfo.glyphs(): - try: - char = unichr(glyph.unicode) - except ValueError: + # We keep ALL Unicode cmaps, not just fontInfo["cmap"].getcmap(3, 1). + # This is playing it extra safe, since it reports inconsistencies. + # Platform 0 is Unicode, platform 3 is Windows. For platform 3, + # encoding 1 is UCS-2 and encoding 10 is UCS-4. + cmap = [t.cmap for t in fontInfo["cmap"].tables + if (t.platformID == 0) + or (t.platformID == 3 and t.platEncID in (1, 10))] + + for char, base_char in chars.iteritems(): + code = ord(char) + names = set(t.get(code) for t in cmap) + if not names: + sys.stderr.write( + "Codepoint {} of font {} maps to no name\n" + .format(code, font)) continue + if len(names) != 1: + sys.stderr.write( + "Codepoint {} of font {} maps to multiple names: {}\n" + .format(code, font, ", ".join(sorted(names)))) + continue + name = names.pop() - if char in chars: - _, depth, _, height = glyph.boundingBox() + height = depth = italic = skew = 0 + glyph = glyf[name] + if glyph.numberOfContours: + height = glyph.yMax + depth = -glyph.yMin + if base_char: + base_char_str = str(ord(base_char)) + base_metrics = start_json[font][base_char_str] + italic = base_metrics["italic"] + skew = base_metrics["skew"] - depth = -depth - - base_char = chars[char] - if base_char: - base_char_str = str(ord(base_char)) - base_metrics = start_json[font][base_char_str] - - italic = base_metrics["italic"] - skew = base_metrics["skew"] - else: - italic = 0 - skew = 0 - - start_json[font][str(ord(char))] = { - "height": height / fontInfo.em, - "depth": depth / fontInfo.em, - "italic": italic, - "skew": skew, - } + start_json[font][str(code)] = { + "height": height / unitsPerEm, + "depth": depth / unitsPerEm, + "italic": italic, + "skew": skew, + } sys.stdout.write( json.dumps(start_json, separators=(',', ':'), sort_keys=True)) diff --git a/src/fontMetricsData.json b/src/fontMetricsData.json index caa5ec5..3518867 100644 --- a/src/fontMetricsData.json +++ b/src/fontMetricsData.json @@ -632,7 +632,7 @@ "8463": {"depth": 0.0, "height": 0.68889, "italic": 0.0, "skew": 0.0} }, "Main-Regular": { - "32": {"depth": -0.0, "height": 0.0, "italic": 0, "skew": 0}, + "32": {"depth": 0.0, "height": 0.0, "italic": 0, "skew": 0}, "33": {"depth": 0.0, "height": 0.69444, "italic": 0.0, "skew": 0.0}, "34": {"depth": 0.0, "height": 0.69444, "italic": 0.0, "skew": 0.0}, "35": {"depth": 0.19444, "height": 0.69444, "italic": 0.0, "skew": 0.0}, @@ -727,7 +727,7 @@ "124": {"depth": 0.25, "height": 0.75, "italic": 0.0, "skew": 0.0}, "125": {"depth": 0.25, "height": 0.75, "italic": 0.0, "skew": 0.0}, "126": {"depth": 0.35, "height": 0.31786, "italic": 0.0, "skew": 0.0}, - "160": {"depth": -0.0, "height": 0.0, "italic": 0, "skew": 0}, + "160": {"depth": 0.0, "height": 0.0, "italic": 0, "skew": 0}, "168": {"depth": 0.0, "height": 0.66786, "italic": 0.0, "skew": 0.0}, "172": {"depth": 0.0, "height": 0.43056, "italic": 0.0, "skew": 0.0}, "175": {"depth": 0.0, "height": 0.56778, "italic": 0.0, "skew": 0.0}, @@ -778,7 +778,7 @@ "8221": {"depth": 0.0, "height": 0.69444, "italic": 0.0, "skew": 0.0}, "8224": {"depth": 0.19444, "height": 0.69444, "italic": 0.0, "skew": 0.0}, "8225": {"depth": 0.19444, "height": 0.69444, "italic": 0.0, "skew": 0.0}, - "8230": {"depth": -0.0, "height": 0.12, "italic": 0, "skew": 0}, + "8230": {"depth": 0.0, "height": 0.12, "italic": 0, "skew": 0}, "8242": {"depth": 0.0, "height": 0.55556, "italic": 0.0, "skew": 0.0}, "8407": {"depth": 0.0, "height": 0.71444, "italic": 0.15382, "skew": 0.0}, "8463": {"depth": 0.0, "height": 0.68889, "italic": 0.0, "skew": 0.0},