Skip to content

Commit faaff16

Browse files
committed
Add support for mtable's align attribute.
1 parent dbe34c9 commit faaff16

File tree

1 file changed

+123
-8
lines changed

1 file changed

+123
-8
lines changed

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

Lines changed: 123 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ function isPercent(x: string) {
6161
return x.match(/%\s*$/);
6262
}
6363

64+
/*
65+
* Split a space-separated string of values
66+
*/
67+
function SPLIT(x: string) {
68+
return x.trim().split(/\s+/);
69+
}
70+
6471
/*****************************************************************/
6572
/*
6673
* The CHTMLmtable wrapper for the MmlMtable object
@@ -84,6 +91,21 @@ export class CHTMLmtable<N, T, D> extends CHTMLWrapper<N, T, D> {
8491
},
8592
'mjx-mtable[width] > mjx-itable': {
8693
width: '100%'
94+
},
95+
'mjx-mtable[align]': {
96+
'vertical-align': 'baseline'
97+
},
98+
'mjx-mtable[align="top"] > mjx-itable': {
99+
'vertical-align': 'top'
100+
},
101+
'mjx-mtable[align="bottom"] > mjx-itable': {
102+
'vertical-align': 'bottom'
103+
},
104+
'mjx-mtable[align="center"] > mjx-itable': {
105+
'vertical-align': 'middle'
106+
},
107+
'mjx-mtable[align="baseline"] > mjx-itable': {
108+
'vertical-align': 'middle'
87109
}
88110
};
89111

@@ -131,7 +153,7 @@ export class CHTMLmtable<N, T, D> extends CHTMLWrapper<N, T, D> {
131153
this.cSpace = this.convertLengths(this.getColumnAttributes('columnspacing'));
132154
this.rSpace = this.convertLengths(this.getRowAttributes('rowspacing'));
133155
this.cLines = this.getColumnAttributes('columnlines').map(x => (x === 'none' ? 0 : .07));
134-
this.rLines = this.getColumnAttributes('rowlines').map(x => (x === 'none' ? 0 : .07));
156+
this.rLines = this.getRowAttributes('rowlines').map(x => (x === 'none' ? 0 : .07));
135157
this.cWidths = this.getColumnWidths();
136158
//
137159
// Stretch the rows and columns
@@ -242,6 +264,7 @@ export class CHTMLmtable<N, T, D> extends CHTMLWrapper<N, T, D> {
242264
this.handleEqualRows();
243265
this.handleFrame();
244266
this.handleWidth();
267+
this.handleAlign();
245268
this.drawBBox();
246269
}
247270

@@ -285,7 +308,7 @@ export class CHTMLmtable<N, T, D> extends CHTMLWrapper<N, T, D> {
285308
// Otherwise, use the height and depths for each row separately.
286309
// Add in the spacing, line widths, and frame size.
287310
//
288-
if (this.node.attributes.get('equalrows')) {
311+
if (this.node.attributes.get('equalrows') as boolean) {
289312
const HD = this.getEqualRowHeight();
290313
height = SUM([].concat(this.rLines, this.rSpace)) + HD * this.numRows;
291314
} else {
@@ -312,14 +335,36 @@ export class CHTMLmtable<N, T, D> extends CHTMLWrapper<N, T, D> {
312335
width = Math.max(this.length2em(w, cwidth) + (this.frame ? .14 : 0), width);
313336
}
314337
//
315-
// Return the bbounding box information
338+
// Return the bounding box information
316339
//
317-
const a = this.font.params.axis_height;
318-
bbox.h = height / 2 + a;
319-
bbox.d = height / 2 - a;
340+
let [h, d] = this.getBBoxHD(height);
341+
bbox.h = h;
342+
bbox.d = d;
320343
bbox.w = width;
321344
}
322345

346+
/*
347+
* @param{number} height The total height of the table
348+
* @return{number[]} The [height, depth] for the aligned table
349+
*/
350+
protected getBBoxHD(height: number) {
351+
const [align, row] = this.getAlignmentRow();
352+
if (row === null) {
353+
const a = this.font.params.axis_height;
354+
const h2 = height / 2;
355+
return ({
356+
top: [0, height],
357+
center: [h2, h2],
358+
bottom: [height, 0],
359+
baseline: [h2, h2],
360+
axis: [h2 + a, h2 - a]
361+
} as {[key: string]: number[]})[align] || [h2, h2];
362+
} else {
363+
const y = this.getVerticalPosition(row, align);
364+
return [y, height - y];
365+
}
366+
}
367+
323368
/******************************************************************/
324369

325370
/*
@@ -537,6 +582,22 @@ export class CHTMLmtable<N, T, D> extends CHTMLWrapper<N, T, D> {
537582
this.adaptor.setAttribute(this.chtml, 'width', w);
538583
}
539584

585+
/*
586+
* Handle alignment of table to surrounding baseline
587+
*/
588+
protected handleAlign() {
589+
const [align, row] = this.getAlignmentRow();
590+
if (row === null) {
591+
if (align !== 'axis') {
592+
this.adaptor.setAttribute(this.chtml, 'align', align);
593+
}
594+
} else {
595+
const y = this.getVerticalPosition(row, align);
596+
this.adaptor.setAttribute(this.chtml, 'align', 'top');
597+
this.adaptor.setStyle(this.chtml, 'verticalAlign', this.em(y));
598+
}
599+
}
600+
540601
/******************************************************************/
541602

542603
/*
@@ -560,7 +621,7 @@ export class CHTMLmtable<N, T, D> extends CHTMLWrapper<N, T, D> {
560621
*/
561622
protected getColumnWidths() {
562623
const width = this.node.attributes.get('width') as string;
563-
if (this.node.attributes.get('equalcolumns')) {
624+
if (this.node.attributes.get('equalcolumns') as boolean) {
564625
return this.getEqualColumns(width);
565626
}
566627
const swidths = this.getColumnAttributes('columnwidth', 0);
@@ -669,8 +730,62 @@ export class CHTMLmtable<N, T, D> extends CHTMLWrapper<N, T, D> {
669730
});
670731
}
671732

733+
/*
734+
* @param{number} i The row number (starting at 0)
735+
* @param{string} align The alignment on that row
736+
* @return{number} The offest of the alignment position from the top of the table
737+
*/
738+
protected getVerticalPosition(i: number, align: string) {
739+
const equal = this.node.attributes.get('equalrows') as boolean;
740+
const {H, D} = this.getTableData();
741+
const HD = (equal ? this.getEqualRowHeight() : 0);
742+
//
743+
// Use half spaces in each row, with frame spacing at top and bottom
744+
//
745+
const space = this.rSpace.map(x => x / 2);
746+
space.unshift(this.fSpace[1]);
747+
space.push(this.fSpace[1]);
748+
//
749+
// Start with frame size and add in spacing, height and depth,
750+
// and line thickness for each row.
751+
//
752+
let y = (this.frame ? .07 : 0);
753+
for (let j = 0; j < i; j++) {
754+
y += space[j] + (equal ? HD : H[j] + D[j]) + space[j + 1] + this.rLines[j];
755+
}
756+
//
757+
// For equal rows, get updated height and depth
758+
//
759+
const [h, d] = (equal ? [(HD + H[i] - D[i]) / 2, (HD - H[i] + D[i])/2] : [H[i], D[i]]);
760+
//
761+
// Add the offset into the specified row
762+
//
763+
y += ({
764+
top: 0,
765+
center: space[i] + (h + d) / 2,
766+
bottom: space[i] + h + d + space[i + 1],
767+
baseline: space[i] + h,
768+
axis: space[i] + h - .25
769+
} as {[name: string]: number} )[align] || 0;
770+
//
771+
// Return the final result
772+
//
773+
return y;
774+
}
775+
672776
/******************************************************************/
673777

778+
/*
779+
* @return{[string,number|null]} The alignment and row number (based at 0) or null
780+
*/
781+
protected getAlignmentRow(): [string, number] {
782+
const [align, row] = SPLIT(this.node.attributes.get('align') as string);
783+
if (row == null) return [align, null];
784+
let i = parseInt(row);
785+
if (i < 0) i += this.numRows;
786+
return [align, i < 1 || i > this.numRows ? null : i - 1];
787+
}
788+
674789
/*
675790
* @param{string} name The name of the attribute to get as an array
676791
* @param{number} i Return this many fewer than numCols entries
@@ -718,7 +833,7 @@ export class CHTMLmtable<N, T, D> extends CHTMLWrapper<N, T, D> {
718833
protected getAttributeArray(name: string) {
719834
const value = this.node.attributes.get(name) as string;
720835
if (!value) return [];
721-
return value.replace(/^\s+/, '').replace(/\s+$/, '').replace(/\s+/g, ' ').split(/ /);
836+
return SPLIT(value);
722837
}
723838

724839
/*

0 commit comments

Comments
 (0)