diff --git a/src/buildHTML.js b/src/buildHTML.js
index a91734e2..944fdc60 100644
--- a/src/buildHTML.js
+++ b/src/buildHTML.js
@@ -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;
};
diff --git a/src/fontMetrics.js b/src/fontMetrics.js
index f8048c16..9c9be045 100644
--- a/src/fontMetrics.js
+++ b/src/fontMetrics.js
@@ -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];
};
diff --git a/test/screenshotter/images/RelativeUnits-chrome.png b/test/screenshotter/images/RelativeUnits-chrome.png
new file mode 100644
index 00000000..fb1795f7
Binary files /dev/null and b/test/screenshotter/images/RelativeUnits-chrome.png differ
diff --git a/test/screenshotter/images/RelativeUnits-firefox.png b/test/screenshotter/images/RelativeUnits-firefox.png
new file mode 100644
index 00000000..68c23cf5
Binary files /dev/null and b/test/screenshotter/images/RelativeUnits-firefox.png differ
diff --git a/test/screenshotter/ss_data.yaml b/test/screenshotter/ss_data.yaml
index a6818a97..348d9849 100644
--- a/test/screenshotter/ss_data.yaml
+++ b/test/screenshotter/ss_data.yaml
@@ -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: