Fix interaction between styles and sizes. (#719)
* Fix interaction between styles and sizes by implementing styles as sizes. Rather than having both `textstyle` CSS classes and `size5` CSS classes affect the font size (and step on each other), implement sizes more the way TeX does: a command like `\displaystyle` changes the current size. This is actually a simplification, since now only `size` affects the size. Simplifies CSS and computation. Many screenshotter tests change; they change to be more like TeX. For instance, `\sqrt` fixes some discrepancies in size treatment. Also: Remove the `Options.withX()` methods in favor of `.havingX()`, which might return the same `options`. Remove `Style.cls()` and `Style.reset()`. Remove `Options.reset()`. You should never modify an `Options`; they should change only by the `havingX()` methods. * Implement TeX sizing for scriptsize/scriptscriptsize. At every size level. Also make the sizes match TeX to the last decimal. * Review comments.
144
src/Options.js
@@ -5,33 +5,43 @@
|
|||||||
* `.reset` functions.
|
* `.reset` functions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const BASESIZE = 6;
|
||||||
|
|
||||||
|
const sizeStyleMap = [
|
||||||
|
// Each element contains [textsize, scriptsize, scriptscriptsize].
|
||||||
|
// The size mappings are taken from TeX with \normalsize=10pt.
|
||||||
|
[1, 1, 1], // size1: [5, 5, 5] \tiny
|
||||||
|
[2, 1, 1], // size2: [6, 5, 5]
|
||||||
|
[3, 1, 1], // size3: [7, 5, 5] \scriptsize
|
||||||
|
[4, 2, 1], // size4: [8, 6, 5] \footnotesize
|
||||||
|
[5, 2, 1], // size5: [9, 6, 5] \small
|
||||||
|
[6, 3, 1], // size6: [10, 7, 5] \normalsize
|
||||||
|
[7, 4, 2], // size7: [12, 8, 6] \large
|
||||||
|
[8, 6, 3], // size8: [14.4, 10, 7] \Large
|
||||||
|
[9, 7, 6], // size9: [17.28, 12, 10] \LARGE
|
||||||
|
[10, 8, 7], // size10: [20.74, 14.4, 12] \huge
|
||||||
|
[11, 10, 9], // size11: [24.88, 20.74, 17.28] \HUGE
|
||||||
|
];
|
||||||
|
|
||||||
|
const sizeMultipliers = [
|
||||||
|
0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.44, 1.728, 2.074, 2.488,
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the main options class. It contains the style, size, color, and font
|
* This is the main options class. It contains the current style, size, color,
|
||||||
* of the current parse level. It also contains the style and size of the parent
|
* and font.
|
||||||
* parse level, so size changes can be handled efficiently.
|
|
||||||
*
|
*
|
||||||
* Each of the `.with*` and `.reset` functions passes its current style and size
|
* Options objects should not be modified. To create a new Options with
|
||||||
* as the parentStyle and parentSize of the new options class, so parent
|
* different properties, call a `.having*` method.
|
||||||
* handling is taken care of automatically.
|
|
||||||
*/
|
*/
|
||||||
function Options(data) {
|
function Options(data) {
|
||||||
this.style = data.style;
|
this.style = data.style;
|
||||||
this.color = data.color;
|
this.color = data.color;
|
||||||
this.size = data.size;
|
this.size = data.size || BASESIZE;
|
||||||
|
this.textSize = data.textSize || this.size;
|
||||||
this.phantom = data.phantom;
|
this.phantom = data.phantom;
|
||||||
this.font = data.font;
|
this.font = data.font;
|
||||||
|
this.sizeMultiplier = sizeMultipliers[this.size - 1];
|
||||||
if (data.parentStyle === undefined) {
|
|
||||||
this.parentStyle = data.style;
|
|
||||||
} else {
|
|
||||||
this.parentStyle = data.parentStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.parentSize === undefined) {
|
|
||||||
this.parentSize = data.size;
|
|
||||||
} else {
|
|
||||||
this.parentSize = data.parentSize;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,9 +52,8 @@ Options.prototype.extend = function(extension) {
|
|||||||
const data = {
|
const data = {
|
||||||
style: this.style,
|
style: this.style,
|
||||||
size: this.size,
|
size: this.size,
|
||||||
|
textSize: this.textSize,
|
||||||
color: this.color,
|
color: this.color,
|
||||||
parentStyle: this.style,
|
|
||||||
parentSize: this.size,
|
|
||||||
phantom: this.phantom,
|
phantom: this.phantom,
|
||||||
font: this.font,
|
font: this.font,
|
||||||
};
|
};
|
||||||
@@ -58,22 +67,66 @@ Options.prototype.extend = function(extension) {
|
|||||||
return new Options(data);
|
return new Options(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function sizeAtStyle(size, style) {
|
||||||
|
return style.size < 2 ? size : sizeStyleMap[size - 1][style.size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new options object with the given style.
|
* Return an options object with the given style. If `this.style === style`,
|
||||||
|
* returns `this`.
|
||||||
*/
|
*/
|
||||||
Options.prototype.withStyle = function(style) {
|
Options.prototype.havingStyle = function(style) {
|
||||||
return this.extend({
|
if (this.style === style) {
|
||||||
style: style,
|
return this;
|
||||||
});
|
} else {
|
||||||
|
return this.extend({
|
||||||
|
style: style,
|
||||||
|
size: sizeAtStyle(this.textSize, style),
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new options object with the given size.
|
* Return an options object with a cramped version of the current style. If
|
||||||
|
* the current style is cramped, returns `this`.
|
||||||
*/
|
*/
|
||||||
Options.prototype.withSize = function(size) {
|
Options.prototype.havingCrampedStyle = function() {
|
||||||
return this.extend({
|
return this.havingStyle(this.style.cramp());
|
||||||
size: size,
|
};
|
||||||
});
|
|
||||||
|
/**
|
||||||
|
* Return an options object with the given size and in at least `\textstyle`.
|
||||||
|
* Returns `this` if appropriate.
|
||||||
|
*/
|
||||||
|
Options.prototype.havingSize = function(size) {
|
||||||
|
if (this.size === size && this.textSize === size) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
return this.extend({
|
||||||
|
style: this.style.text(),
|
||||||
|
size: size,
|
||||||
|
textSize: size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like `this.havingSize(BASESIZE).havingStyle(style)`. If `style` is omitted,
|
||||||
|
* changes to at least `\textstyle`.
|
||||||
|
*/
|
||||||
|
Options.prototype.havingBaseStyle = function(style) {
|
||||||
|
style = style || this.style.text();
|
||||||
|
const wantSize = sizeAtStyle(BASESIZE, style);
|
||||||
|
if (this.size === wantSize && this.textSize === BASESIZE
|
||||||
|
&& this.style === style) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
return this.extend({
|
||||||
|
style: style,
|
||||||
|
size: wantSize,
|
||||||
|
baseSize: BASESIZE,
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -104,11 +157,27 @@ Options.prototype.withFont = function(font) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new options object with the same style, size, and color. This is
|
* Return the CSS sizing classes required to switch from enclosing options
|
||||||
* used so that parent style and size changes are handled correctly.
|
* `oldOptions` to `this`. Returns an array of classes.
|
||||||
*/
|
*/
|
||||||
Options.prototype.reset = function() {
|
Options.prototype.sizingClasses = function(oldOptions) {
|
||||||
return this.extend({});
|
if (oldOptions.size !== this.size) {
|
||||||
|
return ["sizing", "reset-size" + oldOptions.size, "size" + this.size];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the CSS sizing classes required to switch to the base size. Like
|
||||||
|
* `this.havingSize(BASESIZE).sizingClasses(this)`.
|
||||||
|
*/
|
||||||
|
Options.prototype.baseSizingClasses = function() {
|
||||||
|
if (this.size !== BASESIZE) {
|
||||||
|
return ["sizing", "reset-size" + this.size, "size" + BASESIZE];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -186,4 +255,9 @@ Options.prototype.getColor = function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base size index.
|
||||||
|
*/
|
||||||
|
Options.BASESIZE = BASESIZE;
|
||||||
|
|
||||||
module.exports = Options;
|
module.exports = Options;
|
||||||
|
@@ -390,8 +390,8 @@ Parser.prototype.parseAtom = function() {
|
|||||||
|
|
||||||
// A list of the size-changing functions, for use in parseImplicitGroup
|
// A list of the size-changing functions, for use in parseImplicitGroup
|
||||||
const sizeFuncs = [
|
const sizeFuncs = [
|
||||||
"\\tiny", "\\scriptsize", "\\footnotesize", "\\small", "\\normalsize",
|
"\\tiny", "\\sixptsize", "\\scriptsize", "\\footnotesize", "\\small",
|
||||||
"\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge",
|
"\\normalsize", "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge",
|
||||||
];
|
];
|
||||||
|
|
||||||
// A list of the style-changing functions, for use in parseImplicitGroup
|
// A list of the style-changing functions, for use in parseImplicitGroup
|
||||||
@@ -483,7 +483,7 @@ Parser.prototype.parseImplicitGroup = function() {
|
|||||||
const body = this.parseExpression(false);
|
const body = this.parseExpression(false);
|
||||||
return new ParseNode("sizing", {
|
return new ParseNode("sizing", {
|
||||||
// Figure out what size to use based on the list of functions above
|
// Figure out what size to use based on the list of functions above
|
||||||
size: "size" + (utils.indexOf(sizeFuncs, func) + 1),
|
size: utils.indexOf(sizeFuncs, func) + 1,
|
||||||
value: body,
|
value: body,
|
||||||
}, this.mode);
|
}, this.mode);
|
||||||
} else if (utils.contains(styleFuncs, func)) {
|
} else if (utils.contains(styleFuncs, func)) {
|
||||||
|
53
src/Style.js
@@ -22,15 +22,12 @@ for (let i = 0; i < 3; i++) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The main style class. Contains a unique id for the style, a size (which is
|
* The main style class. Contains a unique id for the style, a size (which is
|
||||||
* the same for cramped and uncramped version of a style), a cramped flag, and a
|
* the same for cramped and uncramped version of a style), and a cramped flag.
|
||||||
* size multiplier, which gives the size difference between a style and
|
|
||||||
* textstyle.
|
|
||||||
*/
|
*/
|
||||||
function Style(id, size, multiplier, cramped) {
|
function Style(id, size, cramped) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.cramped = cramped;
|
this.cramped = cramped;
|
||||||
this.sizeMultiplier = multiplier;
|
|
||||||
this.metrics = metrics[size > 0 ? size - 1 : 0];
|
this.metrics = metrics[size > 0 ? size - 1 : 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,17 +70,10 @@ Style.prototype.cramp = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTML class name, like "displaystyle cramped"
|
* Get a text or display version of this style.
|
||||||
*/
|
*/
|
||||||
Style.prototype.cls = function() {
|
Style.prototype.text = function() {
|
||||||
return sizeNames[this.size] + (this.cramped ? " cramped" : " uncramped");
|
return styles[text[this.id]];
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HTML Reset class name, like "reset-textstyle"
|
|
||||||
*/
|
|
||||||
Style.prototype.reset = function() {
|
|
||||||
return resetNames[this.size];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,32 +93,16 @@ const Sc = 5;
|
|||||||
const SS = 6;
|
const SS = 6;
|
||||||
const SSc = 7;
|
const SSc = 7;
|
||||||
|
|
||||||
// String names for the different sizes
|
|
||||||
const sizeNames = [
|
|
||||||
"displaystyle textstyle",
|
|
||||||
"textstyle",
|
|
||||||
"scriptstyle",
|
|
||||||
"scriptscriptstyle",
|
|
||||||
];
|
|
||||||
|
|
||||||
// Reset names for the different sizes
|
|
||||||
const resetNames = [
|
|
||||||
"reset-textstyle",
|
|
||||||
"reset-textstyle",
|
|
||||||
"reset-scriptstyle",
|
|
||||||
"reset-scriptscriptstyle",
|
|
||||||
];
|
|
||||||
|
|
||||||
// Instances of the different styles
|
// Instances of the different styles
|
||||||
const styles = [
|
const styles = [
|
||||||
new Style(D, 0, 1.0, false),
|
new Style(D, 0, false),
|
||||||
new Style(Dc, 0, 1.0, true),
|
new Style(Dc, 0, true),
|
||||||
new Style(T, 1, 1.0, false),
|
new Style(T, 1, false),
|
||||||
new Style(Tc, 1, 1.0, true),
|
new Style(Tc, 1, true),
|
||||||
new Style(S, 2, 0.7, false),
|
new Style(S, 2, false),
|
||||||
new Style(Sc, 2, 0.7, true),
|
new Style(Sc, 2, true),
|
||||||
new Style(SS, 3, 0.5, false),
|
new Style(SS, 3, false),
|
||||||
new Style(SSc, 3, 0.5, true),
|
new Style(SSc, 3, true),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Lookup tables for switching from one style to another
|
// Lookup tables for switching from one style to another
|
||||||
@@ -137,6 +111,7 @@ const sub = [Sc, Sc, Sc, Sc, SSc, SSc, SSc, SSc];
|
|||||||
const fracNum = [T, Tc, S, Sc, SS, SSc, SS, SSc];
|
const fracNum = [T, Tc, S, Sc, SS, SSc, SS, SSc];
|
||||||
const fracDen = [Tc, Tc, Sc, Sc, SSc, SSc, SSc, SSc];
|
const fracDen = [Tc, Tc, Sc, Sc, SSc, SSc, SSc, SSc];
|
||||||
const cramp = [Dc, Dc, Tc, Tc, Sc, Sc, SSc, SSc];
|
const cramp = [Dc, Dc, Tc, Tc, Sc, Sc, SSc, SSc];
|
||||||
|
const text = [D, Dc, T, Tc, T, Tc, T, Tc];
|
||||||
|
|
||||||
// We only export some of the styles. Also, we don't export the `Style` class so
|
// We only export some of the styles. Also, we don't export the `Style` class so
|
||||||
// no more styles can be generated.
|
// no more styles can be generated.
|
||||||
|
@@ -63,6 +63,7 @@ const makeSymbol = function(value, fontFamily, mode, options, classes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options) {
|
if (options) {
|
||||||
|
symbolNode.maxFontSize = options.sizeMultiplier;
|
||||||
if (options.style.isTight()) {
|
if (options.style.isTight()) {
|
||||||
symbolNode.classes.push("mtight");
|
symbolNode.classes.push("mtight");
|
||||||
}
|
}
|
||||||
@@ -239,11 +240,10 @@ const makeFragment = function(children) {
|
|||||||
*/
|
*/
|
||||||
const makeFontSizer = function(options, fontSize) {
|
const makeFontSizer = function(options, fontSize) {
|
||||||
const fontSizeInner = makeSpan([], [new domTree.symbolNode("\u200b")]);
|
const fontSizeInner = makeSpan([], [new domTree.symbolNode("\u200b")]);
|
||||||
fontSizeInner.style.fontSize =
|
fontSizeInner.style.fontSize = fontSize + "em";
|
||||||
(fontSize / options.style.sizeMultiplier) + "em";
|
|
||||||
|
|
||||||
const fontSizer = makeSpan(
|
const fontSizer = makeSpan(
|
||||||
["fontsize-ensurer", "reset-" + options.size, "size5"],
|
["fontsize-ensurer"].concat(options.baseSizingClasses()),
|
||||||
[fontSizeInner]);
|
[fontSizeInner]);
|
||||||
|
|
||||||
return fontSizer;
|
return fontSizer;
|
||||||
@@ -378,20 +378,6 @@ const makeVList = function(children, positionType, positionData, options) {
|
|||||||
return vlist;
|
return vlist;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A table of size -> font size for the different sizing functions
|
|
||||||
const 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,
|
|
||||||
};
|
|
||||||
|
|
||||||
// A map of spacing functions to their attributes, like size and corresponding
|
// A map of spacing functions to their attributes, like size and corresponding
|
||||||
// CSS class
|
// CSS class
|
||||||
const spacingFunctions = {
|
const spacingFunctions = {
|
||||||
@@ -486,6 +472,5 @@ module.exports = {
|
|||||||
makeVList: makeVList,
|
makeVList: makeVList,
|
||||||
makeOrd: makeOrd,
|
makeOrd: makeOrd,
|
||||||
prependChildren: prependChildren,
|
prependChildren: prependChildren,
|
||||||
sizingMultiplier: sizingMultiplier,
|
|
||||||
spacingFunctions: spacingFunctions,
|
spacingFunctions: spacingFunctions,
|
||||||
};
|
};
|
||||||
|
407
src/buildHTML.js
@@ -216,10 +216,8 @@ const isCharacterBox = function(group) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const makeNullDelimiter = function(options, classes) {
|
const makeNullDelimiter = function(options, classes) {
|
||||||
return makeSpan(classes.concat([
|
const moreClasses = ["nulldelimiter"].concat(options.baseSizingClasses());
|
||||||
"sizing", "reset-" + options.size, "size5",
|
return makeSpan(classes.concat(moreClasses));
|
||||||
options.style.reset(), Style.TEXT.cls(),
|
|
||||||
"nulldelimiter"]));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -267,9 +265,8 @@ groupTypes.punct = function(group, options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.ordgroup = function(group, options) {
|
groupTypes.ordgroup = function(group, options) {
|
||||||
return makeSpan(
|
return makeSpan(["mord"],
|
||||||
["mord", options.style.cls()],
|
buildExpression(group.value, options, true),
|
||||||
buildExpression(group.value, options.reset(), true),
|
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -283,7 +280,7 @@ groupTypes.text = function(group, options) {
|
|||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return makeSpan(["mord", "text", newOptions.style.cls()],
|
return makeSpan(["mord", "text"],
|
||||||
inner, newOptions);
|
inner, newOptions);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -311,43 +308,33 @@ groupTypes.supsub = function(group, options) {
|
|||||||
return groupTypes[group.value.base.type](group, options);
|
return groupTypes[group.value.base.type](group, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
const base = buildGroup(group.value.base, options.reset());
|
const base = buildGroup(group.value.base, options);
|
||||||
let supmid;
|
let supm;
|
||||||
let submid;
|
let subm;
|
||||||
let sup;
|
|
||||||
let sub;
|
|
||||||
|
|
||||||
const style = options.style;
|
const style = options.style;
|
||||||
let newOptions;
|
let newOptions;
|
||||||
|
|
||||||
|
// Rule 18a
|
||||||
|
let supShift = 0;
|
||||||
|
let subShift = 0;
|
||||||
|
|
||||||
if (group.value.sup) {
|
if (group.value.sup) {
|
||||||
newOptions = options.withStyle(style.sup());
|
newOptions = options.havingStyle(style.sup());
|
||||||
sup = buildGroup(group.value.sup, newOptions);
|
supm = buildGroup(group.value.sup, newOptions, options);
|
||||||
supmid = makeSpan([style.reset(), style.sup().cls()],
|
if (!isCharacterBox(group.value.base)) {
|
||||||
[sup], newOptions);
|
supShift = base.height - newOptions.style.metrics.supDrop
|
||||||
|
* newOptions.sizeMultiplier / options.sizeMultiplier;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group.value.sub) {
|
if (group.value.sub) {
|
||||||
newOptions = options.withStyle(style.sub());
|
newOptions = options.havingStyle(style.sub());
|
||||||
sub = buildGroup(group.value.sub, newOptions);
|
subm = buildGroup(group.value.sub, newOptions, options);
|
||||||
submid = makeSpan([style.reset(), style.sub().cls()],
|
if (!isCharacterBox(group.value.base)) {
|
||||||
[sub], newOptions);
|
subShift = base.depth + newOptions.style.metrics.subDrop
|
||||||
}
|
* newOptions.sizeMultiplier / options.sizeMultiplier;
|
||||||
|
}
|
||||||
// Rule 18a
|
|
||||||
let supShift;
|
|
||||||
let subShift;
|
|
||||||
if (isCharacterBox(group.value.base)) {
|
|
||||||
supShift = 0;
|
|
||||||
subShift = 0;
|
|
||||||
} else {
|
|
||||||
const supstyle = style.sup();
|
|
||||||
supShift = base.height - supstyle.metrics.supDrop
|
|
||||||
* supstyle.sizeMultiplier;
|
|
||||||
|
|
||||||
const substyle = style.sub();
|
|
||||||
subShift = base.depth + substyle.metrics.subDrop
|
|
||||||
* substyle.sizeMultiplier;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rule 18c
|
// Rule 18c
|
||||||
@@ -362,8 +349,7 @@ groupTypes.supsub = function(group, options) {
|
|||||||
|
|
||||||
// scriptspace is a font-size-independent size, so scale it
|
// scriptspace is a font-size-independent size, so scale it
|
||||||
// appropriately
|
// appropriately
|
||||||
const multiplier = Style.TEXT.sizeMultiplier *
|
const multiplier = options.sizeMultiplier;
|
||||||
style.sizeMultiplier;
|
|
||||||
const scriptspace =
|
const scriptspace =
|
||||||
(0.5 / fontMetrics.metrics.ptPerEm) / multiplier + "em";
|
(0.5 / fontMetrics.metrics.ptPerEm) / multiplier + "em";
|
||||||
|
|
||||||
@@ -372,10 +358,10 @@ groupTypes.supsub = function(group, options) {
|
|||||||
// Rule 18b
|
// Rule 18b
|
||||||
subShift = Math.max(
|
subShift = Math.max(
|
||||||
subShift, style.metrics.sub1,
|
subShift, style.metrics.sub1,
|
||||||
sub.height - 0.8 * style.metrics.xHeight);
|
subm.height - 0.8 * style.metrics.xHeight);
|
||||||
|
|
||||||
supsub = buildCommon.makeVList([
|
supsub = buildCommon.makeVList([
|
||||||
{type: "elem", elem: submid},
|
{type: "elem", elem: subm},
|
||||||
], "shift", subShift, options);
|
], "shift", subShift, options);
|
||||||
|
|
||||||
supsub.children[0].style.marginRight = scriptspace;
|
supsub.children[0].style.marginRight = scriptspace;
|
||||||
@@ -389,25 +375,25 @@ groupTypes.supsub = function(group, options) {
|
|||||||
} else if (!group.value.sub) {
|
} else if (!group.value.sub) {
|
||||||
// Rule 18c, d
|
// Rule 18c, d
|
||||||
supShift = Math.max(supShift, minSupShift,
|
supShift = Math.max(supShift, minSupShift,
|
||||||
sup.depth + 0.25 * style.metrics.xHeight);
|
supm.depth + 0.25 * style.metrics.xHeight);
|
||||||
|
|
||||||
supsub = buildCommon.makeVList([
|
supsub = buildCommon.makeVList([
|
||||||
{type: "elem", elem: supmid},
|
{type: "elem", elem: supm},
|
||||||
], "shift", -supShift, options);
|
], "shift", -supShift, options);
|
||||||
|
|
||||||
supsub.children[0].style.marginRight = scriptspace;
|
supsub.children[0].style.marginRight = scriptspace;
|
||||||
} else {
|
} else {
|
||||||
supShift = Math.max(
|
supShift = Math.max(
|
||||||
supShift, minSupShift, sup.depth + 0.25 * style.metrics.xHeight);
|
supShift, minSupShift, supm.depth + 0.25 * style.metrics.xHeight);
|
||||||
subShift = Math.max(subShift, style.metrics.sub2);
|
subShift = Math.max(subShift, style.metrics.sub2);
|
||||||
|
|
||||||
const ruleWidth = fontMetrics.metrics.defaultRuleThickness;
|
const ruleWidth = fontMetrics.metrics.defaultRuleThickness;
|
||||||
|
|
||||||
// Rule 18e
|
// Rule 18e
|
||||||
if ((supShift - sup.depth) - (sub.height - subShift) <
|
if ((supShift - supm.depth) - (subm.height - subShift) <
|
||||||
4 * ruleWidth) {
|
4 * ruleWidth) {
|
||||||
subShift = 4 * ruleWidth - (supShift - sup.depth) + sub.height;
|
subShift = 4 * ruleWidth - (supShift - supm.depth) + subm.height;
|
||||||
const psi = 0.8 * style.metrics.xHeight - (supShift - sup.depth);
|
const psi = 0.8 * style.metrics.xHeight - (supShift - supm.depth);
|
||||||
if (psi > 0) {
|
if (psi > 0) {
|
||||||
supShift += psi;
|
supShift += psi;
|
||||||
subShift -= psi;
|
subShift -= psi;
|
||||||
@@ -415,8 +401,8 @@ groupTypes.supsub = function(group, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
supsub = buildCommon.makeVList([
|
supsub = buildCommon.makeVList([
|
||||||
{type: "elem", elem: submid, shift: subShift},
|
{type: "elem", elem: subm, shift: subShift},
|
||||||
{type: "elem", elem: supmid, shift: -supShift},
|
{type: "elem", elem: supm, shift: -supShift},
|
||||||
], "individualShift", null, options);
|
], "individualShift", null, options);
|
||||||
|
|
||||||
// See comment above about subscripts not being shifted
|
// See comment above about subscripts not being shifted
|
||||||
@@ -450,23 +436,19 @@ groupTypes.genfrac = function(group, options) {
|
|||||||
const dstyle = style.fracDen();
|
const dstyle = style.fracDen();
|
||||||
let newOptions;
|
let newOptions;
|
||||||
|
|
||||||
newOptions = options.withStyle(nstyle);
|
newOptions = options.havingStyle(nstyle);
|
||||||
const numer = buildGroup(group.value.numer, newOptions);
|
const numerm = buildGroup(group.value.numer, newOptions, options);
|
||||||
const numerreset = makeSpan([style.reset(), nstyle.cls()],
|
|
||||||
[numer], newOptions);
|
|
||||||
|
|
||||||
newOptions = options.withStyle(dstyle);
|
newOptions = options.havingStyle(dstyle);
|
||||||
const denom = buildGroup(group.value.denom, newOptions);
|
const denomm = buildGroup(group.value.denom, newOptions, options);
|
||||||
const denomreset = makeSpan([style.reset(), dstyle.cls()],
|
|
||||||
[denom], newOptions);
|
|
||||||
|
|
||||||
let ruleWidth;
|
let rule;
|
||||||
if (group.value.hasBarLine) {
|
if (group.value.hasBarLine) {
|
||||||
ruleWidth = fontMetrics.metrics.defaultRuleThickness /
|
rule = makeLineSpan("frac-line", options);
|
||||||
options.style.sizeMultiplier;
|
|
||||||
} else {
|
} else {
|
||||||
ruleWidth = 0;
|
rule = null;
|
||||||
}
|
}
|
||||||
|
const ruleWidth = rule ? rule.height : 0;
|
||||||
|
|
||||||
// Rule 15b
|
// Rule 15b
|
||||||
let numShift;
|
let numShift;
|
||||||
@@ -495,53 +477,48 @@ groupTypes.genfrac = function(group, options) {
|
|||||||
if (ruleWidth === 0) {
|
if (ruleWidth === 0) {
|
||||||
// Rule 15c
|
// Rule 15c
|
||||||
const candidateClearance =
|
const candidateClearance =
|
||||||
(numShift - numer.depth) - (denom.height - denomShift);
|
(numShift - numerm.depth) - (denomm.height - denomShift);
|
||||||
if (candidateClearance < clearance) {
|
if (candidateClearance < clearance) {
|
||||||
numShift += 0.5 * (clearance - candidateClearance);
|
numShift += 0.5 * (clearance - candidateClearance);
|
||||||
denomShift += 0.5 * (clearance - candidateClearance);
|
denomShift += 0.5 * (clearance - candidateClearance);
|
||||||
}
|
}
|
||||||
|
|
||||||
frac = buildCommon.makeVList([
|
frac = buildCommon.makeVList([
|
||||||
{type: "elem", elem: denomreset, shift: denomShift},
|
{type: "elem", elem: denomm, shift: denomShift},
|
||||||
{type: "elem", elem: numerreset, shift: -numShift},
|
{type: "elem", elem: numerm, shift: -numShift},
|
||||||
], "individualShift", null, options);
|
], "individualShift", null, options);
|
||||||
} else {
|
} else {
|
||||||
// Rule 15d
|
// Rule 15d
|
||||||
const axisHeight = style.metrics.axisHeight;
|
const axisHeight = style.metrics.axisHeight;
|
||||||
|
|
||||||
if ((numShift - numer.depth) - (axisHeight + 0.5 * ruleWidth) <
|
if ((numShift - numerm.depth) - (axisHeight + 0.5 * ruleWidth) <
|
||||||
clearance) {
|
clearance) {
|
||||||
numShift +=
|
numShift +=
|
||||||
clearance - ((numShift - numer.depth) -
|
clearance - ((numShift - numerm.depth) -
|
||||||
(axisHeight + 0.5 * ruleWidth));
|
(axisHeight + 0.5 * ruleWidth));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((axisHeight - 0.5 * ruleWidth) - (denom.height - denomShift) <
|
if ((axisHeight - 0.5 * ruleWidth) - (denomm.height - denomShift) <
|
||||||
clearance) {
|
clearance) {
|
||||||
denomShift +=
|
denomShift +=
|
||||||
clearance - ((axisHeight - 0.5 * ruleWidth) -
|
clearance - ((axisHeight - 0.5 * ruleWidth) -
|
||||||
(denom.height - denomShift));
|
(denomm.height - denomShift));
|
||||||
}
|
}
|
||||||
|
|
||||||
const mid = makeSpan(
|
|
||||||
[options.style.reset(), Style.TEXT.cls(), "frac-line"]);
|
|
||||||
// Manually set the height of the line because its height is
|
|
||||||
// created in CSS
|
|
||||||
mid.height = ruleWidth;
|
|
||||||
|
|
||||||
const midShift = -(axisHeight - 0.5 * ruleWidth);
|
const midShift = -(axisHeight - 0.5 * ruleWidth);
|
||||||
|
|
||||||
frac = buildCommon.makeVList([
|
frac = buildCommon.makeVList([
|
||||||
{type: "elem", elem: denomreset, shift: denomShift},
|
{type: "elem", elem: denomm, shift: denomShift},
|
||||||
{type: "elem", elem: mid, shift: midShift},
|
{type: "elem", elem: rule, shift: midShift},
|
||||||
{type: "elem", elem: numerreset, shift: -numShift},
|
{type: "elem", elem: numerm, shift: -numShift},
|
||||||
], "individualShift", null, options);
|
], "individualShift", null, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since we manually change the style sometimes (with \dfrac or \tfrac),
|
// Since we manually change the style sometimes (with \dfrac or \tfrac),
|
||||||
// account for the possible size change here.
|
// account for the possible size change here.
|
||||||
frac.height *= style.sizeMultiplier / options.style.sizeMultiplier;
|
newOptions = options.havingStyle(style);
|
||||||
frac.depth *= style.sizeMultiplier / options.style.sizeMultiplier;
|
frac.height *= newOptions.sizeMultiplier / options.sizeMultiplier;
|
||||||
|
frac.depth *= newOptions.sizeMultiplier / options.sizeMultiplier;
|
||||||
|
|
||||||
// Rule 15e
|
// Rule 15e
|
||||||
let delimSize;
|
let delimSize;
|
||||||
@@ -558,18 +535,18 @@ groupTypes.genfrac = function(group, options) {
|
|||||||
} else {
|
} else {
|
||||||
leftDelim = delimiter.customSizedDelim(
|
leftDelim = delimiter.customSizedDelim(
|
||||||
group.value.leftDelim, delimSize, true,
|
group.value.leftDelim, delimSize, true,
|
||||||
options.withStyle(style), group.mode, ["mopen"]);
|
options.havingStyle(style), group.mode, ["mopen"]);
|
||||||
}
|
}
|
||||||
if (group.value.rightDelim == null) {
|
if (group.value.rightDelim == null) {
|
||||||
rightDelim = makeNullDelimiter(options, ["mclose"]);
|
rightDelim = makeNullDelimiter(options, ["mclose"]);
|
||||||
} else {
|
} else {
|
||||||
rightDelim = delimiter.customSizedDelim(
|
rightDelim = delimiter.customSizedDelim(
|
||||||
group.value.rightDelim, delimSize, true,
|
group.value.rightDelim, delimSize, true,
|
||||||
options.withStyle(style), group.mode, ["mclose"]);
|
options.havingStyle(style), group.mode, ["mclose"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["mord", options.style.reset(), style.cls()],
|
["mord"].concat(newOptions.sizingClasses(options)),
|
||||||
[leftDelim, makeSpan(["mfrac"], [frac]), rightDelim],
|
[leftDelim, makeSpan(["mfrac"], [frac]), rightDelim],
|
||||||
options);
|
options);
|
||||||
};
|
};
|
||||||
@@ -770,18 +747,18 @@ groupTypes.spacing = function(group, options) {
|
|||||||
|
|
||||||
groupTypes.llap = function(group, options) {
|
groupTypes.llap = function(group, options) {
|
||||||
const inner = makeSpan(
|
const inner = makeSpan(
|
||||||
["inner"], [buildGroup(group.value.body, options.reset())]);
|
["inner"], [buildGroup(group.value.body, options)]);
|
||||||
const fix = makeSpan(["fix"], []);
|
const fix = makeSpan(["fix"], []);
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["mord", "llap", options.style.cls()], [inner, fix], options);
|
["mord", "llap"], [inner, fix], options);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.rlap = function(group, options) {
|
groupTypes.rlap = function(group, options) {
|
||||||
const inner = makeSpan(
|
const inner = makeSpan(
|
||||||
["inner"], [buildGroup(group.value.body, options.reset())]);
|
["inner"], [buildGroup(group.value.body, options)]);
|
||||||
const fix = makeSpan(["fix"], []);
|
const fix = makeSpan(["fix"], []);
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["mord", "rlap", options.style.cls()], [inner, fix], options);
|
["mord", "rlap"], [inner, fix], options);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.op = function(group, options) {
|
groupTypes.op = function(group, options) {
|
||||||
@@ -831,7 +808,7 @@ groupTypes.op = function(group, options) {
|
|||||||
// don't actually apply this here, but instead it is used either in
|
// don't actually apply this here, but instead it is used either in
|
||||||
// the vlist creation or separately when there are no limits.
|
// the vlist creation or separately when there are no limits.
|
||||||
baseShift = (base.height - base.depth) / 2 -
|
baseShift = (base.height - base.depth) / 2 -
|
||||||
style.metrics.axisHeight * style.sizeMultiplier;
|
style.metrics.axisHeight * options.sizeMultiplier;
|
||||||
|
|
||||||
// The slant of the symbol is just its italic correction.
|
// The slant of the symbol is just its italic correction.
|
||||||
slant = base.italic;
|
slant = base.italic;
|
||||||
@@ -857,33 +834,29 @@ groupTypes.op = function(group, options) {
|
|||||||
// in a new span so it is an inline, and works.
|
// in a new span so it is an inline, and works.
|
||||||
base = makeSpan([], [base]);
|
base = makeSpan([], [base]);
|
||||||
|
|
||||||
let supmid;
|
let supm;
|
||||||
let supKern;
|
let supKern;
|
||||||
let submid;
|
let subm;
|
||||||
let subKern;
|
let subKern;
|
||||||
let newOptions;
|
let newOptions;
|
||||||
// We manually have to handle the superscripts and subscripts. This,
|
// We manually have to handle the superscripts and subscripts. This,
|
||||||
// aside from the kern calculations, is copied from supsub.
|
// aside from the kern calculations, is copied from supsub.
|
||||||
if (supGroup) {
|
if (supGroup) {
|
||||||
newOptions = options.withStyle(style.sup());
|
newOptions = options.havingStyle(style.sup());
|
||||||
const sup = buildGroup(supGroup, newOptions);
|
supm = buildGroup(supGroup, newOptions, options);
|
||||||
supmid = makeSpan([style.reset(), style.sup().cls()],
|
|
||||||
[sup], newOptions);
|
|
||||||
|
|
||||||
supKern = Math.max(
|
supKern = Math.max(
|
||||||
fontMetrics.metrics.bigOpSpacing1,
|
fontMetrics.metrics.bigOpSpacing1,
|
||||||
fontMetrics.metrics.bigOpSpacing3 - sup.depth);
|
fontMetrics.metrics.bigOpSpacing3 - supm.depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subGroup) {
|
if (subGroup) {
|
||||||
newOptions = options.withStyle(style.sub());
|
newOptions = options.havingStyle(style.sub());
|
||||||
const sub = buildGroup(subGroup, newOptions);
|
subm = buildGroup(subGroup, newOptions, options);
|
||||||
submid = makeSpan([style.reset(), style.sub().cls()],
|
|
||||||
[sub], newOptions);
|
|
||||||
|
|
||||||
subKern = Math.max(
|
subKern = Math.max(
|
||||||
fontMetrics.metrics.bigOpSpacing2,
|
fontMetrics.metrics.bigOpSpacing2,
|
||||||
fontMetrics.metrics.bigOpSpacing4 - sub.height);
|
fontMetrics.metrics.bigOpSpacing4 - subm.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the final group as a vlist of the possible subscript, base,
|
// Build the final group as a vlist of the possible subscript, base,
|
||||||
@@ -896,7 +869,7 @@ groupTypes.op = function(group, options) {
|
|||||||
|
|
||||||
finalGroup = buildCommon.makeVList([
|
finalGroup = buildCommon.makeVList([
|
||||||
{type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
|
{type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
|
||||||
{type: "elem", elem: submid},
|
{type: "elem", elem: subm},
|
||||||
{type: "kern", size: subKern},
|
{type: "kern", size: subKern},
|
||||||
{type: "elem", elem: base},
|
{type: "elem", elem: base},
|
||||||
], "top", top, options);
|
], "top", top, options);
|
||||||
@@ -912,7 +885,7 @@ groupTypes.op = function(group, options) {
|
|||||||
finalGroup = buildCommon.makeVList([
|
finalGroup = buildCommon.makeVList([
|
||||||
{type: "elem", elem: base},
|
{type: "elem", elem: base},
|
||||||
{type: "kern", size: supKern},
|
{type: "kern", size: supKern},
|
||||||
{type: "elem", elem: supmid},
|
{type: "elem", elem: supm},
|
||||||
{type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
|
{type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
|
||||||
], "bottom", bottom, options);
|
], "bottom", bottom, options);
|
||||||
|
|
||||||
@@ -925,17 +898,17 @@ groupTypes.op = function(group, options) {
|
|||||||
return base;
|
return base;
|
||||||
} else {
|
} else {
|
||||||
bottom = fontMetrics.metrics.bigOpSpacing5 +
|
bottom = fontMetrics.metrics.bigOpSpacing5 +
|
||||||
submid.height + submid.depth +
|
subm.height + subm.depth +
|
||||||
subKern +
|
subKern +
|
||||||
base.depth + baseShift;
|
base.depth + baseShift;
|
||||||
|
|
||||||
finalGroup = buildCommon.makeVList([
|
finalGroup = buildCommon.makeVList([
|
||||||
{type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
|
{type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
|
||||||
{type: "elem", elem: submid},
|
{type: "elem", elem: subm},
|
||||||
{type: "kern", size: subKern},
|
{type: "kern", size: subKern},
|
||||||
{type: "elem", elem: base},
|
{type: "elem", elem: base},
|
||||||
{type: "kern", size: supKern},
|
{type: "kern", size: supKern},
|
||||||
{type: "elem", elem: supmid},
|
{type: "elem", elem: supm},
|
||||||
{type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
|
{type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
|
||||||
], "bottom", bottom, options);
|
], "bottom", bottom, options);
|
||||||
|
|
||||||
@@ -1034,29 +1007,33 @@ groupTypes.katex = function(group, options) {
|
|||||||
["mord", "katex-logo"], [k, a, t, e, x], options);
|
["mord", "katex-logo"], [k, a, t, e, x], options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const makeLineSpan = function(className, options) {
|
||||||
|
const baseOptions = options.havingBaseStyle();
|
||||||
|
const line = makeSpan(
|
||||||
|
[className].concat(baseOptions.sizingClasses(options)),
|
||||||
|
[], options);
|
||||||
|
line.height = fontMetrics.metrics.defaultRuleThickness /
|
||||||
|
options.sizeMultiplier;
|
||||||
|
line.maxFontSize = 1.0;
|
||||||
|
return line;
|
||||||
|
};
|
||||||
|
|
||||||
groupTypes.overline = function(group, options) {
|
groupTypes.overline = function(group, options) {
|
||||||
// Overlines are handled in the TeXbook pg 443, Rule 9.
|
// Overlines are handled in the TeXbook pg 443, Rule 9.
|
||||||
const style = options.style;
|
|
||||||
|
|
||||||
// Build the inner group in the cramped style.
|
// Build the inner group in the cramped style.
|
||||||
const innerGroup = buildGroup(group.value.body,
|
const innerGroup = buildGroup(group.value.body,
|
||||||
options.withStyle(style.cramp()));
|
options.havingCrampedStyle());
|
||||||
|
|
||||||
const ruleWidth = fontMetrics.metrics.defaultRuleThickness /
|
|
||||||
style.sizeMultiplier;
|
|
||||||
|
|
||||||
// Create the line above the body
|
// Create the line above the body
|
||||||
const line = makeSpan(
|
const line = makeLineSpan("overline-line", options);
|
||||||
[style.reset(), Style.TEXT.cls(), "overline-line"]);
|
|
||||||
line.height = ruleWidth;
|
|
||||||
line.maxFontSize = 1.0;
|
|
||||||
|
|
||||||
// Generate the vlist, with the appropriate kerns
|
// Generate the vlist, with the appropriate kerns
|
||||||
const vlist = buildCommon.makeVList([
|
const vlist = buildCommon.makeVList([
|
||||||
{type: "elem", elem: innerGroup},
|
{type: "elem", elem: innerGroup},
|
||||||
{type: "kern", size: 3 * ruleWidth},
|
{type: "kern", size: 3 * line.height},
|
||||||
{type: "elem", elem: line},
|
{type: "elem", elem: line},
|
||||||
{type: "kern", size: ruleWidth},
|
{type: "kern", size: line.height},
|
||||||
], "firstBaseline", null, options);
|
], "firstBaseline", null, options);
|
||||||
|
|
||||||
return makeSpan(["mord", "overline"], [vlist], options);
|
return makeSpan(["mord", "overline"], [vlist], options);
|
||||||
@@ -1064,24 +1041,17 @@ groupTypes.overline = function(group, options) {
|
|||||||
|
|
||||||
groupTypes.underline = function(group, options) {
|
groupTypes.underline = function(group, options) {
|
||||||
// Underlines are handled in the TeXbook pg 443, Rule 10.
|
// Underlines are handled in the TeXbook pg 443, Rule 10.
|
||||||
const style = options.style;
|
|
||||||
|
|
||||||
// Build the inner group.
|
// Build the inner group.
|
||||||
const innerGroup = buildGroup(group.value.body, options);
|
const innerGroup = buildGroup(group.value.body, options);
|
||||||
|
|
||||||
const ruleWidth = fontMetrics.metrics.defaultRuleThickness /
|
|
||||||
style.sizeMultiplier;
|
|
||||||
|
|
||||||
// Create the line above the body
|
// Create the line above the body
|
||||||
const line = makeSpan([style.reset(), Style.TEXT.cls(), "underline-line"]);
|
const line = makeLineSpan("underline-line", options);
|
||||||
line.height = ruleWidth;
|
|
||||||
line.maxFontSize = 1.0;
|
|
||||||
|
|
||||||
// Generate the vlist, with the appropriate kerns
|
// Generate the vlist, with the appropriate kerns
|
||||||
const vlist = buildCommon.makeVList([
|
const vlist = buildCommon.makeVList([
|
||||||
{type: "kern", size: ruleWidth},
|
{type: "kern", size: line.height},
|
||||||
{type: "elem", elem: line},
|
{type: "elem", elem: line},
|
||||||
{type: "kern", size: 3 * ruleWidth},
|
{type: "kern", size: 3 * line.height},
|
||||||
{type: "elem", elem: innerGroup},
|
{type: "elem", elem: innerGroup},
|
||||||
], "top", innerGroup.height, options);
|
], "top", innerGroup.height, options);
|
||||||
|
|
||||||
@@ -1090,32 +1060,24 @@ groupTypes.underline = function(group, options) {
|
|||||||
|
|
||||||
groupTypes.sqrt = function(group, options) {
|
groupTypes.sqrt = function(group, options) {
|
||||||
// Square roots are handled in the TeXbook pg. 443, Rule 11.
|
// Square roots are handled in the TeXbook pg. 443, Rule 11.
|
||||||
const style = options.style;
|
|
||||||
|
|
||||||
// First, we do the same steps as in overline to build the inner group
|
// First, we do the same steps as in overline to build the inner group
|
||||||
// and line
|
// and line
|
||||||
const inner = buildGroup(
|
const inner = buildGroup(group.value.body, options.havingCrampedStyle());
|
||||||
group.value.body, options.withStyle(style.cramp()));
|
|
||||||
|
|
||||||
const ruleWidth = fontMetrics.metrics.defaultRuleThickness /
|
const line = makeLineSpan("sqrt-line", options);
|
||||||
style.sizeMultiplier;
|
const ruleWidth = line.height;
|
||||||
|
|
||||||
const line = makeSpan(
|
|
||||||
[style.reset(), Style.TEXT.cls(), "sqrt-line"], [],
|
|
||||||
options);
|
|
||||||
line.height = ruleWidth;
|
|
||||||
line.maxFontSize = 1.0;
|
|
||||||
|
|
||||||
let phi = ruleWidth;
|
let phi = ruleWidth;
|
||||||
if (style.id < Style.TEXT.id) {
|
if (options.style.id < Style.TEXT.id) {
|
||||||
phi = style.metrics.xHeight;
|
phi = options.style.metrics.xHeight * options.sizeMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the clearance between the body and line
|
// Calculate the clearance between the body and line
|
||||||
let lineClearance = ruleWidth + phi / 4;
|
let lineClearance = ruleWidth + phi / 4;
|
||||||
|
|
||||||
const innerHeight = (inner.height + inner.depth) * style.sizeMultiplier;
|
const minDelimiterHeight = (inner.height + inner.depth +
|
||||||
const minDelimiterHeight = innerHeight + lineClearance + ruleWidth;
|
lineClearance + ruleWidth) * options.sizeMultiplier;
|
||||||
|
|
||||||
// Create a \surd delimiter of the required minimum size
|
// Create a \surd delimiter of the required minimum size
|
||||||
const delim = makeSpan(["sqrt-sign"], [
|
const delim = makeSpan(["sqrt-sign"], [
|
||||||
@@ -1161,12 +1123,8 @@ groupTypes.sqrt = function(group, options) {
|
|||||||
// Handle the optional root index
|
// Handle the optional root index
|
||||||
|
|
||||||
// The index is always in scriptscript style
|
// The index is always in scriptscript style
|
||||||
const newOptions = options.withStyle(Style.SCRIPTSCRIPT);
|
const newOptions = options.havingStyle(Style.SCRIPTSCRIPT);
|
||||||
const root = buildGroup(group.value.index, newOptions);
|
const rootm = buildGroup(group.value.index, newOptions, options);
|
||||||
const rootWrap = makeSpan(
|
|
||||||
[style.reset(), Style.SCRIPTSCRIPT.cls()],
|
|
||||||
[root],
|
|
||||||
newOptions);
|
|
||||||
|
|
||||||
// Figure out the height and depth of the inner part
|
// Figure out the height and depth of the inner part
|
||||||
const innerRootHeight = Math.max(delim.height, body.height);
|
const innerRootHeight = Math.max(delim.height, body.height);
|
||||||
@@ -1178,7 +1136,7 @@ groupTypes.sqrt = function(group, options) {
|
|||||||
|
|
||||||
// Build a VList with the superscript shifted up correctly
|
// Build a VList with the superscript shifted up correctly
|
||||||
const rootVList = buildCommon.makeVList(
|
const rootVList = buildCommon.makeVList(
|
||||||
[{type: "elem", elem: rootWrap}],
|
[{type: "elem", elem: rootm}],
|
||||||
"shift", -toShift, options);
|
"shift", -toShift, options);
|
||||||
// Add a class surrounding it so we can add on the appropriate
|
// Add a class surrounding it so we can add on the appropriate
|
||||||
// kerning
|
// kerning
|
||||||
@@ -1189,35 +1147,37 @@ groupTypes.sqrt = function(group, options) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.sizing = function(group, options) {
|
function sizingGroup(value, options, baseOptions) {
|
||||||
// Handle sizing operators like \Huge. Real TeX doesn't actually allow
|
const inner = buildExpression(value, options, false);
|
||||||
// these functions inside of math expressions, so we do some special
|
const multiplier = options.sizeMultiplier / baseOptions.sizeMultiplier;
|
||||||
// handling.
|
|
||||||
const inner = buildExpression(group.value.value,
|
|
||||||
options.withSize(group.value.size), false);
|
|
||||||
|
|
||||||
// Compute the correct maxFontSize.
|
|
||||||
const style = options.style;
|
|
||||||
const fontSize = buildCommon.sizingMultiplier[group.value.size] *
|
|
||||||
style.sizeMultiplier;
|
|
||||||
|
|
||||||
// Add size-resetting classes to the inner list and set maxFontSize
|
// Add size-resetting classes to the inner list and set maxFontSize
|
||||||
// manually. Handle nested size changes.
|
// manually. Handle nested size changes.
|
||||||
for (let i = 0; i < inner.length; i++) {
|
for (let i = 0; i < inner.length; i++) {
|
||||||
const pos = utils.indexOf(inner[i].classes, "sizing");
|
const pos = utils.indexOf(inner[i].classes, "sizing");
|
||||||
if (pos < 0) {
|
if (pos < 0) {
|
||||||
inner[i].classes.push("sizing", "reset-" + options.size,
|
Array.prototype.push.apply(inner[i].classes,
|
||||||
group.value.size, style.cls());
|
options.sizingClasses(baseOptions));
|
||||||
inner[i].maxFontSize = fontSize;
|
} else if (inner[i].classes[pos + 1] === "reset-size" + options.size) {
|
||||||
} 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
|
// 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)
|
// `\Huge a \small b`. Override the old size (the `reset-` class)
|
||||||
// but not the new size.
|
// but not the new size.
|
||||||
inner[i].classes[pos + 1] = "reset-" + options.size;
|
inner[i].classes[pos + 1] = "reset-size" + baseOptions.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inner[i].height *= multiplier;
|
||||||
|
inner[i].depth *= multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildCommon.makeFragment(inner);
|
return buildCommon.makeFragment(inner);
|
||||||
|
}
|
||||||
|
|
||||||
|
groupTypes.sizing = function(group, options) {
|
||||||
|
// Handle sizing operators like \Huge. Real TeX doesn't actually allow
|
||||||
|
// these functions inside of math expressions, so we do some special
|
||||||
|
// handling.
|
||||||
|
const newOptions = options.havingSize(group.value.size);
|
||||||
|
return sizingGroup(group.value.value, newOptions, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.styling = function(group, options) {
|
groupTypes.styling = function(group, options) {
|
||||||
@@ -1232,25 +1192,8 @@ groupTypes.styling = function(group, options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const newStyle = styleMap[group.value.style];
|
const newStyle = styleMap[group.value.style];
|
||||||
const newOptions = options.withStyle(newStyle);
|
const newOptions = options.havingStyle(newStyle);
|
||||||
|
return sizingGroup(group.value.value, newOptions, options);
|
||||||
// Build the inner expression in the new style.
|
|
||||||
const inner = buildExpression(
|
|
||||||
group.value.value, newOptions, false);
|
|
||||||
|
|
||||||
// Add style-resetting classes to the inner list. Handle nested changes.
|
|
||||||
for (let i = 0; i < inner.length; i++) {
|
|
||||||
const 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) {
|
groupTypes.font = function(group, options) {
|
||||||
@@ -1275,7 +1218,7 @@ groupTypes.delimsizing = function(group, options) {
|
|||||||
|
|
||||||
groupTypes.leftright = function(group, options) {
|
groupTypes.leftright = function(group, options) {
|
||||||
// Build the inner expression
|
// Build the inner expression
|
||||||
const inner = buildExpression(group.value.body, options.reset(), true);
|
const inner = buildExpression(group.value.body, options, true);
|
||||||
|
|
||||||
let innerHeight = 0;
|
let innerHeight = 0;
|
||||||
let innerDepth = 0;
|
let innerDepth = 0;
|
||||||
@@ -1291,13 +1234,11 @@ groupTypes.leftright = function(group, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const style = options.style;
|
|
||||||
|
|
||||||
// The size of delimiters is the same, regardless of what style we are
|
// The size of delimiters is the same, regardless of what style we are
|
||||||
// in. Thus, to correctly calculate the size of delimiter we need around
|
// in. Thus, to correctly calculate the size of delimiter we need around
|
||||||
// a group, we scale down the inner size based on the size.
|
// a group, we scale down the inner size based on the size.
|
||||||
innerHeight *= style.sizeMultiplier;
|
innerHeight *= options.sizeMultiplier;
|
||||||
innerDepth *= style.sizeMultiplier;
|
innerDepth *= options.sizeMultiplier;
|
||||||
|
|
||||||
let leftDelim;
|
let leftDelim;
|
||||||
if (group.value.left === ".") {
|
if (group.value.left === ".") {
|
||||||
@@ -1343,8 +1284,7 @@ groupTypes.leftright = function(group, options) {
|
|||||||
// Add it to the end of the expression.
|
// Add it to the end of the expression.
|
||||||
inner.push(rightDelim);
|
inner.push(rightDelim);
|
||||||
|
|
||||||
return makeSpan(
|
return makeSpan(["minner"], inner, options);
|
||||||
["minner", style.cls()], inner, options);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.middle = function(group, options) {
|
groupTypes.middle = function(group, options) {
|
||||||
@@ -1376,9 +1316,9 @@ groupTypes.rule = function(group, options) {
|
|||||||
|
|
||||||
// The sizes of rules are absolute, so make it larger if we are in a
|
// The sizes of rules are absolute, so make it larger if we are in a
|
||||||
// smaller style.
|
// smaller style.
|
||||||
shift /= style.sizeMultiplier;
|
shift /= options.sizeMultiplier;
|
||||||
width /= style.sizeMultiplier;
|
width /= options.sizeMultiplier;
|
||||||
height /= style.sizeMultiplier;
|
height /= options.sizeMultiplier;
|
||||||
|
|
||||||
// Style the rule to the right size
|
// Style the rule to the right size
|
||||||
rule.style.borderRightWidth = width + "em";
|
rule.style.borderRightWidth = width + "em";
|
||||||
@@ -1403,7 +1343,7 @@ groupTypes.kern = function(group, options) {
|
|||||||
dimension = calculateSize(group.value.dimension, style);
|
dimension = calculateSize(group.value.dimension, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
dimension /= style.sizeMultiplier;
|
dimension /= options.sizeMultiplier;
|
||||||
|
|
||||||
rule.style.marginLeft = dimension + "em";
|
rule.style.marginLeft = dimension + "em";
|
||||||
|
|
||||||
@@ -1436,13 +1376,11 @@ groupTypes.accent = function(group, options) {
|
|||||||
|
|
||||||
// Rerender the supsub group with its new base, and store that
|
// Rerender the supsub group with its new base, and store that
|
||||||
// result.
|
// result.
|
||||||
supsubGroup = buildGroup(
|
supsubGroup = buildGroup(supsub, options);
|
||||||
supsub, options.reset());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the base group
|
// Build the base group
|
||||||
const body = buildGroup(
|
const body = buildGroup(base, options.havingCrampedStyle());
|
||||||
base, options.withStyle(style.cramp()));
|
|
||||||
|
|
||||||
// Does the accent need to shift for the skew of a character?
|
// Does the accent need to shift for the skew of a character?
|
||||||
const mustShift = group.value.isShifty && isCharacterBox(base);
|
const mustShift = group.value.isShifty && isCharacterBox(base);
|
||||||
@@ -1458,8 +1396,7 @@ groupTypes.accent = function(group, options) {
|
|||||||
// innermost character. To do that, we find the innermost character:
|
// innermost character. To do that, we find the innermost character:
|
||||||
const baseChar = getBaseElem(base);
|
const baseChar = getBaseElem(base);
|
||||||
// Then, we render its group to get the symbol inside it
|
// Then, we render its group to get the symbol inside it
|
||||||
const baseGroup = buildGroup(
|
const baseGroup = buildGroup(baseChar, options.havingCrampedStyle());
|
||||||
baseChar, options.withStyle(style.cramp()));
|
|
||||||
// Finally, we pull the skew off of the symbol.
|
// Finally, we pull the skew off of the symbol.
|
||||||
skew = baseGroup.skew;
|
skew = baseGroup.skew;
|
||||||
// Note that we now throw away baseGroup, because the layers we
|
// Note that we now throw away baseGroup, because the layers we
|
||||||
@@ -1543,28 +1480,23 @@ groupTypes.horizBrace = function(group, options) {
|
|||||||
const hasSupSub = (group.type === "supsub");
|
const hasSupSub = (group.type === "supsub");
|
||||||
let supSubGroup;
|
let supSubGroup;
|
||||||
let newOptions;
|
let newOptions;
|
||||||
let supSubReset;
|
|
||||||
if (hasSupSub) {
|
if (hasSupSub) {
|
||||||
// Ref: LaTeX source2e: }}}}\limits}
|
// Ref: LaTeX source2e: }}}}\limits}
|
||||||
// i.e. LaTeX treats the brace similar to an op and passes it
|
// i.e. LaTeX treats the brace similar to an op and passes it
|
||||||
// with \limits, so we need to assign supsub style.
|
// with \limits, so we need to assign supsub style.
|
||||||
if (group.value.sup) {
|
if (group.value.sup) {
|
||||||
newOptions = options.withStyle(style.sup());
|
newOptions = options.havingStyle(style.sup());
|
||||||
supSubGroup = buildGroup(group.value.sup, newOptions);
|
supSubGroup = buildGroup(group.value.sup, newOptions, options);
|
||||||
supSubReset = makeSpan([style.reset(), style.sup().cls()],
|
|
||||||
[supSubGroup], newOptions);
|
|
||||||
} else {
|
} else {
|
||||||
newOptions = options.withStyle(style.sub());
|
newOptions = options.havingStyle(style.sub());
|
||||||
supSubGroup = buildGroup(group.value.sub, newOptions);
|
supSubGroup = buildGroup(group.value.sub, newOptions, options);
|
||||||
supSubReset = makeSpan([style.reset(), style.sub().cls()],
|
|
||||||
[supSubGroup], newOptions);
|
|
||||||
}
|
}
|
||||||
group = group.value.base;
|
group = group.value.base;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the base group
|
// Build the base group
|
||||||
const body = buildGroup(
|
const body = buildGroup(
|
||||||
group.value.base, options.withStyle(style.cramp()));
|
group.value.base, options.havingStyle(style.cramp()));
|
||||||
|
|
||||||
// Create the stretchy element
|
// Create the stretchy element
|
||||||
const braceBody = stretchy.svgSpan(group, options);
|
const braceBody = stretchy.svgSpan(group, options);
|
||||||
@@ -1603,14 +1535,14 @@ groupTypes.horizBrace = function(group, options) {
|
|||||||
vlist = buildCommon.makeVList([
|
vlist = buildCommon.makeVList([
|
||||||
{type: "elem", elem: vSpan},
|
{type: "elem", elem: vSpan},
|
||||||
{type: "kern", size: 0.2},
|
{type: "kern", size: 0.2},
|
||||||
{type: "elem", elem: supSubReset},
|
{type: "elem", elem: supSubGroup},
|
||||||
], "firstBaseline", null, options);
|
], "firstBaseline", null, options);
|
||||||
} else {
|
} else {
|
||||||
vlist = buildCommon.makeVList([
|
vlist = buildCommon.makeVList([
|
||||||
{type: "elem", elem: supSubReset},
|
{type: "elem", elem: supSubGroup},
|
||||||
{type: "kern", size: 0.2},
|
{type: "kern", size: 0.2},
|
||||||
{type: "elem", elem: vSpan},
|
{type: "elem", elem: vSpan},
|
||||||
], "bottom", vSpan.depth + 0.2 + supSubReset.height,
|
], "bottom", vSpan.depth + 0.2 + supSubGroup.height,
|
||||||
options);
|
options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1638,10 +1570,10 @@ groupTypes.accentUnder = function(group, options) {
|
|||||||
|
|
||||||
groupTypes.enclose = function(group, options) {
|
groupTypes.enclose = function(group, options) {
|
||||||
// \cancel, \bcancel, \xcancel, \sout, \fbox
|
// \cancel, \bcancel, \xcancel, \sout, \fbox
|
||||||
const inner = buildGroup(group.value.body, options.reset());
|
const inner = buildGroup(group.value.body, options);
|
||||||
|
|
||||||
const label = group.value.label.substr(1);
|
const label = group.value.label.substr(1);
|
||||||
const scale = options.style.sizeMultiplier;
|
const scale = options.sizeMultiplier;
|
||||||
let img;
|
let img;
|
||||||
let pad = 0;
|
let pad = 0;
|
||||||
let imgShift = 0;
|
let imgShift = 0;
|
||||||
@@ -1695,19 +1627,14 @@ groupTypes.xArrow = function(group, options) {
|
|||||||
// Build the argument groups in the appropriate style.
|
// Build the argument groups in the appropriate style.
|
||||||
// Ref: amsmath.dtx: \hbox{$\scriptstyle\mkern#3mu{#6}\mkern#4mu$}%
|
// Ref: amsmath.dtx: \hbox{$\scriptstyle\mkern#3mu{#6}\mkern#4mu$}%
|
||||||
|
|
||||||
let newOptions = options.withStyle(style.sup());
|
let newOptions = options.havingStyle(style.sup());
|
||||||
const upperGroup = buildGroup(group.value.body, newOptions);
|
const upperGroup = buildGroup(group.value.body, newOptions, options);
|
||||||
const upperGroupWrap = makeSpan([style.reset(), style.sup().cls()],
|
|
||||||
[upperGroup], newOptions);
|
|
||||||
|
|
||||||
let lowerGroup;
|
let lowerGroup;
|
||||||
let lowerGroupWrap;
|
|
||||||
if (group.value.below) {
|
if (group.value.below) {
|
||||||
// Build the lower group
|
// Build the lower group
|
||||||
newOptions = options.withStyle(style.sub());
|
newOptions = options.havingStyle(style.sub());
|
||||||
lowerGroup = buildGroup(group.value.below, newOptions);
|
lowerGroup = buildGroup(group.value.below, newOptions, options);
|
||||||
lowerGroupWrap = makeSpan([style.reset(), style.sub().cls()],
|
|
||||||
[lowerGroup], newOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const arrowBody = stretchy.svgSpan(group, options);
|
const arrowBody = stretchy.svgSpan(group, options);
|
||||||
@@ -1720,16 +1647,16 @@ groupTypes.xArrow = function(group, options) {
|
|||||||
let vlist;
|
let vlist;
|
||||||
if (group.value.below) {
|
if (group.value.below) {
|
||||||
const lowerShift = -style.metrics.axisHeight
|
const lowerShift = -style.metrics.axisHeight
|
||||||
+ lowerGroupWrap.height + arrowBody.height
|
+ lowerGroup.height + arrowBody.height
|
||||||
+ 0.111;
|
+ 0.111;
|
||||||
vlist = buildCommon.makeVList([
|
vlist = buildCommon.makeVList([
|
||||||
{type: "elem", elem: upperGroupWrap, shift: upperShift},
|
{type: "elem", elem: upperGroup, shift: upperShift},
|
||||||
{type: "elem", elem: arrowBody, shift: arrowShift},
|
{type: "elem", elem: arrowBody, shift: arrowShift},
|
||||||
{type: "elem", elem: lowerGroupWrap, shift: lowerShift},
|
{type: "elem", elem: lowerGroup, shift: lowerShift},
|
||||||
], "individualShift", null, options);
|
], "individualShift", null, options);
|
||||||
} else {
|
} else {
|
||||||
vlist = buildCommon.makeVList([
|
vlist = buildCommon.makeVList([
|
||||||
{type: "elem", elem: upperGroupWrap, shift: upperShift},
|
{type: "elem", elem: upperGroup, shift: upperShift},
|
||||||
{type: "elem", elem: arrowBody, shift: arrowShift},
|
{type: "elem", elem: arrowBody, shift: arrowShift},
|
||||||
], "individualShift", null, options);
|
], "individualShift", null, options);
|
||||||
}
|
}
|
||||||
@@ -1763,31 +1690,23 @@ groupTypes.mclass = function(group, options) {
|
|||||||
* function for it. It also handles the interaction of size and style changes
|
* function for it. It also handles the interaction of size and style changes
|
||||||
* between parents and children.
|
* between parents and children.
|
||||||
*/
|
*/
|
||||||
const buildGroup = function(group, options) {
|
const buildGroup = function(group, options, baseOptions) {
|
||||||
if (!group) {
|
if (!group) {
|
||||||
return makeSpan();
|
return makeSpan();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupTypes[group.type]) {
|
if (groupTypes[group.type]) {
|
||||||
// Call the groupTypes function
|
// Call the groupTypes function
|
||||||
const groupNode = groupTypes[group.type](group, options);
|
let groupNode = groupTypes[group.type](group, options);
|
||||||
let multiplier;
|
|
||||||
|
|
||||||
// If the style changed between the parent and the current group,
|
|
||||||
// account for the size difference
|
|
||||||
if (options.style !== options.parentStyle) {
|
|
||||||
multiplier = options.style.sizeMultiplier /
|
|
||||||
options.parentStyle.sizeMultiplier;
|
|
||||||
|
|
||||||
groupNode.height *= multiplier;
|
|
||||||
groupNode.depth *= multiplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the size changed between the parent and the current group, account
|
// If the size changed between the parent and the current group, account
|
||||||
// for that size difference.
|
// for that size difference.
|
||||||
if (options.size !== options.parentSize) {
|
if (baseOptions && options.size !== baseOptions.size) {
|
||||||
multiplier = buildCommon.sizingMultiplier[options.size] /
|
groupNode = makeSpan(options.sizingClasses(baseOptions),
|
||||||
buildCommon.sizingMultiplier[options.parentSize];
|
[groupNode], options);
|
||||||
|
|
||||||
|
const multiplier = options.sizeMultiplier /
|
||||||
|
baseOptions.sizeMultiplier;
|
||||||
|
|
||||||
groupNode.height *= multiplier;
|
groupNode.height *= multiplier;
|
||||||
groupNode.depth *= multiplier;
|
groupNode.depth *= multiplier;
|
||||||
@@ -1811,7 +1730,7 @@ const buildHTML = function(tree, options) {
|
|||||||
|
|
||||||
// Build the expression contained in the tree
|
// Build the expression contained in the tree
|
||||||
const expression = buildExpression(tree, options, true);
|
const expression = buildExpression(tree, options, true);
|
||||||
const body = makeSpan(["base", options.style.cls()], expression, options);
|
const body = makeSpan(["base"], expression, options);
|
||||||
|
|
||||||
// Add struts, which ensure that the top of the HTML element falls at the
|
// 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
|
// height of the expression, and the bottom of the HTML element falls at the
|
||||||
|
@@ -480,7 +480,8 @@ groupTypes.styling = function(group, options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.sizing = function(group, options) {
|
groupTypes.sizing = function(group, options) {
|
||||||
const inner = buildExpression(group.value.value, options);
|
const newOptions = options.havingSize(group.value.size);
|
||||||
|
const inner = buildExpression(group.value.value, newOptions);
|
||||||
|
|
||||||
const node = new mathMLTree.MathNode("mstyle", inner);
|
const node = new mathMLTree.MathNode("mstyle", inner);
|
||||||
|
|
||||||
@@ -489,8 +490,7 @@ groupTypes.sizing = function(group, options) {
|
|||||||
// in, so we can't reset the size to normal before changing it. Now
|
// in, so we can't reset the size to normal before changing it. Now
|
||||||
// that we're passing an options parameter we should be able to fix
|
// that we're passing an options parameter we should be able to fix
|
||||||
// this.
|
// this.
|
||||||
node.setAttribute(
|
node.setAttribute("mathsize", newOptions.sizeMultiplier + "em");
|
||||||
"mathsize", buildCommon.sizingMultiplier[group.value.size] + "em");
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
@@ -18,7 +18,6 @@ const buildTree = function(tree, expression, settings) {
|
|||||||
// Setup the default options
|
// Setup the default options
|
||||||
const options = new Options({
|
const options = new Options({
|
||||||
style: startStyle,
|
style: startStyle,
|
||||||
size: "size5",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// `buildHTML` sometimes messes with the parse tree (like turning bins ->
|
// `buildHTML` sometimes messes with the parse tree (like turning bins ->
|
||||||
|
@@ -44,33 +44,36 @@ const getMetrics = function(symbol, font) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a symbol in the given font size (note size is an integer)
|
|
||||||
*/
|
|
||||||
const mathrmSize = function(value, size, mode, options) {
|
|
||||||
return buildCommon.makeSymbol(value, "Size" + size + "-Regular",
|
|
||||||
mode, options);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts a delimiter span in a given style, and adds appropriate height, depth,
|
* Puts a delimiter span in a given style, and adds appropriate height, depth,
|
||||||
* and maxFontSizes.
|
* and maxFontSizes.
|
||||||
*/
|
*/
|
||||||
const styleWrap = function(delim, toStyle, options, classes) {
|
const styleWrap = function(delim, toStyle, options, classes) {
|
||||||
classes = classes || [];
|
const newOptions = options.havingBaseStyle(toStyle);
|
||||||
|
|
||||||
const span = makeSpan(
|
const span = makeSpan(
|
||||||
classes.concat(["style-wrap", options.style.reset(), toStyle.cls()]),
|
(classes || []).concat(newOptions.sizingClasses(options)),
|
||||||
[delim], options);
|
[delim], options);
|
||||||
|
|
||||||
const multiplier = toStyle.sizeMultiplier / options.style.sizeMultiplier;
|
span.height *= newOptions.sizeMultiplier / options.sizeMultiplier;
|
||||||
|
span.depth *= newOptions.sizeMultiplier / options.sizeMultiplier;
|
||||||
span.height *= multiplier;
|
span.maxFontSize = newOptions.sizeMultiplier;
|
||||||
span.depth *= multiplier;
|
|
||||||
span.maxFontSize = toStyle.sizeMultiplier;
|
|
||||||
|
|
||||||
return span;
|
return span;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const centerSpan = function(span, options, style) {
|
||||||
|
const newOptions = options.havingBaseStyle(style);
|
||||||
|
const shift =
|
||||||
|
(1 - options.sizeMultiplier / newOptions.sizeMultiplier) *
|
||||||
|
options.style.metrics.axisHeight;
|
||||||
|
|
||||||
|
span.classes.push("delimcenter");
|
||||||
|
span.style.top = shift + "em";
|
||||||
|
span.height -= shift;
|
||||||
|
span.depth += shift;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a small delimiter. This is a delimiter that comes in the Main-Regular
|
* Makes a small delimiter. This is a delimiter that comes in the Main-Regular
|
||||||
* font, but is restyled to either be in textstyle, scriptstyle, or
|
* font, but is restyled to either be in textstyle, scriptstyle, or
|
||||||
@@ -78,42 +81,33 @@ const styleWrap = function(delim, toStyle, options, classes) {
|
|||||||
*/
|
*/
|
||||||
const makeSmallDelim = function(delim, style, center, options, mode, classes) {
|
const makeSmallDelim = function(delim, style, center, options, mode, classes) {
|
||||||
const text = buildCommon.makeSymbol(delim, "Main-Regular", mode, options);
|
const text = buildCommon.makeSymbol(delim, "Main-Regular", mode, options);
|
||||||
|
|
||||||
const span = styleWrap(text, style, options, classes);
|
const span = styleWrap(text, style, options, classes);
|
||||||
|
|
||||||
if (center) {
|
if (center) {
|
||||||
const shift =
|
centerSpan(span, options, style);
|
||||||
(1 - options.style.sizeMultiplier / style.sizeMultiplier) *
|
|
||||||
options.style.metrics.axisHeight;
|
|
||||||
|
|
||||||
span.style.top = shift + "em";
|
|
||||||
span.height -= shift;
|
|
||||||
span.depth += shift;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return span;
|
return span;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a symbol in the given font size (note size is an integer)
|
||||||
|
*/
|
||||||
|
const mathrmSize = function(value, size, mode, options) {
|
||||||
|
return buildCommon.makeSymbol(value, "Size" + size + "-Regular",
|
||||||
|
mode, options);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a large delimiter. This is a delimiter that comes in the Size1, Size2,
|
* Makes a large delimiter. This is a delimiter that comes in the Size1, Size2,
|
||||||
* Size3, or Size4 fonts. It is always rendered in textstyle.
|
* Size3, or Size4 fonts. It is always rendered in textstyle.
|
||||||
*/
|
*/
|
||||||
const makeLargeDelim = function(delim, size, center, options, mode, classes) {
|
const makeLargeDelim = function(delim, size, center, options, mode, classes) {
|
||||||
const inner = mathrmSize(delim, size, mode, options);
|
const inner = mathrmSize(delim, size, mode, options);
|
||||||
|
|
||||||
const span = styleWrap(
|
const span = styleWrap(
|
||||||
makeSpan(["delimsizing", "size" + size], [inner], options),
|
makeSpan(["delimsizing", "size" + size], [inner], options),
|
||||||
Style.TEXT, options, classes);
|
Style.TEXT, options, classes);
|
||||||
|
|
||||||
if (center) {
|
if (center) {
|
||||||
const shift = (1 - options.style.sizeMultiplier) *
|
centerSpan(span, options, Style.TEXT);
|
||||||
options.style.metrics.axisHeight;
|
|
||||||
|
|
||||||
span.style.top = shift + "em";
|
|
||||||
span.height -= shift;
|
|
||||||
span.depth += shift;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return span;
|
return span;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -283,7 +277,7 @@ const makeStackedDelim = function(delim, heightTotal, center, options, mode,
|
|||||||
// centered around the axis in textstyle.
|
// centered around the axis in textstyle.
|
||||||
let axisHeight = options.style.metrics.axisHeight;
|
let axisHeight = options.style.metrics.axisHeight;
|
||||||
if (center) {
|
if (center) {
|
||||||
axisHeight *= options.style.sizeMultiplier;
|
axisHeight *= options.sizeMultiplier;
|
||||||
}
|
}
|
||||||
// Calculate the depth
|
// Calculate the depth
|
||||||
const depth = realHeightTotal / 2 - axisHeight;
|
const depth = realHeightTotal / 2 - axisHeight;
|
||||||
@@ -317,10 +311,11 @@ const makeStackedDelim = function(delim, heightTotal, center, options, mode,
|
|||||||
inners.push(makeInner(top, font, mode));
|
inners.push(makeInner(top, font, mode));
|
||||||
|
|
||||||
// Finally, build the vlist
|
// Finally, build the vlist
|
||||||
const inner = buildCommon.makeVList(inners, "bottom", depth, options);
|
const newOptions = options.havingBaseStyle(Style.TEXT);
|
||||||
|
const inner = buildCommon.makeVList(inners, "bottom", depth, newOptions);
|
||||||
|
|
||||||
return styleWrap(
|
return styleWrap(
|
||||||
makeSpan(["delimsizing", "mult"], [inner], options),
|
makeSpan(["delimsizing", "mult"], [inner], newOptions),
|
||||||
Style.TEXT, options, classes);
|
Style.TEXT, options, classes);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -455,7 +450,8 @@ const traverseSequence = function(delim, height, sequence, options) {
|
|||||||
// account for the style change size.
|
// account for the style change size.
|
||||||
|
|
||||||
if (sequence[i].type === "small") {
|
if (sequence[i].type === "small") {
|
||||||
heightDepth *= sequence[i].style.sizeMultiplier;
|
const newOptions = options.havingBaseStyle(sequence[i].style);
|
||||||
|
heightDepth *= newOptions.sizeMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the delimiter at this size works for the given height.
|
// Check if the delimiter at this size works for the given height.
|
||||||
@@ -514,7 +510,7 @@ const makeLeftRightDelim = function(delim, height, depth, options, mode,
|
|||||||
classes) {
|
classes) {
|
||||||
// We always center \left/\right delimiters, so the axis is always shifted
|
// We always center \left/\right delimiters, so the axis is always shifted
|
||||||
const axisHeight =
|
const axisHeight =
|
||||||
options.style.metrics.axisHeight * options.style.sizeMultiplier;
|
options.style.metrics.axisHeight * options.sizeMultiplier;
|
||||||
|
|
||||||
// Taken from TeX source, tex.web, function make_left_right
|
// Taken from TeX source, tex.web, function make_left_right
|
||||||
const delimiterFactor = 901;
|
const delimiterFactor = 901;
|
||||||
|
@@ -232,22 +232,6 @@
|
|||||||
& + .mop.mtight { margin-left: @thinspace; }
|
& + .mop.mtight { margin-left: @thinspace; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.reset-textstyle.textstyle { font-size: 1em; }
|
|
||||||
.reset-textstyle.scriptstyle { font-size: 0.7em; }
|
|
||||||
.reset-textstyle.scriptscriptstyle { font-size: 0.5em; }
|
|
||||||
|
|
||||||
.reset-scriptstyle.textstyle { font-size: 1.42857em; }
|
|
||||||
.reset-scriptstyle.scriptstyle { font-size: 1em; }
|
|
||||||
.reset-scriptstyle.scriptscriptstyle { font-size: 0.71429em; }
|
|
||||||
|
|
||||||
.reset-scriptscriptstyle.textstyle { font-size: 2em; }
|
|
||||||
.reset-scriptscriptstyle.scriptstyle { font-size: 1.4em; }
|
|
||||||
.reset-scriptscriptstyle.scriptscriptstyle { font-size: 1em; }
|
|
||||||
|
|
||||||
.style-wrap {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vlist {
|
.vlist {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
@@ -448,15 +432,16 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
@size1: 0.5;
|
@size1: 0.5;
|
||||||
@size2: 0.7;
|
@size2: 0.6;
|
||||||
@size3: 0.8;
|
@size3: 0.7;
|
||||||
@size4: 0.9;
|
@size4: 0.8;
|
||||||
@size5: 1.0;
|
@size5: 0.9;
|
||||||
@size6: 1.2;
|
@size6: 1.0;
|
||||||
@size7: 1.44;
|
@size7: 1.2;
|
||||||
@size8: 1.73;
|
@size8: 1.44;
|
||||||
@size9: 2.07;
|
@size9: 1.728;
|
||||||
@size10: 2.49;
|
@size10: 2.074;
|
||||||
|
@size11: 2.488;
|
||||||
|
|
||||||
.generate-size-change(@from, @to) {
|
.generate-size-change(@from, @to) {
|
||||||
&.reset-size@{from}.size@{to} {
|
&.reset-size@{from}.size@{to} {
|
||||||
@@ -466,13 +451,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.generate-to-size-change(@from, @currTo) when (@currTo =< 10) {
|
.generate-to-size-change(@from, @currTo) when (@currTo =< 11) {
|
||||||
.generate-size-change(@from, @currTo);
|
.generate-size-change(@from, @currTo);
|
||||||
|
|
||||||
.generate-to-size-change(@from, (@currTo + 1));
|
.generate-to-size-change(@from, (@currTo + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
.generate-from-size-change(@currFrom) when (@currFrom =< 10) {
|
.generate-from-size-change(@currFrom) when (@currFrom =< 11) {
|
||||||
.generate-to-size-change(@currFrom, 1);
|
.generate-to-size-change(@currFrom, 1);
|
||||||
|
|
||||||
.generate-from-size-change((@currFrom + 1));
|
.generate-from-size-change((@currFrom + 1));
|
||||||
@@ -502,6 +487,10 @@
|
|||||||
width: @nulldelimiterspace;
|
width: @nulldelimiterspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.delimcenter {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.op-symbol {
|
.op-symbol {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
@@ -17,7 +17,7 @@ const Style = require("../src/Style");
|
|||||||
const defaultSettings = new Settings({});
|
const defaultSettings = new Settings({});
|
||||||
const defaultOptions = new Options({
|
const defaultOptions = new Options({
|
||||||
style: Style.TEXT,
|
style: Style.TEXT,
|
||||||
size: "size5",
|
size: 5,
|
||||||
});
|
});
|
||||||
|
|
||||||
const _getBuilt = function(expr, settings) {
|
const _getBuilt = function(expr, settings) {
|
||||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 10 KiB |