From ec62ec39d82b6b42ee2ec9ad866a79c56b8e2990 Mon Sep 17 00:00:00 2001 From: Kevin Barabash Date: Mon, 1 Aug 2016 17:51:40 -0700 Subject: [PATCH] Add support for Latin-1, Cyrillic, and CJK characters inside \text{} (#508) Summary: This diff provides support for Latin-1, Cyrillic, and CJK characters inside \text{} groups. For Latin-1 and Cyrillic characters we use glyph metrics from a glyph from Basic Latin that has roughly the same bounding box. We use the metrics for a capital 'M' to approximate the full-width CJK characters. Half-width characters are not supported yet. Test Plan: - make test - make screenshots Reviewers: emily --- .gitignore | 1 + .travis.yml | 1 + Makefile | 8 +- server.js | 2 + src/Parser.js | 6 + src/domTree.js | 29 +++- src/fontMetrics.js | 148 +++++++++++++++++- src/symbols.js | 22 +++ src/unicodeRegexes.js | 15 ++ static/katex.less | 2 +- test/screenshotter/images/Unicode-chrome.png | Bin 0 -> 25212 bytes test/screenshotter/images/Unicode-firefox.png | Bin 0 -> 24792 bytes test/screenshotter/ss_data.yaml | 1 + test/screenshotter/test.html | 14 ++ test/unicode-spec.js | 103 ++++++++++++ 15 files changed, 348 insertions(+), 4 deletions(-) create mode 100644 src/unicodeRegexes.js create mode 100644 test/screenshotter/images/Unicode-chrome.png create mode 100644 test/screenshotter/images/Unicode-firefox.png create mode 100644 test/unicode-spec.js diff --git a/.gitignore b/.gitignore index 058d8cf4e..ddaaa9335 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ diff.png /test/symgroups.aux /test/symgroups.log /test/symgroups.pdf +/test/screenshotter/unicode-fonts diff --git a/.travis.yml b/.travis.yml index 4228096c7..5b6e28802 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,4 +10,5 @@ before_script: - docker images --no-trunc script: - npm test + - git clone https://github.com/Khan/KaTeX-test-fonts test/screenshotter/unicode-fonts - dockers/Screenshotter/screenshotter.sh --verify diff --git a/Makefile b/Makefile index 546cbaa15..03d3d9596 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,12 @@ build/fonts: cp static/fonts/$$font* $@; \ done +test/screenshotter/unicode-fonts: + git clone https://github.com/Khan/KaTeX-test-fonts test/screenshotter/unicode-fonts + cd test/screenshotter/unicode-fonts && \ + git checkout 99fa66a2da643218754c8236b9f9151cac71ba7c && \ + cd ../../../ + contrib: build/contrib .PHONY: build/contrib @@ -90,5 +96,5 @@ extended_metrics: clean: rm -rf build/* -screenshots: +screenshots: test/screenshotter/unicode-fonts dockers/Screenshotter/screenshotter.sh diff --git a/server.js b/server.js index a6213449c..08eacf1fa 100644 --- a/server.js +++ b/server.js @@ -81,6 +81,8 @@ app.use(express["static"](path.join(__dirname, "static"))); app.use(express["static"](path.join(__dirname, "build"))); app.use("/test", express["static"](path.join(__dirname, "test"))); app.use("/contrib", express["static"](path.join(__dirname, "contrib"))); +// app.use("/unicode-fonts", +// express["static"](path.join(__dirname, "static", "unicode-fonts"))); app.use(function(err, req, res, next) { console.error(err.stack); diff --git a/src/Parser.js b/src/Parser.js index 4bfde913b..97bbbf3fd 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -4,6 +4,7 @@ var environments = require("./environments"); var MacroExpander = require("./MacroExpander"); var symbols = require("./symbols"); var utils = require("./utils"); +var cjkRegex = require("./unicodeRegexes").cjkRegex; var parseData = require("./parseData"); var ParseError = require("./ParseError"); @@ -794,6 +795,11 @@ Parser.prototype.parseSymbol = function() { new ParseNode(symbols[this.mode][nucleus.text].group, nucleus.text, this.mode, nucleus), false, nucleus); + } else if (this.mode === "text" && cjkRegex.test(nucleus.text)) { + this.consume(); + return new ParseFuncOrArgument( + new ParseNode("textord", nucleus.text, this.mode, nucleus), + false, nucleus); } else { return null; } diff --git a/src/domTree.js b/src/domTree.js index e0d8e925a..46b515ea1 100644 --- a/src/domTree.js +++ b/src/domTree.js @@ -7,7 +7,7 @@ * * Similar functions for working with MathML nodes exist in mathMLTree.js. */ - +var unicodeRegexes = require("./unicodeRegexes"); var utils = require("./utils"); /** @@ -169,6 +169,14 @@ documentFragment.prototype.toMarkup = function() { return markup; }; +var iCombinations = { + 'î': '\u0131\u0302', + 'ï': '\u0131\u0308', + 'í': '\u0131\u0301', + // 'ī': '\u0131\u0304', // enable when we add Extended Latin + 'ì': '\u0131\u0300', +}; + /** * A symbol node contains information about a single symbol. It either renders * to a single text node, or a span with a single text node in it, depending on @@ -183,6 +191,25 @@ function symbolNode(value, height, depth, italic, skew, classes, style) { this.classes = classes || []; this.style = style || {}; this.maxFontSize = 0; + + // Mark CJK characters with specific classes so that we can specify which + // fonts to use. This allows us to render these characters with a serif + // font in situations where the browser would either default to a sans serif + // or render a placeholder character. + if (unicodeRegexes.cjkRegex.test(value)) { + // I couldn't find any fonts that contained Hangul as well as all of + // the other characters we wanted to test there for it gets its own + // CSS class. + if (unicodeRegexes.hangulRegex.test(value)) { + this.classes.push('hangul_fallback'); + } else { + this.classes.push('cjk_fallback'); + } + } + + if (/[îïíì]/.test(this.value)) { // add ī when we add Extended Latin + this.value = iCombinations[this.value]; + } } /** diff --git a/src/fontMetrics.js b/src/fontMetrics.js index db9e44bfa..e4e440a78 100644 --- a/src/fontMetrics.js +++ b/src/fontMetrics.js @@ -1,6 +1,7 @@ /* eslint no-unused-vars:0 */ var Style = require("./Style"); +var cjkRegex = require("./unicodeRegexes").cjkRegex; /** * This file contains metrics regarding fonts and individual symbols. The sigma @@ -121,6 +122,145 @@ var metrics = { // This map is generated via `make metrics`. It should not be changed manually. var metricMap = require("./fontMetricsData"); +// These are very rough approximations. We default to Times New Roman which +// should have Latin-1 and Cyrillic characters, but may not depending on the +// operating system. The metrics do not account for extra height from the +// accents. In the case of Cyrillic characters which have both ascenders and +// descenders we prefer approximations with ascenders, primarily to prevent +// the fraction bar or root line from intersecting the glyph. +// TODO(kevinb) allow union of multiple glyph metrics for better accuracy. +var extraCharacterMap = { + // Latin-1 + 'À': 'A', + 'Á': 'A', + 'Â': 'A', + 'Ã': 'A', + 'Ä': 'A', + 'Å': 'A', + 'Æ': 'A', + 'Ç': 'C', + 'È': 'E', + 'É': 'E', + 'Ê': 'E', + 'Ë': 'E', + 'Ì': 'I', + 'Í': 'I', + 'Î': 'I', + 'Ï': 'I', + 'Ð': 'D', + 'Ñ': 'N', + 'Ò': 'O', + 'Ó': 'O', + 'Ô': 'O', + 'Õ': 'O', + 'Ö': 'O', + 'Ø': 'O', + 'Ù': 'U', + 'Ú': 'U', + 'Û': 'U', + 'Ü': 'U', + 'Ý': 'Y', + 'Þ': 'o', + 'ß': 'B', + 'à': 'a', + 'á': 'a', + 'â': 'a', + 'ã': 'a', + 'ä': 'a', + 'å': 'a', + 'æ': 'a', + 'ç': 'c', + 'è': 'e', + 'é': 'e', + 'ê': 'e', + 'ë': 'e', + 'ì': 'i', + 'í': 'i', + 'î': 'i', + 'ï': 'i', + 'ð': 'd', + 'ñ': 'n', + 'ò': 'o', + 'ó': 'o', + 'ô': 'o', + 'õ': 'o', + 'ö': 'o', + 'ø': 'o', + 'ù': 'u', + 'ú': 'u', + 'û': 'u', + 'ü': 'u', + 'ý': 'y', + 'þ': 'o', + 'ÿ': 'y', + + // Cyrillic + 'А': 'A', + 'Б': 'B', + 'В': 'B', + 'Г': 'F', + 'Д': 'A', + 'Е': 'E', + 'Ж': 'K', + 'З': '3', + 'И': 'N', + 'Й': 'N', + 'К': 'K', + 'Л': 'N', + 'М': 'M', + 'Н': 'H', + 'О': 'O', + 'П': 'N', + 'Р': 'P', + 'С': 'C', + 'Т': 'T', + 'У': 'y', + 'Ф': 'O', + 'Х': 'X', + 'Ц': 'U', + 'Ч': 'h', + 'Ш': 'W', + 'Щ': 'W', + 'Ъ': 'B', + 'Ы': 'X', + 'Ь': 'B', + 'Э': '3', + 'Ю': 'X', + 'Я': 'R', + 'а': 'a', + 'б': 'b', + 'в': 'a', + 'г': 'r', + 'д': 'y', + 'е': 'e', + 'ж': 'm', + 'з': 'e', + 'и': 'n', + 'й': 'n', + 'к': 'n', + 'л': 'n', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'n', + 'р': 'p', + 'с': 'c', + 'т': 'o', + 'у': 'y', + 'ф': 'b', + 'х': 'x', + 'ц': 'n', + 'ч': 'n', + 'ш': 'w', + 'щ': 'w', + 'ъ': 'a', + 'ы': 'm', + 'ь': 'a', + 'э': 'e', + 'ю': 'm', + 'я': 'r', +}; + /** * This function is a convenience function for looking up information in the * metricMap table. It takes a character as a string, and a style. @@ -129,7 +269,13 @@ var metricMap = require("./fontMetricsData"); * built using `Make extended_metrics`. */ var getCharacterMetrics = function(character, style) { - var metrics = metricMap[style][character.charCodeAt(0)]; + var ch = character.charCodeAt(0); + if (character[0] in extraCharacterMap) { + ch = extraCharacterMap[character[0]].charCodeAt(0); + } else if (cjkRegex.test(character[0])) { + ch = 'M'.charCodeAt(0); + } + var metrics = metricMap[style][ch]; if (metrics) { return { depth: metrics[0], diff --git a/src/symbols.js b/src/symbols.js index aef7c8748..55301a4e4 100644 --- a/src/symbols.js +++ b/src/symbols.js @@ -630,3 +630,25 @@ for (i = 0; i < letters.length; i++) { defineSymbol(math, main, mathord, ch, ch); defineSymbol(text, main, textord, ch, ch); } + +// Latin-1 letters +for (i = 0x00C0; i <= 0x00D6; i++) { + ch = String.fromCharCode(i); + defineSymbol(text, main, textord, ch, ch); +} + +for (i = 0x00D8; i <= 0x00F6; i++) { + ch = String.fromCharCode(i); + defineSymbol(text, main, textord, ch, ch); +} + +for (i = 0x00F8; i <= 0x00FF; i++) { + ch = String.fromCharCode(i); + defineSymbol(text, main, textord, ch, ch); +} + +// Cyrillic +for (i = 0x0410; i <= 0x044F; i++) { + ch = String.fromCharCode(i); + defineSymbol(text, main, textord, ch, ch); +} diff --git a/src/unicodeRegexes.js b/src/unicodeRegexes.js new file mode 100644 index 000000000..a05d7cd5f --- /dev/null +++ b/src/unicodeRegexes.js @@ -0,0 +1,15 @@ +var hangulRegex = /[\uAC00-\uD7AF]/; + +// This regex combines +// - Hiragana: [\u3040-\u309F] +// - Katakana: [\u30A0-\u30FF] +// - CJK ideograms: [\u4E00-\u9FAF] +// - Hangul syllables: [\uAC00-\uD7AF] +// Notably missing are halfwidth Katakana and Romanji glyphs. +var cjkRegex = + /[\u3040-\u309F]|[\u30A0-\u30FF]|[\u4E00-\u9FAF]|[\uAC00-\uD7AF]/; + +module.exports = { + cjkRegex: cjkRegex, + hangulRegex: hangulRegex, +}; diff --git a/static/katex.less b/static/katex.less index b7c8f84d9..8b11048ab 100644 --- a/static/katex.less +++ b/static/katex.less @@ -15,7 +15,7 @@ } .katex { - font: normal 1.21em KaTeX_Main; + font: normal 1.21em KaTeX_Main, Times New Roman, serif; line-height: 1.2; white-space: nowrap; diff --git a/test/screenshotter/images/Unicode-chrome.png b/test/screenshotter/images/Unicode-chrome.png new file mode 100644 index 0000000000000000000000000000000000000000..6d7472f4a94d7566034152468d5a4ca4d9aa6a8a GIT binary patch literal 25212 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%0A!n=?0Vix=ZC1s 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 literal 0 HcmV?d00001 diff --git a/test/screenshotter/images/Unicode-firefox.png b/test/screenshotter/images/Unicode-firefox.png new file mode 100644 index 0000000000000000000000000000000000000000..bc0fc1bc05984d14afaa2b0df43c05a80ee3f379 GIT binary patch literal 24792 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�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 diff --git a/test/unicode-spec.js b/test/unicode-spec.js new file mode 100644 index 000000000..a78a83aaa --- /dev/null +++ b/test/unicode-spec.js @@ -0,0 +1,103 @@ +/* eslint max-len:0 */ +/* global beforeEach: false */ +/* global jasmine: false */ +/* global expect: false */ +/* global it: false */ +/* global describe: false */ +var ParseError = require("../src/ParseError"); +var parseTree = require("../src/parseTree"); +var Settings = require("../src/Settings"); + +var defaultSettings = new Settings({}); + +var parseAndSetResult = function(expr, result, settings) { + try { + return parseTree(expr, settings || defaultSettings); + } catch (e) { + result.pass = false; + if (e instanceof ParseError) { + result.message = "'" + expr + "' failed " + + "parsing with error: " + e.message; + } else { + result.message = "'" + expr + "' failed " + + "parsing with unknown error: " + e.message; + } + } +}; + +describe("unicode", function() { + beforeEach(function() { + jasmine.addMatchers({ + + toParse: function() { + return { + compare: function(actual, settings) { + var usedSettings = settings ? settings : defaultSettings; + + var result = { + pass: true, + message: "'" + actual + "' succeeded parsing", + }; + parseAndSetResult(actual, result, usedSettings); + return result; + }, + }; + }, + + toNotParse: function() { + return { + compare: function(actual, settings) { + var usedSettings = settings ? settings : defaultSettings; + + var result = { + pass: false, + message: "Expected '" + actual + "' to fail " + + "parsing, but it succeeded", + }; + + try { + parseTree(actual, usedSettings); + } catch (e) { + if (e instanceof ParseError) { + result.pass = true; + result.message = "'" + actual + "' correctly " + + "didn't parse with error: " + e.message; + } else { + result.message = "'" + actual + "' failed " + + "parsing with unknown error: " + e.message; + } + } + + return result; + }, + }; + }, + }); + }); + + it("should parse Latin-1 inside \\text{}", function() { + expect('\\text{ÀàÇçÉéÏïÖöÛû}').toParse(); + }); + + it("should not parse Latin-1 outside \\text{}", function() { + expect('ÀàÇçÉéÏïÖöÛû').toNotParse(); + }); + + it("should parse Cyrillic inside \\text{}", function() { + expect('\\text{БГДЖЗЙЛФЦШЫЮЯ}').toParse(); + }); + + it("should not parse Cyrillic outside \\text{}", function() { + expect('БГДЖЗЙЛФЦШЫЮЯ').toNotParse(); + }); + + it("should parse CJK inside \\text{}", function() { + expect('\\text{私はバナナです}').toParse(); + expect('\\text{여보세요}').toParse(); + }); + + it("should not parse CJK outside \\text{}", function() { + expect('私はバナナです。').toNotParse(); + expect('여보세요').toNotParse(); + }); +});