mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-06 19:58:40 +00:00
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:
committed by
Kevin Barabash
parent
782484eb75
commit
4480f2c987
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -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];
|
||||
};
|
||||
|
BIN
test/screenshotter/images/RelativeUnits-chrome.png
Normal file
BIN
test/screenshotter/images/RelativeUnits-chrome.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
test/screenshotter/images/RelativeUnits-firefox.png
Normal file
BIN
test/screenshotter/images/RelativeUnits-firefox.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
@@ -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:
|
||||
|
Reference in New Issue
Block a user