From 3083efba6689529a9fed1f593a62b4c7a682afaf Mon Sep 17 00:00:00 2001 From: Kevin Barabash Date: Wed, 30 Dec 2015 16:58:50 -0800 Subject: [PATCH] Add support for \kern Summary: This only supports em and ex units and doesn't handle vertical layouts. Negative kerning works. Test Plan: - make test - make screenshots (verify that d is slightly overlapping c in the screenshots) Reviewers: emily --- src/buildHTML.js | 19 ++++++++++++++++ src/buildMathML.js | 7 ++++++ src/functions.js | 10 +++++++++ test/katex-spec.js | 25 +++++++++++++++++++++ test/screenshotter/images/Kern-chrome.png | Bin 0 -> 6390 bytes test/screenshotter/images/Kern-firefox.png | Bin 0 -> 6376 bytes test/screenshotter/ss_data.yaml | 1 + 7 files changed, 62 insertions(+) create mode 100644 test/screenshotter/images/Kern-chrome.png create mode 100644 test/screenshotter/images/Kern-firefox.png diff --git a/src/buildHTML.js b/src/buildHTML.js index dbd33a3fc..a0e2d5220 100644 --- a/src/buildHTML.js +++ b/src/buildHTML.js @@ -1178,6 +1178,25 @@ groupTypes.rule = function(group, options, prev) { return rule; }; +groupTypes.kern = function(group, options, prev) { + // Make an empty span for the rule + var rule = makeSpan(["mord", "rule"], [], options.getColor()); + + var dimension = 0; + if (group.value.dimension) { + dimension = group.value.dimension.number; + if (group.value.dimension.unit === "ex") { + dimension *= fontMetrics.metrics.xHeight; + } + } + + dimension /= options.style.sizeMultiplier; + + rule.style.marginLeft = dimension + "em"; + + return rule; +}; + groupTypes.accent = function(group, options, prev) { // Accents are handled in the TeXbook pg. 443, rule 12. var base = group.value.base; diff --git a/src/buildMathML.js b/src/buildMathML.js index f3c7c5e1c..0a4d54455 100644 --- a/src/buildMathML.js +++ b/src/buildMathML.js @@ -430,6 +430,13 @@ groupTypes.rule = function(group) { return node; }; +groupTypes.kern = function(group) { + // TODO(kevin): Figure out if there's a way to add space in MathML + var node = new mathMLTree.MathNode("mrow"); + + return node; +}; + groupTypes.llap = function(group, options) { var node = new mathMLTree.MathNode( "mpadded", [buildGroup(group.value.body, options)]); diff --git a/src/functions.js b/src/functions.js index 2fa124d3b..ea5c33ea7 100644 --- a/src/functions.js +++ b/src/functions.js @@ -187,6 +187,16 @@ defineFunction("\\rule", { }; }); +defineFunction("\\kern", { + numArgs: 1, + argTypes: ["size"], +}, function(context, args) { + return { + type: "kern", + dimension: args[0].value, + }; +}); + // A KaTeX logo defineFunction("\\KaTeX", { numArgs: 0, diff --git a/test/katex-spec.js b/test/katex-spec.js index 75a0d393e..b6ac37e34 100644 --- a/test/katex-spec.js +++ b/test/katex-spec.js @@ -909,6 +909,31 @@ describe("A rule parser", function() { }); }); +describe("A kern parser", function() { + var emKern = "\\kern{1em}"; + var exKern = "\\kern{1ex}"; + var badUnitRule = "\\kern{1px}"; + var noNumberRule = "\\kern{em}"; + + it("should list the correct units", function() { + var emParse = getParsed(emKern)[0]; + var exParse = getParsed(exKern)[0]; + + expect(emParse.value.dimension.unit).toEqual("em"); + expect(exParse.value.dimension.unit).toEqual("ex"); + }); + + it("should not parse invalid units", function() { + expect(badUnitRule).toNotParse(); + expect(noNumberRule).toNotParse(); + }); + + it("should parse negative sizes", function() { + var parse = getParsed("\\kern{-1em}")[0]; + expect(parse.value.dimension.number).toBeCloseTo(-1); + }); +}); + describe("A left/right parser", function() { var normalLeftRight = "\\left( \\dfrac{x}{y} \\right)"; var emptyRight = "\\left( \\dfrac{x}{y} \\right."; diff --git a/test/screenshotter/images/Kern-chrome.png b/test/screenshotter/images/Kern-chrome.png new file mode 100644 index 0000000000000000000000000000000000000000..232ace52c6d1ffc75e88c2c1e705823543e7ecc0 GIT binary patch literal 6390 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 literal 0 HcmV?d00001 diff --git a/test/screenshotter/ss_data.yaml b/test/screenshotter/ss_data.yaml index d5e8736fa..6b41de8d5 100644 --- a/test/screenshotter/ss_data.yaml +++ b/test/screenshotter/ss_data.yaml @@ -58,6 +58,7 @@ FractionTest: \dfrac{a}{b}\frac{a}{b}\tfrac{a}{b}\;-\dfrac12\;1\tfrac12 Functions: \sin\cos\tan\ln\log GreekLetters: \alpha\beta\gamma\omega KaTeX: \KaTeX +Kern: \frac{a\kern{1em}b}{c}a\kern{1em}b\kern{1ex}c\kern{-0.25em}d Lap: ab\llap{f}cd\rlap{g}h LeftRight: \left( x^2 \right) \left\{ x^{x^{x^{x^x}}} \right. LeftRightListStyling: a+\left(x+y\right)-x