Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve handling of displayed equations next to floating content. (mathjax/MathJax#2351) #448

Merged
merged 3 commits into from
Apr 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 47 additions & 13 deletions ts/output/chtml/Wrappers/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {CommonMath, CommonMathMixin} from '../../common/Wrappers/math.js';
import {MmlMath} from '../../../core/MmlTree/MmlNodes/math.js';
import {MmlNode} from '../../../core/MmlTree/MmlNode.js';
import {StyleList} from '../../common/CssStyles.js';
import {BBox} from '../BBox.js';

/*****************************************************************/
/**
Expand Down Expand Up @@ -60,6 +61,9 @@ export class CHTMLmath<N, T, D> extends CommonMathMixin<CHTMLConstructor<any, an
'text-align': 'center',
margin: '1em 0'
},
'mjx-container[jax="CHTML"][display="true"][width="full"]': {
display: 'flex'
},
'mjx-container[jax="CHTML"][display="true"] mjx-math': {
padding: 0
},
Expand All @@ -78,29 +82,59 @@ export class CHTMLmath<N, T, D> extends CommonMathMixin<CHTMLConstructor<any, an
super.toCHTML(parent);
const chtml = this.chtml;
const adaptor = this.adaptor;
const attributes = this.node.attributes;
const display = (attributes.get('display') === 'block');
const display = (this.node.attributes.get('display') === 'block');
if (display) {
adaptor.setAttribute(chtml, 'display', 'true');
adaptor.setAttribute(parent, 'display', 'true');
this.handleDisplay(parent);
} else {
//
// Transfer right margin to container (for things like $x\hskip -2em y$)
//
const margin = adaptor.getStyle(chtml, 'margin-right');
if (margin) {
adaptor.setStyle(chtml, 'margin-right', '');
adaptor.setStyle(parent, 'margin-right', margin);
adaptor.setStyle(parent, 'width', '0');
}
this.handleInline(parent);
}
adaptor.addClass(chtml, 'MJX-TEX');
}

/**
* Handle displayed equations (set min-width, and so on).
*/
protected handleDisplay(parent: N) {
const adaptor = this.adaptor;
const [align, shift] = this.getAlignShift();
if (align !== 'center') {
adaptor.setAttribute(parent, 'justify', align);
}
if (display && shift && !adaptor.hasAttribute(chtml, 'width')) {
this.setIndent(chtml, align, shift);
if (this.bbox.pwidth === BBox.fullWidth) {
adaptor.setAttribute(parent, 'width', 'full');
if (this.jax.table) {
let {L, w, R} = this.jax.table.getBBox();
if (align === 'right') {
R = Math.max(R || -shift, -shift);
} else if (align === 'left') {
L = Math.max(L || shift, shift);
} else if (align === 'center') {
w += 2 * Math.abs(shift);
}
const W = this.em(Math.max(0, L + w + R));
adaptor.setStyle(parent, 'min-width', W);
adaptor.setStyle(this.jax.table.chtml, 'min-width', W);
}
} else {
this.setIndent(this.chtml, align, shift);
}
}

/**
* Handle in-line expressions
*/
protected handleInline(parent: N) {
//
// Transfer right margin to container (for things like $x\hskip -2em y$)
//
const adaptor = this.adaptor;
const margin = adaptor.getStyle(this.chtml, 'margin-right');
if (margin) {
adaptor.setStyle(this.chtml, 'margin-right', '');
adaptor.setStyle(parent, 'margin-right', margin);
adaptor.setStyle(parent, 'width', '0');
}
}

Expand Down
6 changes: 4 additions & 2 deletions ts/output/chtml/Wrappers/mtable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ CommonMtableMixin<CHTMLmtd<any, any, any>, CHTMLmtr<any, any, any>, CHTMLConstru
},
'mjx-mtable[align="bottom"] > mjx-table': {
'vertical-align': 'bottom'
},
'mjx-mtable[side="right"] mjx-labels': {
'min-width': '100%'
}
};

Expand Down Expand Up @@ -493,8 +496,7 @@ CommonMtableMixin<CHTMLmtd<any, any, any>, CHTMLmtr<any, any, any>, CHTMLConstru
const W = this.node.attributes.get('width') as string;
const {w, L, R} = this.getBBox();
styles.style = {
width: (isPercent(W) ? 'calc(' + W + ' + ' + this.em(L + R) + ')' : this.em(L + w + R)),
minWidth: '100%'
width: (isPercent(W) ? 'calc(' + W + ' + ' + this.em(L + R) + ')' : this.em(L + w + R))
};
}
this.adaptor.append(this.chtml, this.html('mjx-labels', styles, [this.labels]));
Expand Down
1 change: 1 addition & 0 deletions ts/output/common/OutputJax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export abstract class CommonOutputJax<
public document: MathDocument<N, T, D>;
public math: MathItem<N, T, D>;
public container: N;
public table: AnyWrapper;

/**
* The pixels per em for the math item being processed
Expand Down
3 changes: 3 additions & 0 deletions ts/output/common/Wrappers/mtable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,9 @@ export function CommonMtableMixin<C extends AnyWrapper,
this.hasLabels = this.childNodes.reduce((value, row) => value || row.node.isKind('mlabeledtr'), false);
this.findContainer();
this.isTop = !this.container || (this.container.node.isKind('math') && !this.container.parent);
if (this.isTop) {
this.jax.table = this;
}
this.getPercentageWidth();
//
// Get the frame, row, and column parameters
Expand Down
7 changes: 4 additions & 3 deletions ts/output/svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {TeXFont} from './svg/fonts/tex.js';
import {StyleList as CssStyleList} from './common/CssStyles.js';
import {FontCache} from './svg/FontCache.js';

export const SVGNS = "http://www.w3.org/2000/svg";
export const SVGNS = 'http://www.w3.org/2000/svg';
export const XLINKNS = 'http://www.w3.org/1999/xlink';

/*****************************************************************/
Expand Down Expand Up @@ -276,6 +276,7 @@ CommonOutputJax<N, T, D, SVGWrapper<N, T, D>, SVGWrapperFactory<N, T, D>, SVGFon
this.fontCache.clearLocalID();
if (this.minwidth) {
adaptor.setStyle(svg, 'minWidth', this.ex(this.minwidth));
adaptor.setStyle(this.container, 'minWidth', this.ex(this.minwidth));
} else if (this.shift) {
const align = adaptor.getAttribute(this.container, 'justify') || 'center';
this.setIndent(svg, align, this.shift);
Expand All @@ -300,7 +301,7 @@ CommonOutputJax<N, T, D, SVGWrapper<N, T, D>, SVGWrapperFactory<N, T, D>, SVGFon
* @param {number} m A number to be shown in ex
* @return {string} The number with units of ex
*/
ex(m: number) {
public ex(m: number) {
m /= this.font.params.x_height;
return (Math.abs(m) < .001 ? '0' : m.toFixed(3).replace(/\.?0+$/, '') + 'ex');
}
Expand All @@ -311,7 +312,7 @@ CommonOutputJax<N, T, D, SVGWrapper<N, T, D>, SVGWrapperFactory<N, T, D>, SVGFon
* @param {(N|T)[]} children The child nodes for this node
* @return {N} The newly created node in the SVG namespace
*/
svg(kind: string, properties: OptionList = {}, children: (N|T)[] = []) {
public svg(kind: string, properties: OptionList = {}, children: (N|T)[] = []) {
return this.html(kind, properties, children, SVGNS);
}

Expand Down
61 changes: 47 additions & 14 deletions ts/output/svg/Wrappers/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
import {CommonMath, CommonMathMixin} from '../../common/Wrappers/math.js';
import {MmlMath} from '../../../core/MmlTree/MmlNodes/math.js';
import {StyleList} from '../../common/CssStyles.js';
import {BBox} from '../BBox.js';

/*****************************************************************/
/**
Expand All @@ -44,6 +45,9 @@ export class SVGmath<N, T, D> extends CommonMathMixin<SVGConstructor<any, any, a
'text-align': 'center',
margin: '1em 0'
},
'mjx-container[jax="SVG"][display="true"][width="full"]': {
display: 'flex'
},
'mjx-container[jax="SVG"][justify="left"]': {
'text-align': 'left'
},
Expand All @@ -61,26 +65,55 @@ export class SVGmath<N, T, D> extends CommonMathMixin<SVGConstructor<any, any, a
const display = (this.node.attributes.get('display') === 'block');
if (display) {
adaptor.setAttribute(this.jax.container, 'display', 'true');
this.handleDisplay();
}
if (this.jax.document.options.internalSpeechTitles) {
this.handleSpeech();
}
}

/**
* Set the justification, and get the minwidth and shift needed
* for the displayed equation.
*/
protected handleDisplay() {
const [align, shift] = this.getAlignShift();
if (align !== 'center') {
adaptor.setAttribute(this.jax.container, 'justify', align);
this.adaptor.setAttribute(this.jax.container, 'justify', align);
}
if (display && shift) {
if (this.bbox.pwidth === BBox.fullWidth) {
this.adaptor.setAttribute(this.jax.container, 'width', 'full');
if (this.jax.table) {
let {L, w, R} = this.jax.table.getBBox();
if (align === 'right') {
R = Math.max(R || -shift, -shift);
} else if (align === 'left') {
L = Math.max(L || shift, shift);
} else if (align === 'center') {
w += 2 * Math.abs(shift);
}
this.jax.minwidth = Math.max(0, L + w + R);
}
} else {
this.jax.shift = shift;
}
if (this.jax.document.options.internalSpeechTitles) {
const attributes = this.node.attributes;
const speech = (attributes.get('aria-label') || attributes.get('data-semantic-speech')) as string;
if (speech) {
const id = this.getTitleID();
const label = this.svg('title', {id}, [this.text(speech)]);
adaptor.insert(label, adaptor.firstChild(this.element));
adaptor.setAttribute(this.element, 'aria-labeledby', id);
adaptor.removeAttribute(this.element, 'aria-label');
for (const child of this.childNodes[0].childNodes) {
adaptor.setAttribute(child.element, 'aria-hidden', 'true');
}
}

/**
* Handle adding speech to the top-level node, if any.
*/
protected handleSpeech() {
const adaptor = this.adaptor;
const attributes = this.node.attributes;
const speech = (attributes.get('aria-label') || attributes.get('data-semantic-speech')) as string;
if (speech) {
const id = this.getTitleID();
const label = this.svg('title', {id}, [this.text(speech)]);
adaptor.insert(label, adaptor.firstChild(this.element));
adaptor.setAttribute(this.element, 'aria-labeledby', id);
adaptor.removeAttribute(this.element, 'aria-label');
for (const child of this.childNodes[0].childNodes) {
adaptor.setAttribute(child.element, 'aria-hidden', 'true');
}
}
}
Expand Down
19 changes: 10 additions & 9 deletions ts/output/svg/Wrappers/mtable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ CommonMtableMixin<SVGmtd<any, any, any>, SVGmtr<any, any, any>, SVGConstructor<a
'stroke-linecap': 'round',
'stroke-dasharray': '0,140'
},
'g[data-mml-node="mtable"] > svg': {
'g[data-mml-node="mtable"] > g > svg': {
overflow: 'visible'
}
}
};

/**
* The column for labels
Expand Down Expand Up @@ -220,7 +220,7 @@ CommonMtableMixin<SVGmtd<any, any, any>, SVGmtr<any, any, any>, SVGConstructor<a
const W = L + this.pWidth + R;
const [align, shift] = this.getAlignShift();
const CW = Math.max(this.isTop ? W : 0, this.container.getWrapWidth(this.containerI)) - L - R;
const dw = w - (this.pWidth > CW ? CW: this.pWidth);
const dw = w - (this.pWidth > CW ? CW : this.pWidth);
const dx = (align === 'left' ? 0 : align === 'right' ? dw : dw / 2);
if (dx) {
const table = this.svg('g', {}, this.adaptor.childNodes(svg));
Expand Down Expand Up @@ -301,7 +301,7 @@ CommonMtableMixin<SVGmtd<any, any, any>, SVGmtr<any, any, any>, SVGConstructor<a
properties['stroke-dasharray'] = (style === 'dotted' ? '0,' : '') + this.fixed(2 * t);
}
}
return properties
return properties;
}

/******************************************************************/
Expand Down Expand Up @@ -375,20 +375,21 @@ CommonMtableMixin<SVGmtd<any, any, any>, SVGmtr<any, any, any>, SVGConstructor<a
const W = L + (this.pWidth || w) + R;
const LW = this.getTableData().L;
const [pad, align, shift] = this.getPadAlignShift(side);
const translate = (shift ? ` translate(${this.fixed(shift)} 0)` : '');
const dx = shift + (align === 'right' ? -W : align === 'center' ? -W / 2 : 0) + L;
const matrix = 'matrix(1 0 0 -1 0 0)';
const scale = `scale(${this.jax.fixed((this.font.params.x_height * 1000) / this.metrics.ex, 2)})`;
const transform = `translate(0, ${this.fixed(h)}) matrix(1 0 0 -1 0 0) ${scale}`;
const transform = `translate(0 ${this.fixed(h)}) ${matrix} ${scale}`;
let table = this.svg('svg', {
'data-table': true,
preserveAspectRatio: (align === 'left' ? 'xMinYMid' : align === 'right' ? 'xMaxYMid' : 'xMidYMid'),
viewBox: [this.fixed(-L), this.fixed(-h), this.fixed(W), this.fixed(h + d)].join(' ')
viewBox: [this.fixed(-dx), this.fixed(-h), 1, this.fixed(h + d)].join(' ')
}, [
this.svg('g', {transform: 'matrix(1 0 0 -1 0 0)' + translate}, adaptor.childNodes(svg))
this.svg('g', {transform: matrix}, adaptor.childNodes(svg))
]);
labels = this.svg('svg', {
'data-labels': true,
preserveAspectRatio: (side === 'left' ? 'xMinYMid' : 'xMaxYMid'),
viewBox: [0, this.fixed(-h), this.fixed(LW), this.fixed(h + d)].join(' ')
viewBox: [side === 'left' ? 0 : this.fixed(LW), this.fixed(-h), 1, this.fixed(h + d)].join(' ')
}, [labels]);
adaptor.append(svg, this.svg('g', {transform: transform}, [table, labels]));
this.place(-L, 0, svg); // remove spacing for L, which is added by the parent during appending
Expand Down