Merge pull request #303 from gagern/metrics

Improve font metrics creation
This commit is contained in:
Kevin Barabash 2015-07-10 09:07:59 -06:00
commit 6357a34828
9 changed files with 1433 additions and 65 deletions

View File

@ -79,8 +79,11 @@ test:
./node_modules/.bin/jasmine-node test/katex-spec.js
./node_modules/.bin/jasmine-node contrib/auto-render/auto-render-spec.js
PERL=perl
PYTHON=$(shell python2 --version >/dev/null 2>&1 && echo python2 || echo python)
metrics:
cd metrics && ./mapping.pl | ./extract_tfms.py | ./extract_ttfs.py | ./replace_line.py
cd metrics && $(PERL) ./mapping.pl | $(PYTHON) ./extract_tfms.py | $(PYTHON) ./extract_ttfs.py | $(PYTHON) ./format_json.py > ../src/fontMetricsData.json
clean:
rm -rf build/*

View File

@ -7,15 +7,15 @@ 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
make metrics
which should generate new metrics and place them into `fontMetrics.js`. You're
done!
which should generate new metrics and place them into `fontMetricsData.json`.
You're done!

View File

@ -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][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))

16
metrics/format_json.py Normal file
View File

@ -0,0 +1,16 @@
#!/usr/bin/env python
import sys
import json
data = json.load(sys.stdin)
sep = "{\n"
for font in sorted(data):
sys.stdout.write(sep + json.dumps(font))
sep = ": {\n "
for glyph in sorted(data[font], key=int):
sys.stdout.write(sep + json.dumps(glyph) + ": ")
sys.stdout.write(json.dumps(data[font][glyph], sort_keys=True))
sep = ",\n "
sep = "\n},\n"
sys.stdout.write("\n}}\n");

View File

@ -135,8 +135,6 @@ $map{cmmi10} = {
0x2E => 0x25B9, # \triangleright
0x2F => 0x25C3, # \triangleleft
0x3A => 0x2E, # .
0x3B => 0x2C, # ,
0x3C => 0x3C, # <
0x3D => 0x2215, # /
0x3E => 0x3E, # >
@ -148,16 +146,19 @@ $map{cmmi10} = {
0x5F => 0x2322, # \frown
0x60 => 0x2113, # \ell
0x7B => 0x131, # \imath
0x7C => 0x237, # \jmath
0x7D => 0x2118, # \wp
0x7E => [0x20D7,-653,0],# \vec
],
"Main-Italic" => [
0x7B => 0x131, # \imath
0x7C => 0x237, # \jmath
]
};
$map{cmsy10} = {
"Main-Regular" => [
[0,1] => 0x2212, # -
0 => 0x2212, # -
1 => 0x22C5, # \cdot
2 => 0xD7, # \times
3 => 0x2217, # \ast
@ -425,8 +426,6 @@ $map{cmti10} = {
[7,8] => 0x3A5, # \Upsilon, \Phi
[9,0xA] => 0x3A8, # \Psi, \Omega
0x10 => 0x131, # \imath (roman)
0x11 => 0x237, # \jmath (roman)
0x12 => [0x300,-511,0], # \grave (combining)
0x13 => [0x301,-511,0], # \acute (combining)
0x14 => [0x30C,-511,0], # \check (combining)
@ -557,8 +556,6 @@ $map{cmmib10} = {
0x2E => 0x25B9, # \triangleright
0x2F => 0x25C3, # \triangleleft
0x3A => 0x2E, # .
0x3B => 0x2C, # ,
0x3C => 0x3C, # <
0x3D => 0x2215, # /
0x3E => 0x3E, # >
@ -571,8 +568,6 @@ $map{cmmib10} = {
0x60 => 0x2113, # \ell
0x68 => 0x210F, # \hbar (bar added below)
0x7B => 0x131, # \imath
0x7C => 0x237, # \jmath
0x7D => 0x2118, # \wp
0x7E => [0x20D7,-729,0],# \vec
],
@ -580,7 +575,7 @@ $map{cmmib10} = {
$map{cmbsy10} = {
"Main-Bold" => [
[0,1] => 0x2212, # -
0 => 0x2212, # -
1 => 0x22C5, # \cdot
2 => 0xD7, # \times
3 => 0x2217, # \ast
@ -973,6 +968,12 @@ sub add_to_output {
"yshift" => $yshift
};
if (defined($output{$mjfont}{$to})) {
print STDERR "Duplicate mapping $to for $mjfont: " .
$output{$mjfont}{$to}{font} . ":" .
$output{$mjfont}{$to}{char} . " vs. $cmfont:$from\n";
die "Duplicate mapping!"; # disable this line to see all of them
}
$output{$mjfont}{$to} = $data;
}

View File

@ -1,17 +0,0 @@
#!/usr/bin/env python2
import sys
with open("../src/fontMetrics.js", "r") as metrics:
old_lines = file.readlines(metrics)
replace = sys.stdin.read()
with open("../src/fontMetrics.js", "w") as output:
for line in old_lines:
if line.startswith("var metricMap"):
output.write("var metricMap = ")
output.write(replace)
output.write(";\n")
else:
output.write(line)

View File

@ -14,7 +14,7 @@
],
"license": "MIT",
"devDependencies": {
"browserify": "~2.29.1",
"browserify": "^10.2.4",
"clean-css": "~2.2.15",
"express": "~3.3.3",
"jasmine-node": "2.0.0-beta4",

File diff suppressed because one or more lines are too long

1351
src/fontMetricsData.json Normal file

File diff suppressed because it is too large Load Diff