Fix spacing between groups to match TeX spacing (#567)

* Internal: Pass full `options` objects to makeSpan/makeSymbol.

Not just the current color. This will facilitate applying options
to built nodes in a standardized way, rather than changing all
callsites.

* Add style switching test: text and scriptstyle in the same group.

* Apply style-specific spacing using different CSS coding.

Specifically, infer style from a class on the *current* element,
rather than the parent element. Use "mtight" class to denote elements
with tight spacing (scriptstyle or scriptscriptstyle). Apply that
class automatically based on options.

* Fix #533, #534, #541.

- #534: Implement getTypeOfGroup for font groups.
- #533, #541: Improve the ways spaces are applied to lists. Since
  CSS adjacency implements mathematical spacing, it's incorrect to
  introduce "convenience spans" for spaces and display changes into
  the generated HTML -- those spans break adjacency. Apply display
  changes directly, and shift space spans into adjacent atoms.

Requires updates to two screenshotter tests, LimitControls and
SupSubLeftAlignReset. The new results for these tests are closer
to TeX output than the old results.

Also requires updates to Jasmine tests, since those assumed output
structures that have changed.

* Fix #136: Size commands generate fragments, not spans.

This is so the size commands don't hide the types of their enclosed
atoms. Addresses #136.

This slightly changes the vertical position of the Sizing test. Not
sure the vertical position matters, so change the test.
This commit is contained in:
Eddie Kohler
2016-11-28 12:55:54 -05:00
committed by Kevin Barabash
parent be96695586
commit 9d3cdf694c
22 changed files with 340 additions and 210 deletions

View File

@@ -87,6 +87,13 @@ Style.prototype.reset = function() {
return resetNames[this.size];
};
/**
* Return if this style is tightly spaced (scriptstyle/scriptscriptstyle)
*/
Style.prototype.isTight = function() {
return this.size >= 2;
};
// IDs of the different styles
var D = 0;
var Dc = 1;

View File

@@ -34,14 +34,16 @@ var mainitLetters = [
* Makes a symbolNode after translation via the list of symbols in symbols.js.
* Correctly pulls out metrics for the character, and optionally takes a list of
* classes to be attached to the node.
*
* TODO: make argument order closer to makeSpan
*/
var makeSymbol = function(value, style, mode, color, classes) {
var makeSymbol = function(value, fontFamily, mode, options, classes) {
// Replace the value with its replaced value from symbol.js
if (symbols[mode][value] && symbols[mode][value].replace) {
value = symbols[mode][value].replace;
}
var metrics = fontMetrics.getCharacterMetrics(value, style);
var metrics = fontMetrics.getCharacterMetrics(value, fontFamily);
var symbolNode;
if (metrics) {
@@ -52,12 +54,17 @@ var makeSymbol = function(value, style, mode, color, classes) {
// TODO(emily): Figure out a good way to only print this in development
typeof console !== "undefined" && console.warn(
"No character metrics for '" + value + "' in style '" +
style + "'");
fontFamily + "'");
symbolNode = new domTree.symbolNode(value, 0, 0, 0, 0, classes);
}
if (color) {
symbolNode.style.color = color;
if (options) {
if (options.style.isTight()) {
symbolNode.classes.push("mtight");
}
if (options.getColor()) {
symbolNode.style.color = options.getColor();
}
}
return symbolNode;
@@ -67,7 +74,7 @@ var makeSymbol = function(value, style, mode, color, classes) {
* Makes a symbol in Main-Regular or AMS-Regular.
* Used for rel, bin, open, close, inner, and punct.
*/
var mathsym = function(value, mode, color, classes) {
var mathsym = function(value, mode, options, classes) {
// Decide what font to render the symbol in by its entry in the symbols
// table.
// Have a special case for when the value = \ because the \ is used as a
@@ -75,22 +82,22 @@ var mathsym = function(value, mode, color, classes) {
// text ordinal and is therefore not present as a symbol in the symbols
// table for text
if (value === "\\" || symbols[mode][value].font === "main") {
return makeSymbol(value, "Main-Regular", mode, color, classes);
return makeSymbol(value, "Main-Regular", mode, options, classes);
} else {
return makeSymbol(
value, "AMS-Regular", mode, color, classes.concat(["amsrm"]));
value, "AMS-Regular", mode, options, classes.concat(["amsrm"]));
}
};
/**
* Makes a symbol in the default font for mathords and textords.
*/
var mathDefault = function(value, mode, color, classes, type) {
var mathDefault = function(value, mode, options, classes, type) {
if (type === "mathord") {
return mathit(value, mode, color, classes);
return mathit(value, mode, options, classes);
} else if (type === "textord") {
return makeSymbol(
value, "Main-Regular", mode, color, classes.concat(["mathrm"]));
value, "Main-Regular", mode, options, classes.concat(["mathrm"]));
} else {
throw new Error("unexpected type: " + type + " in mathDefault");
}
@@ -99,17 +106,17 @@ var mathDefault = function(value, mode, color, classes, type) {
/**
* Makes a symbol in the italic math font.
*/
var mathit = function(value, mode, color, classes) {
var mathit = function(value, mode, options, classes) {
if (/[0-9]/.test(value.charAt(0)) ||
// glyphs for \imath and \jmath do not exist in Math-Italic so we
// need to use Main-Italic instead
utils.contains(mainitLetters, value) ||
utils.contains(greekCapitals, value)) {
return makeSymbol(
value, "Main-Italic", mode, color, classes.concat(["mainit"]));
value, "Main-Italic", mode, options, classes.concat(["mainit"]));
} else {
return makeSymbol(
value, "Math-Italic", mode, color, classes.concat(["mathit"]));
value, "Math-Italic", mode, options, classes.concat(["mathit"]));
}
};
@@ -124,23 +131,22 @@ var makeOrd = function(group, options, type) {
}
var classes = ["mord"];
var color = options.getColor();
var font = options.font;
if (font) {
if (font === "mathit" || utils.contains(mainitLetters, value)) {
return mathit(value, mode, color, classes);
return mathit(value, mode, options, classes);
} else {
var fontName = fontMap[font].fontName;
if (fontMetrics.getCharacterMetrics(value, fontName)) {
return makeSymbol(
value, fontName, mode, color, classes.concat([font]));
value, fontName, mode, options, classes.concat([font]));
} else {
return mathDefault(value, mode, color, classes, type);
return mathDefault(value, mode, options, classes, type);
}
}
} else {
return mathDefault(value, mode, color, classes, type);
return mathDefault(value, mode, options, classes, type);
}
};
@@ -173,20 +179,29 @@ var sizeElementFromChildren = function(elem) {
};
/**
* Makes a span with the given list of classes, list of children, and color.
* Makes a span with the given list of classes, list of children, and options.
*
* TODO: Ensure that `options` is always provided (currently some call sites
* don't pass it).
*/
var makeSpan = function(classes, children, color) {
var span = new domTree.span(classes, children);
var makeSpan = function(classes, children, options) {
var span = new domTree.span(classes, children, options);
sizeElementFromChildren(span);
if (color) {
span.style.color = color;
}
return span;
};
/**
* Prepends the given children to the given span, updating height, depth, and
* maxFontSize.
*/
var prependChildren = function(span, children) {
span.children = children.concat(span.children);
sizeElementFromChildren(span);
};
/**
* Makes a document fragment with the given list of children.
*/
@@ -447,6 +462,7 @@ module.exports = {
makeFragment: makeFragment,
makeVList: makeVList,
makeOrd: makeOrd,
prependChildren: prependChildren,
sizingMultiplier: sizingMultiplier,
spacingFunctions: spacingFunctions,
};

View File

@@ -17,18 +17,57 @@ var utils = require("./utils");
var makeSpan = buildCommon.makeSpan;
var isSpace = function(node) {
return node instanceof domTree.span && node.classes[0] === "mspace";
};
/**
* Take a list of nodes, build them in order, and return a list of the built
* nodes. This function handles the `prev` node correctly, and passes the
* previous element from the list as the prev of the next element.
* previous element from the list as the prev of the next element, ignoring
* spaces. documentFragments are flattened into their contents, so the
* returned list contains no fragments.
*/
var buildExpression = function(expression, options, prev) {
// Parse expressions into `groups`.
var groups = [];
for (var i = 0; i < expression.length; i++) {
var group = expression[i];
groups.push(buildGroup(group, options, prev));
var output = buildGroup(group, options, prev);
if (output instanceof domTree.documentFragment) {
Array.prototype.push.apply(groups, output.children);
} else {
groups.push(output);
}
if (!isSpace(output)) {
prev = group;
}
}
// At this point `groups` consists entirely of `symbolNode`s and `span`s.
// Explicit spaces (e.g., \;, \,) should be ignored with respect to atom
// spacing (e.g., "add thick space between mord and mrel"). Since CSS
// adjacency rules implement atom spacing, spaces should be invisible to
// CSS. So we splice them out of `groups` and into the atoms themselves.
var spaces = null;
for (i = 0; i < groups.length; i++) {
if (isSpace(groups[i])) {
spaces = spaces || [];
spaces.push(groups[i]);
groups.splice(i, 1);
i--;
} else if (spaces) {
if (groups[i] instanceof domTree.symbolNode) {
groups[i] = makeSpan(groups[i].classes, [groups[i]]);
}
buildCommon.prependChildren(groups[i], spaces);
spaces = null;
}
}
if (spaces) {
Array.prototype.push.apply(groups, spaces);
}
return groups;
};
@@ -80,12 +119,13 @@ var getTypeOfGroup = function(group) {
return getTypeOfGroup(group.value.base);
} else if (group.type === "llap" || group.type === "rlap") {
return getTypeOfGroup(group.value);
} else if (group.type === "color") {
return getTypeOfGroup(group.value.value);
} else if (group.type === "sizing") {
return getTypeOfGroup(group.value.value);
} else if (group.type === "styling") {
return getTypeOfGroup(group.value.value);
} else if (group.type === "color" || group.type === "sizing"
|| group.type === "styling") {
// Return type of rightmost element of group.
var atoms = group.value.value;
return getTypeOfGroup(atoms[atoms.length - 1]);
} else if (group.type === "font") {
return getTypeOfGroup(group.value.body);
} else if (group.type === "delimsizing") {
return groupToType[group.value.delimType];
} else {
@@ -203,44 +243,46 @@ groupTypes.bin = function(group, options, prev) {
}
return buildCommon.mathsym(
group.value, group.mode, options.getColor(), [className]);
group.value, group.mode, options, [className]);
};
groupTypes.rel = function(group, options, prev) {
return buildCommon.mathsym(
group.value, group.mode, options.getColor(), ["mrel"]);
group.value, group.mode, options, ["mrel"]);
};
groupTypes.open = function(group, options, prev) {
return buildCommon.mathsym(
group.value, group.mode, options.getColor(), ["mopen"]);
group.value, group.mode, options, ["mopen"]);
};
groupTypes.close = function(group, options, prev) {
return buildCommon.mathsym(
group.value, group.mode, options.getColor(), ["mclose"]);
group.value, group.mode, options, ["mclose"]);
};
groupTypes.inner = function(group, options, prev) {
return buildCommon.mathsym(
group.value, group.mode, options.getColor(), ["minner"]);
group.value, group.mode, options, ["minner"]);
};
groupTypes.punct = function(group, options, prev) {
return buildCommon.mathsym(
group.value, group.mode, options.getColor(), ["mpunct"]);
group.value, group.mode, options, ["mpunct"]);
};
groupTypes.ordgroup = function(group, options, prev) {
return makeSpan(
["mord", options.style.cls()],
buildExpression(group.value, options.reset())
buildExpression(group.value, options.reset()),
options
);
};
groupTypes.text = function(group, options, prev) {
return makeSpan(["text", "mord", options.style.cls()],
buildExpression(group.value.body, options.reset()));
return makeSpan(["mord", "text", options.style.cls()],
buildExpression(group.value.body, options.reset()),
options);
};
groupTypes.color = function(group, options, prev) {
@@ -274,15 +316,20 @@ groupTypes.supsub = function(group, options, prev) {
var sub;
var style = options.style;
var newOptions;
if (group.value.sup) {
sup = buildGroup(group.value.sup, options.withStyle(style.sup()));
supmid = makeSpan([style.reset(), style.sup().cls()], [sup]);
newOptions = options.withStyle(style.sup());
sup = buildGroup(group.value.sup, newOptions);
supmid = makeSpan([style.reset(), style.sup().cls()],
[sup], newOptions);
}
if (group.value.sub) {
sub = buildGroup(group.value.sub, options.withStyle(style.sub()));
submid = makeSpan([style.reset(), style.sub().cls()], [sub]);
newOptions = options.withStyle(style.sub());
sub = buildGroup(group.value.sub, newOptions);
submid = makeSpan([style.reset(), style.sub().cls()],
[sub], newOptions);
}
// Rule 18a
@@ -376,7 +423,8 @@ groupTypes.supsub = function(group, options, prev) {
// We ensure to wrap the supsub vlist in a span.msupsub to reset text-align
return makeSpan([getTypeOfGroup(group.value.base)],
[base, makeSpan(["msupsub"], [supsub])]);
[base, makeSpan(["msupsub"], [supsub])],
options);
};
groupTypes.genfrac = function(group, options, prev) {
@@ -392,12 +440,17 @@ groupTypes.genfrac = function(group, options, prev) {
var nstyle = style.fracNum();
var dstyle = style.fracDen();
var newOptions;
var numer = buildGroup(group.value.numer, options.withStyle(nstyle));
var numerreset = makeSpan([style.reset(), nstyle.cls()], [numer]);
newOptions = options.withStyle(nstyle);
var numer = buildGroup(group.value.numer, newOptions);
var numerreset = makeSpan([style.reset(), nstyle.cls()],
[numer], newOptions);
var denom = buildGroup(group.value.denom, options.withStyle(dstyle));
var denomreset = makeSpan([style.reset(), dstyle.cls()], [denom]);
newOptions = options.withStyle(dstyle);
var denom = buildGroup(group.value.denom, newOptions);
var denomreset = makeSpan([style.reset(), dstyle.cls()],
[denom], newOptions);
var ruleWidth;
if (group.value.hasBarLine) {
@@ -510,7 +563,7 @@ groupTypes.genfrac = function(group, options, prev) {
return makeSpan(
["mord", options.style.reset(), style.cls()],
[leftDelim, makeSpan(["mfrac"], [frac]), rightDelim],
options.getColor());
options);
};
groupTypes.array = function(group, options, prev) {
@@ -674,7 +727,7 @@ groupTypes.array = function(group, options, prev) {
}
}
body = makeSpan(["mtable"], cols);
return makeSpan(["mord"], [body], options.getColor());
return makeSpan(["mord"], [body], options);
};
groupTypes.spacing = function(group, options, prev) {
@@ -684,14 +737,14 @@ groupTypes.spacing = function(group, options, prev) {
// things has an entry in the symbols table, so these will be turned
// into appropriate outputs.
return makeSpan(
["mord", "mspace"],
["mspace"],
[buildCommon.mathsym(group.value, group.mode)]
);
} else {
// Other kinds of spaces are of arbitrary width. We use CSS to
// generate these.
return makeSpan(
["mord", "mspace",
["mspace",
buildCommon.spacingFunctions[group.value].className]);
}
};
@@ -701,7 +754,7 @@ groupTypes.llap = function(group, options, prev) {
["inner"], [buildGroup(group.value.body, options.reset())]);
var fix = makeSpan(["fix"], []);
return makeSpan(
["llap", options.style.cls()], [inner, fix]);
["llap", options.style.cls()], [inner, fix], options);
};
groupTypes.rlap = function(group, options, prev) {
@@ -709,7 +762,7 @@ groupTypes.rlap = function(group, options, prev) {
["inner"], [buildGroup(group.value.body, options.reset())]);
var fix = makeSpan(["fix"], []);
return makeSpan(
["rlap", options.style.cls()], [inner, fix]);
["rlap", options.style.cls()], [inner, fix], options);
};
groupTypes.op = function(group, options, prev) {
@@ -750,8 +803,8 @@ groupTypes.op = function(group, options, prev) {
// If this is a symbol, create the symbol.
var fontName = large ? "Size2-Regular" : "Size1-Regular";
base = buildCommon.makeSymbol(
group.value.body, fontName, "math", options.getColor(),
["op-symbol", large ? "large-op" : "small-op", "mop"]);
group.value.body, fontName, "math", options,
["mop", "op-symbol", large ? "large-op" : "small-op"]);
// Shift the symbol so its center lies on the axis (rule 13). It
// appears that our fonts have the centers of the symbols already
@@ -772,7 +825,7 @@ groupTypes.op = function(group, options, prev) {
for (var i = 1; i < group.value.body.length; i++) {
output.push(buildCommon.mathsym(group.value.body[i], group.mode));
}
base = makeSpan(["mop"], output, options.getColor());
base = makeSpan(["mop"], output, options);
}
if (hasLimits) {
@@ -784,11 +837,14 @@ groupTypes.op = function(group, options, prev) {
var supKern;
var submid;
var subKern;
var newOptions;
// We manually have to handle the superscripts and subscripts. This,
// aside from the kern calculations, is copied from supsub.
if (supGroup) {
var sup = buildGroup(supGroup, options.withStyle(style.sup()));
supmid = makeSpan([style.reset(), style.sup().cls()], [sup]);
newOptions = options.withStyle(style.sup());
var sup = buildGroup(supGroup, newOptions);
supmid = makeSpan([style.reset(), style.sup().cls()],
[sup], newOptions);
supKern = Math.max(
fontMetrics.metrics.bigOpSpacing1,
@@ -796,8 +852,10 @@ groupTypes.op = function(group, options, prev) {
}
if (subGroup) {
var sub = buildGroup(subGroup, options.withStyle(style.sub()));
submid = makeSpan([style.reset(), style.sub().cls()], [sub]);
newOptions = options.withStyle(style.sub());
var sub = buildGroup(subGroup, newOptions);
submid = makeSpan([style.reset(), style.sub().cls()],
[sub], newOptions);
subKern = Math.max(
fontMetrics.metrics.bigOpSpacing2,
@@ -862,7 +920,7 @@ groupTypes.op = function(group, options, prev) {
finalGroup.children[2].style.marginLeft = slant + "em";
}
return makeSpan(["mop", "op-limits"], [finalGroup]);
return makeSpan(["mop", "op-limits"], [finalGroup], options);
} else {
if (group.value.symbol) {
base.style.top = baseShift + "em";
@@ -877,26 +935,26 @@ groupTypes.katex = function(group, options, prev) {
// good, but the offsets for the T, E, and X were taken from the
// definition of \TeX in TeX (see TeXbook pg. 356)
var k = makeSpan(
["k"], [buildCommon.mathsym("K", group.mode)]);
["k"], [buildCommon.mathsym("K", group.mode)], options);
var a = makeSpan(
["a"], [buildCommon.mathsym("A", group.mode)]);
["a"], [buildCommon.mathsym("A", group.mode)], options);
a.height = (a.height + 0.2) * 0.75;
a.depth = (a.height - 0.2) * 0.75;
var t = makeSpan(
["t"], [buildCommon.mathsym("T", group.mode)]);
["t"], [buildCommon.mathsym("T", group.mode)], options);
var e = makeSpan(
["e"], [buildCommon.mathsym("E", group.mode)]);
["e"], [buildCommon.mathsym("E", group.mode)], options);
e.height = (e.height - 0.2155);
e.depth = (e.depth + 0.2155);
var x = makeSpan(
["x"], [buildCommon.mathsym("X", group.mode)]);
["x"], [buildCommon.mathsym("X", group.mode)], options);
return makeSpan(
["katex-logo", "mord"], [k, a, t, e, x], options.getColor());
["mord", "katex-logo"], [k, a, t, e, x], options);
};
groupTypes.overline = function(group, options, prev) {
@@ -924,7 +982,7 @@ groupTypes.overline = function(group, options, prev) {
{type: "kern", size: ruleWidth},
], "firstBaseline", null, options);
return makeSpan(["overline", "mord"], [vlist], options.getColor());
return makeSpan(["mord", "overline"], [vlist], options);
};
groupTypes.underline = function(group, options, prev) {
@@ -950,7 +1008,7 @@ groupTypes.underline = function(group, options, prev) {
{type: "elem", elem: innerGroup},
], "top", innerGroup.height, options);
return makeSpan(["underline", "mord"], [vlist], options.getColor());
return makeSpan(["mord", "underline"], [vlist], options);
};
groupTypes.sqrt = function(group, options, prev) {
@@ -966,7 +1024,7 @@ groupTypes.sqrt = function(group, options, prev) {
var line = makeSpan(
[style.reset(), Style.TEXT.cls(), "sqrt-line"], [],
options.getColor());
options);
line.height = ruleWidth;
line.maxFontSize = 1.0;
@@ -985,7 +1043,7 @@ groupTypes.sqrt = function(group, options, prev) {
var delim = makeSpan(["sqrt-sign"], [
delimiter.customSizedDelim("\\surd", minDelimiterHeight,
false, options, group.mode)],
options.getColor());
options);
var delimDepth = (delim.height + delim.depth) - ruleWidth;
@@ -1019,17 +1077,17 @@ groupTypes.sqrt = function(group, options, prev) {
}
if (!group.value.index) {
return makeSpan(["sqrt", "mord"], [delim, body]);
return makeSpan(["mord", "sqrt"], [delim, body], options);
} else {
// Handle the optional root index
// The index is always in scriptscript style
var root = buildGroup(
group.value.index,
options.withStyle(Style.SCRIPTSCRIPT));
var newOptions = options.withStyle(Style.SCRIPTSCRIPT);
var root = buildGroup(group.value.index, newOptions);
var rootWrap = makeSpan(
[style.reset(), Style.SCRIPTSCRIPT.cls()],
[root]);
[root],
newOptions);
// Figure out the height and depth of the inner part
var innerRootHeight = Math.max(delim.height, body.height);
@@ -1047,7 +1105,8 @@ groupTypes.sqrt = function(group, options, prev) {
// kerning
var rootVListWrap = makeSpan(["root"], [rootVList]);
return makeSpan(["sqrt", "mord"], [rootVListWrap, delim, body]);
return makeSpan(["mord", "sqrt"],
[rootVListWrap, delim, body], options);
}
};
@@ -1058,17 +1117,28 @@ groupTypes.sizing = function(group, options, prev) {
var inner = buildExpression(group.value.value,
options.withSize(group.value.size), prev);
// Compute the correct maxFontSize.
var style = options.style;
var span = makeSpan(["mord"],
[makeSpan(["sizing", "reset-" + options.size, group.value.size,
style.cls()],
inner)]);
// Calculate the correct maxFontSize manually
var fontSize = buildCommon.sizingMultiplier[group.value.size];
span.maxFontSize = fontSize * style.sizeMultiplier;
fontSize = fontSize * style.sizeMultiplier;
return span;
// Add size-resetting classes to the inner list and set maxFontSize
// manually. Handle nested size changes.
for (var i = 0; i < inner.length; i++) {
var pos = utils.indexOf(inner[i].classes, "sizing");
if (pos < 0) {
inner[i].classes.push("sizing", "reset-" + options.size,
group.value.size, style.cls());
inner[i].maxFontSize = fontSize;
} else if (inner[i].classes[pos + 1] === "reset-" + group.value.size) {
// This is a nested size change: e.g., inner[i] is the "b" in
// `\Huge a \small b`. Override the old size (the `reset-` class)
// but not the new size.
inner[i].classes[pos + 1] = "reset-" + options.size;
}
}
return buildCommon.makeFragment(inner);
};
groupTypes.styling = function(group, options, prev) {
@@ -1083,12 +1153,25 @@ groupTypes.styling = function(group, options, prev) {
};
var newStyle = styleMap[group.value.style];
var newOptions = options.withStyle(newStyle);
// Build the inner expression in the new style.
var inner = buildExpression(
group.value.value, options.withStyle(newStyle), prev);
group.value.value, newOptions, prev);
return makeSpan([options.style.reset(), newStyle.cls()], inner);
// Add style-resetting classes to the inner list. Handle nested changes.
for (var i = 0; i < inner.length; i++) {
var pos = utils.indexOf(inner[i].classes, newStyle.reset());
if (pos < 0) {
inner[i].classes.push(options.style.reset(), newStyle.cls());
} else {
// This is a nested style change, as `\textstyle a\scriptstyle b`.
// Only override the old style (the reset class).
inner[i].classes[pos] = options.style.reset();
}
}
return new buildCommon.makeFragment(inner);
};
groupTypes.font = function(group, options, prev) {
@@ -1109,7 +1192,8 @@ groupTypes.delimsizing = function(group, options, prev) {
return makeSpan(
[groupToType[group.value.delimType]],
[delimiter.sizedDelim(
delim, group.value.size, options, group.mode)]);
delim, group.value.size, options, group.mode)],
options);
};
groupTypes.leftright = function(group, options, prev) {
@@ -1160,12 +1244,12 @@ groupTypes.leftright = function(group, options, prev) {
inner.push(rightDelim);
return makeSpan(
["minner", style.cls()], inner, options.getColor());
["minner", style.cls()], inner, options);
};
groupTypes.rule = function(group, options, prev) {
// Make an empty span for the rule
var rule = makeSpan(["mord", "rule"], [], options.getColor());
var rule = makeSpan(["mord", "rule"], [], options);
var style = options.style;
// Calculate the shift, width, and height of the rule, and account for units
@@ -1208,7 +1292,7 @@ groupTypes.rule = function(group, options, prev) {
groupTypes.kern = function(group, options, prev) {
// Make an empty span for the rule
var rule = makeSpan(["mord", "rule"], [], options.getColor());
var rule = makeSpan(["mord", "rule"], [], options);
var style = options.style;
var dimension = 0;
@@ -1290,7 +1374,7 @@ groupTypes.accent = function(group, options, prev) {
// Build the accent
var accent = buildCommon.makeSymbol(
group.value.accent, "Main-Regular", "math", options.getColor());
group.value.accent, "Main-Regular", "math", options);
// Remove the italic correction of the accent, because it only serves to
// shift the accent over to a place we don't want.
accent.italic = 0;
@@ -1315,7 +1399,7 @@ groupTypes.accent = function(group, options, prev) {
// we shift it to the right by 1*skew.
accentBody.children[1].style.marginLeft = 2 * skew + "em";
var accentWrap = makeSpan(["mord", "accent"], [accentBody]);
var accentWrap = makeSpan(["mord", "accent"], [accentBody], options);
if (supsubGroup) {
// Here, we replace the "base" child of the supsub with our newly
@@ -1400,7 +1484,7 @@ var buildHTML = function(tree, options) {
// Build the expression contained in the tree
var expression = buildExpression(tree, options);
var body = makeSpan(["base", options.style.cls()], expression);
var body = makeSpan(["base", options.style.cls()], expression, options);
// Add struts, which ensure that the top of the HTML element falls at the
// height of the expression, and the bottom of the HTML element falls at the

View File

@@ -47,8 +47,9 @@ var getMetrics = function(symbol, font) {
/**
* Builds a symbol in the given font size (note size is an integer)
*/
var mathrmSize = function(value, size, mode) {
return buildCommon.makeSymbol(value, "Size" + size + "-Regular", mode);
var mathrmSize = function(value, size, mode, options) {
return buildCommon.makeSymbol(value, "Size" + size + "-Regular",
mode, options);
};
/**
@@ -57,7 +58,7 @@ var mathrmSize = function(value, size, mode) {
*/
var styleWrap = function(delim, toStyle, options) {
var span = makeSpan(
["style-wrap", options.style.reset(), toStyle.cls()], [delim]);
["style-wrap", options.style.reset(), toStyle.cls()], [delim], options);
var multiplier = toStyle.sizeMultiplier / options.style.sizeMultiplier;
@@ -74,7 +75,7 @@ var styleWrap = function(delim, toStyle, options) {
* scriptscriptstyle.
*/
var makeSmallDelim = function(delim, style, center, options, mode) {
var text = buildCommon.makeSymbol(delim, "Main-Regular", mode);
var text = buildCommon.makeSymbol(delim, "Main-Regular", mode, options);
var span = styleWrap(text, style, options);
@@ -96,11 +97,11 @@ var makeSmallDelim = function(delim, style, center, options, mode) {
* Size3, or Size4 fonts. It is always rendered in textstyle.
*/
var makeLargeDelim = function(delim, size, center, options, mode) {
var inner = mathrmSize(delim, size, mode);
var inner = mathrmSize(delim, size, mode, options);
var span = styleWrap(
makeSpan(["delimsizing", "size" + size],
[inner], options.getColor()),
[inner], options),
Style.TEXT, options);
if (center) {
@@ -318,7 +319,7 @@ var makeStackedDelim = function(delim, heightTotal, center, options, mode) {
var inner = buildCommon.makeVList(inners, "bottom", depth, options);
return styleWrap(
makeSpan(["delimsizing", "mult"], [inner], options.getColor()),
makeSpan(["delimsizing", "mult"], [inner], options),
Style.TEXT, options);
};

View File

@@ -30,14 +30,22 @@ var createClass = function(classes) {
* an inline style. It also contains information about its height, depth, and
* maxFontSize.
*/
function span(classes, children, height, depth, maxFontSize, style) {
function span(classes, children, options) {
this.classes = classes || [];
this.children = children || [];
this.height = height || 0;
this.depth = depth || 0;
this.maxFontSize = maxFontSize || 0;
this.style = style || {};
this.height = 0;
this.depth = 0;
this.maxFontSize = 0;
this.style = {};
this.attributes = {};
if (options) {
if (options.style.isTight()) {
this.classes.push("mtight");
}
if (options.getColor()) {
this.style.color = options.getColor();
}
}
}
/**
@@ -133,11 +141,11 @@ span.prototype.toMarkup = function() {
* contains children and doesn't have any HTML properties. It also keeps track
* of a height, depth, and maxFontSize.
*/
function documentFragment(children, height, depth, maxFontSize) {
function documentFragment(children) {
this.children = children || [];
this.height = height || 0;
this.depth = depth || 0;
this.maxFontSize = maxFontSize || 0;
this.height = 0;
this.depth = 0;
this.maxFontSize = 0;
}
/**

View File

@@ -101,8 +101,8 @@
@mediumspace: 0.22222em;
@thickspace: 0.27778em;
.textstyle {
> .mord {
// These spacings apply in textstyle and displaystyle.
.mord {
& + .mord {}
& + .mop { margin-left: @thinspace; }
& + .mbin { margin-left: @mediumspace; }
@@ -113,7 +113,7 @@
& + .minner { margin-left: @thinspace; }
}
> .mop {
.mop {
& + .mord { margin-left: @thinspace; }
& + .mop { margin-left: @thinspace; }
& + .mbin {}
@@ -124,7 +124,7 @@
& + .minner { margin-left: @thinspace; }
}
> .mbin {
.mbin {
& + .mord { margin-left: @mediumspace; }
& + .mop { margin-left: @mediumspace; }
& + .mbin {}
@@ -135,7 +135,7 @@
& + .minner { margin-left: @mediumspace; }
}
> .mrel {
.mrel {
& + .mord { margin-left: @thickspace; }
& + .mop { margin-left: @thickspace; }
& + .mbin {}
@@ -146,7 +146,7 @@
& + .minner { margin-left: @thickspace; }
}
> .mopen {
.mopen {
& + .mord {}
& + .mop {}
& + .mbin {}
@@ -157,7 +157,7 @@
& + .minner {}
}
> .mclose {
.mclose {
& + .mord {}
& + .mop { margin-left: @thinspace; }
& + .mbin { margin-left: @mediumspace; }
@@ -168,7 +168,7 @@
& + .minner { margin-left: @thinspace; }
}
> .mpunct {
.mpunct {
& + .mord { margin-left: @thinspace; }
& + .mop { margin-left: @thinspace; }
& + .mbin {}
@@ -179,7 +179,7 @@
& + .minner { margin-left: @thinspace; }
}
> .minner {
.minner {
& + .mord { margin-left: @thinspace; }
& + .mop { margin-left: @thinspace; }
& + .mbin { margin-left: @mediumspace; }
@@ -189,23 +189,32 @@
& + .mpunct { margin-left: @thinspace; }
& + .minner { margin-left: @thinspace; }
}
}
// These tighter spacings apply in scriptstyle and scriptscriptstyle.
.mord.mtight { margin-left: 0; }
.mop.mtight { margin-left: 0; }
.mbin.mtight { margin-left: 0; }
.mrel.mtight { margin-left: 0; }
.mopen.mtight { margin-left: 0; }
.mclose.mtight { margin-left: 0; }
.mpunct.mtight { margin-left: 0; }
.minner.mtight { margin-left: 0; }
.mord {
& + .mop { margin-left: @thinspace; }
& + .mop.mtight { margin-left: @thinspace; }
}
.mop {
& + .mord { margin-left: @thinspace; }
& + .mop { margin-left: @thinspace; }
& + .mord.mtight { margin-left: @thinspace; }
& + .mop.mtight { margin-left: @thinspace; }
}
.mclose {
& + .mop { margin-left: @thinspace; }
& + .mop.mtight { margin-left: @thinspace; }
}
.minner {
& + .mop { margin-left: @thinspace; }
& + .mop.mtight { margin-left: @thinspace; }
}
.reset-textstyle.textstyle { font-size: 1em; }

View File

@@ -1581,7 +1581,8 @@ describe("A bin builder", function() {
it("should correctly interact with color objects", function() {
expect(getBuilt("\\blue{x}+y")[1].classes).toContain("mbin");
expect(getBuilt("\\blue{x+}+y")[1].classes).toContain("mord");
expect(getBuilt("\\blue{x+}+y")[1].classes).toContain("mbin");
expect(getBuilt("\\blue{x+}+y")[2].classes).toContain("mord");
});
});
@@ -1695,17 +1696,17 @@ describe("A phantom builder", function() {
});
it("should make the children transparent", function() {
var children = getBuilt("\\phantom{x+1}")[0].children;
var children = getBuilt("\\phantom{x+1}");
expect(children[0].style.color).toBe("transparent");
expect(children[1].style.color).toBe("transparent");
expect(children[2].style.color).toBe("transparent");
});
it("should make all descendants transparent", function() {
var children = getBuilt("\\phantom{x+\\blue{1}}")[0].children;
var children = getBuilt("\\phantom{x+\\blue{1}}");
expect(children[0].style.color).toBe("transparent");
expect(children[1].style.color).toBe("transparent");
expect(children[2].children[0].style.color).toBe("transparent");
expect(children[2].style.color).toBe("transparent");
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -27,6 +27,7 @@ ArrayType: 1\begin{array}{c}2\\3\end{array}4
Baseline: a+b-c\cdot d/e
BasicTest: a
BinomTest: \dbinom{a}{b}\tbinom{a}{b}^{\binom{a}{b}+17}
BoldSpacing: \mathbf{A}^2+\mathbf{B}_3*\mathscr{C}'
Cases: |
f(a,b)=\begin{cases}
a+1&\text{if }b\text{ is odd} \\
@@ -36,6 +37,7 @@ Cases: |
Colors:
tex: \blue{a}\color{#0f0}{b}\color{red}{c}
nolatex: different syntax and different scope
ColorSpacing: \color{red}{\displaystyle \int x} + 1
DashesAndQuotes: \text{``a'' b---c -- d----`e'-{-}-f}--``x''
DeepFontSizing:
tex: |
@@ -82,6 +84,7 @@ MathRm: \mathrm{Ax2k\breve{a}\omega\Omega\imath+\KaTeX}
MathSf: \mathsf{Ax2k\breve{a}\omega\Omega\imath+\KaTeX}
MathScr: \mathscr{Ax2k\breve{a}\omega\Omega\imath+\KaTeX}
MathTt: \mathtt{Ax2k\breve{a}\omega\Omega\imath+\KaTeX}
NegativeSpaceBetweenRel: A =\!= B
NestedFractions: |
\dfrac{\frac{a}{b}}{\frac{c}{d}}\dfrac{\dfrac{a}{b}}
{\dfrac{c}{d}}\frac{\frac{a}{b}}{\frac{c}{d}}
@@ -106,6 +109,7 @@ Sqrt: |
^{\sqrt{\sqrt{\sqrt{x}}}}}
SqrtRoot: |
1+\sqrt[3]{2}+\sqrt[1923^234]{2^{2^{2^{2^{2^{2^{2^{2^{2^{2^{2^2}}}}}}}}}}}
StyleSwitching: a\cdot b\scriptstyle a\cdot ba\textstyle\cdot ba\scriptstyle\cdot b
SupSubCharacterBox: a_2f_2{f}_2{aa}_2{af}_2\mathbf{y}_Ay_A
SupSubHorizSpacing: |
x^{x^{x}}\Big|x_{x_{x_{x_{x}}}}\bigg|x^{x^{x_{x_{x_{x_{x}}}}}}\bigg|