commit 387c159a8e37ee0c7207d25e899e4f70b3b60f50
parent 944b55a6b00b46708bbb16983f234fc08b5bd42f
Author: Emily Eisenberg <emily@khanacademy.org>
Date: Wed, 21 Aug 2013 20:22:24 -0700
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
Diffstat:
4 files changed, 97 insertions(+), 5 deletions(-)
diff --git 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
@@ -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
@@ -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
@@ -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; }
}