Skip to content

Commit a29aa45

Browse files
committed
Add support for \atop (mfrac with linethickness=0)
1 parent 9ea336a commit a29aa45

File tree

1 file changed

+123
-12
lines changed
  • mathjax3-ts/output/chtml/Wrappers

1 file changed

+123
-12
lines changed

mathjax3-ts/output/chtml/Wrappers/mfrac.ts

Lines changed: 123 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,15 @@ export class CHTMLmfrac<N, T, D> extends CHTMLWrapper<N, T, D> {
4949
'mjx-frac[type="d"]': {
5050
'vertical-align': '.04em' // axis_height - 3.5 * rule_thickness
5151
},
52-
'mjx-frac[delims="true"]': {
52+
'mjx-frac[delims]': {
5353
padding: '0 .1em' // .1 (for line's -.1em margin)
5454
},
55+
'mjx-frac[atop]': {
56+
padding: '0 .12em' // nulldelimiterspace
57+
},
58+
'mjx-frac[atop][delims]': {
59+
padding: '0'
60+
},
5561
'mjx-dtable': {
5662
display: 'inline-table',
5763
width: '100%'
@@ -120,10 +126,27 @@ export class CHTMLmfrac<N, T, D> extends CHTMLWrapper<N, T, D> {
120126
* @override
121127
*/
122128
public toCHTML(parent: N) {
123-
const chtml = this.standardCHTMLnode(parent);
124-
const {displaystyle, scriptlevel, linethickness, numalign, denomalign} =
125-
this.node.attributes.getList('displaystyle', 'scriptlevel', 'linethickness', 'numalign', 'denomalign');
126-
const withDelims = this.node.getProperty('withDelims');
129+
this.standardCHTMLnode(parent);
130+
const {linethickness, bevelled} = this.node.attributes.getList('linethickness', 'bevelled');
131+
if (bevelled) {
132+
this.makeBevelled();
133+
} else {
134+
const thickness = this.length2em(String(linethickness));
135+
if (thickness === 0) {
136+
this.makeAtop();
137+
} else {
138+
this.makeFraction(thickness);
139+
}
140+
}
141+
}
142+
143+
/*
144+
* @param{number} t The rule line thickness
145+
*/
146+
protected makeFraction(t: number) {
147+
const {displaystyle, scriptlevel, numalign, denomalign} =
148+
this.node.attributes.getList('displaystyle', 'scriptlevel', 'numalign', 'denomalign');
149+
const withDelims = this.node.getProperty('texWithDelims');
127150
const display = (displaystyle && scriptlevel === 0);
128151
//
129152
// Attributes to set for the different elements making up the fraction
@@ -136,7 +159,6 @@ export class CHTMLmfrac<N, T, D> extends CHTMLWrapper<N, T, D> {
136159
//
137160
// Set the styles to handle the linethickness, if needed
138161
//
139-
const t = this.length2em(linethickness);
140162
const fparam = this.font.params;
141163
if (t !== .06) {
142164
const a = fparam.axis_height;
@@ -152,7 +174,7 @@ export class CHTMLmfrac<N, T, D> extends CHTMLWrapper<N, T, D> {
152174
// Create the DOM tree
153175
//
154176
let num, den;
155-
this.adaptor.append(chtml, this.html('mjx-frac', fattr, [
177+
this.adaptor.append(this.chtml, this.html('mjx-frac', fattr, [
156178
num = this.html('mjx-num', nattr, [this.html('mjx-nstrut', nsattr)]),
157179
this.html('mjx-dbox', {}, [
158180
this.html('mjx-dtable', {}, [
@@ -165,28 +187,117 @@ export class CHTMLmfrac<N, T, D> extends CHTMLWrapper<N, T, D> {
165187
]));
166188
this.childNodes[0].toCHTML(num);
167189
this.childNodes[1].toCHTML(den);
190+
}
191+
192+
protected makeAtop() {
193+
const {displaystyle, scriptlevel, numalign, denomalign} =
194+
this.node.attributes.getList('displaystyle', 'scriptlevel', 'numalign', 'denomalign');
195+
const withDelims = this.node.getProperty('texWithDelims');
196+
const display = (displaystyle && scriptlevel === 0);
197+
//
198+
// Attributes to set for the different elements making up the fraction
199+
//
200+
const attr = (display ? {type: 'd', atop: true} : {atop: true}) as OptionList;
201+
const fattr = (withDelims ? {...attr, delims: true} : {...attr}) as OptionList;
202+
const nattr = (numalign !== 'center' ? {align: numalign} : {}) as OptionList;
203+
const dattr = (denomalign !== 'center' ? {align: denomalign} : {}) as OptionList;
204+
//
205+
// Determine sparation and positioning
206+
//
207+
const {v, q} = this.getUVQ(display);
208+
nattr.style = {'padding-bottom': this.em(q)};
209+
fattr.style = {'vertical-align': this.em(-v)};
210+
//
211+
// Create the DOM tree
212+
//
213+
let num, den;
214+
this.adaptor.append(this.chtml, this.html('mjx-frac', fattr, [
215+
num = this.html('mjx-num', nattr),
216+
den = this.html('mjx-den', dattr)
217+
]));
218+
this.childNodes[0].toCHTML(num);
219+
this.childNodes[1].toCHTML(den);
168220
this.drawBBox();
169221
}
170222

223+
/*
224+
* @param{boolean} display True for diplay-mode fractions
225+
* @return{Object}
226+
* The vertical offsets of the numerator (u), the denominator (v),
227+
* the separation between the two, and the bboxes themselves.
228+
*/
229+
protected getUVQ(display: boolean) {
230+
const nbox = this.childNodes[0].getBBox();
231+
const dbox = this.childNodes[1].getBBox();
232+
const fparam = this.font.params;
233+
//
234+
// Initial offsets (u, v)
235+
// Minimum separation (p)
236+
// Actual separation with initial positions (q)
237+
//
238+
let [u, v] = (display ? [fparam.num1, fparam.denom1] : [fparam.num3, fparam.denom2]);
239+
let p = (display ? 7 : 3) * fparam.rule_thickness;
240+
let q = (u - nbox.d * nbox.scale) - (dbox.h * dbox.scale - v);
241+
//
242+
// If actual separation is less than minimum, move them farther apart
243+
//
244+
if (q < p) {
245+
u += (p - q)/2;
246+
v += (p - q)/2;
247+
q = p;
248+
}
249+
return {u, v, q, nbox, dbox};
250+
}
251+
252+
protected makeBevelled() {
253+
}
254+
255+
171256
/*
172257
* @override
173258
*/
174259
public computeBBox(bbox: BBox) {
175260
bbox.empty();
176-
const {displaystyle, scriptlevel, linethickness} =
177-
this.node.attributes.getList('displaystyle', 'scriptlevel', 'linethickness');
261+
const {linethickness, bevelled} = this.node.attributes.getList('linethickness', 'bevelled');
262+
if (bevelled) {
263+
this.getBevelledBBox(bbox);
264+
} else {
265+
const thickness = this.length2em(String(linethickness));
266+
if (thickness === 0) {
267+
this.getAtopBBox(bbox);
268+
} else {
269+
this.getFractionBBox(bbox, thickness);
270+
}
271+
}
272+
bbox.clean();
273+
}
274+
275+
protected getFractionBBox(bbox: BBox, t: number) {
276+
const {displaystyle, scriptlevel} = this.node.attributes.getList('displaystyle', 'scriptlevel');
178277
const display = displaystyle && scriptlevel === 0;
179278
const nbox = this.childNodes[0].getBBox();
180279
const dbox = this.childNodes[1].getBBox();
181280
const fparam = this.font.params;
182-
const pad = (this.node.getProperty('withDelims') as boolean ? 0 : fparam.nulldelimiterspace);
281+
const pad = (this.node.getProperty('texWithDelims') as boolean ? 0 : fparam.nulldelimiterspace);
183282
const a = fparam.axis_height;
184-
const t = this.length2em(linethickness);
185283
const T = (display ? 3.5 : 1.5) * t;
186284
bbox.combine(nbox, 0, a + T + Math.max(nbox.d * nbox.rscale, (display ? fparam.num1 : fparam.num2) - a - T));
187285
bbox.combine(dbox, 0, a - T - Math.max(dbox.h * dbox.rscale, (display ? fparam.denom1 : fparam.denom2) + a - T));
188286
bbox.w += 2 * pad + .2;
189-
bbox.clean();
287+
}
288+
289+
protected getAtopBBox(bbox: BBox) {
290+
const {displaystyle, scriptlevel} = this.node.attributes.getList('displaystyle', 'scriptlevel');
291+
const display = displaystyle && scriptlevel === 0;
292+
const fparam = this.font.params;
293+
const pad = (this.node.getProperty('texWithDelims') as boolean ? 0 : fparam.nulldelimiterspace);
294+
const {u, v, nbox, dbox} = this.getUVQ(display);
295+
bbox.combine(nbox, 0, u);
296+
bbox.combine(dbox, 0, -v);
297+
bbox.w += 2 * pad;
298+
}
299+
300+
protected getBevelledBBox(bbox: BBox) {
190301
}
191302

192303
/*

0 commit comments

Comments
 (0)