Correct computation of TeX sizes. (#755)

* Em and mu sizes are relative to current font's quad metric,
  which might not be "1em" in CSS terms.
* We don't need the `emPerEx` metric, because we want to convert
  TeX `ex` to CSS `em`, not TeX `ex` to TeX `em`. The `xHeight`
  metric does that already: it is relative to font size.
* Mu sizes scale with the style, em and ex do not (empirically
  checked; agrees with rule 2 of TeXbook Appendix G).
  This corrects a bug in b866cd5224 (in which em scaled),
  and a bug in previous versions (in which mu did not scale).
* Correct rule sizes. Previous comment---"the sizes of rules are
  absolute"---was misleading. Rule sizes are NOT absolute---in
  \large size, a rule denominated in 'em' is larger. But the 'em'
  unit is not sensitive to styles. So now a 1em rule will be the
  same size in super/subscript, as it should be, but an 18mu rule
  will change size in super/subscript, as it should.

Note a TODO.
This commit is contained in:
Eddie Kohler
2017-07-09 17:23:00 -04:00
committed by Kevin Barabash
parent 782484eb75
commit 4480f2c987
5 changed files with 47 additions and 21 deletions

View File

@@ -547,14 +547,41 @@ groupTypes.genfrac = function(group, options) {
options);
};
/**
* Parse a `sizeValue`, as parsed by functions.js argType "size", into
* a CSS em value. `options` gives the current options.
*/
const calculateSize = function(sizeValue, options) {
let x = sizeValue.number;
if (sizeValue.unit === "ex") {
x *= options.fontMetrics().emPerEx;
} else if (sizeValue.unit === "mu") {
x /= 18;
let scale;
// `mu` units scale with scriptstyle/scriptscriptstyle.
// Other units always refer to the *textstyle* font in the current size.
if (sizeValue.unit === "mu") {
scale = options.fontMetrics().cssEmPerMu;
} else {
let unitOptions;
if (options.style.isTight()) {
// isTight() means current style is script/scriptscript.
unitOptions = options.havingStyle(options.style.text());
} else {
unitOptions = options;
}
// TODO: In TeX these units are relative to the quad of the current
// *text* font, e.g. cmr10. KaTeX instead uses values from the
// comparably-sized *Computer Modern symbol* font. At 10pt, these
// match. At 7pt and 5pt, they differ: cmr7=1.138894, cmsy7=1.170641;
// cmr5=1.361133, cmsy5=1.472241. Consider $\scriptsize a\kern1emb$.
// TeX \showlists shows a kern of 1.13889 * fontsize;
// KaTeX shows a kern of 1.171 * fontsize.
if (sizeValue.unit === "ex") {
scale = unitOptions.fontMetrics().xHeight;
} else {
scale = unitOptions.fontMetrics().quad;
}
if (unitOptions !== options) {
scale *= unitOptions.sizeMultiplier / options.sizeMultiplier;
}
}
return x;
return sizeValue.number * scale;
};
groupTypes.array = function(group, options) {
@@ -1312,14 +1339,8 @@ groupTypes.rule = function(group, options) {
shift = calculateSize(group.value.shift, options);
}
let width = calculateSize(group.value.width, options);
let height = calculateSize(group.value.height, options);
// The sizes of rules are absolute, so make it larger if we are in a
// smaller style.
shift /= options.sizeMultiplier;
width /= options.sizeMultiplier;
height /= options.sizeMultiplier;
const width = calculateSize(group.value.width, options);
const height = calculateSize(group.value.height, options);
// Style the rule to the right size
rule.style.borderRightWidth = width + "em";
@@ -1342,15 +1363,11 @@ groupTypes.kern = function(group, options) {
// Make an empty span for the rule
const rule = makeSpan(["mord", "rule"], [], options);
let dimension = 0;
if (group.value.dimension) {
dimension = calculateSize(group.value.dimension, options);
const dimension = calculateSize(group.value.dimension, options);
rule.style.marginLeft = dimension + "em";
}
dimension /= options.sizeMultiplier;
rule.style.marginLeft = dimension + "em";
return rule;
};

View File

@@ -269,7 +269,7 @@ const getFontMetrics = function(size) {
metrics[key] = sigmasAndXis[key][sizeIndex];
}
}
metrics.emPerEx = metrics.xHeight / metrics.quad;
metrics.cssEmPerMu = metrics.quad / 18;
}
return fontMetricsBySizeIndex[sizeIndex];
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -170,6 +170,15 @@ OverUnderset: |
Phantom: \dfrac{1+\phantom{x^{\blue{2}}} = x}{1+x^{\blue{2}} = x}
PrimeSpacing: f'+f_2'+f^{f'}
PrimeSuper: x'^2+x'''^2+x'^2_3+x_3'^2
RelativeUnits: |
\begin{array}{ll}
a\kern1emb^{a\kern1emb^{a\kern1emb}} &
{\footnotesize a\kern1emb^{a\kern1emb^{a\kern1emb}}} \\
a\mkern18mub^{a\mkern18mub^{a\mkern18mub}} &
{\footnotesize a\mkern18mub^{a\mkern18mub^{a\mkern18mub}}} \\
\rule{1em}{1em}^{\rule{1em}{1em}}\rule{18mu}{18mu}^{\rule{18mu}{18mu}} &
{\footnotesize\rule{1em}{1em}^{\rule{1em}{1em}}\rule{18mu}{18mu}^{\rule{18mu}{18mu}}}
\end{array}
RlapBug: \frac{\rlap{x}}{2}
Rule: \rule{1em}{0.5em}\rule{1ex}{2ex}\rule{1em}{1ex}\rule{1em}{0.431ex}
SizingBaseline: