diff --git a/src/core-atoms/array.ts b/src/core-atoms/array.ts index 0b2cf3f7d..5bc31d1b6 100644 --- a/src/core-atoms/array.ts +++ b/src/core-atoms/array.ts @@ -514,6 +514,7 @@ export class ArrayAtom extends Atom { // const separator = new Box(null, { classes: 'vertical-separator' }); + separator.height = totalHeight; separator.setStyle('height', totalHeight, 'em'); separator.setStyle( 'border-right', diff --git a/src/core-atoms/box.ts b/src/core-atoms/box.ts index 178b5f7b7..fd750e08c 100644 --- a/src/core-atoms/box.ts +++ b/src/core-atoms/box.ts @@ -64,10 +64,6 @@ export class BoxAtom extends Atom { const offset = parentContext.toEm(this.offset ?? { dimension: 0 }); base.depth += offset; - // base.setStyle('display', 'inline-block'); - // base.setStyle('position', 'relative'); - // base.setStyle('background-color', 'rgba(255, 0, 0, .2)'); - // base.setStyle('vertical-align', raise, 'em'); const context = new Context({ parent: parentContext }, this.style); @@ -88,10 +84,11 @@ export class BoxAtom extends Atom { box.depth = base.depth + padding; box.setStyle('box-sizing', 'border-box'); box.setStyle('position', 'absolute'); + box.setStyle('top', -padding + 0.3, 'em'); // empirical + box.setStyle('left', 0); box.setStyle('height', box.height + box.depth, 'em'); - box.setStyle('left', 0); box.setStyle('width', '100%'); if (this.backgroundcolor) { diff --git a/src/core-atoms/enclose.ts b/src/core-atoms/enclose.ts index 09989aaf4..80dd25e0e 100644 --- a/src/core-atoms/enclose.ts +++ b/src/core-atoms/enclose.ts @@ -353,6 +353,7 @@ export class EncloseAtom extends Atom { result.setStyle('display', 'inline-block'); // The padding adds to the width and height of the pod + result.width = w - 2 * padding; result.height = notation.height; result.depth = notation.depth; result.left = padding; diff --git a/src/core-atoms/genfrac.ts b/src/core-atoms/genfrac.ts index 49bcc0040..38132dfe8 100644 --- a/src/core-atoms/genfrac.ts +++ b/src/core-atoms/genfrac.ts @@ -149,7 +149,7 @@ export class GenfracAtom extends Atom { : Atom.createBox(denomContext, this.below, { type: 'ignore' }) ?? new Box(null, { type: 'ignore' }); - const ruleWidth = this.hasBarLine ? metrics.defaultRuleThickness : 0; + const ruleThickness = this.hasBarLine ? metrics.defaultRuleThickness : 0; // Rule 15b from TeXBook Appendix G, p.444 // @@ -163,12 +163,12 @@ export class GenfracAtom extends Atom { let denomShift: number; if (fracContext.isDisplayStyle) { numerShift = numContext.metrics.num1; // Set u ← σ8 - clearance = ruleWidth > 0 ? 3 * ruleWidth : 7 * ruleWidth; + clearance = ruleThickness > 0 ? 3 * ruleThickness : 7 * ruleThickness; denomShift = denomContext.metrics.denom1; // V ← σ11 } else { - if (ruleWidth > 0) { + if (ruleThickness > 0) { numerShift = numContext.metrics.num2; // U ← σ9 - clearance = ruleWidth; // Φ ← θ + clearance = ruleThickness; // Φ ← θ } else { numerShift = numContext.metrics.num3; // U ← σ10 clearance = 3 * metrics.defaultRuleThickness; // Φ ← 3 ξ8 @@ -182,7 +182,7 @@ export class GenfracAtom extends Atom { const numerDepth = numerBox.depth; const denomHeight = denomBox.height; let frac: Box; - if (ruleWidth <= 0) { + if (ruleThickness <= 0) { // Rule 15c from Appendix G // No bar line between numerator and denominator const candidateClearance = @@ -209,23 +209,25 @@ export class GenfracAtom extends Atom { } else { // Rule 15d from Appendix G of the TeXBook. // There is a bar line between the numerator and the denominator - const numerLine = AXIS_HEIGHT + ruleWidth / 2; - const denomLine = AXIS_HEIGHT - ruleWidth / 2; - if (numerShift < clearance + numerDepth + numerLine) - numerShift = clearance + numerDepth + numerLine; - - if (denomShift < clearance + denomHeight - denomLine) - denomShift = clearance + denomHeight - denomLine; const fracLine = new Box(null, { classes: 'ML__frac-line', mode: this.mode, style: this.style, }); - // Manually set the height of the frac line because its height is - // created in CSS - fracLine.height = ruleWidth / 2; - fracLine.depth = ruleWidth / 2; + + // const numerLine = AXIS_HEIGHT + ruleThickness / 2; + const denomLine = AXIS_HEIGHT - ruleThickness / 2; + // if (numerShift < clearance + numerDepth + numerLine) + // numerShift = clearance + numerDepth + numerLine; + + fracLine.width = Math.max(numerBox.width, denomBox.width); + fracLine.height = ruleThickness / 2; + fracLine.depth = ruleThickness / 2; + + if (denomShift < clearance + denomHeight - denomLine) + denomShift = clearance + denomHeight - denomLine; + frac = new VBox({ individualShift: [ { diff --git a/src/core-atoms/prompt.ts b/src/core-atoms/prompt.ts index 52f5c6473..ffab7d541 100644 --- a/src/core-atoms/prompt.ts +++ b/src/core-atoms/prompt.ts @@ -68,9 +68,10 @@ export class PromptAtom extends Atom { const content = Atom.createBox(parentContext, this.body); if (!content) return null; - // An empty prompt should not be too small, pretend content has height 0.5em + // An empty prompt should not be too small, pretend content + // has height sigma 5 (x-height) - if (!content.height) content.height = 0.5; + if (!content.height) content.height = context.metrics.xHeight; content.setStyle('vertical-align', -content.height, 'em'); if (this.correctness === 'correct') { @@ -108,19 +109,21 @@ export class PromptAtom extends Atom { }); box.height = base.height + vPadding; box.depth = base.depth + vPadding; + box.width = base.width + 2 * hPadding; box.setStyle('box-sizing', 'border-box'); box.setStyle('position', 'absolute'); - box.setStyle('height', base.height + base.depth + 2 * vPadding, 'em'); - if (hPadding === 0) box.setStyle('width', '100%'); - else { - box.setStyle('width', `calc(100% + ${2 * hPadding}em)`); + box.setStyle('height', base.height + base.depth + 2 * vPadding, 'em'); // @todo: remove + if (hPadding === 0) box.setStyle('width', '100%'); // @todo: remove + if (hPadding !== 0) { + box.setStyle('width', `calc(100% + ${2 * hPadding}em)`); // @todo: remove box.setStyle('top', fboxsep, 'em'); // empirical box.setStyle('left', -hPadding, 'em'); } // empty prompt should be a little wider if (!this.body || this.body.length === 1) { + box.width = 3 * hPadding; box.setStyle('width', `calc(100% + ${3 * hPadding}em)`); box.setStyle('left', -1.5 * hPadding, 'em'); } diff --git a/src/core-definitions/styling.ts b/src/core-definitions/styling.ts index b0504a322..842ad8efe 100644 --- a/src/core-definitions/styling.ts +++ b/src/core-definitions/styling.ts @@ -999,21 +999,31 @@ defineFunction(['overline', 'underline'], '{:auto}', { ); const inner = Atom.createBox(context, atom.body); if (!inner) return null; - const ruleWidth = + const ruleThickness = context.metrics.defaultRuleThickness / context.scalingFactor; const line = new Box(null, { classes: position + '-line' }); - line.height = ruleWidth; - line.maxFontSize = ruleWidth * 1.125 * context.scalingFactor; + line.height = ruleThickness; + line.maxFontSize = ruleThickness * 1.125 * context.scalingFactor; let stack: Box; if (position === 'overline') { stack = new VBox({ shift: 0, - children: [{ box: inner }, 3 * ruleWidth, { box: line }, ruleWidth], + children: [ + { box: inner }, + 3 * ruleThickness, + { box: line }, + ruleThickness, + ], }); } else { stack = new VBox({ top: inner.height, - children: [ruleWidth, { box: line }, 3 * ruleWidth, { box: inner }], + children: [ + ruleThickness, + { box: line }, + 3 * ruleThickness, + { box: inner }, + ], }); } diff --git a/src/core/box.ts b/src/core/box.ts index 663f33211..5579b30f8 100644 --- a/src/core/box.ts +++ b/src/core/box.ts @@ -437,7 +437,8 @@ export class Box implements BoxInterface { svgMarkup += '">'; svgMarkup += body; svgMarkup += ''; - svgMarkup += ' 0 && siblings[i - 1].type === 'skip') { - siblings[i - 1].width += width; - return; - } - siblings.splice(i, 0, new SkipBox(width)); + if (i > 0 && siblings[i - 1].type === 'skip') siblings[i - 1].width += width; + else siblings.splice(i, 0, new SkipBox(width)); } diff --git a/src/core/v-box.ts b/src/core/v-box.ts index c79612534..dc3a52bad 100644 --- a/src/core/v-box.ts +++ b/src/core/v-box.ts @@ -145,6 +145,7 @@ function makeRows( let minPos = depth; let maxPos = depth; let currPos = depth; + let width = 0; for (const child of children) { if (typeof child === 'number') currPos += child; else { @@ -166,6 +167,7 @@ function makeRows( realChildren.push(childWrap); currPos += box.height + box.depth; + width = Math.max(width, childWrap.width); } minPos = Math.min(minPos, currPos); maxPos = Math.max(maxPos, currPos); @@ -175,6 +177,8 @@ function makeRows( // This cell's bottom edge will determine the containing table's baseline // without overly expanding the containing line-box. const vlist = new Box(realChildren, { classes: 'vlist' }); + vlist.width = width; + vlist.height = maxPos; vlist.setStyle('height', maxPos, 'em'); // A second row is used if necessary to represent the vlist's depth. @@ -187,6 +191,7 @@ function makeRows( // text content. And that min-height over-rides our desired height. // So we put another empty box inside the depth strut box. const depthStrut = new Box(new Box(null), { classes: 'vlist' }); + depthStrut.height = -minPos; depthStrut.setStyle('height', -minPos, 'em'); // Safari wants the first row to have inline content; otherwise it @@ -214,15 +219,17 @@ export class VBox extends Box { options?: { classes?: string; type?: BoxType } ) { const [rows, height, depth] = makeRows(content); + super(rows.length === 1 ? rows[0] : rows, { + type: options?.type, classes: (options?.classes ?? '') + ' vlist-t' + (rows.length === 2 ? ' vlist-t2' : ''), - height, - depth, - type: options?.type, }); + this.height = height; + this.depth = depth; + this.width = rows.reduce((acc, row) => Math.max(acc, row.width), 0); } } diff --git a/src/public/core-types.ts b/src/public/core-types.ts index 49e808d83..f3646e2c3 100644 --- a/src/public/core-types.ts +++ b/src/public/core-types.ts @@ -356,7 +356,7 @@ export type BoxCSSProperties = | 'display' | 'font-family' | 'left' - | 'height' + | 'height' // @todo: remove | 'line-height' | 'margin-top' | 'margin-left' @@ -369,7 +369,7 @@ export type BoxCSSProperties = | 'top' | 'bottom' | 'vertical-align' - | 'width' + | 'width' // @todo: remove | 'z-index'; export type MatrixEnvironment = diff --git a/test/smoke/index.html b/test/smoke/index.html index 0d82beb50..558ba3774 100644 --- a/test/smoke/index.html +++ b/test/smoke/index.html @@ -20,6 +20,10 @@ max-width: 640px; width: 100%; } + #mathml-render { + margin-top: 1rem; + margin-bottom: 1rem; + } @@ -39,9 +43,10 @@

Smoke Test

- -=a\phase{abc}\enclose{longdiv}{\frac{1}{12345}}\enclose{box}{\sqrt{\frac{1}{x+1}}} + \sqrt[3]{x} \sqrt{1+2\ne\frac{1}{2345}} + @@ -70,6 +75,7 @@

ASCII Math

MathML

+

Latex to Speakable Text

@@ -291,6 +297,9 @@

Latex to Speakable Text

setHtml('latex', mf.getValue('latex-expanded')); setHtml('ascii-math', mf.getValue('ascii-math')); setHtml('mathml', mf.getValue('math-ml')); + document.getElementById( + 'mathml-render' + ).innerHTML = `${mf.getValue('math-ml')}`; // setHtml('math-json', ce.parse(mf.value)); setHtml('latex2speech', convertLatexToSpeakableText(mf.getValue()));