mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-05 19:28:39 +00:00
Fix ligatures vs. \tt (#1379)
* Fix ligatures not always triggering Example: \text{\rm --} would not cause ligature but should * Remove ligatures in \texttt mode * Add screenshot tests * Fix MathML ligatures * Fix type * Handle \text... fonts in MathML building * Remove leftover console deubgging
This commit is contained in:
committed by
Kevin Barabash
parent
4f9851fb0c
commit
1e645198f7
@@ -198,6 +198,9 @@ export default class Parser {
|
||||
}
|
||||
body.push(atom);
|
||||
}
|
||||
if (this.mode === "text") {
|
||||
this.formLigatures(body);
|
||||
}
|
||||
return this.handleInfixNodes(body);
|
||||
}
|
||||
|
||||
@@ -870,9 +873,6 @@ export default class Parser {
|
||||
this.gullet.endGroup();
|
||||
// Make sure we get a close brace
|
||||
this.expect(optional ? "]" : "}");
|
||||
if (mode === "text") {
|
||||
this.formLigatures(expression);
|
||||
}
|
||||
return newArgument(
|
||||
new ParseNode(
|
||||
"ordgroup", expression, this.mode, firstToken, lastToken),
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
import domTree from "./domTree";
|
||||
import fontMetrics from "./fontMetrics";
|
||||
import symbols from "./symbols";
|
||||
import symbols, {ligatures} from "./symbols";
|
||||
import utils from "./utils";
|
||||
import {wideCharacterFont} from "./wide-character";
|
||||
import {calculateSize} from "./units";
|
||||
@@ -226,7 +226,7 @@ const makeOrd = function<NODETYPE: "spacing" | "mathord" | "textord">(
|
||||
group: ParseNode<NODETYPE>,
|
||||
options: Options,
|
||||
type: "mathord" | "textord",
|
||||
): domTree.symbolNode {
|
||||
): domTree.symbolNode | domTree.documentFragment {
|
||||
const mode = group.mode;
|
||||
const value = group.value;
|
||||
|
||||
@@ -260,9 +260,19 @@ const makeOrd = function<NODETYPE: "spacing" | "mathord" | "textord">(
|
||||
options.fontShape);
|
||||
fontClasses = [fontOrFamily, options.fontWeight, options.fontShape];
|
||||
}
|
||||
|
||||
if (lookupSymbol(value, fontName, mode).metrics) {
|
||||
return makeSymbol(value, fontName, mode, options,
|
||||
classes.concat(fontClasses));
|
||||
} else if (ligatures.hasOwnProperty(value) &&
|
||||
fontName.substr(0, 10) === "Typewriter") {
|
||||
// Deconstruct ligatures in monospace fonts (\texttt, \tt).
|
||||
const parts = [];
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
parts.push(makeSymbol(value[i], fontName, mode, options,
|
||||
classes.concat(fontClasses)));
|
||||
}
|
||||
return makeFragment(parts);
|
||||
} else {
|
||||
return mathDefault(value, mode, options, classes, type);
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ import buildCommon from "./buildCommon";
|
||||
import fontMetrics from "./fontMetrics";
|
||||
import mathMLTree from "./mathMLTree";
|
||||
import ParseError from "./ParseError";
|
||||
import symbols from "./symbols";
|
||||
import symbols, {ligatures} from "./symbols";
|
||||
import utils from "./utils";
|
||||
import {_mathmlGroupBuilders as groupBuilders} from "./defineFunction";
|
||||
|
||||
@@ -16,11 +16,13 @@ import {_mathmlGroupBuilders as groupBuilders} from "./defineFunction";
|
||||
* Takes a symbol and converts it into a MathML text node after performing
|
||||
* optional replacement from symbols.js.
|
||||
*/
|
||||
export const makeText = function(text, mode) {
|
||||
if (symbols[mode][text] && symbols[mode][text].replace) {
|
||||
if (text.charCodeAt(0) !== 0xD835) {
|
||||
text = symbols[mode][text].replace;
|
||||
}
|
||||
export const makeText = function(text, mode, options) {
|
||||
if (symbols[mode][text] && symbols[mode][text].replace &&
|
||||
text.charCodeAt(0) !== 0xD835 &&
|
||||
!(ligatures.hasOwnProperty(text) && options &&
|
||||
((options.fontFamily && options.fontFamily.substr(4, 2) === "tt") ||
|
||||
(options.font && options.font.substr(4, 2) === "tt")))) {
|
||||
text = symbols[mode][text].replace;
|
||||
}
|
||||
|
||||
return new mathMLTree.TextNode(text);
|
||||
@@ -42,6 +44,31 @@ export const makeRow = function(body) {
|
||||
* Returns the math variant as a string or null if none is required.
|
||||
*/
|
||||
export const getVariant = function(group, options) {
|
||||
// Handle \text... font specifiers as best we can.
|
||||
// MathML has a limited list of allowable mathvariant specifiers; see
|
||||
// https://www.w3.org/TR/MathML3/chapter3.html#presm.commatt
|
||||
if (options.fontFamily === "texttt") {
|
||||
return "monospace";
|
||||
} else if (options.fontFamily === "textsf") {
|
||||
if (options.fontShape === "textit" &&
|
||||
options.fontWeight === "textbf") {
|
||||
return "sans-serif-bold-italic";
|
||||
} else if (options.fontShape === "textit") {
|
||||
return "sans-serif-italic";
|
||||
} else if (options.fontWeight === "textbf") {
|
||||
return "bold-sans-serif";
|
||||
} else {
|
||||
return "sans-serif";
|
||||
}
|
||||
} else if (options.fontShape === "textit" &&
|
||||
options.fontWeight === "textbf") {
|
||||
return "bold-italic";
|
||||
} else if (options.fontShape === "textit") {
|
||||
return "italic";
|
||||
} else if (options.fontWeight === "textbf") {
|
||||
return "bold";
|
||||
}
|
||||
|
||||
const font = options.font;
|
||||
if (!font) {
|
||||
return null;
|
||||
@@ -82,7 +109,9 @@ export const buildExpression = function(expression, options) {
|
||||
for (let i = 0; i < expression.length; i++) {
|
||||
const group = buildGroup(expression[i], options);
|
||||
// Concatenate adjacent <mtext>s
|
||||
if (group.type === 'mtext' && lastGroup && lastGroup.type === 'mtext') {
|
||||
if (group.type === 'mtext' && lastGroup && lastGroup.type === 'mtext'
|
||||
&& group.getAttribute('mathvariant') ===
|
||||
lastGroup.getAttribute('mathvariant')) {
|
||||
lastGroup.children.push(...group.children);
|
||||
// Concatenate adjacent <mn>s
|
||||
} else if (group.type === 'mn' &&
|
||||
|
@@ -22,7 +22,7 @@ defineFunctionBuilders({
|
||||
mathmlBuilder(group, options) {
|
||||
const node = new mathMLTree.MathNode(
|
||||
"mi",
|
||||
[mml.makeText(group.value, group.mode)]);
|
||||
[mml.makeText(group.value, group.mode, options)]);
|
||||
|
||||
const variant = mml.getVariant(group, options) || "italic";
|
||||
if (variant !== defaultVariant[node.type]) {
|
||||
@@ -38,8 +38,7 @@ defineFunctionBuilders({
|
||||
return buildCommon.makeOrd(group, options, "textord");
|
||||
},
|
||||
mathmlBuilder(group, options) {
|
||||
const text = mml.makeText(group.value, group.mode);
|
||||
|
||||
const text = mml.makeText(group.value, group.mode, options);
|
||||
const variant = mml.getVariant(group, options) || "normal";
|
||||
|
||||
let node;
|
||||
|
@@ -20,6 +20,20 @@ const textFontShapes = {
|
||||
"\\textit": "textit",
|
||||
};
|
||||
|
||||
const optionsWithFont = (group, options) => {
|
||||
const font = group.value.font;
|
||||
// Checks if the argument is a font family or a font style.
|
||||
if (!font) {
|
||||
return options;
|
||||
} else if (textFontFamilies[font]) {
|
||||
return options.withTextFontFamily(textFontFamilies[font]);
|
||||
} else if (textFontWeights[font]) {
|
||||
return options.withTextFontWeight(textFontWeights[font]);
|
||||
} else {
|
||||
return options.withTextFontShape(textFontShapes[font]);
|
||||
}
|
||||
};
|
||||
|
||||
defineFunction({
|
||||
type: "text",
|
||||
names: [
|
||||
@@ -46,23 +60,13 @@ defineFunction({
|
||||
}, parser.mode);
|
||||
},
|
||||
htmlBuilder(group, options) {
|
||||
const font = group.value.font;
|
||||
// Checks if the argument is a font family or a font style.
|
||||
let newOptions;
|
||||
if (!font) {
|
||||
newOptions = options;
|
||||
} else if (textFontFamilies[font]) {
|
||||
newOptions = options.withTextFontFamily(textFontFamilies[font]);
|
||||
} else if (textFontWeights[font]) {
|
||||
newOptions = options.withTextFontWeight(textFontWeights[font]);
|
||||
} else {
|
||||
newOptions = options.withTextFontShape(textFontShapes[font]);
|
||||
}
|
||||
const newOptions = optionsWithFont(group, options);
|
||||
const inner = html.buildExpression(group.value.body, newOptions, true);
|
||||
buildCommon.tryCombineChars(inner);
|
||||
return buildCommon.makeSpan(["mord", "text"], inner, newOptions);
|
||||
},
|
||||
mathmlBuilder(group, options) {
|
||||
return mml.buildExpressionRow(group.value.body, options);
|
||||
const newOptions = optionsWithFont(group, options);
|
||||
return mml.buildExpressionRow(group.value.body, newOptions);
|
||||
},
|
||||
});
|
||||
|
@@ -50,6 +50,13 @@ export class MathNode {
|
||||
this.attributes[name] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an attribute on a MathML node.
|
||||
*/
|
||||
getAttribute(name: string): string {
|
||||
return this.attributes[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the math node into a MathML-namespaced DOM element.
|
||||
*/
|
||||
|
@@ -700,6 +700,14 @@ defineSymbol(text, main, accent, "\u00a8", '\\"'); // diaresis
|
||||
defineSymbol(text, main, accent, "\u02dd", "\\H"); // double acute
|
||||
defineSymbol(text, main, accent, "\u25ef", "\\textcircled"); // \bigcirc glyph
|
||||
|
||||
// These ligatures are detected and created in Parser.js's `formLigatures`.
|
||||
export const ligatures = {
|
||||
"--": true,
|
||||
"---": true,
|
||||
"``": true,
|
||||
"''": true,
|
||||
};
|
||||
|
||||
defineSymbol(text, main, textord, "\u2013", "--");
|
||||
defineSymbol(text, main, textord, "\u2013", "\\textendash");
|
||||
defineSymbol(text, main, textord, "\u2014", "---");
|
||||
|
@@ -1,5 +1,86 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`A MathML builder \\text fonts become mathvariant 1`] = `
|
||||
|
||||
<math>
|
||||
<semantics>
|
||||
<mrow>
|
||||
<mtext>
|
||||
roman
|
||||
</mtext>
|
||||
<mrow>
|
||||
<mtext mathvariant="italic">
|
||||
italic
|
||||
</mtext>
|
||||
<mrow>
|
||||
<mtext mathvariant="bold-italic">
|
||||
bold
|
||||
</mtext>
|
||||
<mtext>
|
||||
</mtext>
|
||||
<mtext mathvariant="bold-italic">
|
||||
italic
|
||||
</mtext>
|
||||
</mrow>
|
||||
</mrow>
|
||||
<mtext mathvariant="bold">
|
||||
bold
|
||||
</mtext>
|
||||
<mrow>
|
||||
<mtext mathvariant="sans-serif">
|
||||
ss
|
||||
</mtext>
|
||||
<mrow>
|
||||
<mtext mathvariant="sans-serif-italic">
|
||||
italic
|
||||
</mtext>
|
||||
<mrow>
|
||||
<mtext mathvariant="sans-serif-bold-italic">
|
||||
bold
|
||||
</mtext>
|
||||
<mtext>
|
||||
</mtext>
|
||||
<mtext mathvariant="sans-serif-bold-italic">
|
||||
italic
|
||||
</mtext>
|
||||
</mrow>
|
||||
</mrow>
|
||||
<mtext mathvariant="bold-sans-serif">
|
||||
bold
|
||||
</mtext>
|
||||
</mrow>
|
||||
<mrow>
|
||||
<mtext mathvariant="monospace">
|
||||
tt
|
||||
</mtext>
|
||||
<mrow>
|
||||
<mtext mathvariant="monospace">
|
||||
italic
|
||||
</mtext>
|
||||
<mrow>
|
||||
<mtext mathvariant="monospace">
|
||||
bold
|
||||
</mtext>
|
||||
<mtext>
|
||||
</mtext>
|
||||
<mtext mathvariant="monospace">
|
||||
italic
|
||||
</mtext>
|
||||
</mrow>
|
||||
</mrow>
|
||||
<mtext mathvariant="monospace">
|
||||
bold
|
||||
</mtext>
|
||||
</mrow>
|
||||
</mrow>
|
||||
<annotation encoding="application/x-tex">
|
||||
\\text{roman\\textit{italic\\textbf{bold italic}}\\textbf{bold}\\textsf{ss\\textit{italic\\textbf{bold italic}}\\textbf{bold}}\\texttt{tt\\textit{italic\\textbf{bold italic}}\\textbf{bold}}}
|
||||
</annotation>
|
||||
</semantics>
|
||||
</math>
|
||||
|
||||
`;
|
||||
|
||||
exports[`A MathML builder accents turn into <mover accent="true"> in MathML 1`] = `
|
||||
|
||||
<math>
|
||||
@@ -57,6 +138,52 @@ exports[`A MathML builder accents turn into <mover accent="true"> in MathML 1`]
|
||||
|
||||
`;
|
||||
|
||||
exports[`A MathML builder ligatures render properly 1`] = `
|
||||
|
||||
<math>
|
||||
<semantics>
|
||||
<mrow>
|
||||
<mtext>
|
||||
“‘Hi—-”’
|
||||
</mtext>
|
||||
<mo>
|
||||
−
|
||||
</mo>
|
||||
<mo>
|
||||
−
|
||||
</mo>
|
||||
<mtext mathvariant="monospace">
|
||||
\`\`‘Hi----''’
|
||||
</mtext>
|
||||
<mrow>
|
||||
<mtext>
|
||||
\`\`
|
||||
</mtext>
|
||||
<mtext mathvariant="monospace">
|
||||
‘Hi
|
||||
</mtext>
|
||||
<mtext>
|
||||
---
|
||||
</mtext>
|
||||
<mtext mathvariant="monospace">
|
||||
-
|
||||
</mtext>
|
||||
<mtext>
|
||||
''
|
||||
</mtext>
|
||||
<mtext mathvariant="monospace">
|
||||
’
|
||||
</mtext>
|
||||
</mrow>
|
||||
</mrow>
|
||||
<annotation encoding="application/x-tex">
|
||||
\\text{\`\`\`Hi----'''}--\\texttt{\`\`\`Hi----'''}\\text{\\tt \`\`\`Hi----'''}
|
||||
</annotation>
|
||||
</semantics>
|
||||
</math>
|
||||
|
||||
`;
|
||||
|
||||
exports[`A MathML builder normal spaces render normally 1`] = `
|
||||
|
||||
<math>
|
||||
|
@@ -118,4 +118,18 @@ describe("A MathML builder", function() {
|
||||
"\\mkern1mu\\mkern3mu\\mkern4mu\\mkern5mu" +
|
||||
"\\mkern-1mu\\mkern-3mu\\mkern-4mu\\mkern-5mu")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('ligatures render properly', () => {
|
||||
expect(getMathML("\\text{```Hi----'''}--" +
|
||||
"\\texttt{```Hi----'''}" +
|
||||
"\\text{\\tt ```Hi----'''}")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('\\text fonts become mathvariant', () => {
|
||||
expect(getMathML("\\text{" +
|
||||
"roman\\textit{italic\\textbf{bold italic}}\\textbf{bold}" +
|
||||
"\\textsf{ss\\textit{italic\\textbf{bold italic}}\\textbf{bold}}" +
|
||||
"\\texttt{tt\\textit{italic\\textbf{bold italic}}\\textbf{bold}}}"))
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 16 KiB |
Binary file not shown.
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 14 KiB |
@@ -76,7 +76,12 @@ Colors:
|
||||
ColorImplicit: bl{ack\color{red}red\textcolor{green}{green}red\color{blue}blue}black
|
||||
ColorSpacing: \textcolor{red}{\displaystyle \int x} + 1
|
||||
Colorbox: a \colorbox{teal} B \fcolorbox{blue}{red}{C} e+\colorbox{teal}x
|
||||
DashesAndQuotes: \text{``a'' b---c -- d----`e'-{-}-f}--``x''
|
||||
DashesAndQuotes: |
|
||||
\begin{array}{l}
|
||||
\text{``a'' b---c -- d----`e'-{-}-f} -- \\
|
||||
\text{\it ``a'' b---c -- d----`e'-{-}-f} ``x'' \\
|
||||
\text{\tt ``a''---} \texttt{``a''---} \mathtt{--} \\
|
||||
\end{array}
|
||||
DeepFontSizing:
|
||||
tex: |
|
||||
a^{\big| x^{\big(}}_{\Big\uparrow} +
|
||||
|
Reference in New Issue
Block a user