/************************************************************* * * Copyright (c) 2017 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 CommonMsubsup wrapper mixin for the MmlMsubsup object * and the special cases CommonMsub and CommonMsup * * @author dpvc@mathjax.org (Davide Cervone) */ import {AnyWrapper, Constructor} from '../Wrapper.js'; import {CommonScriptbase, ScriptbaseConstructor} from './scriptbase.js'; import {BBox} from '../BBox.js'; import {MmlMsubsup, MmlMsub, MmlMsup} from '../../../core/MmlTree/MmlNodes/msubsup.js'; /*****************************************************************/ /** * The CommonMsub interface * * @template W The child-node Wrapper class */ export interface CommonMsub extends CommonScriptbase { } /** * Shorthand for the CommonMsub constructor * * @template W The child-node Wrapper class */ export type MsubConstructor = Constructor>; /*****************************************************************/ /** * The CommonMsub wrapper mixin for the MmlMsub object * * @template W The child-node Wrapper class * @template T The Wrapper class constructor type */ export function CommonMsubMixin>(Base: T): MsubConstructor & T { return class extends Base { /** * @override */ public get script() { return this.childNodes[(this.node as MmlMsub).sub]; } /** * Get the shift for the subscript * * @override */ public getOffset(bbox: BBox, sbox: BBox) { return [0, -this.getV(bbox, sbox)]; } }; } /*****************************************************************/ /** * The CommonMsup interface * * @template W The child-node Wrapper class */ export interface CommonMsup extends CommonScriptbase { } /** * Shorthand for the CommonMsup constructor * * @template W The child-node Wrapper class */ export type MsupConstructor = Constructor>; /*****************************************************************/ /** * The CommonMsup wrapper mixin for the MmlMsup object * * @template W The child-node Wrapper class * @template T The Wrapper class constructor type */ export function CommonMsupMixin>(Base: T): MsubConstructor & T { return class extends Base { /** * @override */ public get script() { return this.childNodes[(this.node as MmlMsup).sup]; } /** * Get the shift for the superscript * * @override */ public getOffset(bbox: BBox, sbox: BBox) { const x = (this.baseCore.bbox.ic ? .2 * this.baseCore.bbox.ic + .05 : 0); return [x, this.getU(bbox, sbox)]; } }; } /*****************************************************************/ /** * The CommonMsubsup interface * * @template W The child-node Wrapper class */ export interface CommonMsubsup extends CommonScriptbase { /** * Cached values for the script offsets and separation (so if they are * computed in computeBBox(), they don't have to be recomputed during output) */ UVQ: number[]; /** * The wrapper for the subscript */ readonly subChild: W; /** * The wrapper for the superscript */ readonly supChild: W; /** * Get the shift for the scripts and their separation (TeXBook Appendix G 18adef) * * @param {BBox} basebox The bounding box of the base * @param {BBox} subbox The bounding box of the superscript * @param {BBox} supbox The bounding box of the subscript * @return {number[]} The vertical offsets for super and subscripts, and the space between them */ getUVQ(basebox: BBox, subbox: BBox, supbox: BBox): number[]; } /** * Shorthand for the CommonMsubsup constructor * * @template W The child-node Wrapper class */ export type MsubsupConstructor = Constructor>; /*****************************************************************/ /** * The CommomMsubsup wrapper for the MmlMsubsup object * * @template W The child-node Wrapper class * @template T The Wrapper class constructor type */ export function CommonMsubsupMixin>(Base: T): MsubsupConstructor & T { return class extends Base { /** * Cached values for the script offsets and separation (so if they are * computed in computeBBox(), they don't have to be recomputed during output) */ public UVQ: number[] = null; /** * @return {W} The wrapper for the subscript */ public get subChild() { return this.childNodes[(this.node as MmlMsubsup).sub]; } /** * @return {W} The wrapper for the superscript */ public get supChild() { return this.childNodes[(this.node as MmlMsubsup).sup]; } /** * @override */ public computeBBox(bbox: BBox, recompute: boolean = false) { const basebox = this.baseChild.getBBox(); const subbox = this.subChild.getBBox(); const supbox = this.supChild.getBBox(); bbox.empty(); bbox.append(basebox); const w = bbox.w; const [u, v, q] = this.getUVQ(basebox, subbox, supbox); bbox.combine(subbox, w, v); bbox.combine(supbox, w + this.coreIC(), u); bbox.w += this.font.params.scriptspace; bbox.clean(); this.setChildPWidths(recompute); } /** * Get the shift for the scripts and their separation (TeXBook Appendix G 18adef) * * @param {BBox} basebox The bounding box of the base * @param {BBox} subbox The bounding box of the superscript * @param {BBox} supbox The bounding box of the subscript * @return {number[]} The vertical offsets for super and subscripts, and the space between them */ public getUVQ(basebox: BBox, subbox: BBox, supbox: BBox) { if (this.UVQ) return this.UVQ; const tex = this.font.params; const t = 3 * tex.rule_thickness; const subscriptshift = this.length2em(this.node.attributes.get('subscriptshift'), tex.sub2); const drop = (this.isCharBase() ? 0 : basebox.d + tex.sub_drop * subbox.rscale); // // u and v are the veritcal shifts of the scripts, initially set to minimum values and then adjusted // let [u, v] = [this.getU(basebox, supbox), Math.max(drop, subscriptshift)]; // // q is the space currently between the super- and subscripts. // If it is less than 3 rule thicknesses, // increase the subscript offset to make the space 3 rule thicknesses // If the bottom of the superscript is below 4/5 of the x-height // raise both the super- and subscripts by the difference // (make the bottom of the superscript be at 4/5 the x-height, and the // subscript 3 rule thickness below that). // let q = (u - supbox.d * supbox.rscale) - (subbox.h * subbox.rscale - v); if (q < t) { v += t - q; const p = (4/5) * tex.x_height - (u - supbox.d * supbox.rscale); if (p > 0) { u += p; v -= p; } } // // Make sure the shifts are at least the minimum amounts and // return the shifts and the space between the scripts // u = Math.max(this.length2em(this.node.attributes.get('superscriptshift'), u), u); v = Math.max(this.length2em(this.node.attributes.get('subscriptshift'), v), v); q = (u - supbox.d * supbox.rscale) - (subbox.h * subbox.rscale - v); this.UVQ = [u, -v, q]; return this.UVQ; } }; }