From 643fa47845d15e34e0f98657d25c2f123a8ee5c0 Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Thu, 14 Sep 2023 10:18:13 -0400 Subject: [PATCH 1/2] Update check for spacelike mml nodes to be more sensible. (mathjax/MathJax#3098) --- ts/core/MmlTree/MmlNodes/mspace.ts | 4 +++- ts/core/MmlTree/MmlNodes/mtext.ts | 11 +++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ts/core/MmlTree/MmlNodes/mspace.ts b/ts/core/MmlTree/MmlNodes/mspace.ts index 933d0746d..87d2fd559 100644 --- a/ts/core/MmlTree/MmlNodes/mspace.ts +++ b/ts/core/MmlTree/MmlNodes/mspace.ts @@ -77,10 +77,12 @@ export class MmlMspace extends AbstractMmlTokenNode { } /** + * Only make mspace be space-like if it doesn't have certain attributes + * * @override */ public get isSpacelike() { - return true; + return this.attributes.getExplicit('linebreak') === undefined && this.canBreak; } /** diff --git a/ts/core/MmlTree/MmlNodes/mtext.ts b/ts/core/MmlTree/MmlNodes/mtext.ts index 6d22a9ba8..7445ed66b 100644 --- a/ts/core/MmlTree/MmlNodes/mtext.ts +++ b/ts/core/MmlTree/MmlNodes/mtext.ts @@ -51,11 +51,18 @@ export class MmlMtext extends AbstractMmlTokenNode { } /** - * is always space-like according to the spec + * is always space-like according to the spec, + * but we make it so only if it contains only spaces and + * doesn't have certain attributes + * * @override */ public get isSpacelike() { - return true; + const attributes = this.attributes; + const spaces = !!this.getText().match(/^\s*$/); + return spaces && (attributes.getExplicit('mathbackground') === undefined && + attributes.getExplicit('background') === undefined && + attributes.getExplicit('style') === undefined); } } From bbdbc31f716e618a90a892b24b98e9530bef2869 Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Sun, 17 Sep 2023 08:22:49 -0400 Subject: [PATCH 2/2] Add methods to attributes for checking for explicit attributes and removing an attribute, and use them when appropriate. --- ts/a11y/semantic-enrich.ts | 2 +- ts/core/MmlTree/Attributes.ts | 31 +++++++++++++++++++++++--- ts/core/MmlTree/MmlNodes/maction.ts | 5 ++--- ts/core/MmlTree/MmlNodes/mspace.ts | 24 ++++++++++++-------- ts/core/MmlTree/MmlNodes/mtable.ts | 4 ++-- ts/core/MmlTree/MmlNodes/mtext.ts | 20 +++++++++++------ ts/core/MmlTree/MmlNodes/munderover.ts | 2 +- ts/input/tex/FilterUtil.ts | 20 ++++++++--------- ts/input/tex/NodeUtil.ts | 2 +- ts/input/tex/textmacros/TextParser.ts | 4 ++-- ts/output/common.ts | 2 +- ts/output/common/Wrapper.ts | 4 ++-- ts/output/common/Wrappers/mo.ts | 4 ++-- 13 files changed, 80 insertions(+), 44 deletions(-) diff --git a/ts/a11y/semantic-enrich.ts b/ts/a11y/semantic-enrich.ts index 7387b53be..6dcfa748d 100644 --- a/ts/a11y/semantic-enrich.ts +++ b/ts/a11y/semantic-enrich.ts @@ -237,7 +237,7 @@ export function EnrichedMathItemMixin is always space-like according to the spec, + * According to the spec, is always space-like, * but we make it so only if it contains only spaces and - * doesn't have certain attributes + * doesn't have certain attributes. * * @override */ public get isSpacelike() { - const attributes = this.attributes; - const spaces = !!this.getText().match(/^\s*$/); - return spaces && (attributes.getExplicit('mathbackground') === undefined && - attributes.getExplicit('background') === undefined && - attributes.getExplicit('style') === undefined); + return !!this.getText().match(/^\s*$/) && + !this.attributes.hasOneOf(MmlMtext.NONSPACELIKE); } } diff --git a/ts/core/MmlTree/MmlNodes/munderover.ts b/ts/core/MmlTree/MmlNodes/munderover.ts index 610e4b421..d984e4855 100644 --- a/ts/core/MmlTree/MmlNodes/munderover.ts +++ b/ts/core/MmlTree/MmlNodes/munderover.ts @@ -144,7 +144,7 @@ export class MmlMunderover extends AbstractMmlBaseNode { protected setInheritedAccent(n: number, accent: string, display: boolean, level: number, prime: boolean, force: boolean) { let node = this.childNodes[n]; - if (this.attributes.getExplicit(accent) == null && node.isEmbellished) { + if (!this.attributes.hasExplicit(accent) && node.isEmbellished) { let value = node.coreMO().attributes.get('accent'); this.attributes.setInherited(accent, value); if (value !== this.attributes.getDefault(accent)) { diff --git a/ts/input/tex/FilterUtil.ts b/ts/input/tex/FilterUtil.ts index cbf296694..e08ca9ffd 100644 --- a/ts/input/tex/FilterUtil.ts +++ b/ts/input/tex/FilterUtil.ts @@ -62,20 +62,20 @@ namespace FilterUtil { * Visitor that removes superfluous attributes from nodes. I.e., if a node has * an attribute, which is also an inherited attribute it will be removed. This * is necessary as attributes are set bottom up in the parser. - * @param {ParseOptions} data The parse options. + * @param {ParseOptions} data The parse options. */ export let cleanAttributes = function(arg: {data: ParseOptions}) { let node = arg.data.root; node.walkTree((mml: MmlNode, _d: any) => { - let attribs = mml.attributes as any; + let attribs = mml.attributes; if (!attribs) { return; } - const keep = new Set((attribs.get('mjx-keep-attrs') || '').split(/ /)); - delete (attribs.getAllAttributes())['mjx-keep-attrs']; + const keep = new Set((attribs.get('mjx-keep-attrs') as string || '').split(/ /)); + attribs.unset('mjx-keep-attrs'); for (const key of attribs.getExplicitNames()) { - if (!keep.has(key) && attribs.attributes[key] === mml.attributes.getInherited(key)) { - delete attribs.attributes[key]; + if (!keep.has(key) && attribs.get(key) === mml.attributes.getInherited(key)) { + attribs.unset(key); } } }, {}); @@ -121,11 +121,11 @@ namespace FilterUtil { m2.setProperty('relationsCombined', true); } else { // @test Preset Rspace Lspace - if (mo.attributes.getExplicit('rspace') == null) { + if (!mo.attributes.hasExplicit('rspace')) { // @test Mulitrel Mathvariant 3, Mulitrel Mathvariant 4 NodeUtil.setAttribute(mo, 'rspace', '0pt'); } - if (m2.attributes.getExplicit('lspace') == null) { + if (!m2.attributes.hasExplicit('lspace')) { // @test Mulitrel Mathvariant 3, Mulitrel Mathvariant 4 NodeUtil.setAttribute(m2, 'lspace', '0pt'); } @@ -173,7 +173,7 @@ namespace FilterUtil { return exp.filter(x => { return x !== space && (x !== 'stretchy' || - attr.getExplicit('stretchy')); + attr.hasExplicit('stretchy')); }); }; let attr1 = node1.attributes; @@ -252,7 +252,7 @@ namespace FilterUtil { } const base = mml.childNodes[(mml as any).base]; const mo = base.coreMO(); - if (base.getProperty('movablelimits') && !mo.attributes.getExplicit('movablelimits')) { + if (base.getProperty('movablelimits') && !mo.attributes.hasExplicit('movablelimits')) { let node = options.nodeFactory.create('node', subsup, mml.childNodes); NodeUtil.copyAttributes(mml, node); if (mml.parent) { diff --git a/ts/input/tex/NodeUtil.ts b/ts/input/tex/NodeUtil.ts index d461a6fd8..e14e05ffb 100644 --- a/ts/input/tex/NodeUtil.ts +++ b/ts/input/tex/NodeUtil.ts @@ -164,7 +164,7 @@ namespace NodeUtil { * @param {string} attr An attribute name. */ export function removeAttribute(node: MmlNode, attr: string): void { - delete (node.attributes.getAllAttributes())[attr]; + node.attributes.unset(attr); } diff --git a/ts/input/tex/textmacros/TextParser.ts b/ts/input/tex/textmacros/TextParser.ts index 350b1a76a..b9b7ea0a4 100644 --- a/ts/input/tex/textmacros/TextParser.ts +++ b/ts/input/tex/textmacros/TextParser.ts @@ -160,7 +160,7 @@ export class TextParser extends TexParser { mml = this.create('node', 'TeXAtom', [mml]); // make sure the math is an ORD } for (const name of ['mathsize', 'mathcolor']) { - if (env[name] && !mml.attributes.getExplicit(name)) { + if (env[name] && !mml.attributes.hasExplicit(name)) { if (!mml.isToken && !mml.isKind('mstyle')) { mml = this.create('node', 'mstyle', [mml]); } @@ -183,7 +183,7 @@ export class TextParser extends TexParser { const env = this.stack.env; if (!mml.isToken) return; for (const name of ['mathsize', 'mathcolor', 'mathvariant']) { - if (env[name] && !mml.attributes.getExplicit(name)) { + if (env[name] && !mml.attributes.hasExplicit(name)) { NodeUtil.setAttribute(mml, name, env[name]); } } diff --git a/ts/output/common.ts b/ts/output/common.ts index 88160659f..6d63e4e5e 100644 --- a/ts/output/common.ts +++ b/ts/output/common.ts @@ -424,7 +424,7 @@ export abstract class CommonOutputJax< marked = this.markInlineBreak(marked, forcebreak, linebreak, node, child); } } else if ((child.isKind('mstyle') && !child.attributes.get('style') && - !child.attributes.getExplicit('mathbackground')) || child.isKind('semantics')) { + !child.attributes.hasExplicit('mathbackground')) || child.isKind('semantics')) { this.markInlineBreaks(child.childNodes[0]); if (child.getProperty('process-breaks')) { child.setProperty('inline-breaks', true); diff --git a/ts/output/common/Wrapper.ts b/ts/output/common/Wrapper.ts index f161232df..2ab30d1e8 100644 --- a/ts/output/common/Wrapper.ts +++ b/ts/output/common/Wrapper.ts @@ -818,7 +818,7 @@ export class CommonWrapper< if (!this.node.isToken) return; const attributes = this.node.attributes; let variant = attributes.get('mathvariant') as string; - if (attributes.getExplicit('mathvariant')) { + if (attributes.hasExplicit('mathvariant')) { if (!this.font.getVariant(variant)) { console.warn(`Invalid variant: ${variant}`); variant = 'normal'; @@ -890,7 +890,7 @@ export class CommonWrapper< // // If there is a fontsize and no mathsize attribute, is that // - if (fontsize && !attributes.getExplicit('mathsize')) { + if (fontsize && !attributes.hasExplicit('mathsize')) { mathsize = fontsize; } // diff --git a/ts/output/common/Wrappers/mo.ts b/ts/output/common/Wrappers/mo.ts index 86d0e6419..0941f082d 100644 --- a/ts/output/common/Wrappers/mo.ts +++ b/ts/output/common/Wrappers/mo.ts @@ -313,7 +313,7 @@ export function CommonMoMixin< // // Check for a null delimiter and add the null-delimiter space // - if (bbox.w === 0 && this.node.attributes.getExplicit('fence') && + if (bbox.w === 0 && this.node.attributes.hasExplicit('fence') && (this.node as MmlMo).getText() === '' && (this.node.texClass === TEXCLASS.OPEN || this.node.texClass === TEXCLASS.CLOSE) && !this.jax.options.mathmlSpacing) { @@ -627,7 +627,7 @@ export function CommonMoMixin< this.variant = (this.node.attributes.get('displaystyle') ? '-largeop' : '-smallop'); return; } - if (!this.node.attributes.getExplicit('mathvariant') && + if (!this.node.attributes.hasExplicit('mathvariant') && this.node.getProperty('pseudoscript') === false) { this.variant = '-tex-variant'; return;