add initial marp implementation with sample content and build configuration
This commit is contained in:
+65
@@ -0,0 +1,65 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGTeXAtom wrapper for the MmlTeXAtom object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonTeXAtomMixin} from '../../common/Wrappers/TeXAtom.js';
|
||||
import {TeXAtom} from '../../../core/MmlTree/MmlNodes/TeXAtom.js';
|
||||
import {TEXCLASS, TEXCLASSNAMES} from '../../../core/MmlTree/MmlNode.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGTeXAtom wrapper for the TeXAtom object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGTeXAtom<N, T, D> extends
|
||||
CommonTeXAtomMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The TeXAtom wrapper
|
||||
*/
|
||||
public static kind = TeXAtom.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
super.toSVG(parent);
|
||||
this.adaptor.setAttribute(this.element, 'data-mjx-texclass', TEXCLASSNAMES[this.node.texClass]);
|
||||
//
|
||||
// Center VCENTER atoms vertically
|
||||
//
|
||||
if (this.node.texClass === TEXCLASS.VCENTER) {
|
||||
const bbox = this.childNodes[0].getBBox(); // get unmodified bbox of children
|
||||
const {h, d} = bbox;
|
||||
const a = this.font.params.axis_height;
|
||||
const dh = ((h + d) / 2 + a) - h; // new height minus old height
|
||||
const translate = 'translate(0 ' + this.fixed(dh) + ')';
|
||||
this.adaptor.setAttribute(this.element, 'transform', translate);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGTextNode wrapper for the TextNode object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {TextNode} from '../../../core/MmlTree/MmlNode.js';
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonTextNodeMixin} from '../../common/Wrappers/TextNode.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGTextNode wrapper for the TextNode object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGTextNode<N, T, D> extends
|
||||
CommonTextNodeMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The TextNode wrapper
|
||||
*/
|
||||
public static kind = TextNode.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-container[jax="SVG"] path[data-c], mjx-container[jax="SVG"] use[data-c]': {
|
||||
'stroke-width': 3
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const text = (this.node as TextNode).getText();
|
||||
const variant = this.parent.variant;
|
||||
if (text.length === 0) return;
|
||||
if (variant === '-explicitFont') {
|
||||
this.element = this.adaptor.append(parent, this.jax.unknownText(text, variant));
|
||||
} else {
|
||||
const chars = this.remappedText(text, variant);
|
||||
if (this.parent.childNodes.length > 1) {
|
||||
parent = this.element = this.adaptor.append(parent, this.svg('g', {'data-mml-node': 'text'}));
|
||||
}
|
||||
let x = 0;
|
||||
for (const n of chars) {
|
||||
x += this.placeChar(n, x, 0, parent, variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmaction wrapper for the MmlMaction object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMactionMixin} from '../../common/Wrappers/maction.js';
|
||||
import {ActionDef} from '../../common/Wrappers/maction.js';
|
||||
import {EventHandler, TooltipData} from '../../common/Wrappers/maction.js';
|
||||
import {MmlMaction} from '../../../core/MmlTree/MmlNodes/maction.js';
|
||||
import {TextNode, AbstractMmlNode} from '../../../core/MmlTree/MmlNode.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmaction wrapper for the MmlMaction object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmaction<N, T, D> extends
|
||||
CommonMactionMixin<SVGWrapper<any, any, any>, SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The maction wrapper
|
||||
*/
|
||||
public static kind = MmlMaction.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'[jax="SVG"] mjx-tool': {
|
||||
display: 'inline-block',
|
||||
position: 'relative',
|
||||
width: 0, height: 0
|
||||
},
|
||||
'[jax="SVG"] mjx-tool > mjx-tip': {
|
||||
position: 'absolute',
|
||||
top: 0, left: 0
|
||||
},
|
||||
'mjx-tool > mjx-tip': {
|
||||
display: 'inline-block',
|
||||
padding: '.2em',
|
||||
border: '1px solid #888',
|
||||
'font-size': '70%',
|
||||
'background-color': '#F8F8F8',
|
||||
color: 'black',
|
||||
'box-shadow': '2px 2px 5px #AAAAAA'
|
||||
},
|
||||
'g[data-mml-node="maction"][data-toggle]': {
|
||||
cursor: 'pointer'
|
||||
},
|
||||
'mjx-status': {
|
||||
display: 'block',
|
||||
position: 'fixed',
|
||||
left: '1em',
|
||||
bottom: '1em',
|
||||
'min-width': '25%',
|
||||
padding: '.2em .4em',
|
||||
border: '1px solid #888',
|
||||
'font-size': '90%',
|
||||
'background-color': '#F8F8F8',
|
||||
color: 'black'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The valid action types and their handlers
|
||||
*/
|
||||
public static actions = new Map([
|
||||
['toggle', [(node, _data) => {
|
||||
//
|
||||
// Mark which child is selected
|
||||
//
|
||||
node.adaptor.setAttribute(node.element, 'data-toggle', node.node.attributes.get('selection') as string);
|
||||
//
|
||||
// Cache the data needed to select another node
|
||||
//
|
||||
const math = node.factory.jax.math;
|
||||
const document = node.factory.jax.document;
|
||||
const mml = node.node as MmlMaction;
|
||||
//
|
||||
// Add a click handler that changes the selection and rerenders the expression
|
||||
//
|
||||
node.setEventHandler('click', (event: Event) => {
|
||||
if (!math.end.node) {
|
||||
//
|
||||
// If the MathItem was created by hand, it might not have a node
|
||||
// telling it where to replace the existing math, so set it.
|
||||
//
|
||||
math.start.node = math.end.node = math.typesetRoot;
|
||||
math.start.n = math.end.n = 0;
|
||||
}
|
||||
mml.nextToggleSelection();
|
||||
math.rerender(document);
|
||||
event.stopPropagation();
|
||||
});
|
||||
}, {}]],
|
||||
|
||||
['tooltip', [(node, data) => {
|
||||
const tip = node.childNodes[1];
|
||||
if (!tip) return;
|
||||
const rect = node.firstChild();
|
||||
if (tip.node.isKind('mtext')) {
|
||||
//
|
||||
// Text tooltips are handled through title attributes
|
||||
//
|
||||
const text = (tip.node as TextNode).getText();
|
||||
node.adaptor.insert(node.svg('title', {}, [node.text(text)]), rect);
|
||||
} else {
|
||||
//
|
||||
// Math tooltips are handled through hidden nodes and event handlers
|
||||
//
|
||||
const adaptor = node.adaptor;
|
||||
const container = node.jax.container;
|
||||
const math = (node.node as AbstractMmlNode).factory.create('math', {}, [node.childNodes[1].node]);
|
||||
const tool = node.html('mjx-tool', {}, [node.html('mjx-tip')]);
|
||||
const hidden = adaptor.append(rect, node.svg('foreignObject', {style: {display: 'none'}}, [tool]));
|
||||
node.jax.processMath(math, adaptor.firstChild(tool));
|
||||
node.childNodes[1].node.parent = node.node;
|
||||
//
|
||||
// Set up the event handlers to display and remove the tooltip
|
||||
//
|
||||
node.setEventHandler('mouseover', (event: Event) => {
|
||||
data.stopTimers(node, data);
|
||||
data.hoverTimer.set(node, setTimeout(() => {
|
||||
adaptor.setStyle(tool, 'left', '0');
|
||||
adaptor.setStyle(tool, 'top', '0');
|
||||
adaptor.append(container, tool);
|
||||
const tbox = adaptor.nodeBBox(tool);
|
||||
const nbox = adaptor.nodeBBox(node.element);
|
||||
const dx = (nbox.right - tbox.left) / node.metrics.em + node.dx;
|
||||
const dy = (nbox.bottom - tbox.bottom) / node.metrics.em + node.dy;
|
||||
adaptor.setStyle(tool, 'left', node.px(dx));
|
||||
adaptor.setStyle(tool, 'top', node.px(dy));
|
||||
}, data.postDelay));
|
||||
event.stopPropagation();
|
||||
});
|
||||
node.setEventHandler('mouseout', (event: Event) => {
|
||||
data.stopTimers(node, data);
|
||||
const timer = setTimeout(() => adaptor.append(hidden, tool), data.clearDelay);
|
||||
data.clearTimer.set(node, timer);
|
||||
event.stopPropagation();
|
||||
});
|
||||
}
|
||||
}, TooltipData]],
|
||||
|
||||
['statusline', [(node, data) => {
|
||||
const tip = node.childNodes[1];
|
||||
if (!tip) return;
|
||||
if (tip.node.isKind('mtext')) {
|
||||
const adaptor = node.adaptor;
|
||||
const text = (tip.node as TextNode).getText();
|
||||
adaptor.setAttribute(node.element, 'data-statusline', text);
|
||||
//
|
||||
// Set up event handlers to change the status window
|
||||
//
|
||||
node.setEventHandler('mouseover', (event: Event) => {
|
||||
if (data.status === null) {
|
||||
const body = adaptor.body(adaptor.document);
|
||||
data.status = adaptor.append(body, node.html('mjx-status', {}, [node.text(text)]));
|
||||
}
|
||||
event.stopPropagation();
|
||||
});
|
||||
node.setEventHandler('mouseout', (event: Event) => {
|
||||
if (data.status) {
|
||||
adaptor.remove(data.status);
|
||||
data.status = null;
|
||||
}
|
||||
event.stopPropagation();
|
||||
});
|
||||
}
|
||||
}, {
|
||||
status: null // cached status line
|
||||
}]]
|
||||
|
||||
] as ActionDef<SVGmaction<any, any, any>>[]);
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const svg = this.standardSVGnode(parent);
|
||||
const child = this.selected;
|
||||
const {h, d, w} = child.getOuterBBox();
|
||||
this.adaptor.append(this.element, this.svg('rect', {
|
||||
width: this.fixed(w), height: this.fixed(h + d), y: this.fixed(-d),
|
||||
fill: 'none', 'pointer-events': 'all'
|
||||
}));
|
||||
child.toSVG(svg);
|
||||
const bbox = child.getOuterBBox();
|
||||
if (child.element) {
|
||||
child.place(bbox.L * bbox.rscale, 0);
|
||||
}
|
||||
this.action(this, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event handler to the output for this maction
|
||||
*/
|
||||
public setEventHandler(type: string, handler: EventHandler) {
|
||||
(this.element as any).addEventListener(type, handler);
|
||||
}
|
||||
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmath wrapper for the MmlMath object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMathMixin} from '../../common/Wrappers/math.js';
|
||||
import {MmlMath} from '../../../core/MmlTree/MmlNodes/math.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmath wrapper for the MmlMath object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmath<N, T, D> extends
|
||||
CommonMathMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The math wrapper
|
||||
*/
|
||||
public static kind = MmlMath.prototype.kind;
|
||||
|
||||
/**
|
||||
* @overreide
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-container[jax="SVG"][display="true"]': {
|
||||
display: 'block',
|
||||
'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'
|
||||
},
|
||||
'mjx-container[jax="SVG"][justify="right"]': {
|
||||
'text-align': 'right'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
super.toSVG(parent);
|
||||
const adaptor = this.adaptor;
|
||||
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') {
|
||||
this.adaptor.setAttribute(this.jax.container, 'justify', align);
|
||||
}
|
||||
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.getOuterBBox();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string} A unique ID to use for aria-labeledby title elements
|
||||
*/
|
||||
protected getTitleID(): string {
|
||||
return 'mjx-svg-title-' + String(this.jax.options.titleID++);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public setChildPWidths(recompute: boolean, w: number = null, _clear: boolean = true) {
|
||||
return super.setChildPWidths(recompute,
|
||||
this.parent ? w : this.metrics.containerWidth / this.jax.pxPerEm,
|
||||
false);
|
||||
}
|
||||
|
||||
}
|
||||
+364
@@ -0,0 +1,364 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmenclose wrapper for the MmlMenclose object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMencloseMixin} from '../../common/Wrappers/menclose.js';
|
||||
import {SVGmsqrt} from './msqrt.js';
|
||||
import * as Notation from '../Notation.js';
|
||||
import {MmlMenclose} from '../../../core/MmlTree/MmlNodes/menclose.js';
|
||||
import {OptionList} from '../../../util/Options.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmenclose wrapper for the MmlMenclose object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmenclose<N, T, D> extends CommonMencloseMixin<
|
||||
SVGWrapper<any, any, any>,
|
||||
SVGmsqrt<any, any, any>,
|
||||
any,
|
||||
SVGConstructor<any, any, any>
|
||||
>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The menclose wrapper
|
||||
*/
|
||||
public static kind = MmlMenclose.prototype.kind;
|
||||
|
||||
/**
|
||||
* The definitions of the various notations
|
||||
*/
|
||||
public static notations: Notation.DefList<SVGmenclose<any, any, any>, any> = new Map([
|
||||
|
||||
Notation.Border('top'),
|
||||
Notation.Border('right'),
|
||||
Notation.Border('bottom'),
|
||||
Notation.Border('left'),
|
||||
|
||||
Notation.Border2('actuarial', 'top', 'right'),
|
||||
Notation.Border2('madruwb', 'bottom', 'right'),
|
||||
|
||||
Notation.DiagonalStrike('up'),
|
||||
Notation.DiagonalStrike('down'),
|
||||
|
||||
['horizontalstrike', {
|
||||
renderer: Notation.RenderLine('horizontal', 'Y'),
|
||||
bbox: (node) => [0, node.padding, 0, node.padding]
|
||||
}],
|
||||
|
||||
['verticalstrike', {
|
||||
renderer: Notation.RenderLine('vertical', 'X'),
|
||||
bbox: (node) => [node.padding, 0, node.padding, 0]
|
||||
}],
|
||||
|
||||
['box', {
|
||||
renderer: (node, _child) => {
|
||||
const {w, h, d} = node.getBBox();
|
||||
node.adaptor.append(node.element, node.box(w, h, d));
|
||||
},
|
||||
bbox: Notation.fullBBox,
|
||||
border: Notation.fullBorder,
|
||||
remove: 'left right top bottom'
|
||||
}],
|
||||
|
||||
['roundedbox', {
|
||||
renderer: (node, _child) => {
|
||||
const {w, h, d} = node.getBBox();
|
||||
const r = node.thickness + node.padding;
|
||||
node.adaptor.append(node.element, node.box(w, h, d, r));
|
||||
},
|
||||
bbox: Notation.fullBBox
|
||||
}],
|
||||
|
||||
['circle', {
|
||||
renderer: (node, _child) => {
|
||||
const {w, h, d} = node.getBBox();
|
||||
node.adaptor.append(node.element, node.ellipse(w, h, d));
|
||||
},
|
||||
bbox: Notation.fullBBox
|
||||
}],
|
||||
|
||||
['phasorangle', {
|
||||
//
|
||||
// Use a bottom border and an upward strike properly angled
|
||||
//
|
||||
renderer: (node, _child) => {
|
||||
const {w, h, d} = node.getBBox();
|
||||
const a = node.getArgMod(1.75 * node.padding, h + d)[0];
|
||||
const t = node.thickness / 2;
|
||||
const HD = h + d;
|
||||
const cos = Math.cos(a);
|
||||
node.adaptor.append(
|
||||
node.element,
|
||||
node.path('mitre', 'M', w, t - d, 'L', t + cos * t, t - d, 'L' , cos * HD + t, HD - d - t)
|
||||
);
|
||||
},
|
||||
bbox: (node) => {
|
||||
const p = node.padding / 2;
|
||||
const t = node.thickness;
|
||||
return [2 * p, p, p + t, 3 * p + t];
|
||||
},
|
||||
border: (node) => [0, 0, node.thickness, 0],
|
||||
remove: 'bottom'
|
||||
}],
|
||||
|
||||
Notation.Arrow('up'),
|
||||
Notation.Arrow('down'),
|
||||
Notation.Arrow('left'),
|
||||
Notation.Arrow('right'),
|
||||
|
||||
Notation.Arrow('updown'),
|
||||
Notation.Arrow('leftright'),
|
||||
|
||||
Notation.DiagonalArrow('updiagonal'), // backward compatibility
|
||||
Notation.DiagonalArrow('northeast'),
|
||||
Notation.DiagonalArrow('southeast'),
|
||||
Notation.DiagonalArrow('northwest'),
|
||||
Notation.DiagonalArrow('southwest'),
|
||||
|
||||
Notation.DiagonalArrow('northeastsouthwest'),
|
||||
Notation.DiagonalArrow('northwestsoutheast'),
|
||||
|
||||
['longdiv', {
|
||||
//
|
||||
// Use a line along the top followed by a half ellipse at the left
|
||||
//
|
||||
renderer: (node, _child) => {
|
||||
const {w, h, d} = node.getBBox();
|
||||
const t = node.thickness / 2;
|
||||
const p = node.padding;
|
||||
node.adaptor.append(
|
||||
node.element,
|
||||
node.path('round',
|
||||
'M', t, t - d,
|
||||
'a', p - t / 2, (h + d) / 2 - 4 * t, 0, '0,1', 0, h + d - 2 * t,
|
||||
'L', w - t, h - t
|
||||
)
|
||||
);
|
||||
},
|
||||
bbox: (node) => {
|
||||
const p = node.padding;
|
||||
const t = node.thickness;
|
||||
return [p + t, p, p, 2 * p + t / 2];
|
||||
}
|
||||
}],
|
||||
|
||||
['radical', {
|
||||
//
|
||||
// Use the msqrt rendering, but remove the extra space due to the radical
|
||||
// (it is added in at the end, so other notations overlap the root)
|
||||
//
|
||||
renderer: (node, child) => {
|
||||
node.msqrt.toSVG(child);
|
||||
const left = node.sqrtTRBL()[3];
|
||||
node.place(-left, 0, child);
|
||||
},
|
||||
//
|
||||
// Create the needed msqrt wrapper
|
||||
//
|
||||
init: (node) => {
|
||||
node.msqrt = node.createMsqrt(node.childNodes[0]);
|
||||
},
|
||||
//
|
||||
// Add back in the padding for the square root
|
||||
//
|
||||
bbox: (node) => node.sqrtTRBL(),
|
||||
//
|
||||
// This notation replaces the child
|
||||
//
|
||||
renderChild: true
|
||||
}]
|
||||
|
||||
] as Notation.DefPair<SVGmenclose<any, any, any>, any>[]);
|
||||
|
||||
/********************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const svg = this.standardSVGnode(parent);
|
||||
//
|
||||
// Create a box at the correct position and add the children
|
||||
//
|
||||
const left = this.getBBoxExtenders()[3];
|
||||
const def: OptionList = {};
|
||||
if (left > 0) {
|
||||
def.transform = 'translate(' + this.fixed(left) + ', 0)';
|
||||
}
|
||||
const block = this.adaptor.append(svg, this.svg('g', def));
|
||||
if (this.renderChild) {
|
||||
this.renderChild(this, block);
|
||||
} else {
|
||||
this.childNodes[0].toSVG(block);
|
||||
}
|
||||
//
|
||||
// Render all the notations for this menclose element
|
||||
//
|
||||
for (const name of Object.keys(this.notations)) {
|
||||
const notation = this.notations[name];
|
||||
!notation.renderChild && notation.renderer(this, svg);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************/
|
||||
|
||||
/**
|
||||
* Create an arrow using SVG elements
|
||||
*
|
||||
* @param {number} W The length of the arrow
|
||||
* @param {number} a The angle for the arrow
|
||||
* @param {boolean} double True if this is a double-headed arrow
|
||||
* @param {string} offset 'X' for vertical arrow, 'Y' for horizontal
|
||||
* @param {number} dist Distance to translate in the offset direction
|
||||
* @return {N} The newly created arrow
|
||||
*/
|
||||
public arrow(W: number, a: number, double: boolean, offset: string = '', dist: number = 0): N {
|
||||
const {w, h, d} = this.getBBox();
|
||||
const dw = (W - w) / 2;
|
||||
const m = (h - d) / 2;
|
||||
const t = this.thickness;
|
||||
const t2 = t / 2;
|
||||
const [x, y, dx] = [t * this.arrowhead.x, t * this.arrowhead.y, t * this.arrowhead.dx];
|
||||
const arrow =
|
||||
(double ?
|
||||
this.fill(
|
||||
'M', w + dw, m, // point of arrow
|
||||
'l', -(x + dx), y, 'l', dx, t2 - y, // upper right head
|
||||
'L', x - dw, m + t2, // upper side of shaft
|
||||
'l', dx, y - t2, 'l', -(x + dx), - y, // left point
|
||||
'l', x + dx, -y, 'l', -dx, y - t2, // lower left head
|
||||
'L', w + dw - x, m - t2, // lower side of shaft
|
||||
'l', -dx, t2 - y, 'Z' // lower head
|
||||
) :
|
||||
this.fill(
|
||||
'M', w + dw, m, // point of arrow
|
||||
'l', -(x + dx), y, 'l', dx, t2 - y, // upper head
|
||||
'L', -dw, m + t2, 'l', 0, -t, // upper side of shaft
|
||||
'L', w + dw - x, m - t2, // lower side of shaft
|
||||
'l', -dx, t2 - y, 'Z' // lower head
|
||||
));
|
||||
const transform = [];
|
||||
if (dist) {
|
||||
transform.push(offset === 'X' ? `translate(${this.fixed(-dist)} 0)` : `translate(0 ${this.fixed(dist)})`);
|
||||
}
|
||||
if (a) {
|
||||
const A = this.jax.fixed(-a * 180 / Math.PI);
|
||||
transform.push(`rotate(${A} ${this.fixed(w / 2)} ${this.fixed(m)})`);
|
||||
}
|
||||
if (transform.length) {
|
||||
this.adaptor.setAttribute(arrow, 'transform', transform.join(' '));
|
||||
}
|
||||
return arrow as N;
|
||||
}
|
||||
|
||||
/********************************************************/
|
||||
|
||||
/**
|
||||
* Create a line element
|
||||
*
|
||||
* @param {number[]} pq The coordinates of the endpoints, [x1, y1, x2, y2]
|
||||
* @return {N} The newly created line element
|
||||
*/
|
||||
public line(pq: [number, number, number, number]): N {
|
||||
const [x1, y1, x2, y2] = pq;
|
||||
return this.svg('line', {
|
||||
x1: this.fixed(x1), y1: this.fixed(y1),
|
||||
x2: this.fixed(x2), y2: this.fixed(y2),
|
||||
'stroke-width': this.fixed(this.thickness)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a rectangle element
|
||||
*
|
||||
* @param {number} w The width of the rectangle
|
||||
* @param {number} h The height of the rectangle
|
||||
* @param {number} d The depth of the rectangle
|
||||
* @param {number=} r The corner radius for a rounded rectangle
|
||||
* @return {N} The newly created line element
|
||||
*/
|
||||
public box(w: number, h: number, d: number, r: number = 0): N {
|
||||
const t = this.thickness;
|
||||
const def: OptionList = {
|
||||
x: this.fixed(t / 2), y: this.fixed(t / 2 - d),
|
||||
width: this.fixed(w - t), height: this.fixed(h + d - t),
|
||||
fill: 'none', 'stroke-width': this.fixed(t)
|
||||
};
|
||||
if (r) {
|
||||
def.rx = this.fixed(r);
|
||||
}
|
||||
return this.svg('rect', def);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ellipse element
|
||||
*
|
||||
* @param {number} w The width of the ellipse
|
||||
* @param {number} h The height of the ellipse
|
||||
* @param {number} d The depth of the ellipse
|
||||
* @return {N} The newly created ellipse node
|
||||
*/
|
||||
public ellipse(w: number, h: number, d: number): N {
|
||||
const t = this.thickness;
|
||||
return this.svg('ellipse', {
|
||||
rx: this.fixed((w - t) / 2), ry: this.fixed((h + d - t) / 2),
|
||||
cx: this.fixed(w / 2), cy: this.fixed((h - d) / 2),
|
||||
'fill': 'none', 'stroke-width': this.fixed(t)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a path element from the commands that specify it
|
||||
*
|
||||
* @param {string} join The join style for the path
|
||||
* @param {(string|number)[]} P The list of commands and coordinates for the path
|
||||
* @return {N} The newly created path
|
||||
*/
|
||||
public path(join: string, ...P: (string | number)[]): N {
|
||||
return this.svg('path', {
|
||||
d: P.map(x => (typeof x === 'string' ? x : this.fixed(x))).join(' '),
|
||||
style: {'stroke-width': this.fixed(this.thickness)},
|
||||
'stroke-linecap': 'round', 'stroke-linejoin': join,
|
||||
fill: 'none'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a filled path element from the commands the specify it
|
||||
* (same as path above, but no thickness adjustments)
|
||||
*
|
||||
* @param {(string|number)[]} P The list of commands and coordinates for the path
|
||||
* @return {N} The newly created path
|
||||
*/
|
||||
public fill(...P: (string | number)[]): N {
|
||||
return this.svg('path', {
|
||||
d: P.map(x => (typeof x === 'string' ? x : this.fixed(x))).join(' ')
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmerror wrapper for the MmlMerror object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper} from '../Wrapper.js';
|
||||
import {MmlMerror} from '../../../core/MmlTree/MmlNodes/merror.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmerror wrapper for the MmlMerror object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class SVGmerror<N, T, D> extends SVGWrapper<N, T, D> {
|
||||
|
||||
/**
|
||||
* The merror wrapper
|
||||
*/
|
||||
public static kind = MmlMerror.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'g[data-mml-node="merror"] > g': {
|
||||
fill: 'red',
|
||||
stroke: 'red'
|
||||
},
|
||||
'g[data-mml-node="merror"] > rect[data-background]': {
|
||||
fill: 'yellow',
|
||||
stroke: 'none'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const svg = this.standardSVGnode(parent);
|
||||
const {h, d, w} = this.getBBox();
|
||||
this.adaptor.append(this.element, this.svg('rect', {
|
||||
'data-background': true,
|
||||
width: this.fixed(w), height: this.fixed(h + d), y: this.fixed(-d)
|
||||
}));
|
||||
const title = this.node.attributes.get('title') as string;
|
||||
if (title) {
|
||||
this.adaptor.append(this.element, this.svg('title', {}, [this.adaptor.text(title)]));
|
||||
}
|
||||
this.addChildren(svg);
|
||||
}
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmfenced wrapper for the MmlMfenced object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMfencedMixin} from '../../common/Wrappers/mfenced.js';
|
||||
import {MmlMfenced} from '../../../core/MmlTree/MmlNodes/mfenced.js';
|
||||
import {SVGinferredMrow} from './mrow.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmfenced wrapper for the MmlMfenced object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class SVGmfenced<N, T, D> extends CommonMfencedMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The mfenced wrapper
|
||||
*/
|
||||
public static kind = MmlMfenced.prototype.kind;
|
||||
|
||||
/**
|
||||
* An mrow used to render the result
|
||||
*/
|
||||
public mrow: SVGinferredMrow<N, T, D>;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const svg = this.standardSVGnode(parent);
|
||||
this.setChildrenParent(this.mrow); // temporarily change parents to the mrow
|
||||
this.mrow.toSVG(svg);
|
||||
this.setChildrenParent(this); // put back the correct parents
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SVGWrapper} parent The parent to use for the fenced children
|
||||
*/
|
||||
protected setChildrenParent(parent: SVGWrapper<N, T, D>) {
|
||||
for (const child of this.childNodes) {
|
||||
child.parent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmfrac wrapper for the MmlMfrac object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMfracMixin} from '../../common/Wrappers/mfrac.js';
|
||||
import {MmlMfrac} from '../../../core/MmlTree/MmlNodes/mfrac.js';
|
||||
import {SVGmo} from './mo.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmfrac wrapper for the MmlMfrac object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class SVGmfrac<N, T, D> extends CommonMfracMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The mfrac wrapper
|
||||
*/
|
||||
public static kind = MmlMfrac.prototype.kind;
|
||||
|
||||
/**
|
||||
* An mo element used to render bevelled fractions
|
||||
*/
|
||||
public bevel: SVGmo<N, T, D>;
|
||||
|
||||
/************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
this.standardSVGnode(parent);
|
||||
const {linethickness, bevelled} = this.node.attributes.getList('linethickness', 'bevelled');
|
||||
const display = this.isDisplay();
|
||||
if (bevelled) {
|
||||
this.makeBevelled(display);
|
||||
} else {
|
||||
const thickness = this.length2em(String(linethickness), .06);
|
||||
if (thickness === 0) {
|
||||
this.makeAtop(display);
|
||||
} else {
|
||||
this.makeFraction(display, thickness);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************/
|
||||
|
||||
/**
|
||||
* @param {boolean} display True when fraction is in display mode
|
||||
* @param {number} t The rule line thickness
|
||||
*/
|
||||
protected makeFraction(display: boolean, t: number) {
|
||||
const svg = this.element;
|
||||
const {numalign, denomalign} = this.node.attributes.getList('numalign', 'denomalign');
|
||||
const [num, den] = this.childNodes;
|
||||
const nbox = num.getOuterBBox();
|
||||
const dbox = den.getOuterBBox();
|
||||
|
||||
const tex = this.font.params;
|
||||
const a = tex.axis_height;
|
||||
const d = .1; // line's extra left- and right-padding
|
||||
const pad = (this.node.getProperty('withDelims') ? 0 : tex.nulldelimiterspace);
|
||||
const W = Math.max((nbox.L + nbox.w + nbox.R) * nbox.rscale,
|
||||
(dbox.L + dbox.w + dbox.R) * dbox.rscale);
|
||||
const nx = this.getAlignX(W, nbox, numalign as string) + d + pad;
|
||||
const dx = this.getAlignX(W, dbox, denomalign as string) + d + pad;
|
||||
const {T, u, v} = this.getTUV(display, t);
|
||||
|
||||
num.toSVG(svg);
|
||||
num.place(nx, a + T + Math.max(nbox.d * nbox.rscale, u));
|
||||
den.toSVG(svg);
|
||||
den.place(dx, a - T - Math.max(dbox.h * dbox.rscale, v));
|
||||
|
||||
this.adaptor.append(svg, this.svg('rect', {
|
||||
width: this.fixed(W + 2 * d), height: this.fixed(t),
|
||||
x: this.fixed(pad), y: this.fixed(a - t / 2)
|
||||
}));
|
||||
}
|
||||
|
||||
/************************************************/
|
||||
|
||||
/**
|
||||
* @param {boolean} display True when fraction is in display mode
|
||||
*/
|
||||
protected makeAtop(display: boolean) {
|
||||
const svg = this.element;
|
||||
const {numalign, denomalign} = this.node.attributes.getList('numalign', 'denomalign');
|
||||
const [num, den] = this.childNodes;
|
||||
const nbox = num.getOuterBBox();
|
||||
const dbox = den.getOuterBBox();
|
||||
|
||||
const tex = this.font.params;
|
||||
const pad = (this.node.getProperty('withDelims') ? 0 : tex.nulldelimiterspace);
|
||||
const W = Math.max((nbox.L + nbox.w + nbox.R) * nbox.rscale,
|
||||
(dbox.L + dbox.w + dbox.R) * dbox.rscale);
|
||||
const nx = this.getAlignX(W, nbox, numalign as string) + pad;
|
||||
const dx = this.getAlignX(W, dbox, denomalign as string) + pad;
|
||||
const {u, v} = this.getUVQ(display);
|
||||
|
||||
num.toSVG(svg);
|
||||
num.place(nx, u);
|
||||
den.toSVG(svg);
|
||||
den.place(dx, -v);
|
||||
}
|
||||
|
||||
/************************************************/
|
||||
|
||||
/**
|
||||
* @param {boolean} display True when fraction is in display mode
|
||||
*/
|
||||
protected makeBevelled(display: boolean) {
|
||||
const svg = this.element;
|
||||
const [num, den] = this.childNodes;
|
||||
const {u, v, delta, nbox, dbox} = this.getBevelData(display);
|
||||
const w = (nbox.L + nbox.w + nbox.R) * nbox.rscale;
|
||||
|
||||
num.toSVG(svg);
|
||||
this.bevel.toSVG(svg);
|
||||
den.toSVG(svg);
|
||||
|
||||
num.place(nbox.L * nbox.rscale, u);
|
||||
this.bevel.place(w - delta / 2, 0);
|
||||
den.place(w + this.bevel.getOuterBBox().w + dbox.L * dbox.rscale - delta, v);
|
||||
}
|
||||
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmglyph wrapper for the MmlMglyph object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMglyphMixin} from '../../common/Wrappers/mglyph.js';
|
||||
import {MmlMglyph} from '../../../core/MmlTree/MmlNodes/mglyph.js';
|
||||
import {SVGTextNode} from './TextNode.js';
|
||||
import {OptionList} from '../../../util/Options.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmglyph wrapper for the MmlMglyph object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmglyph<N, T, D> extends
|
||||
CommonMglyphMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The mglyph wrapper
|
||||
*/
|
||||
public static kind = MmlMglyph.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const svg = this.standardSVGnode(parent);
|
||||
if (this.charWrapper) {
|
||||
(this.charWrapper as SVGTextNode<N, T, D>).toSVG(svg);
|
||||
return;
|
||||
}
|
||||
const {src, alt} = this.node.attributes.getList('src', 'alt');
|
||||
const h = this.fixed(this.height);
|
||||
const w = this.fixed(this.width);
|
||||
const y = this.fixed(this.height + (this.valign || 0));
|
||||
const properties: OptionList = {
|
||||
width: w, height: h,
|
||||
transform: 'translate(0 ' + y + ') matrix(1 0 0 -1 0 0)',
|
||||
preserveAspectRatio: 'none',
|
||||
'aria-label': alt,
|
||||
href: src
|
||||
};
|
||||
const img = this.svg('image', properties);
|
||||
this.adaptor.append(svg, img);
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmi wrapper for the MmlMi object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMiMixin} from '../../common/Wrappers/mi.js';
|
||||
import {MmlMi} from '../../../core/MmlTree/MmlNodes/mi.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmi wrapper for the MmlMi object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmi<N, T, D> extends
|
||||
CommonMiMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The mi wrapper
|
||||
*/
|
||||
public static kind = MmlMi.prototype.kind;
|
||||
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmmultiscripts wrapper for the MmlMmultiscripts object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, Constructor} from '../Wrapper.js';
|
||||
import {SVGmsubsup} from './msubsup.js';
|
||||
import {CommonMmultiscriptsMixin} from '../../common/Wrappers/mmultiscripts.js';
|
||||
import {MmlMmultiscripts} from '../../../core/MmlTree/MmlNodes/mmultiscripts.js';
|
||||
import {split} from '../../../util/string.js';
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* A function taking two widths and returning an offset of the first in the second
|
||||
*/
|
||||
export type AlignFunction = (w: number, W: number) => number;
|
||||
|
||||
/**
|
||||
* Get the function for aligning scripts horizontally (left, center, right)
|
||||
*/
|
||||
export function AlignX(align: string) {
|
||||
return ({
|
||||
left: (_w, _W) => 0,
|
||||
center: (w, W) => (W - w) / 2,
|
||||
right: (w, W) => W - w
|
||||
} as {[name: string]: AlignFunction})[align] || ((_w, _W) => 0) as AlignFunction;
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmmultiscripts wrapper for the MmlMmultiscripts object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmmultiscripts<N, T, D> extends
|
||||
CommonMmultiscriptsMixin<SVGWrapper<any, any, any>, Constructor<SVGmsubsup<any, any, any>>>(SVGmsubsup) {
|
||||
|
||||
/**
|
||||
* The mmultiscripts wrapper
|
||||
*/
|
||||
public static kind = MmlMmultiscripts.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const svg = this.standardSVGnode(parent);
|
||||
const data = this.scriptData;
|
||||
//
|
||||
// Get the alignment for the scripts
|
||||
//
|
||||
const scriptalign = this.node.getProperty('scriptalign') || 'right left';
|
||||
const [preAlign, postAlign] = split(scriptalign + ' ' + scriptalign);
|
||||
//
|
||||
// Combine the bounding boxes of the pre- and post-scripts,
|
||||
// and get the resulting baseline offsets
|
||||
//
|
||||
const sub = this.combinePrePost(data.sub, data.psub);
|
||||
const sup = this.combinePrePost(data.sup, data.psup);
|
||||
const [u, v] = this.getUVQ(sub, sup);
|
||||
//
|
||||
// Place the pre-scripts, then the base, then the post-scripts
|
||||
//
|
||||
let x = 0; // scriptspace
|
||||
if (data.numPrescripts) {
|
||||
x = this.addScripts(.05, u, v, this.firstPrescript, data.numPrescripts, preAlign);
|
||||
}
|
||||
const base = this.baseChild;
|
||||
base.toSVG(svg);
|
||||
base.place(x, 0);
|
||||
x += base.getOuterBBox().w;
|
||||
if (data.numScripts) {
|
||||
this.addScripts(x, u, v, 1, data.numScripts, postAlign);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a table with the super and subscripts properly separated and aligned.
|
||||
*
|
||||
* @param {number} x The x offset of the scripts
|
||||
* @param {number} u The baseline offset for the superscripts
|
||||
* @param {number} v The baseline offset for the subscripts
|
||||
* @param {number} i The starting index for the scripts
|
||||
* @param {number} n The number of sub/super-scripts
|
||||
* @param {string} align The alignment for the scripts
|
||||
* @return {number} The right-hand offset of the scripts
|
||||
*/
|
||||
protected addScripts(x: number, u: number, v: number, i: number, n: number, align: string): number {
|
||||
const adaptor = this.adaptor;
|
||||
const alignX = AlignX(align);
|
||||
const supRow = adaptor.append(this.element, this.svg('g'));
|
||||
const subRow = adaptor.append(this.element, this.svg('g'));
|
||||
this.place(x, u, supRow);
|
||||
this.place(x, v, subRow);
|
||||
let m = i + 2 * n;
|
||||
let dx = 0;
|
||||
while (i < m) {
|
||||
const [sub, sup] = [this.childNodes[i++], this.childNodes[i++]];
|
||||
const [subbox, supbox] = [sub.getOuterBBox(), sup.getOuterBBox()];
|
||||
const [subr, supr] = [subbox.rscale, supbox.rscale];
|
||||
const w = Math.max(subbox.w * subr, supbox.w * supr);
|
||||
sub.toSVG(subRow);
|
||||
sup.toSVG(supRow);
|
||||
sub.place(dx + alignX(subbox.w * subr, w), 0);
|
||||
sup.place(dx + alignX(supbox.w * supr, w), 0);
|
||||
dx += w;
|
||||
}
|
||||
return x + dx;
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmn wrapper for the MmlMn object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMnMixin} from '../../common/Wrappers/mn.js';
|
||||
import {MmlMn} from '../../../core/MmlTree/MmlNodes/mn.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmn wrapper for the MmlMn object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmn<N, T, D> extends
|
||||
CommonMnMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The mn wrapper
|
||||
*/
|
||||
public static kind = MmlMn.prototype.kind;
|
||||
|
||||
}
|
||||
+306
@@ -0,0 +1,306 @@
|
||||
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmo wrapper for the MmlMo object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMoMixin} from '../../common/Wrappers/mo.js';
|
||||
import {MmlMo} from '../../../core/MmlTree/MmlNodes/mo.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {DIRECTION, SVGCharData, SVGCharOptions} from '../FontData.js';
|
||||
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
const VFUZZ = 0.1; // overlap for vertical stretchy glyphs
|
||||
const HFUZZ = 0.1; // overlap for horizontal stretchy glyphs
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmo wrapper for the MmlMo object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmo<N, T, D> extends
|
||||
CommonMoMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The mo wrapper
|
||||
*/
|
||||
public static kind = MmlMo.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const attributes = this.node.attributes;
|
||||
const symmetric = (attributes.get('symmetric') as boolean) && this.stretch.dir !== DIRECTION.Horizontal;
|
||||
const stretchy = this.stretch.dir !== DIRECTION.None;
|
||||
if (stretchy && this.size === null) {
|
||||
this.getStretchedVariant([]);
|
||||
}
|
||||
let svg = this.standardSVGnode(parent);
|
||||
if (stretchy && this.size < 0) {
|
||||
this.stretchSVG();
|
||||
} else {
|
||||
const u = (symmetric || attributes.get('largeop') ? this.fixed(this.getCenterOffset()) : '0');
|
||||
const v = (this.node.getProperty('mathaccent') ? this.fixed(this.getAccentOffset()) : '0');
|
||||
if (u !== '0' || v !== '0') {
|
||||
this.adaptor.setAttribute(svg, 'transform', `translate(${v} ${u})`);
|
||||
}
|
||||
this.addChildren(svg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the SVG for a multi-character stretchy delimiter
|
||||
*/
|
||||
protected stretchSVG() {
|
||||
const stretch = this.stretch.stretch;
|
||||
const variants = this.getStretchVariants();
|
||||
const bbox = this.getBBox();
|
||||
if (this.stretch.dir === DIRECTION.Vertical) {
|
||||
this.stretchVertical(stretch, variants, bbox);
|
||||
} else {
|
||||
this.stretchHorizontal(stretch, variants, bbox);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the variant array for the assembly pieces
|
||||
*/
|
||||
protected getStretchVariants() {
|
||||
const c = this.stretch.c || this.getText().codePointAt(0);
|
||||
const variants = [] as string[];
|
||||
for (const i of this.stretch.stretch.keys()) {
|
||||
variants[i] = this.font.getStretchVariant(c, i);
|
||||
}
|
||||
return variants;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number[]} stretch The characters to use for stretching
|
||||
* @param {string[]} variant The variants for the parts to use for stretching
|
||||
* @param {BBox} bbox The full size of the stretched character
|
||||
*/
|
||||
protected stretchVertical(stretch: number[], variant: string[], bbox: BBox) {
|
||||
const {h, d, w} = bbox;
|
||||
const T = this.addTop(stretch[0], variant[0], h, w);
|
||||
const B = this.addBot(stretch[2], variant[2], d, w);
|
||||
if (stretch.length === 4) {
|
||||
const [H, D] = this.addMidV(stretch[3], variant[3], w);
|
||||
this.addExtV(stretch[1], variant[1], h, 0, T, H, w);
|
||||
this.addExtV(stretch[1], variant[1], 0, d, D, B, w);
|
||||
} else {
|
||||
this.addExtV(stretch[1], variant[1], h, d, T, B, w);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number[]} stretch The characters to use for stretching
|
||||
* @param {string[]} variant The variants for the parts to use for stretching
|
||||
* @param {BBox} bbox The full size of the stretched character
|
||||
*/
|
||||
protected stretchHorizontal(stretch: number[], variant: string[], bbox: BBox) {
|
||||
const w = bbox.w;
|
||||
const L = this.addLeft(stretch[0], variant[0]);
|
||||
const R = this.addRight(stretch[2], variant[2], w);
|
||||
if (stretch.length === 4) {
|
||||
const [x1, x2] = this.addMidH(stretch[3], variant[3], w);
|
||||
const w2 = w / 2;
|
||||
this.addExtH(stretch[1], variant[1], w2, L, w2 - x1);
|
||||
this.addExtH(stretch[1], variant[1], w2, x2 - w2, R, w2);
|
||||
} else {
|
||||
this.addExtH(stretch[1], variant[1], w, L, R);
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
|
||||
/**
|
||||
* @param {number} n The number of the character to look up
|
||||
* @param {string} variant The variant for the character to look up
|
||||
* @return {SVGCharData} The full CharData object, with CharOptions guaranteed to be defined
|
||||
*/
|
||||
protected getChar(n: number, variant: string): SVGCharData {
|
||||
const char = this.font.getChar(variant, n) || [0, 0, 0, null];
|
||||
return [char[0], char[1], char[2], char[3] || {}] as [number, number, number, SVGCharOptions];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n The character code for the glyph
|
||||
* @param {string} variant The variant for the glyph
|
||||
* @param {number} x The x position of the glyph
|
||||
* @param {number} y The y position of the glyph
|
||||
* @param {N} parent The container for the glyph
|
||||
* @return {number} The width of the character placed
|
||||
*/
|
||||
protected addGlyph(n: number, variant: string, x: number, y: number, parent: N = null): number {
|
||||
return this.placeChar(n, x, y, parent || this.element, variant);
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
|
||||
/**
|
||||
* @param {number} n The character number for the top glyph
|
||||
* @param {string} v The variant for the top glyph
|
||||
* @param {number} H The height of the stretched delimiter
|
||||
* @param {number} W The width of the stretched delimiter
|
||||
* @return {number} The total height of the top glyph
|
||||
*/
|
||||
protected addTop(n: number, v: string, H: number, W: number): number {
|
||||
if (!n) return 0;
|
||||
const [h, d, w] = this.getChar(n, v);
|
||||
this.addGlyph(n, v, (W - w) / 2, H - h);
|
||||
return h + d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n The character number for the extender glyph
|
||||
* @param {string} v The variant for the extender glyph
|
||||
* @param {number} H The height of the stretched delimiter
|
||||
* @param {number} D The depth of the stretched delimiter
|
||||
* @param {number} T The height of the top glyph in the delimiter
|
||||
* @param {number} B The height of the bottom glyph in the delimiter
|
||||
* @param {number} W The width of the stretched delimiter
|
||||
*/
|
||||
protected addExtV(n: number, v: string, H: number, D: number, T: number, B: number, W: number) {
|
||||
if (!n) return;
|
||||
T = Math.max(0, T - VFUZZ); // A little overlap on top
|
||||
B = Math.max(0, B - VFUZZ); // A little overlap on bottom
|
||||
const adaptor = this.adaptor;
|
||||
const [h, d, w] = this.getChar(n, v);
|
||||
const Y = H + D - T - B; // The height of the extender
|
||||
const s = 1.5 * Y / (h + d); // Scale height by 1.5 to avoid bad ends
|
||||
// (glyphs with rounded or anti-aliased ends don't stretch well,
|
||||
// so this makes for sharper ends)
|
||||
const y = (s * (h - d) - Y) / 2; // The bottom point to clip the extender
|
||||
if (Y <= 0) return;
|
||||
const svg = this.svg('svg', {
|
||||
width: this.fixed(w), height: this.fixed(Y),
|
||||
y: this.fixed(B - D), x: this.fixed((W - w) / 2),
|
||||
viewBox: [0, y, w, Y].map(x => this.fixed(x)).join(' ')
|
||||
});
|
||||
this.addGlyph(n, v, 0, 0, svg);
|
||||
const glyph = adaptor.lastChild(svg);
|
||||
adaptor.setAttribute(glyph, 'transform', `scale(1,${this.jax.fixed(s)})`);
|
||||
adaptor.append(this.element, svg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n The character number for the bottom glyph
|
||||
* @param {string} v The variant for the bottom glyph
|
||||
* @param {number} D The depth of the stretched delimiter
|
||||
* @param {number} W The width of the stretched delimiter
|
||||
* @return {number} The total height of the bottom glyph
|
||||
*/
|
||||
protected addBot(n: number, v: string, D: number, W: number): number {
|
||||
if (!n) return 0;
|
||||
const [h, d, w] = this.getChar(n, v);
|
||||
this.addGlyph(n, v, (W - w) / 2, d - D);
|
||||
return h + d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n The character number for the middle glyph
|
||||
* @param {string} v The variant for the middle glyph
|
||||
* @param {number} W The width of the stretched delimiter
|
||||
* @return {[number, number]} The top and bottom positions of the middle glyph
|
||||
*/
|
||||
protected addMidV(n: number, v: string, W: number): [number, number] {
|
||||
if (!n) return [0, 0];
|
||||
const [h, d, w] = this.getChar(n, v);
|
||||
const y = (d - h) / 2 + this.font.params.axis_height;
|
||||
this.addGlyph(n, v, (W - w) / 2, y);
|
||||
return [h + y, d - y];
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
|
||||
/**
|
||||
* @param {number} n The character number for the left glyph of the stretchy character
|
||||
* @param {string} v The variant for the left glyph
|
||||
* @return {number} The width of the left glyph
|
||||
*/
|
||||
protected addLeft(n: number, v: string): number {
|
||||
return (n ? this.addGlyph(n, v, 0, 0) : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n The character number for the extender glyph of the stretchy character
|
||||
* @param {string} v The variant for the extender glyph
|
||||
* @param {number} W The width of the stretched character
|
||||
* @param {number} L The width of the left glyph of the stretchy character
|
||||
* @param {number} R The width of the right glyph of the stretchy character
|
||||
* @param {number} x The x-position of the extender (needed for ones with two extenders)
|
||||
*/
|
||||
protected addExtH(n: number, v: string, W: number, L: number, R: number, x: number = 0) {
|
||||
if (!n) return;
|
||||
R = Math.max(0, R - HFUZZ); // A little less than the width of the right glyph
|
||||
L = Math.max(0, L - HFUZZ); // A little less than the width of the left glyph
|
||||
const adaptor = this.adaptor;
|
||||
const [h, d, w] = this.getChar(n, v);
|
||||
const X = W - L - R; // The width of the extender
|
||||
const Y = h + d + 2 * VFUZZ; // The height (plus some fuzz) of the extender
|
||||
const s = 1.5 * (X / w); // Scale the width so that left- and right-bearing won't hurt us
|
||||
const D = -(d + VFUZZ); // The bottom position of the glyph
|
||||
if (X <= 0) return;
|
||||
const svg = this.svg('svg', {
|
||||
width: this.fixed(X), height: this.fixed(Y),
|
||||
x: this.fixed(x + L), y: this.fixed(D),
|
||||
viewBox: [(s * w - X) / 2, D, X, Y].map(x => this.fixed(x)).join(' ')
|
||||
});
|
||||
this.addGlyph(n, v, 0, 0, svg);
|
||||
const glyph = adaptor.lastChild(svg);
|
||||
adaptor.setAttribute(glyph, 'transform', 'scale(' + this.jax.fixed(s) + ',1)');
|
||||
adaptor.append(this.element, svg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n The character number for the right glyph of the stretchy character
|
||||
* @param {string} v The variant for the right glyph
|
||||
* @param {number} W The width of the stretched character
|
||||
* @return {number} The width of the right glyph
|
||||
*/
|
||||
protected addRight(n: number, v: string, W: number): number {
|
||||
if (!n) return 0;
|
||||
const w = this.getChar(n, v)[2];
|
||||
return this.addGlyph(n, v, W - w, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n The character number for the middle glyph of the stretchy character
|
||||
* @param {string} v The variant for the middle glyph
|
||||
* @param {number} W The width of the stretched character
|
||||
* @return {[number, number]} The positions of the left and right edges of the middle glyph
|
||||
*/
|
||||
protected addMidH(n: number, v: string, W: number): [number, number] {
|
||||
if (!n) return [0, 0];
|
||||
const w = this.getChar(n, v)[2];
|
||||
this.addGlyph(n, v, (W - w) / 2, 0);
|
||||
return [(W - w) / 2, (W + w) / 2];
|
||||
}
|
||||
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmpadded wrapper for the MmlMpadded object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMpaddedMixin} from '../../common/Wrappers/mpadded.js';
|
||||
import {MmlMpadded} from '../../../core/MmlTree/MmlNodes/mpadded.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmpadded wrapper for the MmlMpadded object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmpadded<N, T, D> extends
|
||||
CommonMpaddedMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The mpadded wrapper
|
||||
*/
|
||||
public static kind = MmlMpadded.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
let svg = this.standardSVGnode(parent);
|
||||
const [ , , , , , dw, x, y, dx] = this.getDimens();
|
||||
const align = (this.node.attributes.get('data-align') as string) || 'left';
|
||||
const X = x + dx - (dw < 0 && align !== 'left' ? align === 'center' ? dw / 2 : dw : 0);
|
||||
//
|
||||
// If there is a horizontal or vertical shift,
|
||||
// use relative positioning to move the contents
|
||||
//
|
||||
if (X || y) {
|
||||
svg = this.adaptor.append(svg, this.svg('g'));
|
||||
this.place(X, y, svg);
|
||||
}
|
||||
this.addChildren(svg);
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmphantom wrapper for the MmlMphantom object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper} from '../Wrapper.js';
|
||||
import {MmlMphantom} from '../../../core/MmlTree/MmlNodes/mphantom.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmi wrapper for the MmlMi object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class SVGmphantom<N, T, D> extends SVGWrapper<N, T, D> {
|
||||
|
||||
/**
|
||||
* The mphantom wrapper
|
||||
*/
|
||||
public static kind = MmlMphantom.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
this.standardSVGnode(parent);
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGMroot wrapper for the MmlMroot object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, Constructor} from '../Wrapper.js';
|
||||
import {SVGmsqrt} from './msqrt.js';
|
||||
import {CommonMrootMixin} from '../../common/Wrappers/mroot.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {MmlMroot} from '../../../core/MmlTree/MmlNodes/mroot.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmroot wrapper for the MmlMroot object (extends SVGmsqrt)
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class SVGmroot<N, T, D> extends CommonMrootMixin<Constructor<SVGmsqrt<any, any, any>>>(SVGmsqrt) {
|
||||
|
||||
/**
|
||||
* The mroot wrapper
|
||||
*/
|
||||
public static kind = MmlMroot.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected addRoot(ROOT: N, root: SVGWrapper<N, T, D>, sbox: BBox, H: number) {
|
||||
root.toSVG(ROOT);
|
||||
const [x, h, dx] = this.getRootDimens(sbox, H);
|
||||
const bbox = root.getOuterBBox();
|
||||
root.place(dx * bbox.rscale, h);
|
||||
this.dx = x;
|
||||
}
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmrow wrapper for the MmlMrow object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor, Constructor} from '../Wrapper.js';
|
||||
import {CommonMrowMixin} from '../../common/Wrappers/mrow.js';
|
||||
import {CommonInferredMrowMixin} from '../../common/Wrappers/mrow.js';
|
||||
import {MmlMrow, MmlInferredMrow} from '../../../core/MmlTree/MmlNodes/mrow.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmrow wrapper for the MmlMrow object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmrow<N, T, D> extends
|
||||
CommonMrowMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The mrow wrapper
|
||||
*/
|
||||
public static kind = MmlMrow.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const svg = (this.node.isInferred ? (this.element = parent) : this.standardSVGnode(parent));
|
||||
this.addChildren(svg);
|
||||
// FIXME: handle line breaks
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGinferredMrow wrapper for the MmlInferredMrow object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGinferredMrow<N, T, D> extends
|
||||
CommonInferredMrowMixin<Constructor<SVGmrow<any, any, any>>>(SVGmrow) {
|
||||
|
||||
/**
|
||||
* The inferred-mrow wrapper
|
||||
*/
|
||||
public static kind = MmlInferredMrow.prototype.kind;
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGms wrapper for the MmlMs object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMsMixin} from '../../common/Wrappers/ms.js';
|
||||
import {MmlMs} from '../../../core/MmlTree/MmlNodes/ms.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGms wrapper for the MmlMs object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGms<N, T, D> extends
|
||||
CommonMsMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The ms wrapper
|
||||
*/
|
||||
public static kind = MmlMs.prototype.kind;
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmspace wrapper for the MmlMspace object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMspaceMixin} from '../../common/Wrappers/mspace.js';
|
||||
import {MmlMspace} from '../../../core/MmlTree/MmlNodes/mspace.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmspace wrapper for the MmlMspace object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmspace<N, T, D> extends
|
||||
CommonMspaceMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The mspace wrapper
|
||||
*/
|
||||
public static kind = MmlMspace.prototype.kind;
|
||||
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmsqrt wrapper for the MmlMsqrt object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMsqrtMixin} from '../../common/Wrappers/msqrt.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {MmlMsqrt} from '../../../core/MmlTree/MmlNodes/msqrt.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmsqrt wrapper for the MmlMsqrt object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class SVGmsqrt<N, T, D> extends CommonMsqrtMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The msqrt wrapper
|
||||
*/
|
||||
public static kind = MmlMsqrt.prototype.kind;
|
||||
|
||||
/**
|
||||
* Indent due to root
|
||||
*/
|
||||
public dx: number = 0;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const surd = this.childNodes[this.surd];
|
||||
const base = this.childNodes[this.base];
|
||||
const root = (this.root ? this.childNodes[this.root] : null);
|
||||
//
|
||||
// Get the parameters for the spacing of the parts
|
||||
//
|
||||
const sbox = surd.getBBox();
|
||||
const bbox = base.getOuterBBox();
|
||||
const q = this.getPQ(sbox)[1];
|
||||
const t = this.font.params.rule_thickness * this.bbox.scale;
|
||||
const H = bbox.h + q + t;
|
||||
//
|
||||
// Create the SVG structure for the root
|
||||
//
|
||||
const SVG = this.standardSVGnode(parent);
|
||||
const BASE = this.adaptor.append(SVG, this.svg('g'));
|
||||
//
|
||||
// Place the children
|
||||
//
|
||||
this.addRoot(SVG, root, sbox, H);
|
||||
surd.toSVG(SVG);
|
||||
surd.place(this.dx, H - sbox.h);
|
||||
base.toSVG(BASE);
|
||||
base.place(this.dx + sbox.w, 0);
|
||||
this.adaptor.append(SVG, this.svg('rect', {
|
||||
width: this.fixed(bbox.w), height: this.fixed(t),
|
||||
x: this.fixed(this.dx + sbox.w), y: this.fixed(H - t)
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add root HTML (overridden in mroot)
|
||||
*
|
||||
* @param {N} ROOT The container for the root
|
||||
* @param {SVGWrapper} root The wrapped MML root content
|
||||
* @param {BBox} sbox The bounding box of the surd
|
||||
* @param {number} H The height of the root as a whole
|
||||
*/
|
||||
protected addRoot(_ROOT: N, _root: SVGWrapper<N, T, D>, _sbox: BBox, _H: number) {
|
||||
}
|
||||
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmsubsup wrapper for the MmlMsubsup object
|
||||
* and the special cases SVGmsub and SVGmsup
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, Constructor} from '../Wrapper.js';
|
||||
import {SVGscriptbase} from './scriptbase.js';
|
||||
import {CommonMsubMixin} from '../../common/Wrappers/msubsup.js';
|
||||
import {CommonMsupMixin} from '../../common/Wrappers/msubsup.js';
|
||||
import {CommonMsubsupMixin} from '../../common/Wrappers/msubsup.js';
|
||||
import {MmlMsubsup, MmlMsub, MmlMsup} from '../../../core/MmlTree/MmlNodes/msubsup.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmsub wrapper for the MmlMsub object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmsub<N, T, D> extends
|
||||
CommonMsubMixin<SVGWrapper<any, any, any>, Constructor<SVGscriptbase<any, any, any>>>(SVGscriptbase) {
|
||||
|
||||
/**
|
||||
* The msub wrapper
|
||||
*/
|
||||
public static kind = MmlMsub.prototype.kind;
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmsup wrapper for the MmlMsup object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmsup<N, T, D> extends
|
||||
CommonMsupMixin<SVGWrapper<any, any, any>, Constructor<SVGscriptbase<any, any, any>>>(SVGscriptbase) {
|
||||
|
||||
/**
|
||||
* The msup wrapper
|
||||
*/
|
||||
public static kind = MmlMsup.prototype.kind;
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmsubsup wrapper for the MmlMsubsup object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmsubsup<N, T, D> extends
|
||||
CommonMsubsupMixin<SVGWrapper<any, any, any>, Constructor<SVGscriptbase<any, any, any>>>(SVGscriptbase) {
|
||||
|
||||
/**
|
||||
* The msubsup wrapper
|
||||
*/
|
||||
public static kind = MmlMsubsup.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const svg = this.standardSVGnode(parent);
|
||||
const [base, sup, sub] = [this.baseChild, this.supChild, this.subChild];
|
||||
const w = this.getBaseWidth();
|
||||
const x = this.getAdjustedIc();
|
||||
const [u, v] = this.getUVQ();
|
||||
|
||||
base.toSVG(svg);
|
||||
sup.toSVG(svg);
|
||||
sub.toSVG(svg);
|
||||
|
||||
sub.place(w, v);
|
||||
sup.place(w + x, u);
|
||||
}
|
||||
|
||||
}
|
||||
+418
@@ -0,0 +1,418 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmtable wrapper for the MmlMtable object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {SVGWrapperFactory} from '../WrapperFactory.js';
|
||||
import {CommonMtableMixin} from '../../common/Wrappers/mtable.js';
|
||||
import {SVGmtr} from './mtr.js';
|
||||
import {SVGmtd} from './mtd.js';
|
||||
import {MmlMtable} from '../../../core/MmlTree/MmlNodes/mtable.js';
|
||||
import {MmlNode} from '../../../core/MmlTree/MmlNode.js';
|
||||
import {OptionList} from '../../../util/Options.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
|
||||
const CLASSPREFIX = 'mjx-';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmtable wrapper for the MmlMtable object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class SVGmtable<N, T, D> extends
|
||||
CommonMtableMixin<SVGmtd<any, any, any>, SVGmtr<any, any, any>, SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The mtable wrapper
|
||||
*/
|
||||
public static kind = MmlMtable.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'g[data-mml-node="mtable"] > line[data-line], svg[data-table] > g > line[data-line]': {
|
||||
'stroke-width': '70px',
|
||||
fill: 'none'
|
||||
},
|
||||
'g[data-mml-node="mtable"] > rect[data-frame], svg[data-table] > g > rect[data-frame]': {
|
||||
'stroke-width': '70px',
|
||||
fill: 'none'
|
||||
},
|
||||
'g[data-mml-node="mtable"] > .mjx-dashed, svg[data-table] > g > .mjx-dashed': {
|
||||
'stroke-dasharray': '140'
|
||||
},
|
||||
'g[data-mml-node="mtable"] > .mjx-dotted, svg[data-table] > g > .mjx-dotted': {
|
||||
'stroke-linecap': 'round',
|
||||
'stroke-dasharray': '0,140'
|
||||
},
|
||||
'g[data-mml-node="mtable"] > g > svg': {
|
||||
overflow: 'visible'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The column for labels
|
||||
*/
|
||||
public labels: N;
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
constructor(factory: SVGWrapperFactory<N, T, D>, node: MmlNode, parent: SVGWrapper<N, T, D> = null) {
|
||||
super(factory, node, parent);
|
||||
const def: OptionList = {'data-labels': true};
|
||||
if (this.isTop) {
|
||||
def.transform = 'matrix(1 0 0 -1 0 0)';
|
||||
}
|
||||
this.labels = this.svg('g', def);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const svg = this.standardSVGnode(parent);
|
||||
this.placeRows(svg);
|
||||
this.handleColumnLines(svg);
|
||||
this.handleRowLines(svg);
|
||||
this.handleFrame(svg);
|
||||
const dx = this.handlePWidth(svg);
|
||||
this.handleLabels(svg, parent, dx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} svg The container in which to place the rows
|
||||
*/
|
||||
protected placeRows(svg: N) {
|
||||
const equal = this.node.attributes.get('equalrows') as boolean;
|
||||
const {H, D} = this.getTableData();
|
||||
const HD = this.getEqualRowHeight();
|
||||
const rSpace = this.getRowHalfSpacing();
|
||||
const rLines = [this.fLine, ...this.rLines, this.fLine];
|
||||
let y = this.getBBox().h - rLines[0];
|
||||
for (let i = 0; i < this.numRows; i++) {
|
||||
const row = this.childNodes[i];
|
||||
[row.H, row.D] = this.getRowHD(equal, HD, H[i], D[i]);
|
||||
[row.tSpace, row.bSpace] = [rSpace[i], rSpace[i + 1]];
|
||||
[row.tLine, row.bLine] = [rLines[i], rLines[i + 1]];
|
||||
row.toSVG(svg);
|
||||
row.place(0, y - rSpace[i] - row.H);
|
||||
y -= rSpace[i] + row.H + row.D + rSpace[i + 1] + rLines[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} equal True for equal-height rows
|
||||
* @param {number} HD The height of equal-height rows
|
||||
* @param {number} H The natural height of the row
|
||||
* @param {number} D The natural depth of the row
|
||||
* @returns {number[]} The (possibly scaled) height and depth to use
|
||||
*/
|
||||
protected getRowHD(equal: boolean, HD: number, H: number, D: number): [number, number] {
|
||||
return (equal ? [(HD + H - D) / 2, (HD - H + D) / 2] : [H, D]);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public handleColor() {
|
||||
super.handleColor();
|
||||
const rect = this.firstChild();
|
||||
if (rect) {
|
||||
this.adaptor.setAttribute(rect, 'width', this.fixed(this.getWidth()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add vertical lines between columns
|
||||
*
|
||||
* @param {N} svg The container for the table
|
||||
*/
|
||||
protected handleColumnLines(svg: N) {
|
||||
if (this.node.attributes.get('columnlines') === 'none') return;
|
||||
const lines = this.getColumnAttributes('columnlines');
|
||||
if (!lines) return;
|
||||
const cSpace = this.getColumnHalfSpacing();
|
||||
const cLines = this.cLines;
|
||||
const cWidth = this.getComputedWidths();
|
||||
let x = this.fLine;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
x += cSpace[i] + cWidth[i] + cSpace[i + 1];
|
||||
if (lines[i] !== 'none') {
|
||||
this.adaptor.append(svg, this.makeVLine(x, lines[i], cLines[i]));
|
||||
}
|
||||
x += cLines[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add horizontal lines between rows
|
||||
*
|
||||
* @param {N} svg The container for the table
|
||||
*/
|
||||
protected handleRowLines(svg: N) {
|
||||
if (this.node.attributes.get('rowlines') === 'none') return;
|
||||
const lines = this.getRowAttributes('rowlines');
|
||||
if (!lines) return;
|
||||
const equal = this.node.attributes.get('equalrows') as boolean;
|
||||
const {H, D} = this.getTableData();
|
||||
const HD = this.getEqualRowHeight();
|
||||
const rSpace = this.getRowHalfSpacing();
|
||||
const rLines = this.rLines;
|
||||
let y = this.getBBox().h - this.fLine;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const [rH, rD] = this.getRowHD(equal, HD, H[i], D[i]);
|
||||
y -= rSpace[i] + rH + rD + rSpace[i + 1];
|
||||
if (lines[i] !== 'none') {
|
||||
this.adaptor.append(svg, this.makeHLine(y, lines[i], rLines[i]));
|
||||
}
|
||||
y -= rLines[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a frame to the mtable, if needed
|
||||
*
|
||||
* @param {N} svg The container for the table
|
||||
*/
|
||||
protected handleFrame(svg: N) {
|
||||
if (this.frame && this.fLine) {
|
||||
const {h, d, w} = this.getBBox();
|
||||
const style = this.node.attributes.get('frame') as string;
|
||||
this.adaptor.append(svg, this.makeFrame(w, h, d, style));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number} The x-adjustement needed to handle the true size of percentage-width tables
|
||||
*/
|
||||
protected handlePWidth(svg: N): number {
|
||||
if (!this.pWidth) {
|
||||
return 0;
|
||||
}
|
||||
const {w, L, R} = this.getBBox();
|
||||
const W = L + this.pWidth + R;
|
||||
const align = this.getAlignShift()[0];
|
||||
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 dx = (align === 'left' ? 0 : align === 'right' ? dw : dw / 2);
|
||||
if (dx) {
|
||||
const table = this.svg('g', {}, this.adaptor.childNodes(svg));
|
||||
this.place(dx, 0, table);
|
||||
this.adaptor.append(svg, table);
|
||||
}
|
||||
return dx;
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/**
|
||||
* @param {string} style The line style whose class is to be obtained
|
||||
* @returns {string} The class name for the style
|
||||
*/
|
||||
protected lineClass(style: string): string {
|
||||
return CLASSPREFIX + style;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} w The width of the frame
|
||||
* @param {number} h The height of the frame
|
||||
* @param {number} d The depth of the frame
|
||||
* @param {string} style The border style for the frame
|
||||
* @returns {N} The SVG element for the frame
|
||||
*/
|
||||
protected makeFrame(w: number, h: number, d: number, style: string): N {
|
||||
const t = this.fLine;
|
||||
return this.svg('rect', this.setLineThickness(t, style, {
|
||||
'data-frame': true, 'class': this.lineClass(style),
|
||||
width: this.fixed(w - t), height: this.fixed(h + d - t),
|
||||
x: this.fixed(t / 2), y: this.fixed(t / 2 - d)
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} x The x location of the line
|
||||
* @param {string} style The border style for the line
|
||||
* @param {number} t The line thickness
|
||||
* @returns {N} The SVG element for the line
|
||||
*/
|
||||
protected makeVLine(x: number, style: string, t: number): N {
|
||||
const {h, d} = this.getBBox();
|
||||
const dt = (style === 'dotted' ? t / 2 : 0);
|
||||
const X = this.fixed(x + t / 2);
|
||||
return this.svg('line', this.setLineThickness(t, style, {
|
||||
'data-line': 'v', 'class': this.lineClass(style),
|
||||
x1: X, y1: this.fixed(dt - d), x2: X, y2: this.fixed(h - dt)
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} y The y location of the line
|
||||
* @param {string} style The border style for the line
|
||||
* @param {number} t The line thickness
|
||||
* @returns {N} The SVG element for the line
|
||||
*/
|
||||
protected makeHLine(y: number, style: string, t: number): N {
|
||||
const w = this.getBBox().w;
|
||||
const dt = (style === 'dotted' ? t / 2 : 0);
|
||||
const Y = this.fixed(y - t / 2);
|
||||
return this.svg('line', this.setLineThickness(t, style, {
|
||||
'data-line': 'h', 'class': this.lineClass(style),
|
||||
x1: this.fixed(dt), y1: Y, x2: this.fixed(w - dt), y2: Y
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} t The thickness of the line
|
||||
* @param {string} style The border style for the line
|
||||
* @param {OptionList} properties The list of properties to modify
|
||||
* @param {OptionList} The modified properties
|
||||
*/
|
||||
protected setLineThickness(t: number, style: string, properties: OptionList) {
|
||||
if (t !== .07) {
|
||||
properties['stroke-thickness'] = this.fixed(t);
|
||||
if (style !== 'solid') {
|
||||
properties['stroke-dasharray'] = (style === 'dotted' ? '0,' : '') + this.fixed(2 * t);
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/**
|
||||
* Handle addition of labels to the table
|
||||
*
|
||||
* @param {N} svg The container for the table contents
|
||||
* @param {N} parent The parent containing the the table
|
||||
* @param {number} dx The adjustement for percentage width tables
|
||||
*/
|
||||
protected handleLabels(svg: N, _parent: N, dx: number) {
|
||||
if (!this.hasLabels) return;
|
||||
const labels = this.labels;
|
||||
const attributes = this.node.attributes;
|
||||
//
|
||||
// Set the side for the labels
|
||||
//
|
||||
const side = attributes.get('side') as string;
|
||||
//
|
||||
// Add the labels to the table
|
||||
//
|
||||
this.spaceLabels();
|
||||
//
|
||||
// Handle top-level table to make it adapt to container size
|
||||
// but place subtables explicitly
|
||||
//
|
||||
this.isTop ? this.topTable(svg, labels, side) : this.subTable(svg, labels, side, dx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add spacing elements between the label rows to align them with the rest of the table
|
||||
*/
|
||||
protected spaceLabels() {
|
||||
const adaptor = this.adaptor;
|
||||
const h = this.getBBox().h;
|
||||
const L = this.getTableData().L;
|
||||
const space = this.getRowHalfSpacing();
|
||||
//
|
||||
// Start with frame size and add in spacing, height and depth,
|
||||
// and line thickness for each non-labeled row.
|
||||
//
|
||||
let y = h - this.fLine;
|
||||
let current = adaptor.firstChild(this.labels) as N;
|
||||
for (let i = 0; i < this.numRows; i++) {
|
||||
const row = this.childNodes[i] as SVGmtr<N, T, D>;
|
||||
if (row.node.isKind('mlabeledtr')) {
|
||||
const cell = row.childNodes[0];
|
||||
y -= space[i] + row.H;
|
||||
row.placeCell(cell, {x: 0, y: y, w: L, lSpace: 0, rSpace: 0, lLine: 0, rLine: 0});
|
||||
y -= row.D + space[i + 1] + this.rLines[i];
|
||||
current = adaptor.next(current) as N;
|
||||
} else {
|
||||
y -= space[i] + row.H + row.D + space[i + 1] + this.rLines[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles tables with labels so that the label will move with the size of the container
|
||||
*
|
||||
* @param {N} svg The SVG container for the table
|
||||
* @param {N} labels The group of labels
|
||||
* @param {string} side The side alignment (left or right)
|
||||
*/
|
||||
protected topTable(svg: N, labels: N, side: string) {
|
||||
const adaptor = this.adaptor;
|
||||
const {h, d, w, L, R} = this.getBBox();
|
||||
const W = L + (this.pWidth || w) + R;
|
||||
const LW = this.getTableData().L;
|
||||
const [ , align, shift] = this.getPadAlignShift(side);
|
||||
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} ${scale}`;
|
||||
let table = this.svg('svg', {
|
||||
'data-table': true,
|
||||
preserveAspectRatio: (align === 'left' ? 'xMinYMid' : align === 'right' ? 'xMaxYMid' : 'xMidYMid'),
|
||||
viewBox: [this.fixed(-dx), this.fixed(-h), 1, this.fixed(h + d)].join(' ')
|
||||
}, [
|
||||
this.svg('g', {transform: matrix}, adaptor.childNodes(svg))
|
||||
]);
|
||||
labels = this.svg('svg', {
|
||||
'data-labels': true,
|
||||
preserveAspectRatio: (side === 'left' ? 'xMinYMid' : 'xMaxYMid'),
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} svg The SVG container for the table
|
||||
* @param {N} labels The group of labels
|
||||
* @param {string} side The side alignment (left or right)
|
||||
* @param {number} dx The adjustement for percentage width tables
|
||||
*/
|
||||
protected subTable(svg: N, labels: N, side: string, dx: number) {
|
||||
const adaptor = this.adaptor;
|
||||
const {w, L, R} = this.getBBox();
|
||||
const W = L + (this.pWidth || w) + R;
|
||||
const labelW = this.getTableData().L;
|
||||
const align = this.getAlignShift()[0];
|
||||
const CW = Math.max(W, this.container.getWrapWidth(this.containerI));
|
||||
this.place(side === 'left' ?
|
||||
(align === 'left' ? 0 : align === 'right' ? W - CW + dx : (W - CW) / 2 + dx) - L :
|
||||
(align === 'left' ? CW : align === 'right' ? W + dx : (CW + W) / 2 + dx) - L - labelW,
|
||||
0, labels);
|
||||
adaptor.append(svg, labels);
|
||||
}
|
||||
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmtd wrapper for the MmlMtd object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMtdMixin} from '../../common/Wrappers/mtd.js';
|
||||
import {MmlMtd} from '../../../core/MmlTree/MmlNodes/mtd.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmtd wrapper for the MmlMtd object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmtd<N, T, D> extends
|
||||
CommonMtdMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The mtd wrapper
|
||||
*/
|
||||
public static kind = MmlMtd.prototype.kind;
|
||||
|
||||
/**
|
||||
* @param {number} x The x offset of the left side of the cell
|
||||
* @param {number} y The y offset of the baseline of the cell
|
||||
* @param {number} W The width of the cell
|
||||
* @param {number} H The height of the cell
|
||||
* @param {number} D The depth of the cell
|
||||
* @return {[number, number]} The x and y offsets used
|
||||
*/
|
||||
public placeCell(x: number, y: number, W: number, H: number, D: number): [number, number] {
|
||||
const bbox = this.getBBox();
|
||||
const h = Math.max(bbox.h, .75);
|
||||
const d = Math.max(bbox.d, .25);
|
||||
const calign = this.node.attributes.get('columnalign') as string;
|
||||
const ralign = this.node.attributes.get('rowalign') as string;
|
||||
const alignX = this.getAlignX(W, bbox, calign);
|
||||
const alignY = this.getAlignY(H, D, h, d, ralign);
|
||||
this.place(x + alignX, y + alignY);
|
||||
return [alignX, alignY];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} x The x offset of the left side of the cell
|
||||
* @param {number} y The y position of the bottom of the cell
|
||||
* @param {number} W The width of the cell
|
||||
* @param {number} H The height + depth of the cell
|
||||
*/
|
||||
public placeColor(x: number, y: number, W: number, H: number) {
|
||||
const adaptor = this.adaptor;
|
||||
const child = this.firstChild();
|
||||
if (child && adaptor.kind(child) === 'rect' && adaptor.getAttribute(child, 'data-bgcolor')) {
|
||||
adaptor.setAttribute(child, 'x', this.fixed(x));
|
||||
adaptor.setAttribute(child, 'y', this.fixed(y));
|
||||
adaptor.setAttribute(child, 'width', this.fixed(W));
|
||||
adaptor.setAttribute(child, 'height', this.fixed(H));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2019-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmtext wrapper for the MmlMtext object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonMtextMixin} from '../../common/Wrappers/mtext.js';
|
||||
import {MmlMtext} from '../../../core/MmlTree/MmlNodes/mtext.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmtext wrapper for the MmlMtext object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmtext<N, T, D> extends
|
||||
CommonMtextMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The mtext wrapper
|
||||
*/
|
||||
public static kind = MmlMtext.prototype.kind;
|
||||
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmtr wrapper for the MmlMtr object
|
||||
* and SVGmlabeledtr for MmlMlabeledtr
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor, Constructor} from '../Wrapper.js';
|
||||
import {CommonMtrMixin} from '../../common/Wrappers/mtr.js';
|
||||
import {CommonMlabeledtrMixin} from '../../common/Wrappers/mtr.js';
|
||||
import {SVGmtd} from './mtd.js';
|
||||
import {MmlMtr, MmlMlabeledtr} from '../../../core/MmlTree/MmlNodes/mtr.js';
|
||||
|
||||
|
||||
/**
|
||||
* The data needed for placeCell()
|
||||
*/
|
||||
export type SizeData = {
|
||||
x: number,
|
||||
y: number,
|
||||
w: number,
|
||||
lSpace: number,
|
||||
rSpace: number,
|
||||
lLine: number,
|
||||
rLine: number
|
||||
};
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmtr wrapper for the MmlMtr object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class SVGmtr<N, T, D> extends
|
||||
CommonMtrMixin<SVGmtd<any, any, any>, SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The mtr wrapper
|
||||
*/
|
||||
public static kind = MmlMtr.prototype.kind;
|
||||
|
||||
/**
|
||||
* The height of the row
|
||||
*/
|
||||
public H: number;
|
||||
/**
|
||||
* The depth of the row
|
||||
*/
|
||||
public D: number;
|
||||
/**
|
||||
* The space above the row
|
||||
*/
|
||||
public tSpace: number;
|
||||
/**
|
||||
* The space below the row
|
||||
*/
|
||||
public bSpace: number;
|
||||
/**
|
||||
* The line space above the row
|
||||
*/
|
||||
public tLine: number;
|
||||
/**
|
||||
* The line space below the row
|
||||
*/
|
||||
public bLine: number;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const svg = this.standardSVGnode(parent);
|
||||
this.placeCells(svg);
|
||||
this.placeColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the location of the cell contents in the row and expand the cell background colors
|
||||
*
|
||||
* @param {N} svg The container for the table
|
||||
*/
|
||||
protected placeCells(svg: N) {
|
||||
const cSpace = this.parent.getColumnHalfSpacing();
|
||||
const cLines = [this.parent.fLine, ...this.parent.cLines, this.parent.fLine];
|
||||
const cWidth = this.parent.getComputedWidths();
|
||||
const scale = 1 / this.getBBox().rscale;
|
||||
let x = cLines[0];
|
||||
for (let i = 0; i < this.numCells; i++) {
|
||||
const child = this.getChild(i);
|
||||
child.toSVG(svg);
|
||||
x += this.placeCell(child, {
|
||||
x: x, y: 0, lSpace: cSpace[i] * scale, rSpace: cSpace[i + 1] * scale, w: cWidth[i] * scale,
|
||||
lLine: cLines[i] * scale, rLine: cLines[i + 1] * scale
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SVGmtd} cell The cell to place
|
||||
* @param {SizeData} sizes The positioning information
|
||||
* @return {number} The new x position
|
||||
*/
|
||||
public placeCell(cell: SVGmtd<N, T, D>, sizes: SizeData): number {
|
||||
const {x, y, lSpace, w, rSpace, lLine, rLine} = sizes;
|
||||
const scale = 1 / this.getBBox().rscale;
|
||||
const [h, d] = [this.H * scale, this.D * scale];
|
||||
const [t, b] = [this.tSpace * scale, this.bSpace * scale];
|
||||
const [dx, dy] = cell.placeCell(x + lSpace, y, w, h, d);
|
||||
const W = lSpace + w + rSpace;
|
||||
cell.placeColor(-(dx + lSpace + lLine / 2), -(d + b + dy), W + (lLine + rLine) / 2, h + d + t + b);
|
||||
return W + rLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the backgound color to fill the entire row
|
||||
*/
|
||||
protected placeColor() {
|
||||
const scale = 1 / this.getBBox().rscale;
|
||||
const adaptor = this.adaptor;
|
||||
const child = this.firstChild();
|
||||
if (child && adaptor.kind(child) === 'rect' && adaptor.getAttribute(child, 'data-bgcolor')) {
|
||||
const [TL, BL] = [(this.tLine / 2) * scale, (this.bLine / 2) * scale];
|
||||
const [TS, BS] = [this.tSpace * scale, this.bSpace * scale];
|
||||
const [H, D] = [this.H * scale, this.D * scale];
|
||||
adaptor.setAttribute(child, 'y', this.fixed(-(D + BS + BL)));
|
||||
adaptor.setAttribute(child, 'width', this.fixed(this.parent.getWidth() * scale));
|
||||
adaptor.setAttribute(child, 'height', this.fixed(TL + TS + H + D + BS + BL));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGlabeledmtr wrapper for the MmlMlabeledtr object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmlabeledtr<N, T, D> extends
|
||||
CommonMlabeledtrMixin<SVGmtd<any, any, any>, Constructor<SVGmtr<any, any, any>>>(SVGmtr) {
|
||||
|
||||
/**
|
||||
* The mlabeledtr wrapper
|
||||
*/
|
||||
public static kind = MmlMlabeledtr.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
super.toSVG(parent);
|
||||
const child = this.childNodes[0];
|
||||
if (child) {
|
||||
child.toSVG(this.parent.labels);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGmunderover wrapper for the MmlMunderover object
|
||||
* and the special cases SVGmunder and SVGmsup
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, Constructor} from '../Wrapper.js';
|
||||
import {SVGmsubsup, SVGmsub, SVGmsup} from './msubsup.js';
|
||||
import {CommonMunderMixin} from '../../common/Wrappers/munderover.js';
|
||||
import { CommonMoverMixin} from '../../common/Wrappers/munderover.js';
|
||||
import {CommonMunderoverMixin} from '../../common/Wrappers/munderover.js';
|
||||
import {MmlMunderover, MmlMunder, MmlMover} from '../../../core/MmlTree/MmlNodes/munderover.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmunder wrapper for the MmlMunder object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmunder<N, T, D> extends
|
||||
CommonMunderMixin<SVGWrapper<any, any, any>, Constructor<SVGmsub<any, any, any>>>(SVGmsub) {
|
||||
|
||||
/**
|
||||
* The munder wrapper
|
||||
*/
|
||||
public static kind = MmlMunder.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
if (this.hasMovableLimits()) {
|
||||
super.toSVG(parent);
|
||||
return;
|
||||
}
|
||||
|
||||
const svg = this.standardSVGnode(parent);
|
||||
const [base, script] = [this.baseChild, this.scriptChild];
|
||||
const [bbox, sbox] = [base.getOuterBBox(), script.getOuterBBox()];
|
||||
|
||||
base.toSVG(svg);
|
||||
script.toSVG(svg);
|
||||
|
||||
const delta = (this.isLineBelow ? 0 : this.getDelta(true));
|
||||
const v = this.getUnderKV(bbox, sbox)[1];
|
||||
const [bx, sx] = this.getDeltaW([bbox, sbox], [0, -delta]);
|
||||
|
||||
base.place(bx, 0);
|
||||
script.place(sx, v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGmover wrapper for the MmlMover object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmover<N, T, D> extends
|
||||
CommonMoverMixin<SVGWrapper<any, any, any>, Constructor<SVGmsup<any, any, any>>>(SVGmsup) {
|
||||
|
||||
/**
|
||||
* The mover wrapper
|
||||
*/
|
||||
public static kind = MmlMover.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
if (this.hasMovableLimits()) {
|
||||
super.toSVG(parent);
|
||||
return;
|
||||
}
|
||||
const svg = this.standardSVGnode(parent);
|
||||
const [base, script] = [this.baseChild, this.scriptChild];
|
||||
const [bbox, sbox] = [base.getOuterBBox(), script.getOuterBBox()];
|
||||
|
||||
base.toSVG(svg);
|
||||
script.toSVG(svg);
|
||||
|
||||
const delta = (this.isLineAbove ? 0 : this.getDelta());
|
||||
const u = this.getOverKU(bbox, sbox)[1];
|
||||
const [bx, sx] = this.getDeltaW([bbox, sbox], [0, delta]);
|
||||
|
||||
base.place(bx, 0);
|
||||
script.place(sx, u);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/*
|
||||
* The SVGmunderover wrapper for the MmlMunderover object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGmunderover<N, T, D> extends
|
||||
CommonMunderoverMixin<SVGWrapper<any, any, any>, Constructor<SVGmsubsup<any, any, any>>>(SVGmsubsup) {
|
||||
|
||||
/**
|
||||
* The munderover wrapper
|
||||
*/
|
||||
public static kind = MmlMunderover.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
if (this.hasMovableLimits()) {
|
||||
super.toSVG(parent);
|
||||
return;
|
||||
}
|
||||
const svg = this.standardSVGnode(parent);
|
||||
const [base, over, under] = [this.baseChild, this.overChild, this.underChild];
|
||||
const [bbox, obox, ubox] = [base.getOuterBBox(), over.getOuterBBox(), under.getOuterBBox()];
|
||||
|
||||
base.toSVG(svg);
|
||||
under.toSVG(svg);
|
||||
over.toSVG(svg);
|
||||
|
||||
const delta = this.getDelta();
|
||||
const u = this.getOverKU(bbox, obox)[1];
|
||||
const v = this.getUnderKV(bbox, ubox)[1];
|
||||
const [bx, ux, ox] = this.getDeltaW([bbox, ubox, obox],
|
||||
[0, this.isLineBelow ? 0 : -delta, this.isLineAbove ? 0 : delta]);
|
||||
|
||||
base.place(bx, 0);
|
||||
under.place(ux, v);
|
||||
over.place(ox, u);
|
||||
}
|
||||
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the a base class for SVGmsubsup, SVGmunderover
|
||||
* and their relatives. (Since munderover can become msubsup
|
||||
* when movablelimits is set, munderover needs to be able to
|
||||
* do the same thing as msubsup in some cases.)
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonScriptbaseMixin} from '../../common/Wrappers/scriptbase.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* A base class for msup/msub/msubsup and munder/mover/munderover
|
||||
* wrapper implementations
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGscriptbase<N, T, D> extends
|
||||
CommonScriptbaseMixin<SVGWrapper<any, any, any>, SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The scriptbase wrapper
|
||||
*/
|
||||
public static kind = 'scriptbase';
|
||||
|
||||
/**
|
||||
* This gives the common output for msub and msup. It is overridden
|
||||
* for all the others (msubsup, munder, mover, munderover).
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const svg = this.standardSVGnode(parent);
|
||||
const w = this.getBaseWidth();
|
||||
const [x, v] = this.getOffset();
|
||||
this.baseChild.toSVG(svg);
|
||||
this.scriptChild.toSVG(svg);
|
||||
this.scriptChild.place(w + x, v);
|
||||
}
|
||||
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the SVGsemantics wrapper for the MmlSemantics object
|
||||
* and the associated wrappers for annotations
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {SVGWrapper, SVGConstructor} from '../Wrapper.js';
|
||||
import {CommonSemanticsMixin} from '../../common/Wrappers/semantics.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {MmlSemantics, MmlAnnotation, MmlAnnotationXML} from '../../../core/MmlTree/MmlNodes/semantics.js';
|
||||
import {XMLNode} from '../../../core/MmlTree/MmlNode.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGsemantics wrapper for the MmlSemantics object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class SVGsemantics<N, T, D> extends
|
||||
CommonSemanticsMixin<SVGConstructor<any, any, any>>(SVGWrapper) {
|
||||
|
||||
/**
|
||||
* The semantics wrapper
|
||||
*/
|
||||
public static kind = MmlSemantics.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const svg = this.standardSVGnode(parent);
|
||||
if (this.childNodes.length) {
|
||||
this.childNodes[0].toSVG(svg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGannotation wrapper for the MmlAnnotation object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class SVGannotation<N, T, D> extends SVGWrapper<N, T, D> {
|
||||
|
||||
/**
|
||||
* The annotation wrapper
|
||||
*/
|
||||
public static kind = MmlAnnotation.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
// FIXME: output as plain text
|
||||
super.toSVG(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox() {
|
||||
// FIXME: compute using the DOM, if possible
|
||||
return this.bbox;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGannotationXML wrapper for the MmlAnnotationXML object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class SVGannotationXML<N, T, D> extends SVGWrapper<N, T, D> {
|
||||
|
||||
/**
|
||||
* The annotation-xml wrapper
|
||||
*/
|
||||
public static kind = MmlAnnotationXML.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'foreignObject[data-mjx-xml]': {
|
||||
'font-family': 'initial',
|
||||
'line-height': 'normal',
|
||||
overflow: 'visible'
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The SVGxml wrapper for the XMLNode object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class SVGxml<N, T, D> extends SVGWrapper<N, T, D> {
|
||||
|
||||
/**
|
||||
* The XMLNode wrapper
|
||||
*/
|
||||
public static kind = XMLNode.prototype.kind;
|
||||
|
||||
/**
|
||||
* Don't include inline-block CSS for this element
|
||||
*/
|
||||
public static autoStyle = false;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toSVG(parent: N) {
|
||||
const xml = this.adaptor.clone((this.node as XMLNode).getXML() as N);
|
||||
const em = this.jax.math.metrics.em * this.jax.math.metrics.scale;
|
||||
const scale = this.fixed(1 / em);
|
||||
const {w, h, d} = this.getBBox();
|
||||
this.element = this.adaptor.append(parent, this.svg('foreignObject', {
|
||||
'data-mjx-xml': true,
|
||||
y: this.jax.fixed(-h * em) + 'px',
|
||||
width: this.jax.fixed(w * em) + 'px',
|
||||
height: this.jax.fixed((h + d) * em) + 'px',
|
||||
transform: `scale(${scale}) matrix(1 0 0 -1 0 0)`
|
||||
}, [xml]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox(bbox: BBox, _recompute: boolean = false) {
|
||||
const {w, h, d} = this.jax.measureXMLnode((this.node as XMLNode).getXML() as N);
|
||||
bbox.w = w;
|
||||
bbox.h = h;
|
||||
bbox.d = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected getStyles() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected getScale() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected getVariant() {}
|
||||
}
|
||||
Reference in New Issue
Block a user