|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var ParseError = require("./ParseError"); |
|
var Style = require("./Style"); |
|
|
|
var buildCommon = require("./buildCommon"); |
|
var fontMetrics = require("./fontMetrics"); |
|
var symbols = require("./symbols"); |
|
var utils = require("./utils"); |
|
|
|
var makeSpan = buildCommon.makeSpan; |
|
|
|
|
|
|
|
|
|
|
|
var getMetrics = function(symbol, font) { |
|
if (symbols.math[symbol] && symbols.math[symbol].replace) { |
|
return fontMetrics.getCharacterMetrics( |
|
symbols.math[symbol].replace, font); |
|
} else { |
|
return fontMetrics.getCharacterMetrics( |
|
symbol, font); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
var mathrmSize = function(value, size, mode) { |
|
return buildCommon.makeSymbol(value, "Size" + size + "-Regular", mode); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
var styleWrap = function(delim, toStyle, options) { |
|
var span = makeSpan( |
|
["style-wrap", options.style.reset(), toStyle.cls()], [delim]); |
|
|
|
var multiplier = toStyle.sizeMultiplier / options.style.sizeMultiplier; |
|
|
|
span.height *= multiplier; |
|
span.depth *= multiplier; |
|
span.maxFontSize = toStyle.sizeMultiplier; |
|
|
|
return span; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
var makeSmallDelim = function(delim, style, center, options, mode) { |
|
var text = buildCommon.makeSymbol(delim, "Main-Regular", mode); |
|
|
|
var span = styleWrap(text, style, options); |
|
|
|
if (center) { |
|
var shift = |
|
(1 - options.style.sizeMultiplier / style.sizeMultiplier) * |
|
fontMetrics.metrics.axisHeight; |
|
|
|
span.style.top = shift + "em"; |
|
span.height -= shift; |
|
span.depth += shift; |
|
} |
|
|
|
return span; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
var makeLargeDelim = function(delim, size, center, options, mode) { |
|
var inner = mathrmSize(delim, size, mode); |
|
|
|
var span = styleWrap( |
|
makeSpan(["delimsizing", "size" + size], |
|
[inner], options.getColor()), |
|
Style.TEXT, options); |
|
|
|
if (center) { |
|
var shift = (1 - options.style.sizeMultiplier) * |
|
fontMetrics.metrics.axisHeight; |
|
|
|
span.style.top = shift + "em"; |
|
span.height -= shift; |
|
span.depth += shift; |
|
} |
|
|
|
return span; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
var makeInner = function(symbol, font, mode) { |
|
var sizeClass; |
|
|
|
if (font === "Size1-Regular") { |
|
sizeClass = "delim-size1"; |
|
} else if (font === "Size4-Regular") { |
|
sizeClass = "delim-size4"; |
|
} |
|
|
|
var inner = makeSpan( |
|
["delimsizinginner", sizeClass], |
|
[makeSpan([], [buildCommon.makeSymbol(symbol, font, mode)])]); |
|
|
|
|
|
|
|
return {type: "elem", elem: inner}; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
var makeStackedDelim = function(delim, heightTotal, center, options, mode) { |
|
|
|
|
|
var top; |
|
var middle; |
|
var repeat; |
|
var bottom; |
|
top = repeat = bottom = delim; |
|
middle = null; |
|
|
|
var 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 (delim === "[" || delim === "\\lbrack") { |
|
top = "\u23a1"; |
|
repeat = "\u23a2"; |
|
bottom = "\u23a3"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "]" || delim === "\\rbrack") { |
|
top = "\u23a4"; |
|
repeat = "\u23a5"; |
|
bottom = "\u23a6"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "\\lfloor") { |
|
repeat = top = "\u23a2"; |
|
bottom = "\u23a3"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "\\lceil") { |
|
top = "\u23a1"; |
|
repeat = bottom = "\u23a2"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "\\rfloor") { |
|
repeat = top = "\u23a5"; |
|
bottom = "\u23a6"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "\\rceil") { |
|
top = "\u23a4"; |
|
repeat = bottom = "\u23a5"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "(") { |
|
top = "\u239b"; |
|
repeat = "\u239c"; |
|
bottom = "\u239d"; |
|
font = "Size4-Regular"; |
|
} else if (delim === ")") { |
|
top = "\u239e"; |
|
repeat = "\u239f"; |
|
bottom = "\u23a0"; |
|
font = "Size4-Regular"; |
|
} 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") { |
|
top = "\u23a7"; |
|
bottom = "\u23a9"; |
|
repeat = "\u23aa"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "\\rgroup") { |
|
top = "\u23ab"; |
|
bottom = "\u23ad"; |
|
repeat = "\u23aa"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "\\lmoustache") { |
|
top = "\u23a7"; |
|
bottom = "\u23ad"; |
|
repeat = "\u23aa"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "\\rmoustache") { |
|
top = "\u23ab"; |
|
bottom = "\u23a9"; |
|
repeat = "\u23aa"; |
|
font = "Size4-Regular"; |
|
} else if (delim === "\\surd") { |
|
top = "\ue001"; |
|
bottom = "\u23b7"; |
|
repeat = "\ue000"; |
|
font = "Size4-Regular"; |
|
} |
|
|
|
|
|
var topMetrics = getMetrics(top, font); |
|
var topHeightTotal = topMetrics.height + topMetrics.depth; |
|
var repeatMetrics = getMetrics(repeat, font); |
|
var repeatHeightTotal = repeatMetrics.height + repeatMetrics.depth; |
|
var bottomMetrics = getMetrics(bottom, font); |
|
var bottomHeightTotal = bottomMetrics.height + bottomMetrics.depth; |
|
var middleHeightTotal = 0; |
|
var middleFactor = 1; |
|
if (middle !== null) { |
|
var middleMetrics = getMetrics(middle, font); |
|
middleHeightTotal = middleMetrics.height + middleMetrics.depth; |
|
middleFactor = 2; |
|
} |
|
|
|
|
|
|
|
var minHeight = topHeightTotal + bottomHeightTotal + middleHeightTotal; |
|
|
|
|
|
var repeatCount = Math.ceil( |
|
(heightTotal - minHeight) / (middleFactor * repeatHeightTotal)); |
|
|
|
|
|
var realHeightTotal = |
|
minHeight + repeatCount * middleFactor * repeatHeightTotal; |
|
|
|
|
|
|
|
|
|
|
|
var axisHeight = fontMetrics.metrics.axisHeight; |
|
if (center) { |
|
axisHeight *= options.style.sizeMultiplier; |
|
} |
|
|
|
var depth = realHeightTotal / 2 - axisHeight; |
|
|
|
|
|
|
|
|
|
var inners = []; |
|
|
|
|
|
inners.push(makeInner(bottom, font, mode)); |
|
|
|
var i; |
|
if (middle === null) { |
|
|
|
for (i = 0; i < repeatCount; i++) { |
|
inners.push(makeInner(repeat, font, mode)); |
|
} |
|
} else { |
|
|
|
|
|
for (i = 0; i < repeatCount; i++) { |
|
inners.push(makeInner(repeat, font, mode)); |
|
} |
|
inners.push(makeInner(middle, font, mode)); |
|
for (i = 0; i < repeatCount; i++) { |
|
inners.push(makeInner(repeat, font, mode)); |
|
} |
|
} |
|
|
|
|
|
inners.push(makeInner(top, font, mode)); |
|
|
|
|
|
var inner = buildCommon.makeVList(inners, "bottom", depth, options); |
|
|
|
return styleWrap( |
|
makeSpan(["delimsizing", "mult"], [inner], options.getColor()), |
|
Style.TEXT, options); |
|
}; |
|
|
|
|
|
|
|
var stackLargeDelimiters = [ |
|
"(", ")", "[", "\\lbrack", "]", "\\rbrack", |
|
"\\{", "\\lbrace", "\\}", "\\rbrace", |
|
"\\lfloor", "\\rfloor", "\\lceil", "\\rceil", |
|
"\\surd", |
|
]; |
|
|
|
|
|
var stackAlwaysDelimiters = [ |
|
"\\uparrow", "\\downarrow", "\\updownarrow", |
|
"\\Uparrow", "\\Downarrow", "\\Updownarrow", |
|
"|", "\\|", "\\vert", "\\Vert", |
|
"\\lvert", "\\rvert", "\\lVert", "\\rVert", |
|
"\\lgroup", "\\rgroup", "\\lmoustache", "\\rmoustache", |
|
]; |
|
|
|
|
|
var stackNeverDelimiters = [ |
|
"<", ">", "\\langle", "\\rangle", "/", "\\backslash", "\\lt", "\\gt", |
|
]; |
|
|
|
|
|
|
|
|
|
var sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0]; |
|
|
|
|
|
|
|
|
|
var makeSizedDelim = function(delim, size, options, mode) { |
|
|
|
if (delim === "<" || delim === "\\lt") { |
|
delim = "\\langle"; |
|
} else if (delim === ">" || delim === "\\gt") { |
|
delim = "\\rangle"; |
|
} |
|
|
|
|
|
if (utils.contains(stackLargeDelimiters, delim) || |
|
utils.contains(stackNeverDelimiters, delim)) { |
|
return makeLargeDelim(delim, size, false, options, mode); |
|
} else if (utils.contains(stackAlwaysDelimiters, delim)) { |
|
return makeStackedDelim( |
|
delim, sizeToMaxHeight[size], false, options, mode); |
|
} else { |
|
throw new ParseError("Illegal delimiter: '" + delim + "'"); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var 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}, |
|
]; |
|
|
|
|
|
var stackAlwaysDelimiterSequence = [ |
|
{type: "small", style: Style.SCRIPTSCRIPT}, |
|
{type: "small", style: Style.SCRIPT}, |
|
{type: "small", style: Style.TEXT}, |
|
{type: "stack"}, |
|
]; |
|
|
|
|
|
|
|
var 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"}, |
|
]; |
|
|
|
|
|
|
|
|
|
var delimTypeToFont = function(type) { |
|
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"; |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
var traverseSequence = function(delim, height, sequence, options) { |
|
|
|
|
|
|
|
|
|
var start = Math.min(2, 3 - options.style.size); |
|
for (var i = start; i < sequence.length; i++) { |
|
if (sequence[i].type === "stack") { |
|
|
|
break; |
|
} |
|
|
|
var metrics = getMetrics(delim, delimTypeToFont(sequence[i])); |
|
var heightDepth = metrics.height + metrics.depth; |
|
|
|
|
|
|
|
|
|
if (sequence[i].type === "small") { |
|
heightDepth *= sequence[i].style.sizeMultiplier; |
|
} |
|
|
|
|
|
if (heightDepth > height) { |
|
return sequence[i]; |
|
} |
|
} |
|
|
|
|
|
return sequence[sequence.length - 1]; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
var makeCustomSizedDelim = function(delim, height, center, options, mode) { |
|
if (delim === "<" || delim === "\\lt") { |
|
delim = "\\langle"; |
|
} else if (delim === ">" || delim === "\\gt") { |
|
delim = "\\rangle"; |
|
} |
|
|
|
|
|
var sequence; |
|
if (utils.contains(stackNeverDelimiters, delim)) { |
|
sequence = stackNeverDelimiterSequence; |
|
} else if (utils.contains(stackLargeDelimiters, delim)) { |
|
sequence = stackLargeDelimiterSequence; |
|
} else { |
|
sequence = stackAlwaysDelimiterSequence; |
|
} |
|
|
|
|
|
var delimType = traverseSequence(delim, height, sequence, options); |
|
|
|
|
|
|
|
if (delimType.type === "small") { |
|
return makeSmallDelim(delim, delimType.style, center, options, mode); |
|
} else if (delimType.type === "large") { |
|
return makeLargeDelim(delim, delimType.size, center, options, mode); |
|
} else if (delimType.type === "stack") { |
|
return makeStackedDelim(delim, height, center, options, mode); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
var makeLeftRightDelim = function(delim, height, depth, options, mode) { |
|
|
|
var axisHeight = |
|
fontMetrics.metrics.axisHeight * options.style.sizeMultiplier; |
|
|
|
|
|
var delimiterFactor = 901; |
|
var delimiterExtend = 5.0 / fontMetrics.metrics.ptPerEm; |
|
|
|
var maxDistFromAxis = Math.max( |
|
height - axisHeight, depth + axisHeight); |
|
|
|
var totalHeight = Math.max( |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
maxDistFromAxis / 500 * delimiterFactor, |
|
2 * maxDistFromAxis - delimiterExtend); |
|
|
|
|
|
|
|
return makeCustomSizedDelim(delim, totalHeight, true, options, mode); |
|
}; |
|
|
|
module.exports = { |
|
sizedDelim: makeSizedDelim, |
|
customSizedDelim: makeCustomSizedDelim, |
|
leftRightDelim: makeLeftRightDelim, |
|
}; |
|
|