site/node_modules/katex/src/Options.js
2024-10-14 08:09:33 +02:00

320 lines
9.2 KiB
JavaScript

// @flow
/**
* This file contains information about the options that the Parser carries
* around with it while parsing. Data is held in an `Options` object, and when
* recursing, a new `Options` object can be created with the `.with*` and
* `.reset` functions.
*/
import {getGlobalMetrics} from "./fontMetrics";
import type {FontMetrics} from "./fontMetrics";
import type {StyleInterface} from "./Style";
const sizeStyleMap = [
// Each element contains [textsize, scriptsize, scriptscriptsize].
// The size mappings are taken from TeX with \normalsize=10pt.
[1, 1, 1], // size1: [5, 5, 5] \tiny
[2, 1, 1], // size2: [6, 5, 5]
[3, 1, 1], // size3: [7, 5, 5] \scriptsize
[4, 2, 1], // size4: [8, 6, 5] \footnotesize
[5, 2, 1], // size5: [9, 6, 5] \small
[6, 3, 1], // size6: [10, 7, 5] \normalsize
[7, 4, 2], // size7: [12, 8, 6] \large
[8, 6, 3], // size8: [14.4, 10, 7] \Large
[9, 7, 6], // size9: [17.28, 12, 10] \LARGE
[10, 8, 7], // size10: [20.74, 14.4, 12] \huge
[11, 10, 9], // size11: [24.88, 20.74, 17.28] \HUGE
];
const sizeMultipliers = [
// fontMetrics.js:getGlobalMetrics also uses size indexes, so if
// you change size indexes, change that function.
0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.44, 1.728, 2.074, 2.488,
];
const sizeAtStyle = function(size: number, style: StyleInterface): number {
return style.size < 2 ? size : sizeStyleMap[size - 1][style.size - 1];
};
// In these types, "" (empty string) means "no change".
export type FontWeight = "textbf" | "textmd" | "";
export type FontShape = "textit" | "textup" | "";
export type OptionsData = {
style: StyleInterface;
color?: string | void;
size?: number;
textSize?: number;
phantom?: boolean;
font?: string;
fontFamily?: string;
fontWeight?: FontWeight;
fontShape?: FontShape;
sizeMultiplier?: number;
maxSize: number;
minRuleThickness: number;
};
/**
* This is the main options class. It contains the current style, size, color,
* and font.
*
* Options objects should not be modified. To create a new Options with
* different properties, call a `.having*` method.
*/
class Options {
style: StyleInterface;
color: string | void;
size: number;
textSize: number;
phantom: boolean;
// A font family applies to a group of fonts (i.e. SansSerif), while a font
// represents a specific font (i.e. SansSerif Bold).
// See: https://tex.stackexchange.com/questions/22350/difference-between-textrm-and-mathrm
font: string;
fontFamily: string;
fontWeight: FontWeight;
fontShape: FontShape;
sizeMultiplier: number;
maxSize: number;
minRuleThickness: number;
_fontMetrics: FontMetrics | void;
/**
* The base size index.
*/
static BASESIZE: number = 6;
constructor(data: OptionsData) {
this.style = data.style;
this.color = data.color;
this.size = data.size || Options.BASESIZE;
this.textSize = data.textSize || this.size;
this.phantom = !!data.phantom;
this.font = data.font || "";
this.fontFamily = data.fontFamily || "";
this.fontWeight = data.fontWeight || '';
this.fontShape = data.fontShape || '';
this.sizeMultiplier = sizeMultipliers[this.size - 1];
this.maxSize = data.maxSize;
this.minRuleThickness = data.minRuleThickness;
this._fontMetrics = undefined;
}
/**
* Returns a new options object with the same properties as "this". Properties
* from "extension" will be copied to the new options object.
*/
extend(extension: $Shape<OptionsData>): Options {
const data = {
style: this.style,
size: this.size,
textSize: this.textSize,
color: this.color,
phantom: this.phantom,
font: this.font,
fontFamily: this.fontFamily,
fontWeight: this.fontWeight,
fontShape: this.fontShape,
maxSize: this.maxSize,
minRuleThickness: this.minRuleThickness,
};
for (const key in extension) {
if (extension.hasOwnProperty(key)) {
data[key] = extension[key];
}
}
return new Options(data);
}
/**
* Return an options object with the given style. If `this.style === style`,
* returns `this`.
*/
havingStyle(style: StyleInterface): Options {
if (this.style === style) {
return this;
} else {
return this.extend({
style: style,
size: sizeAtStyle(this.textSize, style),
});
}
}
/**
* Return an options object with a cramped version of the current style. If
* the current style is cramped, returns `this`.
*/
havingCrampedStyle(): Options {
return this.havingStyle(this.style.cramp());
}
/**
* Return an options object with the given size and in at least `\textstyle`.
* Returns `this` if appropriate.
*/
havingSize(size: number): Options {
if (this.size === size && this.textSize === size) {
return this;
} else {
return this.extend({
style: this.style.text(),
size: size,
textSize: size,
sizeMultiplier: sizeMultipliers[size - 1],
});
}
}
/**
* Like `this.havingSize(BASESIZE).havingStyle(style)`. If `style` is omitted,
* changes to at least `\textstyle`.
*/
havingBaseStyle(style: StyleInterface): Options {
style = style || this.style.text();
const wantSize = sizeAtStyle(Options.BASESIZE, style);
if (this.size === wantSize && this.textSize === Options.BASESIZE
&& this.style === style) {
return this;
} else {
return this.extend({
style: style,
size: wantSize,
});
}
}
/**
* Remove the effect of sizing changes such as \Huge.
* Keep the effect of the current style, such as \scriptstyle.
*/
havingBaseSizing(): Options {
let size;
switch (this.style.id) {
case 4:
case 5:
size = 3; // normalsize in scriptstyle
break;
case 6:
case 7:
size = 1; // normalsize in scriptscriptstyle
break;
default:
size = 6; // normalsize in textstyle or displaystyle
}
return this.extend({
style: this.style.text(),
size: size,
});
}
/**
* Create a new options object with the given color.
*/
withColor(color: string): Options {
return this.extend({
color: color,
});
}
/**
* Create a new options object with "phantom" set to true.
*/
withPhantom(): Options {
return this.extend({
phantom: true,
});
}
/**
* Creates a new options object with the given math font or old text font.
* @type {[type]}
*/
withFont(font: string): Options {
return this.extend({
font,
});
}
/**
* Create a new options objects with the given fontFamily.
*/
withTextFontFamily(fontFamily: string): Options {
return this.extend({
fontFamily,
font: "",
});
}
/**
* Creates a new options object with the given font weight
*/
withTextFontWeight(fontWeight: FontWeight): Options {
return this.extend({
fontWeight,
font: "",
});
}
/**
* Creates a new options object with the given font weight
*/
withTextFontShape(fontShape: FontShape): Options {
return this.extend({
fontShape,
font: "",
});
}
/**
* Return the CSS sizing classes required to switch from enclosing options
* `oldOptions` to `this`. Returns an array of classes.
*/
sizingClasses(oldOptions: Options): Array<string> {
if (oldOptions.size !== this.size) {
return ["sizing", "reset-size" + oldOptions.size, "size" + this.size];
} else {
return [];
}
}
/**
* Return the CSS sizing classes required to switch to the base size. Like
* `this.havingSize(BASESIZE).sizingClasses(this)`.
*/
baseSizingClasses(): Array<string> {
if (this.size !== Options.BASESIZE) {
return ["sizing", "reset-size" + this.size, "size" + Options.BASESIZE];
} else {
return [];
}
}
/**
* Return the font metrics for this size.
*/
fontMetrics(): FontMetrics {
if (!this._fontMetrics) {
this._fontMetrics = getGlobalMetrics(this.size);
}
return this._fontMetrics;
}
/**
* Gets the CSS color of the current options object
*/
getColor(): string | void {
if (this.phantom) {
return "transparent";
} else {
return this.color;
}
}
}
export default Options;