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: