|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import ParseError from "./ParseError"; |
|
import Style from "./Style"; |
|
|
|
import {PathNode, SvgNode, SymbolNode} from "./domTree"; |
|
import {sqrtPath, innerPath, tallDelim} from "./svgGeometry"; |
|
import buildCommon from "./buildCommon"; |
|
import {getCharacterMetrics} from "./fontMetrics"; |
|
import symbols from "./symbols"; |
|
import utils from "./utils"; |
|
import {makeEm} from "./units"; |
|
import fontMetricsData from "./fontMetricsData"; |
|
|
|
import type Options from "./Options"; |
|
import type {CharacterMetrics} from "./fontMetrics"; |
|
import type {HtmlDomNode, DomSpan, SvgSpan} from "./domTree"; |
|
import type {Mode} from "./types"; |
|
import type {StyleInterface} from "./Style"; |
|
import type {VListElem} from "./buildCommon"; |
|
|
|
|
|
|
|
|
|
|
|
const getMetrics = function( |
|
symbol: string, |
|
font: string, |
|
mode: Mode, |
|
): CharacterMetrics { |
|
const replace = symbols.math[symbol] && symbols.math[symbol].replace; |
|
const metrics = |
|
getCharacterMetrics(replace || symbol, font, mode); |
|
if (!metrics) { |
|
throw new Error(`Unsupported symbol ${symbol} and font size ${font}.`); |
|
} |
|
return metrics; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const styleWrap = function( |
|
delim: HtmlDomNode, |
|
toStyle: StyleInterface, |
|
options: Options, |
|
classes: string[], |
|
): DomSpan { |
|
const newOptions = options.havingBaseStyle(toStyle); |
|
|
|
const span = buildCommon.makeSpan( |
|
classes.concat(newOptions.sizingClasses(options)), |
|
[delim], options); |
|
|
|
const delimSizeMultiplier = |
|
newOptions.sizeMultiplier / options.sizeMultiplier; |
|
span.height *= delimSizeMultiplier; |
|
span.depth *= delimSizeMultiplier; |
|
span.maxFontSize = newOptions.sizeMultiplier; |
|
|
|
return span; |
|
}; |
|
|
|
const centerSpan = function( |
|
span: DomSpan, |
|
options: Options, |
|
style: StyleInterface, |
|
) { |
|
const newOptions = options.havingBaseStyle(style); |
|
const shift = |
|
(1 - options.sizeMultiplier / newOptions.sizeMultiplier) * |
|
options.fontMetrics().axisHeight; |
|
|
|
span.classes.push("delimcenter"); |
|
span.style.top = makeEm(shift); |
|
span.height -= shift; |
|
span.depth += shift; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
const makeSmallDelim = function( |
|
delim: string, |
|
style: StyleInterface, |
|
center: boolean, |
|
options: Options, |
|
mode: Mode, |
|
classes: string[], |
|
): DomSpan { |
|
const text = buildCommon.makeSymbol(delim, "Main-Regular", mode, options); |
|
const span = styleWrap(text, style, options, classes); |
|
if (center) { |
|
centerSpan(span, options, style); |
|
} |
|
return span; |
|
}; |
|
|
|
|
|
|
|
|
|
const mathrmSize = function( |
|
value: string, |
|
size: number, |
|
mode: Mode, |
|
options: Options, |
|
): SymbolNode { |
|
return buildCommon.makeSymbol(value, "Size" + size + "-Regular", |
|
mode, options); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const makeLargeDelim = function(delim, |
|
size: number, |
|
center: boolean, |
|
options: Options, |
|
mode: Mode, |
|
classes: string[], |
|
): DomSpan { |
|
const inner = mathrmSize(delim, size, mode, options); |
|
const span = styleWrap( |
|
buildCommon.makeSpan(["delimsizing", "size" + size], [inner], options), |
|
Style.TEXT, options, classes); |
|
if (center) { |
|
centerSpan(span, options, Style.TEXT); |
|
} |
|
return span; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const makeGlyphSpan = function( |
|
symbol: string, |
|
font: "Size1-Regular" | "Size4-Regular", |
|
mode: Mode, |
|
): VListElem { |
|
let sizeClass; |
|
|
|
if (font === "Size1-Regular") { |
|
sizeClass = "delim-size1"; |
|
} else { |
|
sizeClass = "delim-size4"; |
|
} |
|
|
|
const corner = buildCommon.makeSpan( |
|
["delimsizinginner", sizeClass], |
|
[buildCommon.makeSpan([], [buildCommon.makeSymbol(symbol, font, mode)])]); |
|
|
|
|
|
|
|
return {type: "elem", elem: corner}; |
|
}; |
|
|
|
const makeInner = function( |
|
ch: string, |
|
height: number, |
|
options: Options |
|
): VListElem { |
|
|
|
const width = fontMetricsData['Size4-Regular'][ch.charCodeAt(0)] |
|
? fontMetricsData['Size4-Regular'][ch.charCodeAt(0)][4] |
|
: fontMetricsData['Size1-Regular'][ch.charCodeAt(0)][4]; |
|
const path = new PathNode("inner", innerPath(ch, Math.round(1000 * height))); |
|
const svgNode = new SvgNode([path], { |
|
"width": makeEm(width), |
|
"height": makeEm(height), |
|
|
|
"style": "width:" + makeEm(width), |
|
"viewBox": "0 0 " + 1000 * width + " " + Math.round(1000 * height), |
|
"preserveAspectRatio": "xMinYMin", |
|
}); |
|
const span = buildCommon.makeSvgSpan([], [svgNode], options); |
|
span.height = height; |
|
span.style.height = makeEm(height); |
|
span.style.width = makeEm(width); |
|
return {type: "elem", elem: span}; |
|
}; |
|
|
|
|
|
const lapInEms = 0.008; |
|
const lap = {type: "kern", size: -1 * lapInEms}; |
|
const verts = ["|", "\\lvert", "\\rvert", "\\vert"]; |
|
const doubleVerts = ["\\|", "\\lVert", "\\rVert", "\\Vert"]; |
|
|
|
|
|
|
|
|
|
|
|
const makeStackedDelim = function( |
|
delim: string, |
|
heightTotal: number, |
|
center: boolean, |
|
options: Options, |
|
mode: Mode, |
|
classes: string[], |
|
): DomSpan { |
|
|
|
|
|
let top; |
|
let middle; |
|
let repeat; |
|
let bottom; |
|
let svgLabel = ""; |
|
let viewBoxWidth = 0; |
|
top = repeat = bottom = delim; |
|
middle = null; |
|
|
|
let font = "Size1-Regular"; |
|
|
|
|
|
|
|
|
|
if (delim === "\\uparrow") { |
|
repeat = bottom = "\u23d0"; |
|
} else if (delim === "\\Uparrow") { |
|
repeat = bottom = "\u2016"; |
|
} else if (delim === "\\downarrow") { |
|
top = repeat = "\u23d0"; |
|
} else if (delim === "\\Downarrow") { |
|
top = repeat = "\u2016"; |
|
} else if (delim === "\\updownarrow") { |
|
top = "\\uparrow"; |
|
repeat = "\u23d0"; |
|
bottom = "\\downarrow"; |
|
} else if (delim === "\\Updownarrow") { |
|
top = "\\Uparrow"; |
|
repeat = "\u2016"; |
|
bottom = "\\Downarrow"; |
|
} else if (utils.contains(verts, delim)) { |
|
repeat = "\u2223"; |
|
svgLabel = "vert"; |
|
viewBoxWidth = 333; |
|
} else if (utils.contains(doubleVerts, delim)) { |
|
repeat = "\u2225"; |
|
svgLabel = "doublevert"; |
|
viewBoxWidth = 556; |
|
} else if (delim === "[" || delim === "\\lbrack") { |
|
top = "\u23a1"; |
|
repeat = "\u23a2"; |
|
bottom = "\u23a3"; |
|
font = "Size4-Regular"; |
|
svgLabel = "lbrack"; |
|
viewBoxWidth = 667; |
|
} else if (delim === "]" || delim === "\\rbrack") { |
|
top = "\u23a4"; |
|
repeat = "\u23a5"; |
|
bottom = "\u23a6"; |
|
font = "Size4-Regular"; |
|
svgLabel = "rbrack"; |
|
viewBoxWidth = 667; |
|
} else if (delim === "\\lfloor" || delim === "\u230a") { |
|
repeat = top = "\u23a2"; |
|
bottom = "\u23a3"; |
|
font = "Size4-Regular"; |
|
svgLabel = "lfloor"; |
|
viewBoxWidth = 667; |
|
} else if (delim === "\\lceil" || delim === "\u2308") { |
|
top = "\u23a1"; |
|
repeat = bottom = "\u23a2"; |
|
font = "Size4-Regular"; |
|
svgLabel = "lceil"; |
|
viewBoxWidth = 667; |
|
} else if (delim === "\\rfloor" || delim === "\u230b") { |
|
repeat = top = "\u23a5"; |
|
bottom = "\u23a6"; |
|
font = "Size4-Regular"; |
|
svgLabel = "rfloor"; |
|
viewBoxWidth = 667; |
|
} else if (delim === "\\rceil" || delim === "\u2309") { |
|
top = "\u23a4"; |
|
repeat = bottom = "\u23a5"; |
|
font = "Size4-Regular"; |
|
svgLabel = "rceil"; |
|
viewBoxWidth = 667; |
|
} else if (delim === "(" || delim === "\\lparen") { |
|
top = "\u239b"; |
|
repeat = "\u239c"; |
|
bottom = "\u239d"; |
|
font = "Size4-Regular"; |
|
svgLabel = "lparen"; |
|
viewBoxWidth = 875; |
|
} else if (delim === ")" || delim === "\\rparen") { |
|
top = "\u239e"; |
|
repeat = "\u239f"; |
|
bottom = "\u23a0"; |
|
font = "Size4-Regular"; |
|
svgLabel = "rparen"; |
|
viewBoxWidth = 875; |
|
} else if (delim === "\\{" || delim === "\\lbrace") { |
|
top = "\u23a7"; |
|
middle = "\u23a8"; |
|
bottom = "\u23a9"; |
|
repeat = "\u23aa"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "\\}" || delim === "\\rbrace") { |
|
top = "\u23ab"; |
|
middle = "\u23ac"; |
|
bottom = "\u23ad"; |
|
repeat = "\u23aa"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "\\lgroup" || delim === "\u27ee") { |
|
top = "\u23a7"; |
|
bottom = "\u23a9"; |
|
repeat = "\u23aa"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "\\rgroup" || delim === "\u27ef") { |
|
top = "\u23ab"; |
|
bottom = "\u23ad"; |
|
repeat = "\u23aa"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "\\lmoustache" || delim === "\u23b0") { |
|
top = "\u23a7"; |
|
bottom = "\u23ad"; |
|
repeat = "\u23aa"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "\\rmoustache" || delim === "\u23b1") { |
|
top = "\u23ab"; |
|
bottom = "\u23a9"; |
|
repeat = "\u23aa"; |
|
font = "Size4-Regular"; |
|
} |
|
|
|
|
|
const topMetrics = getMetrics(top, font, mode); |
|
const topHeightTotal = topMetrics.height + topMetrics.depth; |
|
const repeatMetrics = getMetrics(repeat, font, mode); |
|
const repeatHeightTotal = repeatMetrics.height + repeatMetrics.depth; |
|
const bottomMetrics = getMetrics(bottom, font, mode); |
|
const bottomHeightTotal = bottomMetrics.height + bottomMetrics.depth; |
|
let middleHeightTotal = 0; |
|
let middleFactor = 1; |
|
if (middle !== null) { |
|
const middleMetrics = getMetrics(middle, font, mode); |
|
middleHeightTotal = middleMetrics.height + middleMetrics.depth; |
|
middleFactor = 2; |
|
} |
|
|
|
|
|
|
|
const minHeight = topHeightTotal + bottomHeightTotal + middleHeightTotal; |
|
|
|
|
|
const repeatCount = Math.max(0, Math.ceil( |
|
(heightTotal - minHeight) / (middleFactor * repeatHeightTotal))); |
|
|
|
|
|
const realHeightTotal = |
|
minHeight + repeatCount * middleFactor * repeatHeightTotal; |
|
|
|
|
|
|
|
|
|
|
|
let axisHeight = options.fontMetrics().axisHeight; |
|
if (center) { |
|
axisHeight *= options.sizeMultiplier; |
|
} |
|
|
|
const depth = realHeightTotal / 2 - axisHeight; |
|
|
|
|
|
|
|
const stack = []; |
|
|
|
if (svgLabel.length > 0) { |
|
|
|
|
|
const midHeight = realHeightTotal - topHeightTotal - bottomHeightTotal; |
|
const viewBoxHeight = Math.round(realHeightTotal * 1000); |
|
const pathStr = tallDelim(svgLabel, Math.round(midHeight * 1000)); |
|
const path = new PathNode(svgLabel, pathStr); |
|
const width = (viewBoxWidth / 1000).toFixed(3) + "em"; |
|
const height = (viewBoxHeight / 1000).toFixed(3) + "em"; |
|
const svg = new SvgNode([path], { |
|
"width": width, |
|
"height": height, |
|
"viewBox": `0 0 ${viewBoxWidth} ${viewBoxHeight}`, |
|
}); |
|
const wrapper = buildCommon.makeSvgSpan([], [svg], options); |
|
wrapper.height = viewBoxHeight / 1000; |
|
wrapper.style.width = width; |
|
wrapper.style.height = height; |
|
stack.push({type: "elem", elem: wrapper}); |
|
} else { |
|
|
|
|
|
stack.push(makeGlyphSpan(bottom, font, mode)); |
|
stack.push(lap); |
|
|
|
if (middle === null) { |
|
|
|
|
|
const innerHeight = realHeightTotal - topHeightTotal - bottomHeightTotal |
|
+ 2 * lapInEms; |
|
stack.push(makeInner(repeat, innerHeight, options)); |
|
} else { |
|
|
|
|
|
const innerHeight = (realHeightTotal - topHeightTotal - |
|
bottomHeightTotal - middleHeightTotal) / 2 + 2 * lapInEms; |
|
stack.push(makeInner(repeat, innerHeight, options)); |
|
|
|
stack.push(lap); |
|
stack.push(makeGlyphSpan(middle, font, mode)); |
|
stack.push(lap); |
|
stack.push(makeInner(repeat, innerHeight, options)); |
|
} |
|
|
|
|
|
stack.push(lap); |
|
stack.push(makeGlyphSpan(top, font, mode)); |
|
} |
|
|
|
|
|
const newOptions = options.havingBaseStyle(Style.TEXT); |
|
const inner = buildCommon.makeVList({ |
|
positionType: "bottom", |
|
positionData: depth, |
|
children: stack, |
|
}, newOptions); |
|
|
|
return styleWrap( |
|
buildCommon.makeSpan(["delimsizing", "mult"], [inner], newOptions), |
|
Style.TEXT, options, classes); |
|
}; |
|
|
|
|
|
|
|
const vbPad = 80; |
|
const emPad = 0.08; |
|
|
|
const sqrtSvg = function( |
|
sqrtName: string, |
|
height: number, |
|
viewBoxHeight: number, |
|
extraVinculum: number, |
|
options: Options, |
|
): SvgSpan { |
|
const path = sqrtPath(sqrtName, extraVinculum, viewBoxHeight); |
|
const pathNode = new PathNode(sqrtName, path); |
|
|
|
const svg = new SvgNode([pathNode], { |
|
|
|
"width": "400em", |
|
"height": makeEm(height), |
|
"viewBox": "0 0 400000 " + viewBoxHeight, |
|
"preserveAspectRatio": "xMinYMin slice", |
|
}); |
|
|
|
return buildCommon.makeSvgSpan(["hide-tail"], [svg], options); |
|
}; |
|
|
|
|
|
|
|
|
|
const makeSqrtImage = function( |
|
height: number, |
|
options: Options, |
|
): { |
|
span: SvgSpan, |
|
ruleWidth: number, |
|
advanceWidth: number, |
|
} { |
|
|
|
|
|
const newOptions = options.havingBaseSizing(); |
|
|
|
|
|
const delim = traverseSequence("\\surd", height * newOptions.sizeMultiplier, |
|
stackLargeDelimiterSequence, newOptions); |
|
|
|
let sizeMultiplier = newOptions.sizeMultiplier; |
|
|
|
|
|
|
|
const extraVinculum = Math.max(0, |
|
options.minRuleThickness - options.fontMetrics().sqrtRuleThickness); |
|
|
|
|
|
let span; |
|
let spanHeight = 0; |
|
let texHeight = 0; |
|
let viewBoxHeight = 0; |
|
let advanceWidth; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (delim.type === "small") { |
|
|
|
|
|
viewBoxHeight = 1000 + 1000 * extraVinculum + vbPad; |
|
if (height < 1.0) { |
|
sizeMultiplier = 1.0; |
|
} else if (height < 1.4) { |
|
sizeMultiplier = 0.7; |
|
} |
|
spanHeight = (1.0 + extraVinculum + emPad) / sizeMultiplier; |
|
texHeight = (1.00 + extraVinculum) / sizeMultiplier; |
|
span = sqrtSvg("sqrtMain", spanHeight, viewBoxHeight, extraVinculum, |
|
options); |
|
span.style.minWidth = "0.853em"; |
|
advanceWidth = 0.833 / sizeMultiplier; |
|
|
|
} else if (delim.type === "large") { |
|
|
|
viewBoxHeight = (1000 + vbPad) * sizeToMaxHeight[delim.size]; |
|
texHeight = (sizeToMaxHeight[delim.size] + extraVinculum) / sizeMultiplier; |
|
spanHeight = (sizeToMaxHeight[delim.size] + extraVinculum + emPad) |
|
/ sizeMultiplier; |
|
span = sqrtSvg("sqrtSize" + delim.size, spanHeight, viewBoxHeight, |
|
extraVinculum, options); |
|
span.style.minWidth = "1.02em"; |
|
advanceWidth = 1.0 / sizeMultiplier; |
|
|
|
} else { |
|
|
|
|
|
spanHeight = height + extraVinculum + emPad; |
|
texHeight = height + extraVinculum; |
|
viewBoxHeight = Math.floor(1000 * height + extraVinculum) + vbPad; |
|
span = sqrtSvg("sqrtTall", spanHeight, viewBoxHeight, extraVinculum, |
|
options); |
|
span.style.minWidth = "0.742em"; |
|
advanceWidth = 1.056; |
|
} |
|
|
|
span.height = texHeight; |
|
span.style.height = makeEm(spanHeight); |
|
|
|
return { |
|
span, |
|
advanceWidth, |
|
|
|
|
|
|
|
|
|
ruleWidth: (options.fontMetrics().sqrtRuleThickness + extraVinculum) |
|
* sizeMultiplier, |
|
}; |
|
}; |
|
|
|
|
|
|
|
const stackLargeDelimiters = [ |
|
"(", "\\lparen", ")", "\\rparen", |
|
"[", "\\lbrack", "]", "\\rbrack", |
|
"\\{", "\\lbrace", "\\}", "\\rbrace", |
|
"\\lfloor", "\\rfloor", "\u230a", "\u230b", |
|
"\\lceil", "\\rceil", "\u2308", "\u2309", |
|
"\\surd", |
|
]; |
|
|
|
|
|
const stackAlwaysDelimiters = [ |
|
"\\uparrow", "\\downarrow", "\\updownarrow", |
|
"\\Uparrow", "\\Downarrow", "\\Updownarrow", |
|
"|", "\\|", "\\vert", "\\Vert", |
|
"\\lvert", "\\rvert", "\\lVert", "\\rVert", |
|
"\\lgroup", "\\rgroup", "\u27ee", "\u27ef", |
|
"\\lmoustache", "\\rmoustache", "\u23b0", "\u23b1", |
|
]; |
|
|
|
|
|
const stackNeverDelimiters = [ |
|
"<", ">", "\\langle", "\\rangle", "/", "\\backslash", "\\lt", "\\gt", |
|
]; |
|
|
|
|
|
|
|
|
|
const sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0]; |
|
|
|
|
|
|
|
|
|
const makeSizedDelim = function( |
|
delim: string, |
|
size: number, |
|
options: Options, |
|
mode: Mode, |
|
classes: string[], |
|
): DomSpan { |
|
|
|
if (delim === "<" || delim === "\\lt" || delim === "\u27e8") { |
|
delim = "\\langle"; |
|
} else if (delim === ">" || delim === "\\gt" || delim === "\u27e9") { |
|
delim = "\\rangle"; |
|
} |
|
|
|
|
|
if (utils.contains(stackLargeDelimiters, delim) || |
|
utils.contains(stackNeverDelimiters, delim)) { |
|
return makeLargeDelim(delim, size, false, options, mode, classes); |
|
} else if (utils.contains(stackAlwaysDelimiters, delim)) { |
|
return makeStackedDelim( |
|
delim, sizeToMaxHeight[size], false, options, mode, classes); |
|
} else { |
|
throw new ParseError("Illegal delimiter: '" + delim + "'"); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type Delimiter = |
|
{type: "small", style: StyleInterface} | |
|
{type: "large", size: 1 | 2 | 3 | 4} | |
|
{type: "stack"}; |
|
|
|
|
|
const stackNeverDelimiterSequence = [ |
|
{type: "small", style: Style.SCRIPTSCRIPT}, |
|
{type: "small", style: Style.SCRIPT}, |
|
{type: "small", style: Style.TEXT}, |
|
{type: "large", size: 1}, |
|
{type: "large", size: 2}, |
|
{type: "large", size: 3}, |
|
{type: "large", size: 4}, |
|
]; |
|
|
|
|
|
const stackAlwaysDelimiterSequence = [ |
|
{type: "small", style: Style.SCRIPTSCRIPT}, |
|
{type: "small", style: Style.SCRIPT}, |
|
{type: "small", style: Style.TEXT}, |
|
{type: "stack"}, |
|
]; |
|
|
|
|
|
|
|
const stackLargeDelimiterSequence = [ |
|
{type: "small", style: Style.SCRIPTSCRIPT}, |
|
{type: "small", style: Style.SCRIPT}, |
|
{type: "small", style: Style.TEXT}, |
|
{type: "large", size: 1}, |
|
{type: "large", size: 2}, |
|
{type: "large", size: 3}, |
|
{type: "large", size: 4}, |
|
{type: "stack"}, |
|
]; |
|
|
|
|
|
|
|
|
|
|
|
const delimTypeToFont = function(type: Delimiter): string { |
|
if (type.type === "small") { |
|
return "Main-Regular"; |
|
} else if (type.type === "large") { |
|
return "Size" + type.size + "-Regular"; |
|
} else if (type.type === "stack") { |
|
return "Size4-Regular"; |
|
} else { |
|
throw new Error(`Add support for delim type '${type.type}' here.`); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const traverseSequence = function( |
|
delim: string, |
|
height: number, |
|
sequence: Delimiter[], |
|
options: Options, |
|
): Delimiter { |
|
|
|
|
|
|
|
|
|
const start = Math.min(2, 3 - options.style.size); |
|
for (let i = start; i < sequence.length; i++) { |
|
if (sequence[i].type === "stack") { |
|
|
|
break; |
|
} |
|
|
|
const metrics = getMetrics(delim, delimTypeToFont(sequence[i]), "math"); |
|
let heightDepth = metrics.height + metrics.depth; |
|
|
|
|
|
|
|
|
|
if (sequence[i].type === "small") { |
|
const newOptions = options.havingBaseStyle(sequence[i].style); |
|
heightDepth *= newOptions.sizeMultiplier; |
|
} |
|
|
|
|
|
if (heightDepth > height) { |
|
return sequence[i]; |
|
} |
|
} |
|
|
|
|
|
return sequence[sequence.length - 1]; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const makeCustomSizedDelim = function( |
|
delim: string, |
|
height: number, |
|
center: boolean, |
|
options: Options, |
|
mode: Mode, |
|
classes: string[], |
|
): DomSpan { |
|
if (delim === "<" || delim === "\\lt" || delim === "\u27e8") { |
|
delim = "\\langle"; |
|
} else if (delim === ">" || delim === "\\gt" || delim === "\u27e9") { |
|
delim = "\\rangle"; |
|
} |
|
|
|
|
|
let sequence; |
|
if (utils.contains(stackNeverDelimiters, delim)) { |
|
sequence = stackNeverDelimiterSequence; |
|
} else if (utils.contains(stackLargeDelimiters, delim)) { |
|
sequence = stackLargeDelimiterSequence; |
|
} else { |
|
sequence = stackAlwaysDelimiterSequence; |
|
} |
|
|
|
|
|
const delimType = traverseSequence(delim, height, sequence, options); |
|
|
|
|
|
|
|
|
|
if (delimType.type === "small") { |
|
return makeSmallDelim(delim, delimType.style, center, options, |
|
mode, classes); |
|
} else if (delimType.type === "large") { |
|
return makeLargeDelim(delim, delimType.size, center, options, mode, |
|
classes); |
|
} else { |
|
return makeStackedDelim(delim, height, center, options, mode, |
|
classes); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const makeLeftRightDelim = function( |
|
delim: string, |
|
height: number, |
|
depth: number, |
|
options: Options, |
|
mode: Mode, |
|
classes: string[], |
|
): DomSpan { |
|
|
|
const axisHeight = |
|
options.fontMetrics().axisHeight * options.sizeMultiplier; |
|
|
|
|
|
const delimiterFactor = 901; |
|
const delimiterExtend = 5.0 / options.fontMetrics().ptPerEm; |
|
|
|
const maxDistFromAxis = Math.max( |
|
height - axisHeight, depth + axisHeight); |
|
|
|
const totalHeight = Math.max( |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
maxDistFromAxis / 500 * delimiterFactor, |
|
2 * maxDistFromAxis - delimiterExtend); |
|
|
|
|
|
|
|
return makeCustomSizedDelim(delim, totalHeight, true, options, mode, classes); |
|
}; |
|
|
|
export default { |
|
sqrtImage: makeSqrtImage, |
|
sizedDelim: makeSizedDelim, |
|
sizeToMaxHeight: sizeToMaxHeight, |
|
customSizedDelim: makeCustomSizedDelim, |
|
leftRightDelim: makeLeftRightDelim, |
|
}; |
|
|