Enable minRuleThickness in rendering options (#1964)
* Enable minRuleThickness in rendering options * fix typo * Fix lint errors * Fix default * Remove border from \colorbox * Fix lint error * Update array screenshot * Add border to \fcolorbox * Edit comment * Add comment * Pick up review comments * Fix lint errors * Edit \sqrt screenshot test * Update screenshots * Screenshot update take 2 * Improve \sqrt SVG paths * Fix lint error
@@ -14,6 +14,7 @@ You can provide an object of options as the last argument to [`katex.render` and
|
||||
- `throwOnError`: `boolean`. If `true` (the default), KaTeX will throw a `ParseError` when it encounters an unsupported command or invalid LaTeX. If `false`, KaTeX will render unsupported commands as text, and render invalid LaTeX as its source code with hover text giving the error, in the color given by `errorColor`.
|
||||
- `errorColor`: `string`. A color string given in the format `"#XXX"` or `"#XXXXXX"`. This option determines the color that unsupported commands and invalid LaTeX are rendered in when `throwOnError` is set to `false`. (default: `#cc0000`)
|
||||
- `macros`: `object`. A collection of custom macros. Each macro is a property with a name like `\name` (written `"\\name"` in JavaScript) which maps to a string that describes the expansion of the macro, or a function that accepts an instance of `MacroExpander` as first argument and returns the expansion as a string. `MacroExpander` is an internal API and subject to non-backwards compatible changes. See [`src/macros.js`](https://github.com/KaTeX/KaTeX/blob/master/src/macros.js) for its usage. Single-character keys can also be included in which case the character will be redefined as the given macro (similar to TeX active characters). *This object will be modified* if the LaTeX code defines its own macros via `\gdef`, which enables consecutive calls to KaTeX to share state.
|
||||
- `minRuleThickness`: `number`. Specifies a minimum thickness, in ems, for fraction lines, `\sqrt` top lines, `{array}` vertical lines, `\hline`, `\hdashline`, `\underline`, `\overline`, and the borders of `\fbox`, `\boxed`, and `\fcolorbox`. The usual value for these items is `0.04`, so for `minRuleThickness` to be effective it should probably take a value slightly above `0.04`, say `0.05` or `0.06`. Negative values will be ignored.
|
||||
- `colorIsTextColor`: `boolean`. If `true`, `\color` will work like LaTeX's `\textcolor`, and take two arguments (e.g., `\color{blue}{hello}`), which restores the old behavior of KaTeX (pre-0.8.0). If `false` (the default), `\color` will work like LaTeX's `\color`, and take one argument (e.g., `\color{blue}hello`). In both cases, `\textcolor` works as in LaTeX (e.g., `\textcolor{blue}{hello}`).
|
||||
- `maxSize`: `number`. All user-specified sizes, e.g. in `\rule{500em}{500em}`, will be capped to `maxSize` ems. If set to `Infinity` (the default), users can make elements and spaces arbitrarily large.
|
||||
- `maxExpand`: `number`. Limit the number of macro expansions to the specified number, to prevent e.g. infinite macro loops. If set to `Infinity`, the macro expander will try to fully expand as in LaTeX. (default: 1000)
|
||||
|
@@ -52,6 +52,7 @@ export type OptionsData = {
|
||||
fontShape?: FontShape;
|
||||
sizeMultiplier?: number;
|
||||
maxSize: number;
|
||||
minRuleThickness: number;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -76,6 +77,7 @@ class Options {
|
||||
fontShape: FontShape;
|
||||
sizeMultiplier: number;
|
||||
maxSize: number;
|
||||
minRuleThickness: number;
|
||||
_fontMetrics: FontMetrics | void;
|
||||
|
||||
/**
|
||||
@@ -95,6 +97,7 @@ class Options {
|
||||
this.fontShape = data.fontShape || '';
|
||||
this.sizeMultiplier = sizeMultipliers[this.size - 1];
|
||||
this.maxSize = data.maxSize;
|
||||
this.minRuleThickness = data.minRuleThickness;
|
||||
this._fontMetrics = undefined;
|
||||
}
|
||||
|
||||
@@ -114,6 +117,7 @@ class Options {
|
||||
fontWeight: this.fontWeight,
|
||||
fontShape: this.fontShape,
|
||||
maxSize: this.maxSize,
|
||||
minRuleThickness: this.minRuleThickness,
|
||||
};
|
||||
|
||||
for (const key in extension) {
|
||||
|
@@ -24,6 +24,7 @@ export type SettingsOptions = {
|
||||
throwOnError?: boolean;
|
||||
errorColor?: string;
|
||||
macros?: MacroMap;
|
||||
minRuleThickness?: number;
|
||||
colorIsTextColor?: boolean;
|
||||
strict?: boolean | "ignore" | "warn" | "error" | StrictFunction;
|
||||
maxSize?: number;
|
||||
@@ -49,6 +50,7 @@ class Settings {
|
||||
throwOnError: boolean;
|
||||
errorColor: string;
|
||||
macros: MacroMap;
|
||||
minRuleThickness: number;
|
||||
colorIsTextColor: boolean;
|
||||
strict: boolean | "ignore" | "warn" | "error" | StrictFunction;
|
||||
maxSize: number;
|
||||
@@ -65,6 +67,10 @@ class Settings {
|
||||
this.throwOnError = utils.deflt(options.throwOnError, true);
|
||||
this.errorColor = utils.deflt(options.errorColor, "#cc0000");
|
||||
this.macros = options.macros || {};
|
||||
this.minRuleThickness = Math.max(
|
||||
0,
|
||||
utils.deflt(options.minRuleThickness, 0)
|
||||
);
|
||||
this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false);
|
||||
this.strict = utils.deflt(options.strict, "warn");
|
||||
this.maxSize = Math.max(0, utils.deflt(options.maxSize, Infinity));
|
||||
|
@@ -423,7 +423,10 @@ const makeLineSpan = function(
|
||||
thickness?: number,
|
||||
) {
|
||||
const line = makeSpan([className], [], options);
|
||||
line.height = thickness || options.fontMetrics().defaultRuleThickness;
|
||||
line.height = Math.max(
|
||||
thickness || options.fontMetrics().defaultRuleThickness,
|
||||
options.minRuleThickness,
|
||||
);
|
||||
line.style.borderBottomWidth = line.height + "em";
|
||||
line.maxFontSize = 1.0;
|
||||
return line;
|
||||
|
@@ -13,6 +13,7 @@ const optionsFromSettings = function(settings: Settings) {
|
||||
return new Options({
|
||||
style: (settings.displayMode ? Style.DISPLAY : Style.TEXT),
|
||||
maxSize: settings.maxSize,
|
||||
minRuleThickness: settings.minRuleThickness,
|
||||
});
|
||||
};
|
||||
|
||||
|
@@ -25,6 +25,7 @@ import ParseError from "./ParseError";
|
||||
import Style from "./Style";
|
||||
|
||||
import {PathNode, SvgNode, SymbolNode} from "./domTree";
|
||||
import {sqrtPath} from "./svgGeometry";
|
||||
import buildCommon from "./buildCommon";
|
||||
import {getCharacterMetrics} from "./fontMetrics";
|
||||
import symbols from "./symbols";
|
||||
@@ -377,21 +378,11 @@ const sqrtSvg = function(
|
||||
sqrtName: string,
|
||||
height: number,
|
||||
viewBoxHeight: number,
|
||||
extraViniculum: number,
|
||||
options: Options,
|
||||
): SvgSpan {
|
||||
let alternate;
|
||||
if (sqrtName === "sqrtTall") {
|
||||
// sqrtTall is from glyph U23B7 in the font KaTeX_Size4-Regular
|
||||
// One path edge has a variable length. It runs from the viniculumn
|
||||
// to a point near (14 units) the bottom of the surd. The viniculum
|
||||
// is 40 units thick. So the length of the line in question is:
|
||||
const vertSegment = viewBoxHeight - 54 - vbPad;
|
||||
alternate = `M702 ${vbPad}H400000v40H742v${vertSegment}l-4 4-4 4c-.667.7
|
||||
-2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1h-12l-28-84c-16.667-52-96.667
|
||||
-294.333-240-727l-212 -643 -85 170c-4-3.333-8.333-7.667-13 -13l-13-13l77-155
|
||||
77-156c66 199.333 139 419.667 219 661 l218 661zM702 ${vbPad}H400000v40H742z`;
|
||||
}
|
||||
const pathNode = new PathNode(sqrtName, alternate);
|
||||
const path = sqrtPath(sqrtName, extraViniculum, viewBoxHeight);
|
||||
const pathNode = new PathNode(sqrtName, path);
|
||||
|
||||
const svg = new SvgNode([pathNode], {
|
||||
// Note: 1000:1 ratio of viewBox to document em width.
|
||||
@@ -425,6 +416,11 @@ const makeSqrtImage = function(
|
||||
|
||||
let sizeMultiplier = newOptions.sizeMultiplier; // default
|
||||
|
||||
// The standard sqrt SVGs each have a 0.04em thick viniculum.
|
||||
// If Settings.minRuleThickness is larger than that, we add extraViniculum.
|
||||
const extraViniculum = Math.max(0,
|
||||
options.minRuleThickness - options.fontMetrics().sqrtRuleThickness);
|
||||
|
||||
// Create a span containing an SVG image of a sqrt symbol.
|
||||
let span;
|
||||
let spanHeight = 0;
|
||||
@@ -440,34 +436,39 @@ const makeSqrtImage = function(
|
||||
|
||||
if (delim.type === "small") {
|
||||
// Get an SVG that is derived from glyph U+221A in font KaTeX-Main.
|
||||
viewBoxHeight = 1000 + vbPad; // 1000 unit glyph height.
|
||||
// 1000 unit normal glyph height.
|
||||
viewBoxHeight = 1000 + 1000 * extraViniculum + vbPad;
|
||||
if (height < 1.0) {
|
||||
sizeMultiplier = 1.0; // mimic a \textfont radical
|
||||
} else if (height < 1.4) {
|
||||
sizeMultiplier = 0.7; // mimic a \scriptfont radical
|
||||
}
|
||||
spanHeight = (1.0 + emPad) / sizeMultiplier;
|
||||
texHeight = 1.00 / sizeMultiplier;
|
||||
span = sqrtSvg("sqrtMain", spanHeight, viewBoxHeight, options);
|
||||
spanHeight = (1.0 + extraViniculum + emPad) / sizeMultiplier;
|
||||
texHeight = (1.00 + extraViniculum) / sizeMultiplier;
|
||||
span = sqrtSvg("sqrtMain", spanHeight, viewBoxHeight, extraViniculum,
|
||||
options);
|
||||
span.style.minWidth = "0.853em";
|
||||
advanceWidth = 0.833 / sizeMultiplier; // from the font.
|
||||
|
||||
} else if (delim.type === "large") {
|
||||
// These SVGs come from fonts: KaTeX_Size1, _Size2, etc.
|
||||
viewBoxHeight = (1000 + vbPad) * sizeToMaxHeight[delim.size];
|
||||
texHeight = sizeToMaxHeight[delim.size] / sizeMultiplier;
|
||||
spanHeight = (sizeToMaxHeight[delim.size] + emPad) / sizeMultiplier;
|
||||
span = sqrtSvg("sqrtSize" + delim.size, spanHeight, viewBoxHeight, options);
|
||||
texHeight = (sizeToMaxHeight[delim.size] + extraViniculum) / sizeMultiplier;
|
||||
spanHeight = (sizeToMaxHeight[delim.size] + extraViniculum + emPad)
|
||||
/ sizeMultiplier;
|
||||
span = sqrtSvg("sqrtSize" + delim.size, spanHeight, viewBoxHeight,
|
||||
extraViniculum, options);
|
||||
span.style.minWidth = "1.02em";
|
||||
advanceWidth = 1.0 / sizeMultiplier; // 1.0 from the font.
|
||||
|
||||
} else {
|
||||
// Tall sqrt. In TeX, this would be stacked using multiple glyphs.
|
||||
// We'll use a single SVG to accomplish the same thing.
|
||||
spanHeight = height + emPad;
|
||||
texHeight = height;
|
||||
viewBoxHeight = Math.floor(1000 * height) + vbPad;
|
||||
span = sqrtSvg("sqrtTall", spanHeight, viewBoxHeight, options);
|
||||
spanHeight = height + extraViniculum + emPad;
|
||||
texHeight = height + extraViniculum;
|
||||
viewBoxHeight = Math.floor(1000 * height + extraViniculum) + vbPad;
|
||||
span = sqrtSvg("sqrtTall", spanHeight, viewBoxHeight, extraViniculum,
|
||||
options);
|
||||
span.style.minWidth = "0.742em";
|
||||
advanceWidth = 1.056;
|
||||
}
|
||||
@@ -482,7 +483,8 @@ const makeSqrtImage = function(
|
||||
// This actually should depend on the chosen font -- e.g. \boldmath
|
||||
// should use the thicker surd symbols from e.g. KaTeX_Main-Bold, and
|
||||
// have thicker rules.
|
||||
ruleWidth: options.fontMetrics().sqrtRuleThickness * sizeMultiplier,
|
||||
ruleWidth: (options.fontMetrics().sqrtRuleThickness + extraViniculum)
|
||||
* sizeMultiplier,
|
||||
};
|
||||
};
|
||||
|
||||
|
@@ -13,7 +13,7 @@
|
||||
*/
|
||||
import {scriptFromCodepoint} from "./unicodeScripts";
|
||||
import utils from "./utils";
|
||||
import svgGeometry from "./svgGeometry";
|
||||
import {path} from "./svgGeometry";
|
||||
import type Options from "./Options";
|
||||
import {DocumentFragment} from "./tree";
|
||||
|
||||
@@ -136,12 +136,16 @@ export type CssStyle = $Shape<{
|
||||
backgroundColor: string,
|
||||
borderBottomWidth: string,
|
||||
borderColor: string,
|
||||
borderRightStyle: string,
|
||||
borderRightWidth: string,
|
||||
borderTopWidth: string,
|
||||
borderStyle: string;
|
||||
borderWidth: string,
|
||||
bottom: string,
|
||||
color: string,
|
||||
height: string,
|
||||
left: string,
|
||||
margin: string,
|
||||
marginLeft: string,
|
||||
marginRight: string,
|
||||
marginTop: string,
|
||||
@@ -529,7 +533,7 @@ export class PathNode implements VirtualNode {
|
||||
|
||||
constructor(pathName: string, alternate?: string) {
|
||||
this.pathName = pathName;
|
||||
this.alternate = alternate; // Used only for tall \sqrt
|
||||
this.alternate = alternate; // Used only for \sqrt
|
||||
}
|
||||
|
||||
toNode(): Node {
|
||||
@@ -539,7 +543,7 @@ export class PathNode implements VirtualNode {
|
||||
if (this.alternate) {
|
||||
node.setAttribute("d", this.alternate);
|
||||
} else {
|
||||
node.setAttribute("d", svgGeometry.path[this.pathName]);
|
||||
node.setAttribute("d", path[this.pathName]);
|
||||
}
|
||||
|
||||
return node;
|
||||
@@ -549,7 +553,7 @@ export class PathNode implements VirtualNode {
|
||||
if (this.alternate) {
|
||||
return `<path d='${this.alternate}'/>`;
|
||||
} else {
|
||||
return `<path d='${svgGeometry.path[this.pathName]}'/>`;
|
||||
return `<path d='${path[this.pathName]}'/>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -174,6 +174,12 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
|
||||
let body = new Array(nr);
|
||||
const hlines = [];
|
||||
|
||||
const ruleThickness = Math.max(
|
||||
// From LaTeX \showthe\arrayrulewidth. Equals 0.04 em.
|
||||
(options.fontMetrics().arrayRuleWidth),
|
||||
options.minRuleThickness, // User override.
|
||||
);
|
||||
|
||||
// Horizontal spacing
|
||||
const pt = 1 / options.fontMetrics().ptPerEm;
|
||||
let arraycolsep = 5 * pt; // default value, i.e. \arraycolsep in article.cls
|
||||
@@ -284,20 +290,15 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
|
||||
cols.push(colSep);
|
||||
}
|
||||
|
||||
if (colDescr.separator === "|") {
|
||||
if (colDescr.separator === "|" || colDescr.separator === ":") {
|
||||
const lineType = (colDescr.separator === "|") ? "solid" : "dashed";
|
||||
const separator = buildCommon.makeSpan(
|
||||
["vertical-separator"], [], options
|
||||
);
|
||||
separator.style.height = totalHeight + "em";
|
||||
separator.style.verticalAlign =
|
||||
-(totalHeight - offset) + "em";
|
||||
|
||||
cols.push(separator);
|
||||
} else if (colDescr.separator === ":") {
|
||||
const separator = buildCommon.makeSpan(
|
||||
["vertical-separator", "vs-dashed"], [], options
|
||||
);
|
||||
separator.style.height = totalHeight + "em";
|
||||
separator.style.borderRightWidth = `${ruleThickness}em`;
|
||||
separator.style.borderRightStyle = lineType;
|
||||
separator.style.margin = `0 -${ruleThickness / 2}em`;
|
||||
separator.style.verticalAlign =
|
||||
-(totalHeight - offset) + "em";
|
||||
|
||||
@@ -361,8 +362,9 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
|
||||
|
||||
// Add \hline(s), if any.
|
||||
if (hlines.length > 0) {
|
||||
const line = buildCommon.makeLineSpan("hline", options, 0.05);
|
||||
const dashes = buildCommon.makeLineSpan("hdashline", options, 0.05);
|
||||
const line = buildCommon.makeLineSpan("hline", options, ruleThickness);
|
||||
const dashes = buildCommon.makeLineSpan("hdashline", options,
|
||||
ruleThickness);
|
||||
const vListElems = [{type: "elem", elem: body, shift: 0}];
|
||||
while (hlines.length > 0) {
|
||||
const hline = hlines.pop();
|
||||
|
@@ -82,6 +82,14 @@ const sigmasAndXis = {
|
||||
// The space between adjacent `|` columns in an array definition. From
|
||||
// `\showthe\doublerulesep` in LaTeX. Equals 2.0 / ptPerEm.
|
||||
doubleRuleSep: [0.2, 0.2, 0.2],
|
||||
|
||||
// The width of separator lines in {array} environments. From
|
||||
// `\showthe\arrayrulewidth` in LaTeX. Equals 0.4 / ptPerEm.
|
||||
arrayRuleWidth: [0.04, 0.04, 0.04],
|
||||
|
||||
// Two values from LaTeX source2e:
|
||||
fboxsep: [0.3, 0.3, 0.3], // 3 pt / ptPerEm
|
||||
fboxrule: [0.04, 0.04, 0.04], // 0.4 pt / ptPerEm
|
||||
};
|
||||
|
||||
// This map contains a mapping from font name and character code to character
|
||||
|
@@ -46,15 +46,24 @@ const htmlBuilder = (group, options) => {
|
||||
|
||||
// Add vertical padding
|
||||
let vertPad = 0;
|
||||
// ref: LaTeX source2e: \fboxsep = 3pt; \fboxrule = .4pt
|
||||
let ruleThickness = 0;
|
||||
// ref: cancel package: \advance\totalheight2\p@ % "+2"
|
||||
if (/box/.test(label)) {
|
||||
vertPad = label === "colorbox" ? 0.3 : 0.34;
|
||||
ruleThickness = Math.max(
|
||||
options.fontMetrics().fboxrule, // default
|
||||
options.minRuleThickness, // User override.
|
||||
);
|
||||
vertPad = options.fontMetrics().fboxsep +
|
||||
(label === "colorbox" ? 0 : ruleThickness);
|
||||
} else {
|
||||
vertPad = isSingleChar ? 0.2 : 0;
|
||||
}
|
||||
|
||||
img = stretchy.encloseSpan(inner, label, vertPad, options);
|
||||
if (/fbox|boxed|fcolorbox/.test(label)) {
|
||||
img.style.borderStyle = "solid";
|
||||
img.style.borderWidth = `${ruleThickness}em`;
|
||||
}
|
||||
imgShift = inner.depth + vertPad;
|
||||
|
||||
if (group.backgroundColor) {
|
||||
@@ -111,6 +120,7 @@ const htmlBuilder = (group, options) => {
|
||||
};
|
||||
|
||||
const mathmlBuilder = (group, options) => {
|
||||
let fboxsep = 0;
|
||||
const node = new mathMLTree.MathNode(
|
||||
(group.label.indexOf("colorbox") > -1) ? "mpadded" : "menclose",
|
||||
[mml.buildGroup(group.body, options)]
|
||||
@@ -132,12 +142,17 @@ const mathmlBuilder = (group, options) => {
|
||||
case "\\colorbox":
|
||||
// <menclose> doesn't have a good notation option. So use <mpadded>
|
||||
// instead. Set some attributes that come included with <menclose>.
|
||||
node.setAttribute("width", "+6pt");
|
||||
node.setAttribute("height", "+6pt");
|
||||
node.setAttribute("lspace", "3pt"); // LaTeX source2e: \fboxsep = 3pt
|
||||
node.setAttribute("voffset", "3pt");
|
||||
fboxsep = options.fontMetrics().fboxsep *
|
||||
options.fontMetrics().ptPerEm;
|
||||
node.setAttribute("width", `+${2 * fboxsep}pt`);
|
||||
node.setAttribute("height", `+${2 * fboxsep}pt`);
|
||||
node.setAttribute("lspace", `${fboxsep}pt`); //
|
||||
node.setAttribute("voffset", `${fboxsep}pt`);
|
||||
if (group.label === "\\fcolorbox") {
|
||||
const thk = options.fontMetrics().defaultRuleThickness;
|
||||
const thk = Math.max(
|
||||
options.fontMetrics().fboxrule, // default
|
||||
options.minRuleThickness, // user override
|
||||
);
|
||||
node.setAttribute("style", "border: " + thk + "em solid " +
|
||||
String(group.borderColor));
|
||||
}
|
||||
|
@@ -31,13 +31,14 @@ defineFunction({
|
||||
const line = buildCommon.makeLineSpan("overline-line", options);
|
||||
|
||||
// Generate the vlist, with the appropriate kerns
|
||||
const defaultRuleThickness = options.fontMetrics().defaultRuleThickness;
|
||||
const vlist = buildCommon.makeVList({
|
||||
positionType: "firstBaseline",
|
||||
children: [
|
||||
{type: "elem", elem: innerGroup},
|
||||
{type: "kern", size: 3 * line.height},
|
||||
{type: "kern", size: 3 * defaultRuleThickness},
|
||||
{type: "elem", elem: line},
|
||||
{type: "kern", size: line.height},
|
||||
{type: "kern", size: defaultRuleThickness},
|
||||
],
|
||||
}, options);
|
||||
|
||||
|
@@ -29,13 +29,14 @@ defineFunction({
|
||||
const line = buildCommon.makeLineSpan("underline-line", options);
|
||||
|
||||
// Generate the vlist, with the appropriate kerns
|
||||
const defaultRuleThickness = options.fontMetrics().defaultRuleThickness;
|
||||
const vlist = buildCommon.makeVList({
|
||||
positionType: "top",
|
||||
positionData: innerGroup.height,
|
||||
children: [
|
||||
{type: "kern", size: line.height},
|
||||
{type: "kern", size: defaultRuleThickness},
|
||||
{type: "elem", elem: line},
|
||||
{type: "kern", size: 3 * line.height},
|
||||
{type: "kern", size: 3 * defaultRuleThickness},
|
||||
{type: "elem", elem: innerGroup},
|
||||
],
|
||||
}, options);
|
||||
|
@@ -409,15 +409,10 @@
|
||||
.mtable {
|
||||
.vertical-separator {
|
||||
display: inline-block;
|
||||
margin: 0 -0.025em;
|
||||
border-right: 0.05em solid;
|
||||
// margin and border-right are set in Javascript
|
||||
min-width: 1px; // Prevent Chrome from omitting a line.
|
||||
}
|
||||
|
||||
.vs-dashed {
|
||||
border-right: 0.05em dashed;
|
||||
}
|
||||
|
||||
.arraycolsep {
|
||||
display: inline-block;
|
||||
}
|
||||
|
@@ -1,61 +1,152 @@
|
||||
// @flow
|
||||
/**
|
||||
* This file provides support to domTree.js
|
||||
* This file provides support to domTree.js and delimiter.js.
|
||||
* It's a storehouse of path geometry for SVG images.
|
||||
*/
|
||||
|
||||
// In all paths below, the viewBox-to-em scale is 1000:1.
|
||||
|
||||
const hLinePad = 80; // padding above a sqrt viniculum.
|
||||
const hLinePad = 80; // padding above a sqrt viniculum. Prevents image cropping.
|
||||
|
||||
const path: {[string]: string} = {
|
||||
// The viniculum of a \sqrt can be made thicker by a KaTeX rendering option.
|
||||
// Think of variable extraViniculum as two detours in the SVG path.
|
||||
// The detour begins at the lower left of the area labeled extraViniculum below.
|
||||
// The detour proceeds one extraViniculum distance up and slightly to the right,
|
||||
// displacing the radiused corner between surd and viniculum. The radius is
|
||||
// traversed as usual, then the detour resumes. It goes right, to the end of
|
||||
// the very long viniculumn, then down one extraViniculum distance,
|
||||
// after which it resumes regular path geometry for the radical.
|
||||
/* viniculum
|
||||
/
|
||||
/▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒←extraViniculum
|
||||
/ █████████████████████←0.04em (40 unit) std viniculum thickness
|
||||
/ /
|
||||
/ /
|
||||
/ /\
|
||||
/ / surd
|
||||
*/
|
||||
|
||||
const sqrtMain = function(extraViniculum: number, hLinePad: number): string {
|
||||
// sqrtMain path geometry is from glyph U221A in the font KaTeX Main
|
||||
// All surds have 80 units padding above the viniculumn.
|
||||
sqrtMain: `M95,${622 + hLinePad}c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,
|
||||
-10,-9.5,-14c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54c44.2,-33.3,65.8,
|
||||
-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10s173,378,173,378c0.7,0,
|
||||
35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429c69,-144,104.5,-217.7,106.5,
|
||||
-221c5.3,-9.3,12,-14,20,-14H400000v40H845.2724s-225.272,467,-225.272,467
|
||||
s-235,486,-235,486c-2.7,4.7,-9,7,-19,7c-6,0,-10,-1,-12,-3s-194,-422,-194,-422
|
||||
s-65,47,-65,47z M834 ${hLinePad}H400000v40H845z`,
|
||||
return `M95,${622 + extraViniculum + hLinePad}
|
||||
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
|
||||
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
|
||||
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
|
||||
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
|
||||
c69,-144,104.5,-217.7,106.5,-221
|
||||
l${extraViniculum / 2.075} -${extraViniculum}
|
||||
c5.3,-9.3,12,-14,20,-14
|
||||
H400000v${40 + extraViniculum}H845.2724
|
||||
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
|
||||
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
|
||||
Ml${834 + extraViniculum} ${hLinePad}h400000v${40 + extraViniculum}h-400000z`;
|
||||
};
|
||||
|
||||
const sqrtSize1 = function(extraViniculum: number, hLinePad: number): string {
|
||||
// size1 is from glyph U221A in the font KaTeX_Size1-Regular
|
||||
sqrtSize1: `M263,${601 + hLinePad}c0.7,0,18,39.7,52,119c34,79.3,68.167,
|
||||
158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120c340,-704.7,510.7,-1060.3,512,-1067
|
||||
c4.7,-7.3,11,-11,19,-11H40000v40H1012.3s-271.3,567,-271.3,567c-38.7,80.7,-84,
|
||||
175,-136,283c-52,108,-89.167,185.3,-111.5,232c-22.3,46.7,-33.8,70.3,-34.5,71
|
||||
c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1s-109,-253,-109,-253c-72.7,-168,-109.3,
|
||||
-252,-110,-252c-10.7,8,-22,16.7,-34,26c-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26
|
||||
s76,-59,76,-59s76,-60,76,-60z M1001 ${hLinePad}H40000v40H1012z`,
|
||||
return `M263,${601 + extraViniculum + hLinePad}c0.7,0,18,39.7,52,119
|
||||
c34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120
|
||||
c340,-704.7,510.7,-1060.3,512,-1067
|
||||
l${extraViniculum / 2.084} -${extraViniculum}
|
||||
c4.7,-7.3,11,-11,19,-11
|
||||
H40000v${40 + extraViniculum}H1012.3
|
||||
s-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232
|
||||
c-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1
|
||||
s-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26
|
||||
c-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z
|
||||
M${1001 + extraViniculum} ${hLinePad}h400000v${40 + extraViniculum}h-400000z`;
|
||||
};
|
||||
|
||||
const sqrtSize2 = function(extraViniculum: number, hLinePad: number): string {
|
||||
// size2 is from glyph U221A in the font KaTeX_Size2-Regular
|
||||
// The 80 units padding is most obvious here. Note start node at M1001 80.
|
||||
sqrtSize2: `M1001,${hLinePad}H400000v40H1013.1s-83.4,268,-264.1,840c-180.7,
|
||||
572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7s-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,
|
||||
-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744c-10,12,-21,25,-33,39s-32,39,-32,39
|
||||
c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30c26.7,-32.7,52,-63,76,-91s52,-60,52,-60
|
||||
s208,722,208,722c56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,
|
||||
-658.5c53.7,-170.3,84.5,-266.8,92.5,-289.5c4,-6.7,10,-10,18,-10z
|
||||
M1001 ${hLinePad}H400000v40H1013z`,
|
||||
return `M983 ${10 + extraViniculum + hLinePad}
|
||||
l${extraViniculum / 3.13} -${extraViniculum}
|
||||
c4,-6.7,10,-10,18,-10 H400000v${40 + extraViniculum}
|
||||
H1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7
|
||||
s-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744
|
||||
c-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30
|
||||
c26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722
|
||||
c56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5
|
||||
c53.7,-170.3,84.5,-266.8,92.5,-289.5z
|
||||
M${1001 + extraViniculum} ${hLinePad}h400000v${40 + extraViniculum}h-400000z`;
|
||||
};
|
||||
|
||||
const sqrtSize3 = function(extraViniculum: number, hLinePad: number): string {
|
||||
// size3 is from glyph U221A in the font KaTeX_Size3-Regular
|
||||
sqrtSize3: `M424,${2398 + hLinePad}c-1.3,-0.7,-38.5,-172,-111.5,-514c-73,
|
||||
-342,-109.8,-513.3,-110.5,-514c0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,
|
||||
25c-5.7,9.3,-9.8,16,-12.5,20s-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,
|
||||
-13s76,-122,76,-122s77,-121,77,-121s209,968,209,968c0,-2,84.7,-361.7,254,-1079
|
||||
c169.3,-717.3,254.7,-1077.7,256,-1081c4,-6.7,10,-10,18,-10H400000v40H1014.6
|
||||
s-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185c-2,6,-10,9,-24,9
|
||||
c-8,0,-12,-0.7,-12,-2z M1001 ${hLinePad}H400000v40H1014z`,
|
||||
return `M424,${2398 + extraViniculum + hLinePad}
|
||||
c-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514
|
||||
c0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20
|
||||
s-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121
|
||||
s209,968,209,968c0,-2,84.7,-361.7,254,-1079c169.3,-717.3,254.7,-1077.7,256,-1081
|
||||
l${extraViniculum / 4.223} -${extraViniculum}c4,-6.7,10,-10,18,-10 H400000
|
||||
v${40 + extraViniculum}H1014.6
|
||||
s-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185
|
||||
c-2,6,-10,9,-24,9
|
||||
c-8,0,-12,-0.7,-12,-2z M${1001 + extraViniculum} ${hLinePad}
|
||||
h400000v${40 + extraViniculum}h-400000z`;
|
||||
};
|
||||
|
||||
const sqrtSize4 = function(extraViniculum: number, hLinePad: number): string {
|
||||
// size4 is from glyph U221A in the font KaTeX_Size4-Regular
|
||||
sqrtSize4: `M473,${2713 + hLinePad}c339.3,-1799.3,509.3,-2700,510,-2702
|
||||
c3.3,-7.3,9.3,-11,18,-11H400000v40H1017.7s-90.5,478,-276.2,1466c-185.7,988,
|
||||
-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9c-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,
|
||||
-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200c0,-1.3,-5.3,8.7,-16,30c-10.7,
|
||||
21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26s76,-153,76,-153s77,-151,
|
||||
77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104,606z
|
||||
M1001 ${hLinePad}H400000v40H1017z`,
|
||||
return `M473,${2713 + extraViniculum + hLinePad}
|
||||
c339.3,-1799.3,509.3,-2700,510,-2702 l${extraViniculum / 5.298} -${extraViniculum}
|
||||
c3.3,-7.3,9.3,-11,18,-11 H400000v${40 + extraViniculum}H1017.7
|
||||
s-90.5,478,-276.2,1466c-185.7,988,-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9
|
||||
c-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200
|
||||
c0,-1.3,-5.3,8.7,-16,30c-10.7,21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26
|
||||
s76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104,
|
||||
606zM${1001 + extraViniculum} ${hLinePad}h400000v${40 + extraViniculum}H1017.7z`;
|
||||
};
|
||||
|
||||
const sqrtTall = function(
|
||||
extraViniculum: number,
|
||||
hLinePad: number,
|
||||
viewBoxHeight: number
|
||||
): string {
|
||||
// sqrtTall is from glyph U23B7 in the font KaTeX_Size4-Regular
|
||||
// One path edge has a variable length. It runs vertically from the viniculumn
|
||||
// to a point near (14 units) the bottom of the surd. The viniculum
|
||||
// is normally 40 units thick. So the length of the line in question is:
|
||||
const vertSegment = viewBoxHeight - 54 - hLinePad - extraViniculum;
|
||||
|
||||
return `M702 ${extraViniculum + hLinePad}H400000${40 + extraViniculum}
|
||||
H742v${vertSegment}l-4 4-4 4c-.667.7 -2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1
|
||||
h-12l-28-84c-16.667-52-96.667 -294.333-240-727l-212 -643 -85 170
|
||||
c-4-3.333-8.333-7.667-13 -13l-13-13l77-155 77-156c66 199.333 139 419.667
|
||||
219 661 l218 661zM702 ${hLinePad}H400000v${40 + extraViniculum}H742z`;
|
||||
};
|
||||
|
||||
export const sqrtPath = function(
|
||||
size: string,
|
||||
extraViniculum: number,
|
||||
viewBoxHeight: number
|
||||
): string {
|
||||
extraViniculum = 1000 * extraViniculum; // Convert from document ems to viewBox.
|
||||
let path = "";
|
||||
|
||||
switch (size) {
|
||||
case "sqrtMain":
|
||||
path = sqrtMain(extraViniculum, hLinePad);
|
||||
break;
|
||||
case "sqrtSize1":
|
||||
path = sqrtSize1(extraViniculum, hLinePad);
|
||||
break;
|
||||
case "sqrtSize2":
|
||||
path = sqrtSize2(extraViniculum, hLinePad);
|
||||
break;
|
||||
case "sqrtSize3":
|
||||
path = sqrtSize3(extraViniculum, hLinePad);
|
||||
break;
|
||||
case "sqrtSize4":
|
||||
path = sqrtSize4(extraViniculum, hLinePad);
|
||||
break;
|
||||
case "sqrtTall":
|
||||
path = sqrtTall(extraViniculum, hLinePad, viewBoxHeight);
|
||||
}
|
||||
return path;
|
||||
};
|
||||
|
||||
export const path: {[string]: string} = {
|
||||
// The doubleleftarrow geometry is from glyph U+21D0 in the font KaTeX Main
|
||||
doubleleftarrow: `M262 157
|
||||
l10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3
|
||||
@@ -363,6 +454,3 @@ c4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,
|
||||
c-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z
|
||||
M500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z`,
|
||||
};
|
||||
|
||||
|
||||
export default {path};
|
||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 19 KiB |
@@ -323,7 +323,9 @@ Spacing: |
|
||||
\end{matrix}
|
||||
Sqrt: |
|
||||
\sqrt{\sqrt{\sqrt{x}}}_{\sqrt{\sqrt{x}}}^{\sqrt{\sqrt{\sqrt{x}}}
|
||||
^{\sqrt{\sqrt{\sqrt{x}}}}}
|
||||
^{\sqrt{\sqrt{\sqrt{x}}}}} \\
|
||||
\sqrt{\frac{\frac{A}{B}}{\frac{A}{B}}} \;
|
||||
\sqrt{\frac{\frac{\frac{A}{B}}{\frac{A}{B}}}{\frac{\frac{A}{B}}{\frac{A}{B}}}}
|
||||
SqrtRoot: |
|
||||
\begin{array}{l}
|
||||
1+\sqrt[3]{2}+\sqrt[1923^234]{2^{2^{2^{2^{2^{2^{2^{2^{2^{2^{2^2}}}}}}}}}}} \\
|
||||
|