From 387c159a8e37ee0c7207d25e899e4f70b3b60f50 Mon Sep 17 00:00:00 2001 From: Emily Eisenberg Date: Wed, 21 Aug 2013 20:22:24 -0700 Subject: [PATCH] Add sizing functions (like \small) Summary: Right now, when the size gets bigger, this still doesn't work, so there's a check to prevent that. However, functions that go smaller (like `\small`, `\tiny`, etc) do work. Also, we can't seem to use the sizing functions inside of fractions (so something like `\dfrac{\small\frac{x}{y}}{z}` doesn't work). However, the most prominent use case is `\small` as the outer-most object, so this is still helpful. This commit has the parsing and stuff to handle all of it, but it'll throw an error if you try to do something that doesn't work. (For the record, "doesn't work" means "looks bad", not "throws an unexpected error"). Test Plan: Make sure things like `\small x` work, and things like `\Huge x` and `\frac{\small x}{y}` don't. Reviewers: alpert Reviewed By: alpert Differential Revision: http://phabricator.khanacademy.org/D3619 --- Options.js | 28 ++++++++++++++++++++++++---- Parser.js | 20 ++++++++++++++++++++ buildTree.js | 43 ++++++++++++++++++++++++++++++++++++++++++- static/katex.less | 11 +++++++++++ 4 files changed, 97 insertions(+), 5 deletions(-) diff --git a/Options.js b/Options.js index 96a74eb8c..a65ca3ff2 100644 --- a/Options.js +++ b/Options.js @@ -1,23 +1,43 @@ -function Options(style, color, parentStyle) { +function Options(style, size, color, depth, parentStyle, parentSize) { this.style = style; this.color = color; + this.size = size; + + // TODO(emily): Get rid of depth when we can actually use sizing everywhere + if (!depth) { + depth = 0; + } + this.depth = depth; if (!parentStyle) { parentStyle = style; } this.parentStyle = parentStyle; + + if (!parentSize) { + parentSize = size; + } + this.parentSize = parentSize; } Options.prototype.withStyle = function(style) { - return new Options(style, this.color, this.style); + return new Options(style, this.size, this.color, this.depth + 1, + this.style, this.size); +}; + +Options.prototype.withSize = function(size) { + return new Options(this.style, size, this.color, this.depth + 1, + this.style, this.size); }; Options.prototype.withColor = function(color) { - return new Options(this.style, color, this.style); + return new Options(this.style, this.size, color, this.depth + 1, + this.style, this.size); }; Options.prototype.reset = function() { - return new Options(this.style, this.color, this.style); + return new Options(this.style, this.size, this.color, this.depth + 1, + this.style, this.size); }; module.exports = Options; diff --git a/Parser.js b/Parser.js index 2c6a7b5a4..af24b26fe 100644 --- a/Parser.js +++ b/Parser.js @@ -183,6 +183,12 @@ var colorFuncs = [ "\\blue", "\\orange", "\\pink", "\\red", "\\green", "\\gray", "\\purple" ]; +// A list of 1-argument sizing functions +var sizeFuncs = [ + "\\tiny", "\\scriptsize", "\\footnotesize", "\\small", "\\normalsize", + "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge" +]; + // A map of elements that don't have arguments, and should simply be placed // into a group depending on their type. The keys are the groups that items can // be placed in, and the values are lists of element types that should be @@ -344,6 +350,20 @@ Parser.prototype.parseNucleus = function(pos) { throw new ParseError( "Expected group after '" + nucleus.text + "'"); } + } else if (utils.contains(sizeFuncs, nucleus.type)) { + // If this is a color function, parse its argument and return + var group = this.parseGroup(nucleus.position); + if (group) { + return new ParseResult( + new ParseNode("sizing", { + size: "size" + (sizeFuncs.indexOf(nucleus.type) + 1), + value: group.result + }), + group.position); + } else { + throw new ParseError( + "Expected group after '" + nucleus.text + "'"); + } } else if (nucleus.type === "\\llap" || nucleus.type === "\\rlap") { // If this is an llap or rlap, parse its argument and return var group = this.parseGroup(nucleus.position); diff --git a/buildTree.js b/buildTree.js index 008c493bf..b5f2dcac0 100644 --- a/buildTree.js +++ b/buildTree.js @@ -339,9 +339,32 @@ var groupTypes = { var x = makeSpan(["x"], [mathrm("X")]); return makeSpan(["katex-logo", options.color], [k, a, t, e, x]); + }, + + sizing: function(group, options, prev) { + var inner = buildGroup(group.value.value, + options.withSize(group.value.size), prev); + + return makeSpan( + ["reset-" + options.size, group.value.size, + getTypeOfGroup(group.value.value)], + [inner]); } }; +var sizingMultiplier = { + size1: 0.5, + size2: 0.7, + size3: 0.8, + size4: 0.9, + size5: 1.0, + size6: 1.2, + size7: 1.44, + size8: 1.73, + size9: 2.07, + size10: 2.49 +}; + var buildGroup = function(group, options, prev) { if (!group) { return makeSpan(); @@ -363,6 +386,24 @@ var buildGroup = function(group, options, prev) { groupNode.depth *= multiplier; } + if (options.size !== options.parentSize) { + var multiplier = sizingMultiplier[options.size] / + sizingMultiplier[options.parentSize]; + + if (multiplier > 1) { + throw new ParseError( + "Error: Can't go from small to large size"); + } + + if (options.depth > 1) { + throw new ParseError( + "Error: Can't use sizing outside of the root node"); + } + + groupNode.height *= multiplier; + groupNode.depth *= multiplier; + } + return groupNode; } else { throw new ParseError( @@ -481,7 +522,7 @@ var amsrm = function(value) { var buildTree = function(tree) { // Setup the default options - var options = new Options(Style.TEXT, ""); + var options = new Options(Style.TEXT, "size5", ""); var expression = buildExpression(tree, options); var span = makeSpan(["base", options.style.cls()], expression); diff --git a/static/katex.less b/static/katex.less index e77a05448..f026bd474 100644 --- a/static/katex.less +++ b/static/katex.less @@ -288,4 +288,15 @@ big parens margin-left: -0.125em; } } + + .reset-size5.size1 { font-size: 0.5em; } + .reset-size5.size2 { font-size: 0.7em; } + .reset-size5.size3 { font-size: 0.8em; } + .reset-size5.size4 { font-size: 0.9em; } + .reset-size5.size5 { font-size: 1.0em; } + .reset-size5.size6 { font-size: 1.2em; } + .reset-size5.size7 { font-size: 1.44em; } + .reset-size5.size8 { font-size: 1.73em; } + .reset-size5.size9 { font-size: 2.07em; } + .reset-size5.size10 { font-size: 2.49em; } }