Use style specific FONTDIM metrics (#545)

Summary:
FONTDIM metrics include metrics like sup2, sup3, etc. which are used for
position sub/super-scripts, fractions, delimiters, etc.  TeX uses three
different font styles: textfont2 (DISPLAY & TEXT), scriptfont2 (SCRIPT), and
scriptscriptfont2 (SCRIPTSCRIPT) and has different sets of metrics for each.
This diff adds style specific metrics for better TeX compliance.

Notable squashed commits:
- Recreated screenshots (martin)
- fix getEmPerEx to use getXHeight
- regularize how we access options.style, remove unnecessary newlines
- use var style = options.style in more places in buildHTML
This commit is contained in:
Kevin Barabash 2016-11-04 02:45:08 -04:00 committed by Martin von Gagern
parent ace67541a0
commit 22957d40f6
25 changed files with 170 additions and 163 deletions

View File

@ -6,6 +6,21 @@
* information about them. * information about them.
*/ */
var sigmas = require("./fontMetrics.js").sigmas;
var metrics = [{}, {}, {}];
var i;
for (var key in sigmas) {
if (sigmas.hasOwnProperty(key)) {
for (i = 0; i < 3; i++) {
metrics[i][key] = sigmas[key][i];
}
}
}
for (i = 0; i < 3; i++) {
metrics[i].emPerEx = sigmas.xHeight[i] / sigmas.quad[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), a cramped flag, and a
@ -17,6 +32,7 @@ function Style(id, size, multiplier, cramped) {
this.size = size; this.size = size;
this.cramped = cramped; this.cramped = cramped;
this.sizeMultiplier = multiplier; this.sizeMultiplier = multiplier;
this.metrics = metrics[size > 0 ? size - 1 : 0];
} }
/** /**

View File

@ -273,18 +273,16 @@ groupTypes.supsub = function(group, options, prev) {
var sup; var sup;
var sub; var sub;
var style = options.style;
if (group.value.sup) { if (group.value.sup) {
sup = buildGroup(group.value.sup, sup = buildGroup(group.value.sup, options.withStyle(style.sup()));
options.withStyle(options.style.sup())); supmid = makeSpan([style.reset(), style.sup().cls()], [sup]);
supmid = makeSpan(
[options.style.reset(), options.style.sup().cls()], [sup]);
} }
if (group.value.sub) { if (group.value.sub) {
sub = buildGroup(group.value.sub, sub = buildGroup(group.value.sub, options.withStyle(style.sub()));
options.withStyle(options.style.sub())); submid = makeSpan([style.reset(), style.sub().cls()], [sub]);
submid = makeSpan(
[options.style.reset(), options.style.sub().cls()], [sub]);
} }
// Rule 18a // Rule 18a
@ -294,24 +292,24 @@ groupTypes.supsub = function(group, options, prev) {
supShift = 0; supShift = 0;
subShift = 0; subShift = 0;
} else { } else {
supShift = base.height - fontMetrics.metrics.supDrop; supShift = base.height - style.metrics.supDrop;
subShift = base.depth + fontMetrics.metrics.subDrop; subShift = base.depth + style.metrics.subDrop;
} }
// Rule 18c // Rule 18c
var minSupShift; var minSupShift;
if (options.style === Style.DISPLAY) { if (style === Style.DISPLAY) {
minSupShift = fontMetrics.metrics.sup1; minSupShift = style.metrics.sup1;
} else if (options.style.cramped) { } else if (style.cramped) {
minSupShift = fontMetrics.metrics.sup3; minSupShift = style.metrics.sup3;
} else { } else {
minSupShift = fontMetrics.metrics.sup2; minSupShift = style.metrics.sup2;
} }
// scriptspace is a font-size-independent size, so scale it // scriptspace is a font-size-independent size, so scale it
// appropriately // appropriately
var multiplier = Style.TEXT.sizeMultiplier * var multiplier = Style.TEXT.sizeMultiplier *
options.style.sizeMultiplier; style.sizeMultiplier;
var scriptspace = var scriptspace =
(0.5 / fontMetrics.metrics.ptPerEm) / multiplier + "em"; (0.5 / fontMetrics.metrics.ptPerEm) / multiplier + "em";
@ -319,8 +317,8 @@ groupTypes.supsub = function(group, options, prev) {
if (!group.value.sup) { if (!group.value.sup) {
// Rule 18b // Rule 18b
subShift = Math.max( subShift = Math.max(
subShift, fontMetrics.metrics.sub1, subShift, style.metrics.sub1,
sub.height - 0.8 * fontMetrics.metrics.xHeight); sub.height - 0.8 * style.metrics.xHeight);
supsub = buildCommon.makeVList([ supsub = buildCommon.makeVList([
{type: "elem", elem: submid}, {type: "elem", elem: submid},
@ -337,7 +335,7 @@ groupTypes.supsub = function(group, options, prev) {
} 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 * fontMetrics.metrics.xHeight); sup.depth + 0.25 * style.metrics.xHeight);
supsub = buildCommon.makeVList([ supsub = buildCommon.makeVList([
{type: "elem", elem: supmid}, {type: "elem", elem: supmid},
@ -346,9 +344,8 @@ groupTypes.supsub = function(group, options, prev) {
supsub.children[0].style.marginRight = scriptspace; supsub.children[0].style.marginRight = scriptspace;
} else { } else {
supShift = Math.max( supShift = Math.max(
supShift, minSupShift, supShift, minSupShift, sup.depth + 0.25 * style.metrics.xHeight);
sup.depth + 0.25 * fontMetrics.metrics.xHeight); subShift = Math.max(subShift, style.metrics.sub2);
subShift = Math.max(subShift, fontMetrics.metrics.sub2);
var ruleWidth = fontMetrics.metrics.defaultRuleThickness; var ruleWidth = fontMetrics.metrics.defaultRuleThickness;
@ -356,8 +353,7 @@ groupTypes.supsub = function(group, options, prev) {
if ((supShift - sup.depth) - (sub.height - subShift) < if ((supShift - sup.depth) - (sub.height - subShift) <
4 * ruleWidth) { 4 * ruleWidth) {
subShift = 4 * ruleWidth - (supShift - sup.depth) + sub.height; subShift = 4 * ruleWidth - (supShift - sup.depth) + sub.height;
var psi = 0.8 * fontMetrics.metrics.xHeight - var psi = 0.8 * style.metrics.xHeight - (supShift - sup.depth);
(supShift - sup.depth);
if (psi > 0) { if (psi > 0) {
supShift += psi; supShift += psi;
subShift -= psi; subShift -= psi;
@ -387,21 +383,21 @@ groupTypes.genfrac = function(group, options, prev) {
// Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e). // Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e).
// Figure out what style this fraction should be in based on the // Figure out what style this fraction should be in based on the
// function used // function used
var fstyle = options.style; var style = options.style;
if (group.value.size === "display") { if (group.value.size === "display") {
fstyle = Style.DISPLAY; style = Style.DISPLAY;
} else if (group.value.size === "text") { } else if (group.value.size === "text") {
fstyle = Style.TEXT; style = Style.TEXT;
} }
var nstyle = fstyle.fracNum(); var nstyle = style.fracNum();
var dstyle = fstyle.fracDen(); var dstyle = style.fracDen();
var numer = buildGroup(group.value.numer, options.withStyle(nstyle)); var numer = buildGroup(group.value.numer, options.withStyle(nstyle));
var numerreset = makeSpan([fstyle.reset(), nstyle.cls()], [numer]); var numerreset = makeSpan([style.reset(), nstyle.cls()], [numer]);
var denom = buildGroup(group.value.denom, options.withStyle(dstyle)); var denom = buildGroup(group.value.denom, options.withStyle(dstyle));
var denomreset = makeSpan([fstyle.reset(), dstyle.cls()], [denom]); var denomreset = makeSpan([style.reset(), dstyle.cls()], [denom]);
var ruleWidth; var ruleWidth;
if (group.value.hasBarLine) { if (group.value.hasBarLine) {
@ -415,23 +411,23 @@ groupTypes.genfrac = function(group, options, prev) {
var numShift; var numShift;
var clearance; var clearance;
var denomShift; var denomShift;
if (fstyle.size === Style.DISPLAY.size) { if (style.size === Style.DISPLAY.size) {
numShift = fontMetrics.metrics.num1; numShift = style.metrics.num1;
if (ruleWidth > 0) { if (ruleWidth > 0) {
clearance = 3 * ruleWidth; clearance = 3 * ruleWidth;
} else { } else {
clearance = 7 * fontMetrics.metrics.defaultRuleThickness; clearance = 7 * fontMetrics.metrics.defaultRuleThickness;
} }
denomShift = fontMetrics.metrics.denom1; denomShift = style.metrics.denom1;
} else { } else {
if (ruleWidth > 0) { if (ruleWidth > 0) {
numShift = fontMetrics.metrics.num2; numShift = style.metrics.num2;
clearance = ruleWidth; clearance = ruleWidth;
} else { } else {
numShift = fontMetrics.metrics.num3; numShift = style.metrics.num3;
clearance = 3 * fontMetrics.metrics.defaultRuleThickness; clearance = 3 * fontMetrics.metrics.defaultRuleThickness;
} }
denomShift = fontMetrics.metrics.denom2; denomShift = style.metrics.denom2;
} }
var frac; var frac;
@ -450,7 +446,7 @@ groupTypes.genfrac = function(group, options, prev) {
], "individualShift", null, options); ], "individualShift", null, options);
} else { } else {
// Rule 15d // Rule 15d
var axisHeight = fontMetrics.metrics.axisHeight; var axisHeight = style.metrics.axisHeight;
if ((numShift - numer.depth) - (axisHeight + 0.5 * ruleWidth) < if ((numShift - numer.depth) - (axisHeight + 0.5 * ruleWidth) <
clearance) { clearance) {
@ -483,15 +479,15 @@ groupTypes.genfrac = function(group, options, prev) {
// 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 *= fstyle.sizeMultiplier / options.style.sizeMultiplier; frac.height *= style.sizeMultiplier / options.style.sizeMultiplier;
frac.depth *= fstyle.sizeMultiplier / options.style.sizeMultiplier; frac.depth *= style.sizeMultiplier / options.style.sizeMultiplier;
// Rule 15e // Rule 15e
var delimSize; var delimSize;
if (fstyle.size === Style.DISPLAY.size) { if (style.size === Style.DISPLAY.size) {
delimSize = fontMetrics.metrics.delim1; delimSize = style.metrics.delim1;
} else { } else {
delimSize = fontMetrics.metrics.getDelim2(fstyle); delimSize = style.metrics.delim2;
} }
var leftDelim; var leftDelim;
@ -501,18 +497,18 @@ groupTypes.genfrac = function(group, options, prev) {
} else { } else {
leftDelim = delimiter.customSizedDelim( leftDelim = delimiter.customSizedDelim(
group.value.leftDelim, delimSize, true, group.value.leftDelim, delimSize, true,
options.withStyle(fstyle), group.mode); options.withStyle(style), group.mode);
} }
if (group.value.rightDelim == null) { if (group.value.rightDelim == null) {
rightDelim = makeNullDelimiter(options); rightDelim = makeNullDelimiter(options);
} else { } else {
rightDelim = delimiter.customSizedDelim( rightDelim = delimiter.customSizedDelim(
group.value.rightDelim, delimSize, true, group.value.rightDelim, delimSize, true,
options.withStyle(fstyle), group.mode); options.withStyle(style), group.mode);
} }
return makeSpan( return makeSpan(
["mord", options.style.reset(), fstyle.cls()], ["mord", options.style.reset(), style.cls()],
[leftDelim, makeSpan(["mfrac"], [frac]), rightDelim], [leftDelim, makeSpan(["mfrac"], [frac]), rightDelim],
options.getColor()); options.getColor());
}; };
@ -524,6 +520,8 @@ groupTypes.array = function(group, options, prev) {
var nc = 0; var nc = 0;
var body = new Array(nr); var body = new Array(nr);
var style = options.style;
// Horizontal spacing // Horizontal spacing
var pt = 1 / fontMetrics.metrics.ptPerEm; var pt = 1 / fontMetrics.metrics.ptPerEm;
var arraycolsep = 5 * pt; // \arraycolsep in article.cls var arraycolsep = 5 * pt; // \arraycolsep in article.cls
@ -567,7 +565,7 @@ groupTypes.array = function(group, options, prev) {
gap = gap.number; gap = gap.number;
break; break;
case "ex": case "ex":
gap = gap.number * fontMetrics.metrics.emPerEx; gap = gap.number * style.metrics.emPerEx;
break; break;
default: default:
console.error("Can't handle unit " + gap.unit); console.error("Can't handle unit " + gap.unit);
@ -590,7 +588,7 @@ groupTypes.array = function(group, options, prev) {
body[r] = outrow; body[r] = outrow;
} }
var offset = totalHeight / 2 + fontMetrics.metrics.axisHeight; var offset = totalHeight / 2 + style.metrics.axisHeight;
var colDescriptions = group.value.cols || []; var colDescriptions = group.value.cols || [];
var cols = []; var cols = [];
var colSep; var colSep;
@ -729,13 +727,15 @@ groupTypes.op = function(group, options, prev) {
hasLimits = true; hasLimits = true;
} }
var style = options.style;
// Most operators have a large successor symbol, but these don't. // Most operators have a large successor symbol, but these don't.
var noSuccessor = [ var noSuccessor = [
"\\smallint", "\\smallint",
]; ];
var large = false; var large = false;
if (options.style.size === Style.DISPLAY.size && if (style.size === Style.DISPLAY.size &&
group.value.symbol && group.value.symbol &&
!utils.contains(noSuccessor, group.value.body)) { !utils.contains(noSuccessor, group.value.body)) {
@ -748,9 +748,9 @@ groupTypes.op = function(group, options, prev) {
var slant = 0; var slant = 0;
if (group.value.symbol) { if (group.value.symbol) {
// If this is a symbol, create the symbol. // If this is a symbol, create the symbol.
var style = large ? "Size2-Regular" : "Size1-Regular"; var fontName = large ? "Size2-Regular" : "Size1-Regular";
base = buildCommon.makeSymbol( base = buildCommon.makeSymbol(
group.value.body, style, "math", options.getColor(), group.value.body, fontName, "math", options.getColor(),
["op-symbol", large ? "large-op" : "small-op", "mop"]); ["op-symbol", large ? "large-op" : "small-op", "mop"]);
// Shift the symbol so its center lies on the axis (rule 13). It // Shift the symbol so its center lies on the axis (rule 13). It
@ -759,8 +759,7 @@ groupTypes.op = function(group, options, prev) {
// 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 -
fontMetrics.metrics.axisHeight * style.metrics.axisHeight * style.sizeMultiplier;
options.style.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;
@ -788,10 +787,8 @@ groupTypes.op = function(group, options, prev) {
// 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) {
var sup = buildGroup( var sup = buildGroup(supGroup, options.withStyle(style.sup()));
supGroup, options.withStyle(options.style.sup())); supmid = makeSpan([style.reset(), style.sup().cls()], [sup]);
supmid = makeSpan(
[options.style.reset(), options.style.sup().cls()], [sup]);
supKern = Math.max( supKern = Math.max(
fontMetrics.metrics.bigOpSpacing1, fontMetrics.metrics.bigOpSpacing1,
@ -799,11 +796,8 @@ groupTypes.op = function(group, options, prev) {
} }
if (subGroup) { if (subGroup) {
var sub = buildGroup( var sub = buildGroup(subGroup, options.withStyle(style.sub()));
subGroup, options.withStyle(options.style.sub())); submid = makeSpan([style.reset(), style.sub().cls()], [sub]);
submid = makeSpan(
[options.style.reset(), options.style.sub().cls()],
[sub]);
subKern = Math.max( subKern = Math.max(
fontMetrics.metrics.bigOpSpacing2, fontMetrics.metrics.bigOpSpacing2,
@ -907,17 +901,18 @@ groupTypes.katex = function(group, options, prev) {
groupTypes.overline = function(group, options, prev) { groupTypes.overline = function(group, options, prev) {
// Overlines are handled in the TeXbook pg 443, Rule 9. // Overlines are handled in the TeXbook pg 443, Rule 9.
var style = options.style;
// Build the inner group in the cramped style. // Build the inner group in the cramped style.
var innerGroup = buildGroup(group.value.body, var innerGroup = buildGroup(group.value.body,
options.withStyle(options.style.cramp())); options.withStyle(style.cramp()));
var ruleWidth = fontMetrics.metrics.defaultRuleThickness / var ruleWidth = fontMetrics.metrics.defaultRuleThickness /
options.style.sizeMultiplier; style.sizeMultiplier;
// Create the line above the body // Create the line above the body
var line = makeSpan( var line = makeSpan(
[options.style.reset(), Style.TEXT.cls(), "overline-line"]); [style.reset(), Style.TEXT.cls(), "overline-line"]);
line.height = ruleWidth; line.height = ruleWidth;
line.maxFontSize = 1.0; line.maxFontSize = 1.0;
@ -934,16 +929,16 @@ groupTypes.overline = function(group, options, prev) {
groupTypes.underline = function(group, options, prev) { groupTypes.underline = function(group, options, prev) {
// Underlines are handled in the TeXbook pg 443, Rule 10. // Underlines are handled in the TeXbook pg 443, Rule 10.
var style = options.style;
// Build the inner group. // Build the inner group.
var innerGroup = buildGroup(group.value.body, options); var innerGroup = buildGroup(group.value.body, options);
var ruleWidth = fontMetrics.metrics.defaultRuleThickness / var ruleWidth = fontMetrics.metrics.defaultRuleThickness /
options.style.sizeMultiplier; style.sizeMultiplier;
// Create the line above the body // Create the line above the body
var line = makeSpan( var line = makeSpan([style.reset(), Style.TEXT.cls(), "underline-line"]);
[options.style.reset(), Style.TEXT.cls(), "underline-line"]);
line.height = ruleWidth; line.height = ruleWidth;
line.maxFontSize = 1.0; line.maxFontSize = 1.0;
@ -960,31 +955,30 @@ groupTypes.underline = function(group, options, prev) {
groupTypes.sqrt = function(group, options, prev) { groupTypes.sqrt = function(group, options, prev) {
// Square roots are handled in the TeXbook pg. 443, Rule 11. // Square roots are handled in the TeXbook pg. 443, Rule 11.
var 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
var inner = buildGroup(group.value.body, var inner = buildGroup(group.value.body, options.withStyle(style.cramp()));
options.withStyle(options.style.cramp()));
var ruleWidth = fontMetrics.metrics.defaultRuleThickness / var ruleWidth = fontMetrics.metrics.defaultRuleThickness /
options.style.sizeMultiplier; style.sizeMultiplier;
var line = makeSpan( var line = makeSpan(
[options.style.reset(), Style.TEXT.cls(), "sqrt-line"], [], [style.reset(), Style.TEXT.cls(), "sqrt-line"], [],
options.getColor()); options.getColor());
line.height = ruleWidth; line.height = ruleWidth;
line.maxFontSize = 1.0; line.maxFontSize = 1.0;
var phi = ruleWidth; var phi = ruleWidth;
if (options.style.id < Style.TEXT.id) { if (style.id < Style.TEXT.id) {
phi = fontMetrics.metrics.xHeight; phi = style.metrics.xHeight;
} }
// Calculate the clearance between the body and line // Calculate the clearance between the body and line
var lineClearance = ruleWidth + phi / 4; var lineClearance = ruleWidth + phi / 4;
var innerHeight = var innerHeight = (inner.height + inner.depth) * style.sizeMultiplier;
(inner.height + inner.depth) * options.style.sizeMultiplier;
var minDelimiterHeight = innerHeight + lineClearance + ruleWidth; var minDelimiterHeight = innerHeight + lineClearance + ruleWidth;
// Create a \surd delimiter of the required minimum size // Create a \surd delimiter of the required minimum size
@ -1034,7 +1028,7 @@ groupTypes.sqrt = function(group, options, prev) {
group.value.index, group.value.index,
options.withStyle(Style.SCRIPTSCRIPT)); options.withStyle(Style.SCRIPTSCRIPT));
var rootWrap = makeSpan( var rootWrap = makeSpan(
[options.style.reset(), Style.SCRIPTSCRIPT.cls()], [style.reset(), Style.SCRIPTSCRIPT.cls()],
[root]); [root]);
// Figure out the height and depth of the inner part // Figure out the height and depth of the inner part
@ -1064,14 +1058,15 @@ groupTypes.sizing = function(group, options, prev) {
var inner = buildExpression(group.value.value, var inner = buildExpression(group.value.value,
options.withSize(group.value.size), prev); options.withSize(group.value.size), prev);
var style = options.style;
var span = makeSpan(["mord"], var span = makeSpan(["mord"],
[makeSpan(["sizing", "reset-" + options.size, group.value.size, [makeSpan(["sizing", "reset-" + options.size, group.value.size,
options.style.cls()], style.cls()],
inner)]); inner)]);
// Calculate the correct maxFontSize manually // Calculate the correct maxFontSize manually
var fontSize = buildCommon.sizingMultiplier[group.value.size]; var fontSize = buildCommon.sizingMultiplier[group.value.size];
span.maxFontSize = fontSize * options.style.sizeMultiplier; span.maxFontSize = fontSize * style.sizeMultiplier;
return span; return span;
}; };
@ -1080,14 +1075,14 @@ groupTypes.styling = function(group, options, prev) {
// Style changes are handled in the TeXbook on pg. 442, Rule 3. // Style changes are handled in the TeXbook on pg. 442, Rule 3.
// Figure out what style we're changing to. // Figure out what style we're changing to.
var style = { var styleMap = {
"display": Style.DISPLAY, "display": Style.DISPLAY,
"text": Style.TEXT, "text": Style.TEXT,
"script": Style.SCRIPT, "script": Style.SCRIPT,
"scriptscript": Style.SCRIPTSCRIPT, "scriptscript": Style.SCRIPTSCRIPT,
}; };
var newStyle = style[group.value.style]; var newStyle = styleMap[group.value.style];
// Build the inner expression in the new style. // Build the inner expression in the new style.
var inner = buildExpression( var inner = buildExpression(
@ -1130,11 +1125,13 @@ groupTypes.leftright = function(group, options, prev) {
innerDepth = Math.max(inner[i].depth, innerDepth); innerDepth = Math.max(inner[i].depth, innerDepth);
} }
var 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 *= options.style.sizeMultiplier; innerHeight *= style.sizeMultiplier;
innerDepth *= options.style.sizeMultiplier; innerDepth *= style.sizeMultiplier;
var leftDelim; var leftDelim;
if (group.value.left === ".") { if (group.value.left === ".") {
@ -1163,37 +1160,38 @@ groupTypes.leftright = function(group, options, prev) {
inner.push(rightDelim); inner.push(rightDelim);
return makeSpan( return makeSpan(
["minner", options.style.cls()], inner, options.getColor()); ["minner", style.cls()], inner, options.getColor());
}; };
groupTypes.rule = function(group, options, prev) { groupTypes.rule = function(group, options, prev) {
// Make an empty span for the rule // Make an empty span for the rule
var rule = makeSpan(["mord", "rule"], [], options.getColor()); var rule = makeSpan(["mord", "rule"], [], options.getColor());
var style = options.style;
// Calculate the shift, width, and height of the rule, and account for units // Calculate the shift, width, and height of the rule, and account for units
var shift = 0; var shift = 0;
if (group.value.shift) { if (group.value.shift) {
shift = group.value.shift.number; shift = group.value.shift.number;
if (group.value.shift.unit === "ex") { if (group.value.shift.unit === "ex") {
shift *= fontMetrics.metrics.xHeight; shift *= style.metrics.xHeight;
} }
} }
var width = group.value.width.number; var width = group.value.width.number;
if (group.value.width.unit === "ex") { if (group.value.width.unit === "ex") {
width *= fontMetrics.metrics.xHeight; width *= style.metrics.xHeight;
} }
var height = group.value.height.number; var height = group.value.height.number;
if (group.value.height.unit === "ex") { if (group.value.height.unit === "ex") {
height *= fontMetrics.metrics.xHeight; height *= style.metrics.xHeight;
} }
// 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 /= options.style.sizeMultiplier; shift /= style.sizeMultiplier;
width /= options.style.sizeMultiplier; width /= style.sizeMultiplier;
height /= options.style.sizeMultiplier; height /= style.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";
@ -1211,16 +1209,17 @@ groupTypes.rule = function(group, options, prev) {
groupTypes.kern = function(group, options, prev) { groupTypes.kern = function(group, options, prev) {
// Make an empty span for the rule // Make an empty span for the rule
var rule = makeSpan(["mord", "rule"], [], options.getColor()); var rule = makeSpan(["mord", "rule"], [], options.getColor());
var style = options.style;
var dimension = 0; var dimension = 0;
if (group.value.dimension) { if (group.value.dimension) {
dimension = group.value.dimension.number; dimension = group.value.dimension.number;
if (group.value.dimension.unit === "ex") { if (group.value.dimension.unit === "ex") {
dimension *= fontMetrics.metrics.xHeight; dimension *= style.metrics.xHeight;
} }
} }
dimension /= options.style.sizeMultiplier; dimension /= style.sizeMultiplier;
rule.style.marginLeft = dimension + "em"; rule.style.marginLeft = dimension + "em";
@ -1230,6 +1229,7 @@ groupTypes.kern = function(group, options, prev) {
groupTypes.accent = function(group, options, prev) { groupTypes.accent = function(group, options, prev) {
// Accents are handled in the TeXbook pg. 443, rule 12. // Accents are handled in the TeXbook pg. 443, rule 12.
var base = group.value.base; var base = group.value.base;
var style = options.style;
var supsubGroup; var supsubGroup;
if (group.type === "supsub") { if (group.type === "supsub") {
@ -1258,7 +1258,7 @@ groupTypes.accent = function(group, options, prev) {
// Build the base group // Build the base group
var body = buildGroup( var body = buildGroup(
base, options.withStyle(options.style.cramp())); base, options.withStyle(style.cramp()));
// Calculate the skew of the accent. This is based on the line "If the // Calculate the skew of the accent. This is based on the line "If the
// nucleus is not a single character, let s = 0; otherwise set s to the // nucleus is not a single character, let s = 0; otherwise set s to the
@ -1272,7 +1272,7 @@ groupTypes.accent = function(group, options, prev) {
var baseChar = getBaseElem(base); var 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
var baseGroup = buildGroup( var baseGroup = buildGroup(
baseChar, options.withStyle(options.style.cramp())); 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
@ -1284,7 +1284,9 @@ groupTypes.accent = function(group, options, prev) {
} }
// calculate the amount of space between the body and the accent // calculate the amount of space between the body and the accent
var clearance = Math.min(body.height, fontMetrics.metrics.xHeight); var clearance = Math.min(
body.height,
style.metrics.xHeight);
// Build the accent // Build the accent
var accent = buildCommon.makeSymbol( var accent = buildCommon.makeSymbol(

View File

@ -81,7 +81,7 @@ var makeSmallDelim = function(delim, style, center, options, mode) {
if (center) { if (center) {
var shift = var shift =
(1 - options.style.sizeMultiplier / style.sizeMultiplier) * (1 - options.style.sizeMultiplier / style.sizeMultiplier) *
fontMetrics.metrics.axisHeight; options.style.metrics.axisHeight;
span.style.top = shift + "em"; span.style.top = shift + "em";
span.height -= shift; span.height -= shift;
@ -105,7 +105,7 @@ var makeLargeDelim = function(delim, size, center, options, mode) {
if (center) { if (center) {
var shift = (1 - options.style.sizeMultiplier) * var shift = (1 - options.style.sizeMultiplier) *
fontMetrics.metrics.axisHeight; options.style.metrics.axisHeight;
span.style.top = shift + "em"; span.style.top = shift + "em";
span.height -= shift; span.height -= shift;
@ -278,7 +278,7 @@ var makeStackedDelim = function(delim, heightTotal, center, options, mode) {
// that in this context, "center" means that the delimiter should be // that in this context, "center" means that the delimiter should be
// centered around the axis in the current style, while normally it is // centered around the axis in the current style, while normally it is
// centered around the axis in textstyle. // centered around the axis in textstyle.
var axisHeight = fontMetrics.metrics.axisHeight; var axisHeight = options.style.metrics.axisHeight;
if (center) { if (center) {
axisHeight *= options.style.sizeMultiplier; axisHeight *= options.style.sizeMultiplier;
} }
@ -508,7 +508,7 @@ var makeCustomSizedDelim = function(delim, height, center, options, mode) {
var makeLeftRightDelim = function(delim, height, depth, options, mode) { var makeLeftRightDelim = function(delim, height, depth, options, mode) {
// We always center \left/\right delimiters, so the axis is always shifted // We always center \left/\right delimiters, so the axis is always shifted
var axisHeight = var axisHeight =
fontMetrics.metrics.axisHeight * options.style.sizeMultiplier; options.style.metrics.axisHeight * options.style.sizeMultiplier;
// Taken from TeX source, tex.web, function make_left_right // Taken from TeX source, tex.web, function make_left_right
var delimiterFactor = 901; var delimiterFactor = 901;

View File

@ -1,7 +1,7 @@
/* eslint no-constant-condition:0 */ /* eslint no-constant-condition:0 */
var fontMetrics = require("./fontMetrics");
var parseData = require("./parseData"); var parseData = require("./parseData");
var ParseError = require("./ParseError"); var ParseError = require("./ParseError");
var Style = require("./Style");
var ParseNode = parseData.ParseNode; var ParseNode = parseData.ParseNode;
@ -161,7 +161,11 @@ defineEnvironment("cases", {
type: "align", type: "align",
align: "l", align: "l",
pregap: 0, pregap: 0,
postgap: fontMetrics.metrics.quad, // TODO(kevinb) get the current style.
// For now we use the metrics for TEXT style which is what we were
// doing before. Before attempting to get the current style we
// should look at TeX's behavior especially for \over and matrices.
postgap: Style.TEXT.metrics.quad,
}, { }, {
type: "align", type: "align",
align: "l", align: "l",

View File

@ -10,38 +10,52 @@ var cjkRegex = require("./unicodeRegexes").cjkRegex;
* `metrics` variable and the getCharacterMetrics function. * `metrics` variable and the getCharacterMetrics function.
*/ */
// These font metrics are extracted from TeX by using // In TeX, there are actually three sets of dimensions, one for each of
// \font\a=cmmi10 // textstyle, scriptstyle, and scriptscriptstyle. These are provided in the
// \showthe\fontdimenX\a // the arrays below, in that order.
// where X is the corresponding variable number. These correspond to the font //
// parameters of the symbol fonts. In TeX, there are actually three sets of // The font metrics are stored in fonts cmsy10, cmsy7, and cmsy5 respsectively.
// dimensions, one for each of textstyle, scriptstyle, and scriptscriptstyle, // This was determined by running the folllowing script:
// but we only use the textstyle ones, and scale certain dimensions accordingly. //
// See the TeXbook, page 441. // latex -interaction=nonstopmode \
var sigma1 = 0.025; // '\documentclass{article}\usepackage{amsmath}\begin{document}' \
var sigma2 = 0; // '$a$ \expandafter\show\the\textfont2' \
var sigma3 = 0; // '\expandafter\show\the\scriptfont2' \
var sigma4 = 0; // '\expandafter\show\the\scriptscriptfont2' \
var sigma5 = 0.431; // '\stop'
var sigma6 = 1; //
var sigma7 = 0; // The metrics themselves were retreived using the following commands:
var sigma8 = 0.677; //
var sigma9 = 0.394; // tftopl cmsy10
var sigma10 = 0.444; // tftopl cmsy7
var sigma11 = 0.686; // tftopl cmsy5
var sigma12 = 0.345; //
var sigma13 = 0.413; // The output of each of these commands is quite lengthy. The only part we
var sigma14 = 0.363; // care about is the FONTDIMEN section. Each value is measured in EMs.
var sigma15 = 0.289; var sigmas = {
var sigma16 = 0.150; slant: [0.250, 0.250, 0.250], // sigma1
var sigma17 = 0.247; space: [0.000, 0.000, 0.000], // sigma2
var sigma18 = 0.386; stretch: [0.000, 0.000, 0.000], // sigma3
var sigma19 = 0.050; shrink: [0.000, 0.000, 0.000], // sigma4
var sigma20 = 2.390; xHeight: [0.431, 0.431, 0.431], // sigma5
var sigma21 = 1.01; quad: [1.000, 1.171, 1.472], // sigma6
var sigma21Script = 0.81; extraSpace: [0.000, 0.000, 0.000], // sigma7
var sigma21ScriptScript = 0.71; num1: [0.677, 0.732, 0.925], // sigma8
var sigma22 = 0.250; num2: [0.394, 0.384, 0.387], // sigma9
num3: [0.444, 0.471, 0.504], // sigma10
denom1: [0.686, 0.752, 1.025], // sigma11
denom2: [0.345, 0.344, 0.532], // sigma12
sup1: [0.413, 0.503, 0.504], // sigma13
sup2: [0.363, 0.431, 0.404], // sigma14
sup3: [0.289, 0.286, 0.294], // sigma15
sub1: [0.150, 0.143, 0.200], // sigma16
sub2: [0.247, 0.286, 0.400], // sigma17
supDrop: [0.386, 0.353, 0.494], // sigma18
subDrop: [0.050, 0.071, 0.100], // sigma19
delim1: [2.390, 1.700, 1.980], // sigma20
delim2: [1.010, 1.157, 1.420], // sigma21
axisHeight: [0.250, 0.250, 0.250], // sigma22
};
// These font metrics are extracted from TeX by using // These font metrics are extracted from TeX by using
// \font\a=cmex10 // \font\a=cmex10
@ -76,21 +90,6 @@ var doubleRuleSep = 2.0 / ptPerEm;
* This is just a mapping from common names to real metrics * This is just a mapping from common names to real metrics
*/ */
var metrics = { var metrics = {
xHeight: sigma5,
quad: sigma6,
num1: sigma8,
num2: sigma9,
num3: sigma10,
denom1: sigma11,
denom2: sigma12,
sup1: sigma13,
sup2: sigma14,
sup3: sigma15,
sub1: sigma16,
sub2: sigma17,
supDrop: sigma18,
subDrop: sigma19,
axisHeight: sigma22,
defaultRuleThickness: xi8, defaultRuleThickness: xi8,
bigOpSpacing1: xi9, bigOpSpacing1: xi9,
bigOpSpacing2: xi10, bigOpSpacing2: xi10,
@ -98,22 +97,7 @@ var metrics = {
bigOpSpacing4: xi12, bigOpSpacing4: xi12,
bigOpSpacing5: xi13, bigOpSpacing5: xi13,
ptPerEm: ptPerEm, ptPerEm: ptPerEm,
emPerEx: sigma5 / sigma6,
doubleRuleSep: doubleRuleSep, doubleRuleSep: doubleRuleSep,
// TODO(alpert): Missing parallel structure here. We should probably add
// style-specific metrics for all of these.
delim1: sigma20,
getDelim2: function(style) {
if (style.size === Style.TEXT.size) {
return sigma21;
} else if (style.size === Style.SCRIPT.size) {
return sigma21Script;
} else if (style.size === Style.SCRIPTSCRIPT.size) {
return sigma21ScriptScript;
}
throw new Error("Unexpected style size: " + style.size);
},
}; };
// This map contains a mapping from font name and character code to character // This map contains a mapping from font name and character code to character
@ -289,5 +273,6 @@ var getCharacterMetrics = function(character, style) {
module.exports = { module.exports = {
metrics: metrics, metrics: metrics,
sigmas: sigmas,
getCharacterMetrics: getCharacterMetrics, getCharacterMetrics: getCharacterMetrics,
}; };

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB