Skip to content

Commit 56d8590

Browse files
committed
Add support for mub/msup/msubsup
1 parent d491743 commit 56d8590

File tree

3 files changed

+332
-0
lines changed

3 files changed

+332
-0
lines changed

mathjax3-ts/output/chtml/Wrappers.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {CHTMLmrow, CHTMLinferredMrow} from './Wrappers/mrow.js';
3030
import {CHTMLmfrac} from './Wrappers/mfrac.js';
3131
import {CHTMLmsqrt} from './Wrappers/msqrt.js';
3232
import {CHTMLmroot} from './Wrappers/mroot.js';
33+
import {CHTMLmsub, CHTMLmsup, CHTMLmsubsup} from './Wrappers/msubsup.js';
3334
import {CHTMLmtable} from './Wrappers/mtable.js';
3435
import {CHTMLmtr, CHTMLmlabeledtr} from './Wrappers/mtr.js';
3536
import {CHTMLmtd} from './Wrappers/mtd.js';
@@ -47,6 +48,9 @@ export const CHTMLWrappers: {[kind: string]: typeof CHTMLWrapper} = {
4748
[CHTMLmfrac.kind]: CHTMLmfrac,
4849
[CHTMLmsqrt.kind]: CHTMLmsqrt,
4950
[CHTMLmroot.kind]: CHTMLmroot,
51+
[CHTMLmsub.kind]: CHTMLmsub,
52+
[CHTMLmsup.kind]: CHTMLmsup,
53+
[CHTMLmsubsup.kind]: CHTMLmsubsup,
5054
[CHTMLmtable.kind]: CHTMLmtable,
5155
[CHTMLmtr.kind]: CHTMLmtr,
5256
[CHTMLmlabeledtr.kind]: CHTMLmlabeledtr,
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*************************************************************
2+
*
3+
* Copyright (c) 2017 The MathJax Consortium
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/**
19+
* @fileoverview Implements the CHTMLmsubsup wrapper for the MmlMsubsup object
20+
* and the special cases CHTMLmsub and CHTMLmsup
21+
*
22+
* @author dpvc@mathjax.org (Davide Cervone)
23+
*/
24+
25+
import {CHTMLWrapper} from '../Wrapper.js';
26+
import {CHTMLscriptbase} from './scriptbase.js';
27+
import {MmlMsubsup, MmlMsub, MmlMsup} from '../../../core/MmlTree/MmlNodes/msubsup.js';
28+
import {MmlNode} from '../../../core/MmlTree/MmlNode.js';
29+
import {BBox} from '../BBox.js';
30+
import {StyleList} from '../CssStyles.js';
31+
32+
/*****************************************************************/
33+
/*
34+
* The CHTMLmsub wrapper for the MmlMsub object
35+
*/
36+
37+
export class CHTMLmsub extends CHTMLscriptbase {
38+
public static kind = MmlMsub.prototype.kind;
39+
40+
/*
41+
* @override
42+
*/
43+
public get script() {
44+
return this.childNodes[(this.node as MmlMsub).sub];
45+
}
46+
47+
/*
48+
* Get the shift for the subscript
49+
*
50+
* @override
51+
*/
52+
protected getOffset(bbox: BBox, sbox: BBox) {
53+
return -this.getV(bbox, sbox);
54+
}
55+
56+
}
57+
58+
/*****************************************************************/
59+
/*
60+
* The CHTMLmsup wrapper for the MmlMsup object
61+
*/
62+
63+
export class CHTMLmsup extends CHTMLscriptbase {
64+
public static kind = MmlMsup.prototype.kind;
65+
66+
/*
67+
* @override
68+
*/
69+
public get script() {
70+
return this.childNodes[(this.node as MmlMsup).sup];
71+
}
72+
73+
/*
74+
* Get the shift for the superscript
75+
*
76+
* @override
77+
*/
78+
public getOffset(bbox: BBox, sbox: BBox) {
79+
return this.getU(bbox, sbox);
80+
}
81+
82+
}
83+
84+
/*****************************************************************/
85+
/*
86+
* The CHTMLmsubsup wrapper for the MmlMsubsup object
87+
*/
88+
89+
export class CHTMLmsubsup extends CHTMLscriptbase {
90+
public static kind = MmlMsubsup.prototype.kind;
91+
92+
public static styles: StyleList = {
93+
'mjx-script': {
94+
display: 'inline-block',
95+
'padding-right': '.05em' // scriptspace
96+
},
97+
'mjx-script > *': {
98+
display: 'block'
99+
}
100+
};
101+
102+
/*
103+
* Cached values for the script offsets and separation (so if they are
104+
* computed in computeBBox(), they don't have to be recomputed for toCHTML())
105+
*/
106+
protected UVQ: number[] = null;
107+
108+
/*
109+
* @return{CHTMLWrapper} The wrapper for the subscript
110+
*/
111+
public get sub() {
112+
return this.childNodes[(this.node as MmlMsubsup).sub];
113+
}
114+
115+
/*
116+
* @return{CHTMLWrapper} The wrapper for the superscript
117+
*/
118+
public get sup() {
119+
return this.childNodes[(this.node as MmlMsubsup).sup];
120+
}
121+
122+
/*
123+
* @override
124+
*/
125+
public toCHTML(parent: HTMLElement) {
126+
this.chtml = this.standardCHTMLnode(parent);
127+
const [u, v, q] = this.getUVQ(this.base.getBBox(), this.sub.getBBox(), this.sup.getBBox());
128+
const style = {'vertical-align': this.em(v)};
129+
this.base.toCHTML(this.chtml);
130+
const stack = this.chtml.appendChild(this.html('mjx-script', {style}));
131+
this.sup.toCHTML(stack);
132+
stack.appendChild(this.html('mjx-spacer', {style: {'margin-top': this.em(q)}}));
133+
this.sub.toCHTML(stack);
134+
}
135+
136+
/*
137+
* @override
138+
*/
139+
public computeBBox(bbox: BBox) {
140+
const basebox = this.base.getBBox();
141+
const subbox = this.sub.getBBox();
142+
const supbox = this.sup.getBBox();
143+
bbox.empty();
144+
bbox.append(basebox);
145+
const w = bbox.w;
146+
const [u, v, q] = this.getUVQ(basebox, subbox, supbox);
147+
bbox.combine(subbox, w, v);
148+
bbox.combine(supbox, w, u);
149+
bbox.w += this.font.params.scriptspace;
150+
bbox.clean();
151+
}
152+
153+
/*
154+
* Get the shift for the scripts and their separation (TeXBook Appendix G 18adef)
155+
*
156+
* @param{BBox} basebox The bounding box of the base
157+
* @param{BBox} subbox The bounding box of the superscript
158+
* @param{BBox} supbox The bounding box of the subscript
159+
* @return{number[]} The vertical offsets for super and subscripts, and the space between them
160+
*/
161+
protected getUVQ(basebox: BBox, subbox: BBox, supbox: BBox) {
162+
if (this.UVQ) return this.UVQ;
163+
const tex = this.font.params;
164+
const t = 3 * tex.rule_thickness;
165+
let [u, v] = (this.isCharBase() ? [0, 0] : [this.getU(basebox, supbox),
166+
Math.max(basebox.d + tex.sub_drop * subbox.rscale, tex.sub2)]);
167+
let q = (u - supbox.d * supbox.rscale) - (subbox.h * subbox.rscale - v);
168+
if (q < t) {
169+
v += t - q;
170+
const p = (4/5) * tex.x_height - (u - supbox.d * supbox.rscale);
171+
if (p > 0) {
172+
u += p;
173+
v -= p;
174+
}
175+
}
176+
u = Math.max(this.length2em(this.node.attributes.get('superscriptshift'), u), u);
177+
v = Math.max(this.length2em(this.node.attributes.get('subscriptshift'), v), v);
178+
q = (u - supbox.d * supbox.rscale) - (subbox.h * subbox.rscale - v);
179+
this.UVQ = [u, -v, q];
180+
return this.UVQ;
181+
}
182+
183+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*************************************************************
2+
*
3+
* Copyright (c) 2017 The MathJax Consortium
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/**
19+
* @fileoverview Implements the a base class for CHTMLmsubsup and CHTMLmunderover
20+
* and their relatives. (Since munderover can become msubsup
21+
* when movablelimits is set, munderoer needs to be able to
22+
* do the same thing as msubsup in some cases.)
23+
*
24+
* @author dpvc@mathjax.org (Davide Cervone)
25+
*/
26+
27+
import {CHTMLWrapper} from '../Wrapper.js';
28+
import {MmlMsubsup} from '../../../core/MmlTree/MmlNodes/msubsup.js';
29+
import {MmlNode} from '../../../core/MmlTree/MmlNode.js';
30+
import {BBox} from '../BBox.js';
31+
import {StyleList} from '../CssStyles.js';
32+
33+
/*****************************************************************/
34+
/*
35+
* A base class for msup/msub/msubsup and munder/mover/munderover
36+
* wrapper implementations
37+
*/
38+
39+
export class CHTMLscriptbase extends CHTMLWrapper {
40+
public static kind = 'scriptbase';
41+
42+
/*
43+
* @return{CHTMLWrapper} The base element's wrapper
44+
*/
45+
public get base() {
46+
return this.childNodes[(this.node as MmlMsubsup).base];
47+
}
48+
49+
/*
50+
* @return{CHTMLWrapper} The script element's wrapper (overridden in subclasses)
51+
*/
52+
public get script() {
53+
return this.childNodes[1];
54+
}
55+
56+
/*
57+
* This gives the common output for msub and msup. It is overriden
58+
* for all the others (msubsup, munder, mover, munderover).
59+
*
60+
* @override
61+
*/
62+
public toCHTML(parent: HTMLElement) {
63+
this.chtml = this.standardCHTMLnode(parent);
64+
const v = this.getOffset(this.base.getBBox(), this.script.getBBox());
65+
const style = {'vertical-align': this.em(v)};
66+
this.base.toCHTML(this.chtml);
67+
this.script.toCHTML(this.chtml.appendChild(this.html('mjx-script', {style})));
68+
}
69+
70+
/*
71+
* This gives the common bbox for msub and msup. It is overriden
72+
* for all the others (msubsup, munder, mover, munderover).
73+
*
74+
* @override
75+
*/
76+
public computeBBox(bbox: BBox) {
77+
const basebox = this.base.getBBox();
78+
const scriptbox = this.script.getBBox();
79+
bbox.append(basebox);
80+
bbox.combine(scriptbox, bbox.w, this.getOffset(basebox, scriptbox));
81+
bbox.w += this.font.params.scriptspace;
82+
bbox.clean();
83+
}
84+
85+
/*
86+
* Get the shift for the script (implemented in subclasses)
87+
*
88+
* @param{BBox} bbox The bounding box of the base element
89+
* @param{BBox} sbox The bounding box of the script element
90+
* @return{number} The vertical offset for the script
91+
*/
92+
protected getOffset(bbox: BBox, sbox: BBox) {
93+
return 0;
94+
}
95+
96+
/*
97+
* Get the shift for a subscript (TeXBook Appendix G 18ab)
98+
*
99+
* @param{BBox} bbox The bounding box of the base element
100+
* @param{BBox} sbox The bounding box of the superscript element
101+
* @return{number} The vertical offset for the script
102+
*/
103+
protected getV(bbox: BBox, sbox: BBox) {
104+
const tex = this.font.params;
105+
const subscriptshift = this.length2em(this.node.attributes.get('subscriptshift'), tex.sub1);
106+
return Math.max(
107+
this.isCharBase() ? 0 : bbox.d + tex.sub_drop * sbox.rscale,
108+
subscriptshift,
109+
sbox.h * sbox.rscale - (4/5) * tex.x_height
110+
);
111+
}
112+
113+
/*
114+
* Get the shift for a superscript (TeXBook Appendix G 18acd)
115+
*
116+
* @param{BBox} bbox The bounding box of the base element
117+
* @param{BBox} sbox The bounding box of the superscript element
118+
* @return{number} The vertical offset for the script
119+
*/
120+
protected getU(bbox: BBox, sbox: BBox) {
121+
const tex = this.font.params;
122+
const attr = this.node.attributes.getList('displaystyle', 'texprimestyle', 'superscriptshift');
123+
const p = (attr.displaystyle ? tex.sup1 : attr.texprimestyle ? tex.sup3 : tex.sup2);
124+
const superscriptshift = this.length2em(attr.superscriptshift, p);
125+
return Math.max(
126+
this.isCharBase() ? 0 : bbox.h - tex.sup_drop * sbox.rscale,
127+
superscriptshift,
128+
sbox.d * sbox.rscale + (1/4) * tex.x_height
129+
);
130+
}
131+
132+
/*
133+
* @return{boolean} True if the base is an mi, mn, or mo (not a largeop) consisting of a single character
134+
*/
135+
protected isCharBase() {
136+
let base = this.base;
137+
if ((base.node.isKind('mstyle') || base.node.isKind('mrow')) && base.childNodes.length === 1) {
138+
base = base.childNodes[0];
139+
}
140+
return ((base.node.isKind('mo') || base.node.isKind('mi') || base.node.isKind('mn')) &&
141+
base.bbox.rscale === 1 && base.getText().length === 1 &&
142+
!base.node.attributes.get('largeop'));
143+
}
144+
145+
}

0 commit comments

Comments
 (0)