From cf23517499c7755077e89ad0463d5eaeda4e5572 Mon Sep 17 00:00:00 2001 From: Alexander Terenin Date: Tue, 12 Dec 2017 20:09:52 -0800 Subject: [PATCH] Added support for bold italic symbols (#1011) * added support for \boldsymbol * added \boldsymbol tests * added \boldsymbol screenshots * added \bm, snapshot, updated screenshots * updated snapshot for \boldsymbol * properly display \imath and 2 in boldsymbol * implemented \boldsymbol for + * fix typo, remove boldsymbolLetters hardcode * make comment about using Main-Bold more general * fixed trailing space CI error --- src/buildCommon.js | 47 +++++++++++++++--- src/buildMathML.js | 9 +++- src/functions.js | 5 +- static/katex.less | 6 +++ test/__snapshots__/mathml-spec.js.snap | 40 +++++++++++++++ test/mathml-spec.js | 5 ++ .../images/BoldSymbol-chrome.png | Bin 0 -> 13370 bytes .../images/BoldSymbol-firefox.png | Bin 0 -> 11986 bytes test/screenshotter/ss_data.yaml | 1 + 9 files changed, 104 insertions(+), 9 deletions(-) create mode 100644 test/screenshotter/images/BoldSymbol-chrome.png create mode 100644 test/screenshotter/images/BoldSymbol-firefox.png diff --git a/src/buildCommon.js b/src/buildCommon.js index 3f53bf91..e0796707 100644 --- a/src/buildCommon.js +++ b/src/buildCommon.js @@ -113,8 +113,13 @@ const mathsym = function( // Have a special case for when the value = \ because the \ is used as a // textord in unsupported command errors but cannot be parsed as a regular // text ordinal and is therefore not present as a symbol in the symbols - // table for text - if (value === "\\" || symbols[mode][value].font === "main") { + // table for text, as well as a special case for boldsymbol because it + // can be used for bold + and - + if ((options && options.font && options.font === "boldsymbol") && + lookupSymbol(value, "Main-Bold", mode).metrics) { + return makeSymbol(value, "Main-Bold", mode, options, + classes.concat(["mathbf"])); + } else if (value === "\\" || symbols[mode][value].font === "main") { return makeSymbol(value, "Main-Regular", mode, options, classes); } else { return makeSymbol( @@ -179,6 +184,33 @@ const mathit = function( } }; +/** + * Determines which of the two font names (Main-Bold and Math-BoldItalic) and + * corresponding style tags (mathbf or boldsymbol) to use for font "boldsymbol", + * depending on the symbol. Use this function instead of fontMap for font + * "boldsymbol". + */ +const boldsymbol = function( + value: string, + mode: Mode, + options: Options, + classes: string[], +): {| fontName: string, fontClass: string |} { + if (lookupSymbol(value, "Math-BoldItalic", mode).metrics) { + return { + fontName: "Math-BoldItalic", + fontClass: "boldsymbol", + }; + } else { + // Some glyphs do not exist in Math-BoldItalic so we need to use + // Main-Bold instead. + return { + fontName: "Main-Bold", + fontClass: "mathbf", + }; + } +}; + /** * Makes either a mathord or textord in the correct font and color. */ @@ -195,7 +227,9 @@ const makeOrd = function( const font = options.font; if (font) { let fontLookup; - if (font === "mathit" || utils.contains(mainitLetters, value)) { + if (font === "boldsymbol") { + fontLookup = boldsymbol(value, mode, options, classes); + } else if (font === "mathit" || utils.contains(mainitLetters, value)) { fontLookup = mathit(value, mode, options, classes); } else { fontLookup = fontMap[font]; @@ -590,9 +624,10 @@ const fontMap: {[string]: {| variant: string, fontName: string |}} = { fontName: "Main-Italic", }, - // "mathit" is missing because it requires the use of two fonts: Main-Italic - // and Math-Italic. This is handled by a special case in makeOrd which ends - // up calling mathit. + // "mathit" and "boldsymbol" are missing because they require the use of two + // fonts: Main-Italic and Math-Italic for "mathit", and Math-BoldItalic and + // Main-Bold for "boldsymbol". This is handled by a special case in makeOrd + // which ends up calling mathit and boldsymbol. // families "mathbb": { diff --git a/src/buildMathML.js b/src/buildMathML.js index a71a1433..b14d4183 100644 --- a/src/buildMathML.js +++ b/src/buildMathML.js @@ -39,6 +39,8 @@ const getVariant = function(group, options) { const mode = group.mode; if (font === "mathit") { return "italic"; + } else if (font === "boldsymbol") { + return "bold-italic"; } let value = group.value; @@ -106,10 +108,15 @@ groupTypes.textord = function(group, options) { return node; }; -groupTypes.bin = function(group) { +groupTypes.bin = function(group, options) { const node = new mathMLTree.MathNode( "mo", [makeText(group.value, group.mode)]); + const variant = getVariant(group, options); + if (variant === "bold-italic") { + node.setAttribute("mathvariant", variant); + } + return node; }; diff --git a/src/functions.js b/src/functions.js index 08a3d07e..eaa9e509 100644 --- a/src/functions.js +++ b/src/functions.js @@ -147,6 +147,7 @@ const fontAliases = { "\\Bbb": "\\mathbb", "\\bold": "\\mathbf", "\\frak": "\\mathfrak", + "\\bm": "\\boldsymbol", }; const singleCharIntegrals: {[string]: string} = { @@ -244,14 +245,14 @@ defineFunction([ defineFunction([ // styles - "\\mathrm", "\\mathit", "\\mathbf", + "\\mathrm", "\\mathit", "\\mathbf", "\\boldsymbol", // families "\\mathbb", "\\mathcal", "\\mathfrak", "\\mathscr", "\\mathsf", "\\mathtt", // aliases - "\\Bbb", "\\bold", "\\frak", + "\\Bbb", "\\bold", "\\frak", "\\bm", ], { numArgs: 1, greediness: 2, diff --git a/static/katex.less b/static/katex.less index 1c69188f..5e455a40 100644 --- a/static/katex.less +++ b/static/katex.less @@ -75,6 +75,12 @@ font-weight: bold; } + .boldsymbol { + font-family: KaTeX_Math; + font-weight: bold; + font-style: italic; + } + .amsrm { font-family: KaTeX_AMS; } diff --git a/test/__snapshots__/mathml-spec.js.snap b/test/__snapshots__/mathml-spec.js.snap index 723e3642..f45c319f 100644 --- a/test/__snapshots__/mathml-spec.js.snap +++ b/test/__snapshots__/mathml-spec.js.snap @@ -77,6 +77,46 @@ exports[`A MathML builder should make prime operators into nodes 1`] = ` `; +exports[`A MathML builder should render boldsymbol with the correct mathvariants 1`] = ` + + + + + + + A + + + x + + + 2 + + + k + + + ω + + + Ω + + + ı + + + + + + + + + \\boldsymbol{Ax2k\\omega\\Omega\\imath+} + + + + +`; + exports[`A MathML builder should render mathchoice as if there was nothing 1`] = ` diff --git a/test/mathml-spec.js b/test/mathml-spec.js index 7b285058..a931d41f 100644 --- a/test/mathml-spec.js +++ b/test/mathml-spec.js @@ -75,4 +75,9 @@ describe("A MathML builder", function() { expect(getMathML(`x_{y_{\\mathchoice{D}{T}{S}{${cmd}}}}`)) .toMatchSnapshot(); }); + + it("should render boldsymbol with the correct mathvariants", () => { + expect(getMathML(`\\boldsymbol{Ax2k\\omega\\Omega\\imath+}`)) + .toMatchSnapshot(); + }); }); diff --git a/test/screenshotter/images/BoldSymbol-chrome.png b/test/screenshotter/images/BoldSymbol-chrome.png new file mode 100644 index 0000000000000000000000000000000000000000..f4347660ca033700970fd86ba59b1f525d92ec35 GIT binary patch literal 13370 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 literal 0 HcmV?d00001 diff --git a/test/screenshotter/images/BoldSymbol-firefox.png b/test/screenshotter/images/BoldSymbol-firefox.png new file mode 100644 index 0000000000000000000000000000000000000000..f97143aff5956f3e5502a596285d1886f7ef9e0a GIT binary patch literal 11986 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