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
This commit is contained in:
Emily Eisenberg 2013-08-21 20:22:24 -07:00
parent 944b55a6b0
commit 387c159a8e
4 changed files with 97 additions and 5 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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; }
}