From 4a9c2acbf7a50a6205cc3f42f374a4b3047c9bf1 Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Mon, 25 Jul 2016 04:56:31 +0200 Subject: [PATCH] Add some more symbols (#502) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for the following input sequences: -- --- ` ' `` '' \degree \pounds \maltese resulting in – — ‘ ’ “ ” ° £ ✠ symbols already present in our fonts. As part of this modification, the recognition of multiple dashes was moved from the lexer to the parser. This is neccessary since in math mode a sequence of hyphens is just a sequence of minus signs. Just like a pair of apostrophes in math mode is a double prime not a right double quotation mark. To make this easier, parseGroup and parseOptionalGroup have been merged. --- src/Lexer.js | 6 +- src/Parser.js | 83 ++++++++++-------- src/buildCommon.js | 8 +- src/symbols.js | 14 ++- .../images/DashesAndQuotes-chrome.png | Bin 0 -> 8466 bytes .../images/DashesAndQuotes-firefox.png | Bin 0 -> 8203 bytes test/screenshotter/images/Symbols1-chrome.png | Bin 0 -> 6420 bytes .../screenshotter/images/Symbols1-firefox.png | Bin 0 -> 5519 bytes test/screenshotter/ss_data.yaml | 4 + test/screenshotter/test.tex | 2 +- 10 files changed, 73 insertions(+), 44 deletions(-) create mode 100644 test/screenshotter/images/DashesAndQuotes-chrome.png create mode 100644 test/screenshotter/images/DashesAndQuotes-firefox.png create mode 100644 test/screenshotter/images/Symbols1-chrome.png create mode 100644 test/screenshotter/images/Symbols1-firefox.png diff --git a/src/Lexer.js b/src/Lexer.js index 648c705b9..5243bf280 100644 --- a/src/Lexer.js +++ b/src/Lexer.js @@ -63,7 +63,6 @@ Token.prototype.range = function(endToken, text) { /* The following tokenRegex * - matches typical whitespace (but not NBSP etc.) using its first group - * - matches symbol combinations which result in a single output character * - does not match any control character \x00-\x1f except whitespace * - does not match a bare backslash * - matches any ASCII character except those just mentioned @@ -78,9 +77,8 @@ Token.prototype.range = function(endToken, text) { * still reject the input. */ var tokenRegex = new RegExp( - "([ \r\n\t]+)|(" + // whitespace - "---?" + // special combinations - "|[!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint + "([ \r\n\t]+)|" + // whitespace + "([!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint "|[\uD800-\uDBFF][\uDC00-\uDFFF]" + // surrogate pair "|\\\\(?:[a-zA-Z]+|[^\uD800-\uDFFF])" + // function name ")" diff --git a/src/Parser.js b/src/Parser.js index 64b1689b7..4bfde913b 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -545,7 +545,7 @@ Parser.prototype.parseArguments = function(func, funcData) { if (argType) { arg = this.parseGroupOfType(argType, true); } else { - arg = this.parseOptionalGroup(); + arg = this.parseGroup(true); } if (!arg) { args.push(null); @@ -623,12 +623,7 @@ Parser.prototype.parseGroupOfType = function(innerMode, optional) { } // By the time we get here, innerMode is one of "text" or "math". // We switch the mode of the parser, recurse, then restore the old mode. - var res; - if (optional) { - res = this.parseOptionalGroup(); - } else { - res = this.parseGroup(); - } + var res = this.parseGroup(optional); this.switchMode(outerMode); return res; }; @@ -638,7 +633,7 @@ Parser.prototype.parseGroupOfType = function(innerMode, optional) { * brace-enclosed tokens plus some position information. * * @param {string} modeName Used to describe the mode in error messages - * @param {boolean} optional Whether the group is optional or required + * @param {boolean=} optional Whether the group is optional or required */ Parser.prototype.parseStringGroup = function(modeName, optional) { if (optional && this.nextToken.text !== "[") { @@ -707,53 +702,71 @@ Parser.prototype.parseSizeGroup = function(optional) { }; /** - * Parses a group, which is either a single nucleus (like "x") or an expression - * in braces (like "{x+y}") + * If the argument is false or absent, this parses an ordinary group, + * which is either a single nucleus (like "x") or an expression + * in braces (like "{x+y}"). + * If the argument is true, it parses either a bracket-delimited expression + * (like "[x+y]") or returns null to indicate the absence of a + * bracket-enclosed group. * + * @param {boolean=} optional Whether the group is optional or required * @return {?ParseFuncOrArgument} */ -Parser.prototype.parseGroup = function() { +Parser.prototype.parseGroup = function(optional) { var firstToken = this.nextToken; // Try to parse an open brace - if (this.nextToken.text === "{") { + if (this.nextToken.text === (optional ? "[" : "{")) { // If we get a brace, parse an expression this.consume(); - var expression = this.parseExpression(false); + var expression = this.parseExpression(false, optional ? "]" : null); var lastToken = this.nextToken; // Make sure we get a close brace - this.expect("}"); + this.expect(optional ? "]" : "}"); + if (this.mode === "text") { + this.formLigatures(expression); + } return new ParseFuncOrArgument( new ParseNode("ordgroup", expression, this.mode, firstToken, lastToken), false); } else { - // Otherwise, just return a nucleus - return this.parseSymbol(); + // Otherwise, just return a nucleus, or nothing for an optional group + return optional ? null : this.parseSymbol(); } }; /** - * Parses a group, which is an expression in brackets (like "[x+y]") + * Form ligature-like combinations of characters for text mode. + * This includes inputs like "--", "---", "``" and "''". + * The result will simply replace multiple textord nodes with a single + * character in each value by a single textord node having multiple + * characters in its value. The representation is still ASCII source. * - * @return {?ParseFuncOrArgument} + * @param {Array.} group the nodes of this group, + * list will be moified in place */ -Parser.prototype.parseOptionalGroup = function() { - var firstToken = this.nextToken; - // Try to parse an open bracket - if (this.nextToken.text === "[") { - // If we get a brace, parse an expression - this.consume(); - var expression = this.parseExpression(false, "]"); - var lastToken = this.nextToken; - // Make sure we get a close bracket - this.expect("]"); - return new ParseFuncOrArgument( - new ParseNode("ordgroup", expression, this.mode, - firstToken, lastToken), - false); - } else { - // Otherwise, return null, - return null; +Parser.prototype.formLigatures = function(group) { + var i; + var n = group.length - 1; + for (i = 0; i < n; ++i) { + var a = group[i]; + var v = a.value; + if (v === "-" && group[i + 1].value === "-") { + if (i + 1 < n && group[i + 2].value === "-") { + group.splice(i, 3, new ParseNode( + "textord", "---", "text", a, group[i + 2])); + n -= 2; + } else { + group.splice(i, 2, new ParseNode( + "textord", "--", "text", a, group[i + 1])); + n -= 1; + } + } + if ((v === "'" || v === "`") && group[i + 1].value === v) { + group.splice(i, 2, new ParseNode( + "textord", v + v, "text", a, group[i + 1])); + n -= 1; + } } }; diff --git a/src/buildCommon.js b/src/buildCommon.js index b60e1860a..d793b5b38 100644 --- a/src/buildCommon.js +++ b/src/buildCommon.js @@ -23,9 +23,11 @@ var greekCapitals = [ "\\Omega", ]; -var dotlessLetters = [ +// The following have to be loaded from Main-Italic font, using class mainit +var mainitLetters = [ "\u0131", // dotless i, \imath "\u0237", // dotless j, \jmath + "\u00a3", // \pounds ]; /** @@ -101,7 +103,7 @@ var mathit = function(value, mode, color, classes) { if (/[0-9]/.test(value.charAt(0)) || // glyphs for \imath and \jmath do not exist in Math-Italic so we // need to use Main-Italic instead - utils.contains(dotlessLetters, value) || + utils.contains(mainitLetters, value) || utils.contains(greekCapitals, value)) { return makeSymbol( value, "Main-Italic", mode, color, classes.concat(["mainit"])); @@ -126,7 +128,7 @@ var makeOrd = function(group, options, type) { var font = options.font; if (font) { - if (font === "mathit" || utils.contains(dotlessLetters, value)) { + if (font === "mathit" || utils.contains(mainitLetters, value)) { return mathit(value, mode, color, classes); } else { var fontName = fontMap[font].fontName; diff --git a/src/symbols.js b/src/symbols.js index 6170cb5e7..aef7c8748 100644 --- a/src/symbols.js +++ b/src/symbols.js @@ -589,6 +589,18 @@ defineSymbol(math, main, accent, "\u02d9", "\\dot"); defineSymbol(math, main, mathord, "\u0131", "\\imath"); defineSymbol(math, main, mathord, "\u0237", "\\jmath"); +defineSymbol(text, main, textord, "\u2013", "--"); +defineSymbol(text, main, textord, "\u2014", "---"); +defineSymbol(text, main, textord, "\u2018", "`"); +defineSymbol(text, main, textord, "\u2019", "'"); +defineSymbol(text, main, textord, "\u201c", "``"); +defineSymbol(text, main, textord, "\u201d", "''"); +defineSymbol(math, main, textord, "\u00b0", "\\degree"); +defineSymbol(text, main, textord, "\u00b0", "\\degree"); +defineSymbol(math, main, mathord, "\u00a3", "\\pounds"); +defineSymbol(math, ams, textord, "\u2720", "\\maltese"); +defineSymbol(text, ams, textord, "\u2720", "\\maltese"); + defineSymbol(text, main, spacing, "\u00a0", "\\ "); defineSymbol(text, main, spacing, "\u00a0", " "); defineSymbol(text, main, spacing, "\u00a0", "~"); @@ -605,7 +617,7 @@ for (i = 0; i < mathTextSymbols.length; i++) { } // All of these are textords in text mode -var textSymbols = "0123456789`!@*()-=+[]'\";:?/.,"; +var textSymbols = "0123456789!@*()-=+[]\";:?/.,"; for (i = 0; i < textSymbols.length; i++) { ch = textSymbols.charAt(i); defineSymbol(text, main, textord, ch, ch); diff --git a/test/screenshotter/images/DashesAndQuotes-chrome.png b/test/screenshotter/images/DashesAndQuotes-chrome.png new file mode 100644 index 0000000000000000000000000000000000000000..14fdba805131952184af00f006b1a9588a5019c9 GIT binary patch literal 8466 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

Z7 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 literal 0 HcmV?d00001 diff --git a/test/screenshotter/ss_data.yaml b/test/screenshotter/ss_data.yaml index a2ae511d7..4f2ce6d6c 100644 --- a/test/screenshotter/ss_data.yaml +++ b/test/screenshotter/ss_data.yaml @@ -36,6 +36,7 @@ Cases: | Colors: tex: \blue{a}\color{#0f0}{b}\color{red}{c} nolatex: different syntax and different scope +DashesAndQuotes: \text{``a'' b---c -- d----`e'-{-}-f}--``x'' DeepFontSizing: tex: | a^{\big| x^{\big(}}_{\Big\uparrow} + @@ -109,6 +110,9 @@ SupSubHorizSpacing: | SupSubLeftAlignReset: | \omega^8_{888} \quad \frac{1}{\hat{\omega}^{8}_{888}} \quad \displaystyle\sum_{\omega^{8}_{888}} SupSubOffsets: \displaystyle \int_{2+3}x f^{2+3}+3\lim_{2+3+4+5}f +Symbols1: | + \maltese\degree\pounds\$ + \text{\maltese\degree} Text: \frac{a}{b}\text{c~ {ab} \ e}+fg UnsupportedCmds: tex: \err\,\frac\fracerr3\,2^\superr_\suberr\,\sqrt\sqrterr diff --git a/test/screenshotter/test.tex b/test/screenshotter/test.tex index 2945e8b8c..85c5d2032 100644 --- a/test/screenshotter/test.tex +++ b/test/screenshotter/test.tex @@ -1,6 +1,6 @@ \documentclass[10pt]{article} -\usepackage{amsmath,amssymb} +\usepackage{amsmath,amssymb,textcomp,gensymb} \usepackage[mathscr]{eucal} \usepackage{eufrak} \usepackage[papersize={133pt,100pt},margin=0.5pt]{geometry}