Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix rendering of embedded HTML to work with scaling and mathsize changes. (mathjax/MathJax#3116) #1012

Merged
merged 5 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ts/a11y/semantic-enrich.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {Handler} from '../core/Handler.js';
import {MathDocument, AbstractMathDocument, MathDocumentConstructor} from '../core/MathDocument.js';
import {MathItem, AbstractMathItem, STATE, newState} from '../core/MathItem.js';
import {MmlNode} from '../core/MmlTree/MmlNode.js';
import {HtmlNode} from '../core/MmlTree/MmlNodes/HtmlNode.js';
import {MathML} from '../input/mathml.js';
import {SerializedMmlVisitor} from '../core/MmlTree/SerializedMmlVisitor.js';
import {OptionList, expandable} from '../util/Options.js';
Expand Down Expand Up @@ -70,6 +71,10 @@ export class enrichVisitor<N, T, D> extends SerializedMmlVisitor {
return mml;
}

public visitHtmlNode(node: HtmlNode<any>, _space: string): string {
return node.getSerializedXML();
}

public visitMactionNode(node: MmlNode, space: string) {
let [nl, endspace] = (node.childNodes.length === 0 ? ['', ''] : ['\n', space]);
const children = this.childNodeMml(node, space + ' ', nl);
Expand Down
40 changes: 11 additions & 29 deletions ts/core/MmlTree/MmlNodes/HtmlNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,18 @@
* @author dpvc@mathjax.org (Davide Cervone)
*/

import {AbstractMmlEmptyNode} from '../MmlNode.js';
import {XMLNode} from '../MmlNode.js';
import {DOMAdaptor} from '../../DOMAdaptor.js';
import {PropertyList} from '../../Tree/Node.js';


/******************************************************************/
/**
* The HtmlNode calss for storing HTML within token elements
* The HtmlNode class for storing HTML within token elements
*
* @template N The HTMLElement class
*/
export class HtmlNode<N> extends AbstractMmlEmptyNode {
/**
* The HTML content for this node
*/
protected html: N = null;

/**
* DOM adaptor for the content
*/
protected adaptor: DOMAdaptor<any, any, any> = null;
export class HtmlNode<N> extends XMLNode {

/**
* @override
Expand All @@ -51,14 +42,14 @@ export class HtmlNode<N> extends AbstractMmlEmptyNode {
}

/**
* @return {Object} Return the node's HTML content
* @return {N} Return the node's HTML content
*/
public getHTML(): Object {
return this.html;
public getHTML(): N {
return this.getXML() as any as N;
}

/**
* @param {object} html The HTML content to be saved
* @param {N} html The HTML content to be saved
* @param {DOMAdaptor} adaptor DOM adaptor for the content
* @return {HTMLNode} The HTML node (for chaining of method calls)
*/
Expand All @@ -71,37 +62,28 @@ export class HtmlNode<N> extends AbstractMmlEmptyNode {
} catch (error) {
html = adaptor.node('span', {}, [html]);
}
this.html = html;
this.adaptor = adaptor;
return this;
return this.setXML(html, adaptor) as HtmlNode<N>;
}

/**
* @return {string} The serialized HTML content
*/
public getSerializedHTML(): string {
return this.adaptor.outerHTML(this.html);
return this.adaptor.outerHTML(this.xml);
}

/**
* @return {string} The text of the HTML content
*/
public textContent(): string {
return this.adaptor.textContent(this.html);
}

/**
* @override
*/
public copy(): HtmlNode<N> {
return (this.factory.create(this.kind) as HtmlNode<N>).setHTML(this.adaptor.clone(this.html));
return this.adaptor.textContent(this.xml);
}

/**
* Just indicate that this is HTML data
*/
public toString() {
const kind = this.adaptor.kind(this.html);
const kind = this.adaptor.kind(this.xml);
return `HTML=<${kind}>...</${kind}>` ;
}

Expand Down
7 changes: 0 additions & 7 deletions ts/output/chtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,13 +255,6 @@ CommonOutputJax<
this.clearCache();
}

/**
* @override
*/
protected getInitialScale() {
return this.math.metrics.scale;
}

/*****************************************************************/

/**
Expand Down
53 changes: 5 additions & 48 deletions ts/output/chtml/Wrappers/HtmlNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,11 @@
* @author dpvc@mathjax.org (Davide Cervone)
*/

import {CHTML} from '../../chtml.js';
import {ChtmlWrapper, ChtmlWrapperClass} from '../Wrapper.js';
import {ChtmlWrapper} from '../Wrapper.js';
import {ChtmlWrapperFactory} from '../WrapperFactory.js';
import {ChtmlCharOptions, ChtmlVariantData, ChtmlDelimiterData,
ChtmlFontData, ChtmlFontDataClass} from '../FontData.js';
import {CommonHtmlNode, CommonHtmlNodeClass, CommonHtmlNodeMixin} from '../../common/Wrappers/HtmlNode.js';
import {ChtmlXmlNode, ChtmlXmlNodeNTD, ChtmlXmlNodeClass} from './semantics.js';
import {MmlNode} from '../../../core/MmlTree/MmlNode.js';
import {HtmlNode} from '../../../core/MmlTree/MmlNodes/HtmlNode.js';
import {StyleList as StyleList} from '../../../util/StyleList.js';

/*****************************************************************/
/**
Expand All @@ -39,11 +35,7 @@ import {StyleList as StyleList} from '../../../util/StyleList.js';
* @template T The Text node class
* @template D The Document class
*/
export interface ChtmlHtmlNodeNTD<N, T, D> extends ChtmlWrapper<N, T, D>, CommonHtmlNode<
N, T, D,
CHTML<N, T, D>, ChtmlWrapper<N, T, D>, ChtmlWrapperFactory<N, T, D>, ChtmlWrapperClass<N, T, D>,
ChtmlCharOptions, ChtmlVariantData, ChtmlDelimiterData, ChtmlFontData, ChtmlFontDataClass
> {}
export interface ChtmlHtmlNodeNTD<N, T, D> extends ChtmlXmlNodeNTD<N, T, D> {}

/**
* The ChtmlHtmlNodeClass interface for the CHTML HtmlNode wrapper
Expand All @@ -52,11 +44,7 @@ export interface ChtmlHtmlNodeNTD<N, T, D> extends ChtmlWrapper<N, T, D>, Common
* @template T The Text node class
* @template D The Document class
*/
export interface ChtmlHtmlNodeClass<N, T, D> extends ChtmlWrapperClass<N, T, D>, CommonHtmlNodeClass<
N, T, D,
CHTML<N, T, D>, ChtmlWrapper<N, T, D>, ChtmlWrapperFactory<N, T, D>, ChtmlWrapperClass<N, T, D>,
ChtmlCharOptions, ChtmlVariantData, ChtmlDelimiterData, ChtmlFontData, ChtmlFontDataClass
> {
export interface ChtmlHtmlNodeClass<N, T, D> extends ChtmlXmlNodeClass<N, T, D> {
new(factory: ChtmlWrapperFactory<N, T, D>, node: MmlNode, parent?: ChtmlWrapper<N, T, D>): ChtmlHtmlNodeNTD<N, T, D>;
}

Expand All @@ -68,47 +56,16 @@ export interface ChtmlHtmlNodeClass<N, T, D> extends ChtmlWrapperClass<N, T, D>,
*/
export const ChtmlHtmlNode = (function <N, T, D>(): ChtmlHtmlNodeClass<N, T, D> {

const Base = CommonHtmlNodeMixin<
N, T, D,
CHTML<N, T, D>, ChtmlWrapper<N, T, D>, ChtmlWrapperFactory<N, T, D>, ChtmlWrapperClass<N, T, D>,
ChtmlCharOptions, ChtmlVariantData, ChtmlDelimiterData, ChtmlFontData, ChtmlFontDataClass,
ChtmlHtmlNodeClass<N, T, D>
>(ChtmlWrapper);

// Avoid message about base constructors not having the same type
// (they should both be ChtmlWrapper<N, T, D>, but are thought of as different by typescript)
// @ts-ignore
return class ChtmlHtmlNode extends Base implements ChtmlHtmlNodeNTD<N, T, D> {
return class ChtmlHtmlNode extends ChtmlXmlNode implements ChtmlHtmlNodeNTD<N, T, D> {

/**
* @override
*/
public static kind = HtmlNode.prototype.kind;

/**
* @override
*/
public static styles: StyleList = {
'mjx-html': {
'line-height': 'normal',
'text-align': 'initial'
},
'mjx-html-holder': {
display: 'block',
position: 'absolute',
width: '100%',
height: '100%'
}
};

/**
* @override
*/
public toCHTML(parents: N[]) {
this.markUsed();
this.dom = [this.adaptor.append(parents[0], this.getHTML()) as N];
}

};

})<any, any, any>();
84 changes: 56 additions & 28 deletions ts/output/chtml/Wrappers/semantics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ import {ChtmlWrapperFactory} from '../WrapperFactory.js';
import {ChtmlCharOptions, ChtmlVariantData, ChtmlDelimiterData,
ChtmlFontData, ChtmlFontDataClass} from '../FontData.js';
import {CommonSemantics, CommonSemanticsClass, CommonSemanticsMixin} from '../../common/Wrappers/semantics.js';
import {CommonXmlNode, CommonXmlNodeClass, CommonXmlNodeMixin} from '../../common/Wrappers/XmlNode.js';
import {MmlNode} from '../../../core/MmlTree/MmlNode.js';
import {MmlSemantics, MmlAnnotation, MmlAnnotationXML} from '../../../core/MmlTree/MmlNodes/semantics.js';
import {XMLNode} from '../../../core/MmlTree/MmlNode.js';
import {BBox} from '../../../util/BBox.js';
import {StyleList} from '../../../util/StyleList.js';
import {StyleList as Styles} from '../../../util/Styles.js';


/*****************************************************************/
/**
Expand Down Expand Up @@ -167,55 +169,81 @@ export const ChtmlAnnotationXML = (function <N, T, D>(): ChtmlWrapperClass<N, T,


/*****************************************************************/
/**
* The ChtmlXmlNode interface for the CHTML XmlNode wrapper
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
export interface ChtmlXmlNodeNTD<N, T, D> extends ChtmlWrapper<N, T, D>, CommonXmlNode<
N, T, D,
CHTML<N, T, D>, ChtmlWrapper<N, T, D>, ChtmlWrapperFactory<N, T, D>, ChtmlWrapperClass<N, T, D>,
ChtmlCharOptions, ChtmlVariantData, ChtmlDelimiterData, ChtmlFontData, ChtmlFontDataClass
> {}

/**
* The ChtmlXmlNodeClass interface for the CHTML XmlNode wrapper
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
export interface ChtmlXmlNodeClass<N, T, D> extends ChtmlWrapperClass<N, T, D>, CommonXmlNodeClass<
N, T, D,
CHTML<N, T, D>, ChtmlWrapper<N, T, D>, ChtmlWrapperFactory<N, T, D>, ChtmlWrapperClass<N, T, D>,
ChtmlCharOptions, ChtmlVariantData, ChtmlDelimiterData, ChtmlFontData, ChtmlFontDataClass
> {
new(factory: ChtmlWrapperFactory<N, T, D>, node: MmlNode, parent?: ChtmlWrapper<N, T, D>): ChtmlXmlNodeNTD<N, T, D>;
}

/**
* The ChtmlXmlNode wrapper for the XMLNode class
*/
export const ChtmlXmlNode = (function <N, T, D>(): ChtmlWrapperClass<N, T, D> {

return class ChtmlXmlNode extends ChtmlWrapper<N, T, D> {
const Base = CommonXmlNodeMixin<
N, T, D,
CHTML<N, T, D>, ChtmlWrapper<N, T, D>, ChtmlWrapperFactory<N, T, D>, ChtmlWrapperClass<N, T, D>,
ChtmlCharOptions, ChtmlVariantData, ChtmlDelimiterData, ChtmlFontData, ChtmlFontDataClass,
ChtmlXmlNodeClass<N, T, D>
>(ChtmlWrapper);

// Avoid message about base constructors not having the same type
// (they should both be ChtmlWrapper<N, T, D>, but are thought of as different by typescript)
// @ts-ignore
return class ChtmlXmlNode extends Base implements ChtmlXmlNodeNTD<N, T, D> {

/**
* @override
*/
public static kind = XMLNode.prototype.kind;

/**
* Don't set up inline-block styles for this
*/
public static autoStyle = false;

/**
* @override
*/
public toCHTML(parents: N[]) {
this.dom = [this.adaptor.append(parents[0], this.adaptor.clone((this.node as XMLNode).getXML() as N)) as N];
this.markUsed();
this.dom = [this.adaptor.append(parents[0], this.getHTML()) as N];
}

/**
* @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;
public addHDW(html: N, styles: Styles): N {
const scale = this.jax.options.scale;
const {h, d, w} = this.bbox;
const rscale = scale * this.metrics.scale;
styles.width = this.em(w * rscale);
styles.height = this.em((h + d) * rscale);
styles['vertical-align'] = this.em(-d * rscale);
styles.position = 'relative';
return this.html('mjx-html-holder', {style: {
transform: `scale(${this.jax.fixed(scale)})`,
'transform-origin': 'top left',
}}, [html]);
}

/**
* @override
*/
protected getStyles() {}

/**
* @override
*/
protected getScale() {}

/**
* @override
*/
protected getVariant() {}

};

})<any, any, any>();
29 changes: 0 additions & 29 deletions ts/output/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -805,35 +805,6 @@ export abstract class CommonOutputJax<
*/
public abstract measureTextNode(text: N): UnknownBBox;

/**
* Measure the width, height and depth of an annotation-xml node's content
*
* @param{N} xml The xml content node to be measured
* @return {UnknownBBox} The width, height, and depth of the content
*/
public measureXMLnode(xml: N): UnknownBBox {
const adaptor = this.adaptor;
const content = this.html('mjx-xml-block', {style: {display: 'inline-block'}}, [adaptor.clone(xml)]);
const base = this.html('mjx-baseline', {style: {display: 'inline-block', width: 0, height: 0}});
const style = {
position: 'absolute',
display: 'inline-block',
'font-family': 'initial',
'line-height': 'normal'
};
const node = this.html('mjx-measure-xml', {style}, [base, content]);
adaptor.append(adaptor.parent(this.math.start.node), this.container);
adaptor.append(this.container, node);
const em = this.math.metrics.em * this.math.metrics.scale;
const {left, right, bottom, top} = adaptor.nodeBBox(content);
const w = (right - left) / em;
const h = (adaptor.nodeBBox(base).top - top) / em;
const d = (bottom - top) / em - h;
adaptor.remove(this.container);
adaptor.remove(node);
return {w, h, d};
}

/**
* @param {CssFontData} font The family, style, and weight for the given font
* @param {StyleList} styles The style object to add the font data to
Expand Down
13 changes: 13 additions & 0 deletions ts/output/common/Wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,19 @@ export class CommonWrapper<
return rscale;
}

/**
* @return {number} The cumulative relative scale from the root to the current node
*/
public getRScale(): number {
let rscale = 1;
let node = this as any as WW;
while (node) {
rscale *= node.bbox.rscale;
node = node.parent;
}
return rscale;
}

/**
* @return {string} For a token node, the combined text content of the node's children
*/
Expand Down
Loading