mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-06 19:58:40 +00:00
Port delimiter.js to @flow. (#1177)
* Port delimiter.js to @flow. * Responded to comments.
This commit is contained in:
committed by
Kevin Barabash
parent
56cfc7cf86
commit
9e22012619
@@ -377,7 +377,7 @@ const makeFragment = function(
|
||||
|
||||
|
||||
// These are exact object types to catch typos in the names of the optional fields.
|
||||
type VListElem = {|
|
||||
export type VListElem = {|
|
||||
type: "elem",
|
||||
elem: DomChildNode,
|
||||
marginLeft?: string,
|
||||
|
162
src/delimiter.js
162
src/delimiter.js
@@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
/**
|
||||
* This file deals with creating delimiters of various sizes. The TeXbook
|
||||
* discusses these routines on page 441-442, in the "Another subroutine sets box
|
||||
@@ -29,40 +30,61 @@ import fontMetrics from "./fontMetrics";
|
||||
import symbols from "./symbols";
|
||||
import utils from "./utils";
|
||||
|
||||
import type Options from "./Options";
|
||||
import type {CharacterMetrics} from "./fontMetrics";
|
||||
import type {DomChildNode} from "./domTree";
|
||||
import type {Mode} from "./types";
|
||||
import type {StyleInterface} from "./Style";
|
||||
import type {VListElem} from "./buildCommon";
|
||||
|
||||
/**
|
||||
* Get the metrics for a given symbol and font, after transformation (i.e.
|
||||
* after following replacement from symbols.js)
|
||||
*/
|
||||
const getMetrics = function(symbol, font, mode) {
|
||||
if (symbols.math[symbol] && symbols.math[symbol].replace) {
|
||||
return fontMetrics.getCharacterMetrics(
|
||||
symbols.math[symbol].replace, font, mode);
|
||||
} else {
|
||||
return fontMetrics.getCharacterMetrics(
|
||||
symbol, font, mode);
|
||||
const getMetrics = function(
|
||||
symbol: string,
|
||||
font: string,
|
||||
mode: Mode,
|
||||
): CharacterMetrics {
|
||||
const replace = symbols.math[symbol] && symbols.math[symbol].replace;
|
||||
const metrics =
|
||||
fontMetrics.getCharacterMetrics(replace || symbol, font, mode);
|
||||
if (!metrics) {
|
||||
throw new Error(`Unsupported symbol ${symbol} and font size ${font}.`);
|
||||
}
|
||||
return metrics;
|
||||
};
|
||||
|
||||
/**
|
||||
* Puts a delimiter span in a given style, and adds appropriate height, depth,
|
||||
* and maxFontSizes.
|
||||
*/
|
||||
const styleWrap = function(delim, toStyle, options, classes) {
|
||||
const styleWrap = function(
|
||||
delim: DomChildNode,
|
||||
toStyle: StyleInterface,
|
||||
options: Options,
|
||||
classes: string[],
|
||||
): domTree.span {
|
||||
const newOptions = options.havingBaseStyle(toStyle);
|
||||
|
||||
const span = buildCommon.makeSpan(
|
||||
(classes || []).concat(newOptions.sizingClasses(options)),
|
||||
classes.concat(newOptions.sizingClasses(options)),
|
||||
[delim], options);
|
||||
|
||||
span.delimSizeMultiplier = newOptions.sizeMultiplier / options.sizeMultiplier;
|
||||
span.height *= span.delimSizeMultiplier;
|
||||
span.depth *= span.delimSizeMultiplier;
|
||||
const delimSizeMultiplier =
|
||||
newOptions.sizeMultiplier / options.sizeMultiplier;
|
||||
span.height *= delimSizeMultiplier;
|
||||
span.depth *= delimSizeMultiplier;
|
||||
span.maxFontSize = newOptions.sizeMultiplier;
|
||||
|
||||
return span;
|
||||
};
|
||||
|
||||
const centerSpan = function(span, options, style) {
|
||||
const centerSpan = function(
|
||||
span: domTree.span,
|
||||
options: Options,
|
||||
style: StyleInterface,
|
||||
) {
|
||||
const newOptions = options.havingBaseStyle(style);
|
||||
const shift =
|
||||
(1 - options.sizeMultiplier / newOptions.sizeMultiplier) *
|
||||
@@ -79,7 +101,14 @@ const centerSpan = function(span, options, style) {
|
||||
* font, but is restyled to either be in textstyle, scriptstyle, or
|
||||
* scriptscriptstyle.
|
||||
*/
|
||||
const makeSmallDelim = function(delim, style, center, options, mode, classes) {
|
||||
const makeSmallDelim = function(
|
||||
delim: string,
|
||||
style: StyleInterface,
|
||||
center: boolean,
|
||||
options: Options,
|
||||
mode: Mode,
|
||||
classes: string[],
|
||||
): domTree.span {
|
||||
const text = buildCommon.makeSymbol(delim, "Main-Regular", mode, options);
|
||||
const span = styleWrap(text, style, options, classes);
|
||||
if (center) {
|
||||
@@ -91,7 +120,12 @@ const makeSmallDelim = function(delim, style, center, options, mode, classes) {
|
||||
/**
|
||||
* Builds a symbol in the given font size (note size is an integer)
|
||||
*/
|
||||
const mathrmSize = function(value, size, mode, options) {
|
||||
const mathrmSize = function(
|
||||
value: string,
|
||||
size: number,
|
||||
mode: Mode,
|
||||
options: Options,
|
||||
): domTree.symbolNode {
|
||||
return buildCommon.makeSymbol(value, "Size" + size + "-Regular",
|
||||
mode, options);
|
||||
};
|
||||
@@ -100,7 +134,13 @@ const mathrmSize = function(value, size, mode, options) {
|
||||
* Makes a large delimiter. This is a delimiter that comes in the Size1, Size2,
|
||||
* Size3, or Size4 fonts. It is always rendered in textstyle.
|
||||
*/
|
||||
const makeLargeDelim = function(delim, size, center, options, mode, classes) {
|
||||
const makeLargeDelim = function(delim,
|
||||
size: number,
|
||||
center: boolean,
|
||||
options: Options,
|
||||
mode: Mode,
|
||||
classes: string[],
|
||||
): domTree.span {
|
||||
const inner = mathrmSize(delim, size, mode, options);
|
||||
const span = styleWrap(
|
||||
buildCommon.makeSpan(["delimsizing", "size" + size], [inner], options),
|
||||
@@ -115,12 +155,16 @@ const makeLargeDelim = function(delim, size, center, options, mode, classes) {
|
||||
* Make an inner span with the given offset and in the given font. This is used
|
||||
* in `makeStackedDelim` to make the stacking pieces for the delimiter.
|
||||
*/
|
||||
const makeInner = function(symbol, font, mode) {
|
||||
const makeInner = function(
|
||||
symbol: string,
|
||||
font: "Size1-Regular" | "Size4-Regular",
|
||||
mode: Mode,
|
||||
): VListElem {
|
||||
let sizeClass;
|
||||
// Apply the correct CSS class to choose the right font.
|
||||
if (font === "Size1-Regular") {
|
||||
sizeClass = "delim-size1";
|
||||
} else if (font === "Size4-Regular") {
|
||||
} else /* if (font === "Size4-Regular") */ {
|
||||
sizeClass = "delim-size4";
|
||||
}
|
||||
|
||||
@@ -137,8 +181,14 @@ const makeInner = function(symbol, font, mode) {
|
||||
* Make a stacked delimiter out of a given delimiter, with the total height at
|
||||
* least `heightTotal`. This routine is mentioned on page 442 of the TeXbook.
|
||||
*/
|
||||
const makeStackedDelim = function(delim, heightTotal, center, options, mode,
|
||||
classes) {
|
||||
const makeStackedDelim = function(
|
||||
delim: string,
|
||||
heightTotal: number,
|
||||
center: boolean,
|
||||
options: Options,
|
||||
mode: Mode,
|
||||
classes: string[],
|
||||
): domTree.span {
|
||||
// There are four parts, the top, an optional middle, a repeated part, and a
|
||||
// bottom.
|
||||
let top;
|
||||
@@ -323,7 +373,12 @@ const makeStackedDelim = function(delim, heightTotal, center, options, mode,
|
||||
const vbPad = 80; // padding above the surd, measured inside the viewBox.
|
||||
const emPad = 0.08; // padding, in ems, measured in the document.
|
||||
|
||||
const sqrtSvg = function(sqrtName, height, viewBoxHeight, options) {
|
||||
const sqrtSvg = function(
|
||||
sqrtName: string,
|
||||
height: number,
|
||||
viewBoxHeight: number,
|
||||
options: Options,
|
||||
): domTree.span {
|
||||
let alternate;
|
||||
if (sqrtName === "sqrtTall") {
|
||||
// sqrtTall is from glyph U23B7 in the font KaTeX_Size4-Regular
|
||||
@@ -352,7 +407,14 @@ const sqrtSvg = function(sqrtName, height, viewBoxHeight, options) {
|
||||
/**
|
||||
* Make a sqrt image of the given height,
|
||||
*/
|
||||
const makeSqrtImage = function(height, options) {
|
||||
const makeSqrtImage = function(
|
||||
height: number,
|
||||
options: Options,
|
||||
): {
|
||||
span: domTree.span,
|
||||
ruleWidth: number,
|
||||
advanceWidth: number,
|
||||
} {
|
||||
const delim =
|
||||
traverseSequence("\\surd", height, stackLargeDelimiterSequence, options);
|
||||
|
||||
@@ -362,6 +424,7 @@ const makeSqrtImage = function(height, options) {
|
||||
let spanHeight = 0;
|
||||
let texHeight = 0;
|
||||
let viewBoxHeight = 0;
|
||||
let advanceWidth;
|
||||
|
||||
// We create viewBoxes with 80 units of "padding" above each surd.
|
||||
// Then browser rounding error on the parent span height will not
|
||||
@@ -378,7 +441,7 @@ const makeSqrtImage = function(height, options) {
|
||||
texHeight = 1.00 * sizeMultiplier;
|
||||
span = sqrtSvg("sqrtMain", spanHeight, viewBoxHeight, options);
|
||||
span.style.minWidth = "0.853em";
|
||||
span.advanceWidth = 0.833 * sizeMultiplier; // from the font.
|
||||
advanceWidth = 0.833 * sizeMultiplier; // from the font.
|
||||
|
||||
} else if (delim.type === "large") {
|
||||
// These SVGs come from fonts: KaTeX_Size1, _Size2, etc.
|
||||
@@ -387,7 +450,7 @@ const makeSqrtImage = function(height, options) {
|
||||
spanHeight = (sizeToMaxHeight[delim.size] + emPad) / sizeMultiplier;
|
||||
span = sqrtSvg("sqrtSize" + delim.size, spanHeight, viewBoxHeight, options);
|
||||
span.style.minWidth = "1.02em";
|
||||
span.advanceWidth = 1.0 / sizeMultiplier; // from the font
|
||||
advanceWidth = 1.0 / sizeMultiplier; // from the font.
|
||||
|
||||
} else {
|
||||
// Tall sqrt. In TeX, this would be stacked using multiple glyphs.
|
||||
@@ -397,7 +460,7 @@ const makeSqrtImage = function(height, options) {
|
||||
viewBoxHeight = Math.floor(1000 * height) + vbPad;
|
||||
span = sqrtSvg("sqrtTall", spanHeight, viewBoxHeight, options);
|
||||
span.style.minWidth = "0.742em";
|
||||
span.advanceWidth = 1.056 / sizeMultiplier;
|
||||
advanceWidth = 1.056 / sizeMultiplier;
|
||||
}
|
||||
|
||||
span.height = texHeight;
|
||||
@@ -405,6 +468,7 @@ const makeSqrtImage = function(height, options) {
|
||||
|
||||
return {
|
||||
span,
|
||||
advanceWidth,
|
||||
// Calculate the actual line width.
|
||||
// This actually should depend on the chosen font -- e.g. \boldmath
|
||||
// should use the thicker surd symbols from e.g. KaTeX_Main-Bold, and
|
||||
@@ -444,7 +508,13 @@ const sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0];
|
||||
/**
|
||||
* Used to create a delimiter of a specific size, where `size` is 1, 2, 3, or 4.
|
||||
*/
|
||||
const makeSizedDelim = function(delim, size, options, mode, classes) {
|
||||
const makeSizedDelim = function(
|
||||
delim: string,
|
||||
size: number,
|
||||
options: Options,
|
||||
mode: Mode,
|
||||
classes: string[],
|
||||
): domTree.span {
|
||||
// < and > turn into \langle and \rangle in delimiters
|
||||
if (delim === "<" || delim === "\\lt" || delim === "\u27e8") {
|
||||
delim = "\\langle";
|
||||
@@ -476,6 +546,11 @@ const makeSizedDelim = function(delim, size, options, mode, classes) {
|
||||
* them explicitly here.
|
||||
*/
|
||||
|
||||
type Delimiter =
|
||||
{type: "small", style: StyleInterface} |
|
||||
{type: "large", size: 1 | 2 | 3 | 4} |
|
||||
{type: "stack"};
|
||||
|
||||
// Delimiters that never stack try small delimiters and large delimiters only
|
||||
const stackNeverDelimiterSequence = [
|
||||
{type: "small", style: Style.SCRIPTSCRIPT},
|
||||
@@ -510,14 +585,17 @@ const stackLargeDelimiterSequence = [
|
||||
|
||||
/**
|
||||
* Get the font used in a delimiter based on what kind of delimiter it is.
|
||||
* TODO(#963) Use more specific font family return type once that is introduced.
|
||||
*/
|
||||
const delimTypeToFont = function(type) {
|
||||
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.`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -525,7 +603,12 @@ const delimTypeToFont = function(type) {
|
||||
* Traverse a sequence of types of delimiters to decide what kind of delimiter
|
||||
* should be used to create a delimiter of the given height+depth.
|
||||
*/
|
||||
const traverseSequence = function(delim, height, sequence, options) {
|
||||
const traverseSequence = function(
|
||||
delim: string,
|
||||
height: number,
|
||||
sequence: Delimiter[],
|
||||
options: Options,
|
||||
): Delimiter {
|
||||
// Here, we choose the index we should start at in the sequences. In smaller
|
||||
// sizes (which correspond to larger numbers in style.size) we start earlier
|
||||
// in the sequence. Thus, scriptscript starts at index 3-3=0, script starts
|
||||
@@ -562,8 +645,14 @@ const traverseSequence = function(delim, height, sequence, options) {
|
||||
* Make a delimiter of a given height+depth, with optional centering. Here, we
|
||||
* traverse the sequences, and create a delimiter that the sequence tells us to.
|
||||
*/
|
||||
const makeCustomSizedDelim = function(delim, height, center, options, mode,
|
||||
classes) {
|
||||
const makeCustomSizedDelim = function(
|
||||
delim: string,
|
||||
height: number,
|
||||
center: boolean,
|
||||
options: Options,
|
||||
mode: Mode,
|
||||
classes: string[],
|
||||
): domTree.span {
|
||||
if (delim === "<" || delim === "\\lt" || delim === "\u27e8") {
|
||||
delim = "\\langle";
|
||||
} else if (delim === ">" || delim === "\\gt" || delim === "\u27e9") {
|
||||
@@ -602,8 +691,14 @@ const makeCustomSizedDelim = function(delim, height, center, options, mode,
|
||||
* Make a delimiter for use with `\left` and `\right`, given a height and depth
|
||||
* of an expression that the delimiters surround.
|
||||
*/
|
||||
const makeLeftRightDelim = function(delim, height, depth, options, mode,
|
||||
classes) {
|
||||
const makeLeftRightDelim = function(
|
||||
delim: string,
|
||||
height: number,
|
||||
depth: number,
|
||||
options: Options,
|
||||
mode: Mode,
|
||||
classes: string[],
|
||||
): domTree.span {
|
||||
// We always center \left/\right delimiters, so the axis is always shifted
|
||||
const axisHeight =
|
||||
options.fontMetrics().axisHeight * options.sizeMultiplier;
|
||||
@@ -630,8 +725,7 @@ const makeLeftRightDelim = function(delim, height, depth, options, mode,
|
||||
|
||||
// Finally, we defer to `makeCustomSizedDelim` with our calculated total
|
||||
// height
|
||||
return makeCustomSizedDelim(delim, totalHeight, true, options, mode,
|
||||
classes);
|
||||
return makeCustomSizedDelim(delim, totalHeight, true, options, mode, classes);
|
||||
};
|
||||
|
||||
export default {
|
||||
|
@@ -275,7 +275,14 @@ defineFunction({
|
||||
middleDelim = delimiter.sizedDelim(
|
||||
group.value.value, 1, options,
|
||||
group.mode, []);
|
||||
middleDelim.isMiddle = {value: group.value.value, options: options};
|
||||
|
||||
// Property `isMiddle` not defined on `span`. It is only used in
|
||||
// this file above. Fixing this correctly requires refactoring the
|
||||
// htmlBuilder return type to support passing additional data.
|
||||
// An easier, but unideal option would be to add `isMiddle` to
|
||||
// `span` just for this case.
|
||||
// $FlowFixMe
|
||||
middleDelim.isMiddle = {value: group.value.value, options};
|
||||
}
|
||||
return middleDelim;
|
||||
},
|
||||
|
@@ -58,7 +58,7 @@ defineFunction({
|
||||
lineClearance + theta) * options.sizeMultiplier;
|
||||
|
||||
// Create a sqrt SVG of the required minimum size
|
||||
const {span: img, ruleWidth} =
|
||||
const {span: img, ruleWidth, advanceWidth} =
|
||||
delimiter.sqrtImage(minDelimiterHeight, options);
|
||||
|
||||
const delimDepth = img.height - ruleWidth;
|
||||
@@ -72,7 +72,7 @@ defineFunction({
|
||||
// Shift the sqrt image
|
||||
const imgShift = img.height - inner.height - lineClearance - ruleWidth;
|
||||
|
||||
inner.style.paddingLeft = img.advanceWidth + "em";
|
||||
inner.style.paddingLeft = advanceWidth + "em";
|
||||
|
||||
// Overlay the image and the argument.
|
||||
const body = buildCommon.makeVList({
|
||||
|
Reference in New Issue
Block a user