diff --git a/ts/output/chtml/Wrappers/mmultiscripts.ts b/ts/output/chtml/Wrappers/mmultiscripts.ts
index 5cd16c044..b4f3d17b7 100644
--- a/ts/output/chtml/Wrappers/mmultiscripts.ts
+++ b/ts/output/chtml/Wrappers/mmultiscripts.ts
@@ -69,14 +69,14 @@ CommonMmultiscriptsMixin, Constructor, Constructor, Constructor, Constructor, CHTMLConstructor extends CommonMsubsup
combinePrePost(pre: BBox, post: BBox): BBox;
/**
- * @return {ScriptData} The bounding box information about all the scripts
+ * Compute the bounding box information about all the scripts
*/
- getScriptData(): ScriptData;
+ getScriptData(): void;
/**
* @return {ScriptLists} The bounding boxes for all the scripts divided into lists by position
@@ -158,6 +158,14 @@ export function CommonMmultiscriptsMixin<
*/
public firstPrescript = 0;
+ /**
+ * @override
+ */
+ constructor(...args: any[]) {
+ super(...args);
+ this.getScriptData();
+ }
+
/*************************************************************/
/**
@@ -182,10 +190,10 @@ export function CommonMmultiscriptsMixin<
// to get a common offset for both
//
const scriptspace = this.font.params.scriptspace;
- const data = this.getScriptData();
+ const data = this.scriptData;
const sub = this.combinePrePost(data.sub, data.psub);
const sup = this.combinePrePost(data.sup, data.psup);
- const [u, v] = this.getUVQ(data.base, sub, sup);
+ const [u, v] = this.getUVQ(sub, sup);
//
// Lay out the pre-scripts, then the base, then the post-scripts
//
@@ -206,15 +214,9 @@ export function CommonMmultiscriptsMixin<
}
/**
- * @return {ScriptData} The bounding box information about all the scripts
+ * Compute the bounding box information about all the scripts
*/
- public getScriptData(): ScriptData {
- //
- // Return cached data, if any
- //
- if (this.scriptData) {
- return this.scriptData;
- }
+ public getScriptData() {
//
// Initialize the bounding box data
//
@@ -228,13 +230,12 @@ export function CommonMmultiscriptsMixin<
const lists = this.getScriptBBoxLists();
this.combineBBoxLists(data.sub, data.sup, lists.subList, lists.supList);
this.combineBBoxLists(data.psub, data.psup, lists.psubList, lists.psupList);
- this.scriptData.base = lists.base[0];
+ data.base = lists.base[0];
//
// Save the lengths and return the data
//
- this.scriptData.numPrescripts = lists.psubList.length;
- this.scriptData.numScripts = lists.subList.length;
- return this.scriptData;
+ data.numPrescripts = lists.psubList.length;
+ data.numScripts = lists.subList.length;
}
/**
@@ -314,24 +315,24 @@ export function CommonMmultiscriptsMixin<
/**
* @override
*/
- public getUVQ(basebox: BBox, subbox: BBox, supbox: BBox) {
+ public getUVQ(subbox: BBox, supbox: BBox) {
if (!this.UVQ) {
let [u, v, q] = [0, 0, 0];
if (subbox.h === 0 && subbox.d === 0) {
//
// Use placement for superscript only
//
- u = this.getU(basebox, supbox);
+ u = this.getU();
} else if (supbox.h === 0 && supbox.d === 0) {
//
// Use placement for subsccript only
//
- u = -this.getV(basebox, subbox);
+ u = -this.getV();
} else {
//
// Use placement for both
//
- [u, v, q] = super.getUVQ(basebox, subbox, supbox);
+ [u, v, q] = super.getUVQ(subbox, supbox);
}
this.UVQ = [u, v, q];
}
diff --git a/ts/output/common/Wrappers/msubsup.ts b/ts/output/common/Wrappers/msubsup.ts
index 11ae8bd27..389d7f87c 100644
--- a/ts/output/common/Wrappers/msubsup.ts
+++ b/ts/output/common/Wrappers/msubsup.ts
@@ -60,7 +60,7 @@ export function CommonMsubMixin<
/**
* @override
*/
- public get script() {
+ public get scriptChild() {
return this.childNodes[(this.node as MmlMsub).sub];
}
@@ -69,8 +69,8 @@ export function CommonMsubMixin<
*
* @override
*/
- public getOffset(bbox: BBox, sbox: BBox) {
- return [0, -this.getV(bbox, sbox)];
+ public getOffset() {
+ return [0, -this.getV()];
}
};
@@ -103,14 +103,14 @@ export type MsupConstructor = Constructor>;
export function CommonMsupMixin<
W extends AnyWrapper,
T extends ScriptbaseConstructor
->(Base: T): MsubConstructor & T {
+>(Base: T): MsupConstructor & T {
return class extends Base {
/**
* @override
*/
- public get script() {
+ public get scriptChild() {
return this.childNodes[(this.node as MmlMsup).sup];
}
@@ -119,9 +119,9 @@ export function CommonMsupMixin<
*
* @override
*/
- public getOffset(bbox: BBox, sbox: BBox) {
+ public getOffset() {
const x = (this.baseCore.bbox.ic ? .05 * this.baseCore.bbox.ic + .05 : 0);
- return [x * this.coreScale(), this.getU(bbox, sbox)];
+ return [x * this.baseScale, this.getU()];
}
};
@@ -155,12 +155,11 @@ export interface CommonMsubsup extends CommonScriptbase
/**
* Get the shift for the scripts and their separation (TeXBook Appendix G 18adef)
*
- * @param {BBox} basebox The bounding box of the base
* @param {BBox} subbox The bounding box of the superscript
* @param {BBox} supbox The bounding box of the subscript
* @return {number[]} The vertical offsets for super and subscripts, and the space between them
*/
- getUVQ(basebox: BBox, subbox: BBox, supbox: BBox): number[];
+ getUVQ(subbox?: BBox, supbox?: BBox): number[];
}
/**
@@ -209,15 +208,13 @@ export function CommonMsubsupMixin<
*/
public computeBBox(bbox: BBox, recompute: boolean = false) {
const basebox = this.baseChild.getBBox();
- const subbox = this.subChild.getBBox();
- const supbox = this.supChild.getBBox();
+ const [subbox, supbox] = [this.subChild.getBBox(), this.supChild.getBBox()];
bbox.empty();
bbox.append(basebox);
const w = bbox.w;
- const [u, v] = this.getUVQ(basebox, subbox, supbox);
- const x = (this.baseCore.bbox.ic ? this.coreIC() * this.coreScale() : 0);
+ const [u, v] = this.getUVQ();
bbox.combine(subbox, w, v);
- bbox.combine(supbox, w + x, u);
+ bbox.combine(supbox, w + this.baseIc, u);
bbox.w += this.font.params.scriptspace;
bbox.clean();
this.setChildPWidths(recompute);
@@ -226,21 +223,25 @@ export function CommonMsubsupMixin<
/**
* Get the shift for the scripts and their separation (TeXBook Appendix G 18adef)
*
- * @param {BBox} basebox The bounding box of the base
* @param {BBox} subbox The bounding box of the superscript
* @param {BBox} supbox The bounding box of the subscript
* @return {number[]} The vertical offsets for super and subscripts, and the space between them
*/
- public getUVQ(basebox: BBox, subbox: BBox, supbox: BBox): number[] {
+ public getUVQ(
+ subbox: BBox = this.subChild.getBBox(),
+ supbox: BBox = this.supChild.getBBox()
+ ): number[] {
+ const basebox = this.baseCore.getBBox();
if (this.UVQ) return this.UVQ;
const tex = this.font.params;
const t = 3 * tex.rule_thickness;
const subscriptshift = this.length2em(this.node.attributes.get('subscriptshift'), tex.sub2);
- const drop = (this.isCharBase() ? 0 : basebox.d * basebox.rscale + tex.sub_drop * subbox.rscale);
+ const scale = this.baseScale;
+ const drop = (this.baseIsChar && scale === 1 ? 0 : basebox.d * scale + tex.sub_drop * subbox.rscale);
//
// u and v are the veritcal shifts of the scripts, initially set to minimum values and then adjusted
//
- let [u, v] = [this.getU(basebox, supbox), Math.max(drop, subscriptshift)];
+ let [u, v] = [this.getU(), Math.max(drop, subscriptshift)];
//
// q is the space currently between the super- and subscripts.
// If it is less than 3 rule thicknesses,
diff --git a/ts/output/common/Wrappers/munderover.ts b/ts/output/common/Wrappers/munderover.ts
index 1f977aead..4af53e3c3 100644
--- a/ts/output/common/Wrappers/munderover.ts
+++ b/ts/output/common/Wrappers/munderover.ts
@@ -60,7 +60,7 @@ export function CommonMunderMixin<
/**
* @override
*/
- public get script() {
+ public get scriptChild() {
return this.childNodes[(this.node as MmlMunder).under];
}
@@ -83,7 +83,7 @@ export function CommonMunderMixin<
}
bbox.empty();
const basebox = this.baseChild.getBBox();
- const underbox = this.script.getBBox();
+ const underbox = this.scriptChild.getBBox();
const v = this.getUnderKV(basebox, underbox)[1];
const delta = this.getDelta(true);
const [bw, uw] = this.getDeltaW([basebox, underbox], [0, -delta]);
@@ -132,7 +132,7 @@ export function CommonMoverMixin<
/**
* @override
*/
- public get script() {
+ public get scriptChild() {
return this.childNodes[(this.node as MmlMover).over];
}
@@ -155,7 +155,7 @@ export function CommonMoverMixin<
}
bbox.empty();
const basebox = this.baseChild.getBBox();
- const overbox = this.script.getBBox();
+ const overbox = this.scriptChild.getBBox();
const u = this.getOverKU(basebox, overbox)[1];
const delta = this.getDelta();
const [bw, ow] = this.getDeltaW([basebox, overbox], [0, delta]);
diff --git a/ts/output/common/Wrappers/scriptbase.ts b/ts/output/common/Wrappers/scriptbase.ts
index 59f2f1d9a..36cd522c8 100644
--- a/ts/output/common/Wrappers/scriptbase.ts
+++ b/ts/output/common/Wrappers/scriptbase.ts
@@ -46,7 +46,7 @@ export interface CommonScriptbase extends AnyWrapper {
/**
* The core mi or mo of the base (or the base itself if there isn't one)
*/
- baseCore: W;
+ readonly baseCore: W;
/**
* The base element's wrapper
@@ -54,56 +54,90 @@ export interface CommonScriptbase extends AnyWrapper {
readonly baseChild: W;
/**
- * The script element's wrapper (overridden in subclasses)
+ * The relative scaling of the base compared to the munderover/msubsup
*/
- readonly script: W;
+ readonly baseScale: number;
/**
- * @return {boolean} True if the base is an mi, mn, or mo (not a largeop) consisting of a single character
+ * The italic correction of the base (if any)
*/
- isCharBase(): boolean;
+ readonly baseIc: number;
+
+ /**
+ * True if the base is a single character
+ */
+ readonly baseIsChar: boolean;
+
+ /**
+ * The script element's wrapper (overridden in subclasses)
+ */
+ readonly scriptChild: W;
/***************************************************************************/
/*
- * Methods for sub-sup nodes
+ * Methods for information about the core element for the base
*/
/**
- * @return {number} The ic for the core element
+ * @return {W} The wrapper for the base core mi or mo (or whatever)
*/
- coreIC(): number;
+ getBaseCore(): W;
/**
- * @return {number} The relative scaling of the base
+ * @return {W} The base fence item or null
+ */
+ getSemanticBase(): W;
+
+ /**
+ * Recursively retrieves an element for a given fencepointer.
+ *
+ * @param {W} fence The potential fence.
+ * @param {string} id The fencepointer id.
+ * @return {W} The original fence the scripts belong to.
+ */
+ getBaseFence(fence: W, id: string): W;
+
+ /**
+ * @return {number} The scaling factor for the base core relative to the munderover/msubsup
+ */
+ getBaseScale(): number;
+
+ /**
+ * The base's italic correction (properly scaled)
+ */
+ getBaseIc(): number;
+
+ /**
+ * @return {boolean} True if the base is an mi, mn, or mo (not a largeop) consisting of
+ * a single unstretched character
+ */
+ isCharBase(): boolean;
+
+ /***************************************************************************/
+ /*
+ * Methods for sub-sup nodes
*/
- coreScale(): number;
/**
* Get the shift for the script (implemented in subclasses)
*
- * @param {BBox} bbox The bounding box of the base element
- * @param {BBox} sbox The bounding box of the script element
* @return {number[]} The horizontal and vertical offsets for the script
*/
- getOffset(bbox: BBox, sbox: BBox): number[];
+ getOffset(): number[];
/**
* Get the shift for a subscript (TeXBook Appendix G 18ab)
*
- * @param {BBox} bbox The bounding box of the base element
- * @param {BBox} sbox The bounding box of the superscript element
* @return {number} The vertical offset for the script
*/
- getV(bbox: BBox, sbox: BBox): number;
+ getV(): number;
/**
* Get the shift for a superscript (TeXBook Appendix G 18acd)
*
- * @param {BBox} bbox The bounding box of the base element
- * @param {BBox} sbox The bounding box of the superscript element
* @return {number} The vertical offset for the script
*/
- getU(bbox: BBox, sbox: BBox): number;
+ getU(): number;
/***************************************************************************/
/*
@@ -193,6 +227,21 @@ export function CommonScriptbaseMixin<
*/
public baseCore: W;
+ /**
+ * The base element's wrapper
+ */
+ public baseScale: number = 1;
+
+ /**
+ * The relative scaling of the base compared to the munderover/msubsup
+ */
+ public baseIc: number = 0;
+
+ /**
+ * True if the base is a single character
+ */
+ public baseIsChar: boolean = false;
+
/**
* @return {W} The base element's wrapper
*/
@@ -203,7 +252,7 @@ export function CommonScriptbaseMixin<
/**
* @return {W} The script element's wrapper (overridden in subclasses)
*/
- public get script(): W {
+ public get scriptChild(): W {
return this.childNodes[1];
}
@@ -215,77 +264,119 @@ export function CommonScriptbaseMixin<
//
// Find the base core
//
- let core = this.baseCore = this.childNodes[0];
+ const core = this.baseCore = this.getBaseCore();
if (!core) return;
- while (core.childNodes.length === 1 &&
- (core.node.isKind('mrow') || core.node.isKind('TeXAtom') ||
- core.node.isKind('mstyle') || core.node.isKind('mpadded') ||
- core.node.isKind('mphantom') || core.node.isKind('semantics'))) {
- core = core.childNodes[0];
- if (!core) return;
- }
- if (!('noIC' in core)) return;
- this.baseCore = core;
//
// Check if the base is a mi or mo that needs italic correction removed
//
- if (!(this.constructor as CommonScriptbaseClass).useIC) {
- (core as CommonMo).noIC = true;
+ if (('noIC' in core) && !(this.constructor as CommonScriptbaseClass).useIC) {
+ (core as unknown as CommonMo).noIC = true;
+ }
+ //
+ // Get information about the base element
+ //
+ this.baseScale = this.getBaseScale();
+ this.baseIc = this.getBaseIc();
+ this.baseIsChar = this.isCharBase();
+ }
+
+ /***************************************************************************/
+ /*
+ * Methods for information about the core element for the base
+ */
+
+ /**
+ * @return {W} The wrapper for the base core mi or mo (or whatever)
+ */
+ public getBaseCore(): W {
+ let core = this.getSemanticBase() || this.childNodes[0];
+ while (core && (core.childNodes.length === 1 &&
+ (core.node.isKind('mrow') || core.node.isKind('TeXAtom') ||
+ core.node.isKind('mstyle') || core.node.isKind('mpadded') ||
+ core.node.isKind('mphantom') || core.node.isKind('semantics')))) {
+ core = core.childNodes[0];
}
+ return core || this.childNodes[0];
}
/**
- * This gives the common bbox for msub and msup. It is overridden
- * for all the others (msubsup, munder, mover, munderover).
- *
- * @override
+ * @return {W} The base fence item or null
*/
- public computeBBox(bbox: BBox, recompute: boolean = false) {
- const basebox = this.baseChild.getBBox();
- const scriptbox = this.script.getBBox();
- const [x, y] = this.getOffset(basebox, scriptbox);
- bbox.append(basebox);
- bbox.combine(scriptbox, bbox.w + x, y);
- bbox.w += this.font.params.scriptspace;
- bbox.clean();
- this.setChildPWidths(recompute);
+ public getSemanticBase(): W {
+ let fence = this.node.attributes.getExplicit('data-semantic-fencepointer') as string;
+ return this.getBaseFence(this.baseChild, fence);
}
/**
- * @return {number} The ic for the core element
+ * Recursively retrieves an element for a given fencepointer.
+ *
+ * @param {W} fence The potential fence.
+ * @param {string} id The fencepointer id.
+ * @return {W} The original fence the scripts belong to.
*/
- public coreIC(): number {
- const corebox = this.baseCore.getBBox();
- return (corebox.ic ? 1.05 * corebox.ic + .05 : 0);
+ public getBaseFence(fence: W, id: string): W {
+ if (!fence || !fence.node.attributes || !id) {
+ return null;
+ }
+ if (fence.node.attributes.getExplicit('data-semantic-id') === id) {
+ return fence;
+ }
+ for (const child of fence.childNodes) {
+ const result = this.getBaseFence(child, id);
+ if (result) {
+ return result;
+ }
+ }
+ return null;
}
/**
- * @return {number} The relative scaling of the base
+ * @return {number} The scaling factor for the base core relative to the munderover/msubsup
*/
- public coreScale(): number {
- let scale = this.baseChild.getBBox().rscale;
- let base = this.baseChild;
- while ((base.node.isKind('mstyle') || base.node.isKind('mrow') || base.node.isKind('TeXAtom'))
- && base.childNodes.length === 1) {
- base = base.childNodes[0];
- scale *= base.getBBox().rscale;
+ public getBaseScale(): number {
+ let child = this.baseCore as any;
+ let scale = 1;
+ while (child && child !== this) {
+ const bbox = child.getBBox();
+ scale *= bbox.rscale;
+ child = child.parent;
}
return scale;
}
+ /**
+ * The base's italic correction (properly scaled)
+ */
+ public getBaseIc(): number {
+ return (this.baseCore.bbox.ic ? 1.05 * this.baseCore.bbox.ic + .05 : 0) * this.baseScale;
+ }
+
/**
* @return {boolean} True if the base is an mi, mn, or mo (not a largeop) consisting of a single character
*/
public isCharBase(): boolean {
- let base = this.baseChild;
- while ((base.node.isKind('mstyle') || base.node.isKind('mrow')) && base.childNodes.length === 1) {
- base = base.childNodes[0];
- }
- return ((base.node.isKind('mo') || base.node.isKind('mi') || base.node.isKind('mn')) &&
+ let base = this.baseCore;
+ return (((base.node.isKind('mo') && (base as any).size === null) ||
+ base.node.isKind('mi') || base.node.isKind('mn')) &&
base.bbox.rscale === 1 && Array.from(base.getText()).length === 1 &&
!base.node.attributes.get('largeop'));
}
+ /**
+ * This gives the common bbox for msub and msup. It is overridden
+ * for all the others (msubsup, munder, mover, munderover).
+ *
+ * @override
+ */
+ public computeBBox(bbox: BBox, recompute: boolean = false) {
+ const [x, y] = this.getOffset();
+ bbox.append(this.baseChild.getBBox());
+ bbox.combine(this.scriptChild.getBBox(), bbox.w + x, y);
+ bbox.w += this.font.params.scriptspace;
+ bbox.clean();
+ this.setChildPWidths(recompute);
+ }
+
/***************************************************************************/
/*
* Methods for sub-sup nodes
@@ -294,26 +385,25 @@ export function CommonScriptbaseMixin<
/**
* Get the shift for the script (implemented in subclasses)
*
- * @param {BBox} bbox The bounding box of the base element
- * @param {BBox} sbox The bounding box of the script element
* @return {[number, number]} The horizontal and vertical offsets for the script
*/
- public getOffset(_bbox: BBox, _sbox: BBox): [number, number] {
+ public getOffset(): [number, number] {
return [0, 0];
}
/**
* Get the shift for a subscript (TeXBook Appendix G 18ab)
*
- * @param {BBox} bbox The bounding box of the base element
- * @param {BBox} sbox The bounding box of the superscript element
* @return {number} The vertical offset for the script
*/
- public getV(bbox: BBox, sbox: BBox): number {
+ public getV(): number {
+ const bbox = this.baseCore.getBBox();
+ const sbox = this.scriptChild.getBBox();
const tex = this.font.params;
const subscriptshift = this.length2em(this.node.attributes.get('subscriptshift'), tex.sub1);
+ const scale = this.baseScale;
return Math.max(
- this.isCharBase() ? 0 : bbox.d * bbox.rscale + tex.sub_drop * sbox.rscale,
+ this.baseIsChar && scale === 1 ? 0 : bbox.d * scale + tex.sub_drop * sbox.rscale,
subscriptshift,
sbox.h * sbox.rscale - (4 / 5) * tex.x_height
);
@@ -322,18 +412,19 @@ export function CommonScriptbaseMixin<
/**
* Get the shift for a superscript (TeXBook Appendix G 18acd)
*
- * @param {BBox} bbox The bounding box of the base element
- * @param {BBox} sbox The bounding box of the superscript element
* @return {number} The vertical offset for the script
*/
- public getU(bbox: BBox, sbox: BBox): number {
+ public getU(): number {
+ const bbox = this.baseCore.getBBox();
+ const sbox = this.scriptChild.getBBox();
const tex = this.font.params;
const attr = this.node.attributes.getList('displaystyle', 'superscriptshift');
const prime = this.node.getProperty('texprimestyle');
const p = prime ? tex.sup3 : (attr.displaystyle ? tex.sup1 : tex.sup2);
const superscriptshift = this.length2em(attr.superscriptshift, p);
+ const scale = this.baseScale;
return Math.max(
- this.isCharBase() ? 0 : bbox.h * bbox.rscale - tex.sup_drop * sbox.rscale,
+ this.baseIsChar && scale === 1 ? 0 : bbox.h * scale - tex.sup_drop * sbox.rscale,
superscriptshift,
sbox.d * sbox.rscale + (1 / 4) * tex.x_height
);
@@ -419,8 +510,8 @@ export function CommonScriptbaseMixin<
*/
public getDelta(noskew: boolean = false): number {
const accent = this.node.attributes.get('accent');
- const ddelta = (accent && !noskew ? this.baseChild.coreMO().bbox.sk : 0);
- return (DELTA * this.baseCore.bbox.ic / 2 + ddelta) * this.coreScale();
+ const ddelta = (accent && !noskew ? this.baseCore.bbox.sk : 0);
+ return (DELTA * this.baseCore.bbox.ic / 2 + ddelta) * this.baseScale;
}
/**
diff --git a/ts/output/svg/Wrappers/mmultiscripts.ts b/ts/output/svg/Wrappers/mmultiscripts.ts
index 581f58915..147bcb4ad 100644
--- a/ts/output/svg/Wrappers/mmultiscripts.ts
+++ b/ts/output/svg/Wrappers/mmultiscripts.ts
@@ -48,14 +48,14 @@ CommonMmultiscriptsMixin, Constructor, Constructor, Constructor>
}
const svg = this.standardSVGnode(parent);
- const [base, script] = [this.baseChild, this.script];
+ const [base, script] = [this.baseChild, this.scriptChild];
const [bbox, sbox] = [base.getBBox(), script.getBBox()];
base.toSVG(svg);
@@ -108,7 +108,7 @@ CommonMoverMixin, Constructor>>
return;
}
const svg = this.standardSVGnode(parent);
- const [base, script] = [this.baseChild, this.script];
+ const [base, script] = [this.baseChild, this.scriptChild];
const [bbox, sbox] = [base.getBBox(), script.getBBox()];
base.toSVG(svg);
diff --git a/ts/output/svg/Wrappers/scriptbase.ts b/ts/output/svg/Wrappers/scriptbase.ts
index a6e187fab..7db7eeedc 100644
--- a/ts/output/svg/Wrappers/scriptbase.ts
+++ b/ts/output/svg/Wrappers/scriptbase.ts
@@ -59,11 +59,10 @@ CommonScriptbaseMixin, SVGConstructor>(
public toSVG(parent: N) {
const svg = this.standardSVGnode(parent);
const bbox = this.baseChild.getBBox();
- const sbox = this.script.getBBox();
- const [x, v] = this.getOffset(bbox, sbox);
+ const [x, v] = this.getOffset();
this.baseChild.toSVG(svg);
- this.script.toSVG(svg);
- this.script.place(bbox.w * bbox.rscale + x, v);
+ this.scriptChild.toSVG(svg);
+ this.scriptChild.place(bbox.w * bbox.rscale + x, v);
}
}