mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-07 12:18:39 +00:00
To @flow: fontMetrics, fontMetricsData, Options, Settings, Style (#848)
* To @flow: fontMetrics, fontMetricsData, Options, Settings, Style * Don't overuse $Shape, improve type checking for fontMetrics*, make maxSize required in OptionsData and update callers. * Remove eslintrc globals change, since eslint-plugin-flowtype makes it redundant. * Remove extra ?s in Options and Settings * Undo removal of width in fontMetrics and switch to `T | void` for nullable types in Options * fix typing of FontMetrics
This commit is contained in:
committed by
Kevin Barabash
parent
ccf09786cc
commit
0f9fb0a1ce
@@ -1,3 +1,4 @@
|
||||
// @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
|
||||
@@ -6,8 +7,8 @@
|
||||
*/
|
||||
|
||||
import fontMetrics from "./fontMetrics";
|
||||
|
||||
const BASESIZE = 6;
|
||||
import type {FontMetrics} from "./fontMetrics";
|
||||
import type {StyleInterface} from "./Style";
|
||||
|
||||
const sizeStyleMap = [
|
||||
// Each element contains [textsize, scriptsize, scriptscriptsize].
|
||||
@@ -31,10 +32,20 @@ const sizeMultipliers = [
|
||||
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, style) {
|
||||
const sizeAtStyle = function(size: number, style: StyleInterface): number {
|
||||
return style.size < 2 ? size : sizeStyleMap[size - 1][style.size - 1];
|
||||
};
|
||||
|
||||
export type OptionsData = {
|
||||
style: StyleInterface;
|
||||
color?: string | void;
|
||||
size?: number;
|
||||
textSize?: number;
|
||||
phantom?: boolean;
|
||||
font?: string | void;
|
||||
maxSize: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the main options class. It contains the current style, size, color,
|
||||
* and font.
|
||||
@@ -43,23 +54,38 @@ const sizeAtStyle = function(size, style) {
|
||||
* different properties, call a `.having*` method.
|
||||
*/
|
||||
class Options {
|
||||
constructor(data) {
|
||||
style: StyleInterface;
|
||||
color: string | void;
|
||||
size: number;
|
||||
textSize: number;
|
||||
phantom: boolean;
|
||||
font: string | void;
|
||||
sizeMultiplier: number;
|
||||
maxSize: number;
|
||||
_fontMetrics: FontMetrics | void;
|
||||
|
||||
/**
|
||||
* The base size index.
|
||||
*/
|
||||
static BASESIZE = 6;
|
||||
|
||||
constructor(data: OptionsData) {
|
||||
this.style = data.style;
|
||||
this.color = data.color;
|
||||
this.size = data.size || BASESIZE;
|
||||
this.size = data.size || Options.BASESIZE;
|
||||
this.textSize = data.textSize || this.size;
|
||||
this.phantom = data.phantom;
|
||||
this.phantom = !!data.phantom;
|
||||
this.font = data.font;
|
||||
this.sizeMultiplier = sizeMultipliers[this.size - 1];
|
||||
this.maxSize = data.maxSize;
|
||||
this._fontMetrics = null;
|
||||
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) {
|
||||
extend(extension: $Shape<OptionsData>): Options {
|
||||
const data = {
|
||||
style: this.style,
|
||||
size: this.size,
|
||||
@@ -83,7 +109,7 @@ class Options {
|
||||
* Return an options object with the given style. If `this.style === style`,
|
||||
* returns `this`.
|
||||
*/
|
||||
havingStyle(style) {
|
||||
havingStyle(style: StyleInterface): Options {
|
||||
if (this.style === style) {
|
||||
return this;
|
||||
} else {
|
||||
@@ -98,7 +124,7 @@ class Options {
|
||||
* Return an options object with a cramped version of the current style. If
|
||||
* the current style is cramped, returns `this`.
|
||||
*/
|
||||
havingCrampedStyle() {
|
||||
havingCrampedStyle(): Options {
|
||||
return this.havingStyle(this.style.cramp());
|
||||
}
|
||||
|
||||
@@ -106,7 +132,7 @@ class Options {
|
||||
* Return an options object with the given size and in at least `\textstyle`.
|
||||
* Returns `this` if appropriate.
|
||||
*/
|
||||
havingSize(size) {
|
||||
havingSize(size: number): Options {
|
||||
if (this.size === size && this.textSize === size) {
|
||||
return this;
|
||||
} else {
|
||||
@@ -122,17 +148,16 @@ class Options {
|
||||
* Like `this.havingSize(BASESIZE).havingStyle(style)`. If `style` is omitted,
|
||||
* changes to at least `\textstyle`.
|
||||
*/
|
||||
havingBaseStyle(style) {
|
||||
havingBaseStyle(style: StyleInterface): Options {
|
||||
style = style || this.style.text();
|
||||
const wantSize = sizeAtStyle(BASESIZE, style);
|
||||
if (this.size === wantSize && this.textSize === BASESIZE
|
||||
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,
|
||||
baseSize: BASESIZE,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -140,7 +165,7 @@ class Options {
|
||||
/**
|
||||
* Create a new options object with the given color.
|
||||
*/
|
||||
withColor(color) {
|
||||
withColor(color: string): Options {
|
||||
return this.extend({
|
||||
color: color,
|
||||
});
|
||||
@@ -149,7 +174,7 @@ class Options {
|
||||
/**
|
||||
* Create a new options object with "phantom" set to true.
|
||||
*/
|
||||
withPhantom() {
|
||||
withPhantom(): Options {
|
||||
return this.extend({
|
||||
phantom: true,
|
||||
});
|
||||
@@ -158,7 +183,7 @@ class Options {
|
||||
/**
|
||||
* Create a new options objects with the give font.
|
||||
*/
|
||||
withFont(font) {
|
||||
withFont(font: ?string): Options {
|
||||
return this.extend({
|
||||
font: font || this.font,
|
||||
});
|
||||
@@ -168,7 +193,7 @@ class Options {
|
||||
* Return the CSS sizing classes required to switch from enclosing options
|
||||
* `oldOptions` to `this`. Returns an array of classes.
|
||||
*/
|
||||
sizingClasses(oldOptions) {
|
||||
sizingClasses(oldOptions: Options): Array<string> {
|
||||
if (oldOptions.size !== this.size) {
|
||||
return ["sizing", "reset-size" + oldOptions.size, "size" + this.size];
|
||||
} else {
|
||||
@@ -180,9 +205,9 @@ class Options {
|
||||
* Return the CSS sizing classes required to switch to the base size. Like
|
||||
* `this.havingSize(BASESIZE).sizingClasses(this)`.
|
||||
*/
|
||||
baseSizingClasses() {
|
||||
if (this.size !== BASESIZE) {
|
||||
return ["sizing", "reset-size" + this.size, "size" + BASESIZE];
|
||||
baseSizingClasses(): Array<string> {
|
||||
if (this.size !== Options.BASESIZE) {
|
||||
return ["sizing", "reset-size" + this.size, "size" + Options.BASESIZE];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
@@ -191,7 +216,7 @@ class Options {
|
||||
/**
|
||||
* Return the font metrics for this size.
|
||||
*/
|
||||
fontMetrics() {
|
||||
fontMetrics(): FontMetrics {
|
||||
if (!this._fontMetrics) {
|
||||
this._fontMetrics = fontMetrics.getFontMetrics(this.size);
|
||||
}
|
||||
@@ -265,18 +290,18 @@ class Options {
|
||||
* Gets the CSS color of the current options object, accounting for the
|
||||
* `colorMap`.
|
||||
*/
|
||||
getColor() {
|
||||
getColor(): string | void {
|
||||
if (this.phantom) {
|
||||
return "transparent";
|
||||
} else if (
|
||||
this.color != null &&
|
||||
Options.colorMap.hasOwnProperty(this.color)
|
||||
) {
|
||||
return Options.colorMap[this.color];
|
||||
} else {
|
||||
return Options.colorMap[this.color] || this.color;
|
||||
return this.color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The base size index.
|
||||
*/
|
||||
Options.BASESIZE = BASESIZE;
|
||||
|
||||
module.exports = Options;
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
/**
|
||||
* This is a module for storing settings passed into KaTeX. It correctly handles
|
||||
* default settings.
|
||||
@@ -5,6 +6,15 @@
|
||||
|
||||
import utils from "./utils";
|
||||
|
||||
type SettingsOptions = {
|
||||
displayMode?: boolean;
|
||||
throwOnError?: boolean;
|
||||
errorColor?: string;
|
||||
macros?: {[macroName: string]: string};
|
||||
colorIsTextColor?: boolean;
|
||||
maxSize?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* The main Settings object
|
||||
*
|
||||
@@ -16,7 +26,14 @@ import utils from "./utils";
|
||||
* and is placed in a block with vertical margin.
|
||||
*/
|
||||
class Settings {
|
||||
constructor(options) {
|
||||
displayMode: boolean;
|
||||
throwOnError: boolean;
|
||||
errorColor: string;
|
||||
macros: {[macroName: string]: string};
|
||||
colorIsTextColor: boolean;
|
||||
maxSize: number;
|
||||
|
||||
constructor(options: SettingsOptions) {
|
||||
// allow null options
|
||||
options = options || {};
|
||||
this.displayMode = utils.deflt(options.displayMode, false);
|
||||
|
44
src/Style.js
44
src/Style.js
@@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
/**
|
||||
* This file contains information and classes for the various kinds of styles
|
||||
* used in TeX. It provides a generic `Style` class, which holds information
|
||||
@@ -10,8 +11,12 @@
|
||||
* The main style class. Contains a unique id for the style, a size (which is
|
||||
* the same for cramped and uncramped version of a style), and a cramped flag.
|
||||
*/
|
||||
class Style {
|
||||
constructor(id, size, cramped) {
|
||||
class Style implements StyleInterface {
|
||||
id: number;
|
||||
size: number;
|
||||
cramped: boolean;
|
||||
|
||||
constructor(id: number, size: number, cramped: boolean) {
|
||||
this.id = id;
|
||||
this.size = size;
|
||||
this.cramped = cramped;
|
||||
@@ -20,14 +25,14 @@ class Style {
|
||||
/**
|
||||
* Get the style of a superscript given a base in the current style.
|
||||
*/
|
||||
sup() {
|
||||
sup(): Style {
|
||||
return styles[sup[this.id]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the style of a subscript given a base in the current style.
|
||||
*/
|
||||
sub() {
|
||||
sub(): Style {
|
||||
return styles[sub[this.id]];
|
||||
}
|
||||
|
||||
@@ -35,7 +40,7 @@ class Style {
|
||||
* Get the style of a fraction numerator given the fraction in the current
|
||||
* style.
|
||||
*/
|
||||
fracNum() {
|
||||
fracNum(): Style {
|
||||
return styles[fracNum[this.id]];
|
||||
}
|
||||
|
||||
@@ -43,7 +48,7 @@ class Style {
|
||||
* Get the style of a fraction denominator given the fraction in the current
|
||||
* style.
|
||||
*/
|
||||
fracDen() {
|
||||
fracDen(): Style {
|
||||
return styles[fracDen[this.id]];
|
||||
}
|
||||
|
||||
@@ -51,25 +56,41 @@ class Style {
|
||||
* Get the cramped version of a style (in particular, cramping a cramped style
|
||||
* doesn't change the style).
|
||||
*/
|
||||
cramp() {
|
||||
cramp(): Style {
|
||||
return styles[cramp[this.id]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a text or display version of this style.
|
||||
*/
|
||||
text() {
|
||||
text(): Style {
|
||||
return styles[text[this.id]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if this style is tightly spaced (scriptstyle/scriptscriptstyle)
|
||||
* Return true if this style is tightly spaced (scriptstyle/scriptscriptstyle)
|
||||
*/
|
||||
isTight() {
|
||||
isTight(): boolean {
|
||||
return this.size >= 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Export an interface for type checking, but don't expose the implementation.
|
||||
// This way, no more styles can be generated.
|
||||
export interface StyleInterface {
|
||||
id: number;
|
||||
size: number;
|
||||
cramped: boolean;
|
||||
|
||||
sup(): StyleInterface;
|
||||
sub(): StyleInterface;
|
||||
fracNum(): StyleInterface;
|
||||
fracDen(): StyleInterface;
|
||||
cramp(): StyleInterface;
|
||||
text(): StyleInterface;
|
||||
isTight(): boolean;
|
||||
}
|
||||
|
||||
// IDs of the different styles
|
||||
const D = 0;
|
||||
const Dc = 1;
|
||||
@@ -100,8 +121,7 @@ const fracDen = [Tc, Tc, Sc, Sc, SSc, SSc, SSc, SSc];
|
||||
const cramp = [Dc, Dc, Tc, Tc, Sc, Sc, SSc, SSc];
|
||||
const text = [D, Dc, T, Tc, T, Tc, T, Tc];
|
||||
|
||||
// We only export some of the styles. Also, we don't export the `Style` class so
|
||||
// no more styles can be generated.
|
||||
// We only export some of the styles.
|
||||
module.exports = {
|
||||
DISPLAY: styles[D],
|
||||
TEXT: styles[T],
|
||||
|
@@ -3,8 +3,7 @@ import functions from "./functions";
|
||||
import {groupTypes as htmlGroupTypes} from "./buildHTML";
|
||||
import {groupTypes as mathmlGroupTypes} from "./buildMathML";
|
||||
|
||||
// TODO(kevinb) use flow to define a proper type for Options
|
||||
type Options = any;
|
||||
import type Options from "./Options";
|
||||
|
||||
type FunctionSpec<T> = {
|
||||
// Unique string to differentiate parse nodes.
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
import { cjkRegex } from "./unicodeRegexes";
|
||||
|
||||
/**
|
||||
@@ -226,21 +227,32 @@ const extraCharacterMap = {
|
||||
'я': 'r',
|
||||
};
|
||||
|
||||
export type CharacterMetrics = {
|
||||
depth: number;
|
||||
height: number;
|
||||
italic: number;
|
||||
skew: number;
|
||||
width: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function is a convenience function for looking up information in the
|
||||
* metricMap table. It takes a character as a string, and a style.
|
||||
* metricMap table. It takes a character as a string, and a font.
|
||||
*
|
||||
* Note: the `width` property may be undefined if fontMetricsData.js wasn't
|
||||
* built using `Make extended_metrics`.
|
||||
*/
|
||||
const getCharacterMetrics = function(character, style) {
|
||||
const getCharacterMetrics = function(
|
||||
character: string,
|
||||
font: string,
|
||||
): ?CharacterMetrics {
|
||||
let ch = character.charCodeAt(0);
|
||||
if (character[0] in extraCharacterMap) {
|
||||
ch = extraCharacterMap[character[0]].charCodeAt(0);
|
||||
} else if (cjkRegex.test(character[0])) {
|
||||
ch = 'M'.charCodeAt(0);
|
||||
}
|
||||
const metrics = metricMap[style][ch];
|
||||
const metrics = metricMap[font]['' + ch];
|
||||
if (metrics) {
|
||||
return {
|
||||
depth: metrics[0],
|
||||
@@ -252,13 +264,19 @@ const getCharacterMetrics = function(character, style) {
|
||||
}
|
||||
};
|
||||
|
||||
const fontMetricsBySizeIndex = {};
|
||||
type FontSizeIndex = 0 | 1 | 2;
|
||||
export type FontMetrics = {
|
||||
cssEmPerMu: number,
|
||||
[string]: number,
|
||||
};
|
||||
|
||||
const fontMetricsBySizeIndex: {[FontSizeIndex]: FontMetrics} = {};
|
||||
|
||||
/**
|
||||
* Get the font metrics for a given size.
|
||||
*/
|
||||
const getFontMetrics = function(size) {
|
||||
let sizeIndex;
|
||||
const getFontMetrics = function(size: number): FontMetrics {
|
||||
let sizeIndex: FontSizeIndex;
|
||||
if (size >= 5) {
|
||||
sizeIndex = 0;
|
||||
} else if (size >= 3) {
|
||||
@@ -267,13 +285,14 @@ const getFontMetrics = function(size) {
|
||||
sizeIndex = 2;
|
||||
}
|
||||
if (!fontMetricsBySizeIndex[sizeIndex]) {
|
||||
const metrics = fontMetricsBySizeIndex[sizeIndex] = {};
|
||||
const metrics = fontMetricsBySizeIndex[sizeIndex] = {
|
||||
cssEmPerMu: sigmasAndXis.quad[sizeIndex] / 18,
|
||||
};
|
||||
for (const key in sigmasAndXis) {
|
||||
if (sigmasAndXis.hasOwnProperty(key)) {
|
||||
metrics[key] = sigmasAndXis[key][sizeIndex];
|
||||
}
|
||||
}
|
||||
metrics.cssEmPerMu = metrics.quad / 18;
|
||||
}
|
||||
return fontMetricsBySizeIndex[sizeIndex];
|
||||
};
|
||||
|
@@ -1,4 +1,5 @@
|
||||
module.exports = {
|
||||
// @flow
|
||||
const fontMetricsData = {
|
||||
"AMS-Regular": {
|
||||
"65": [0, 0.68889, 0, 0],
|
||||
"66": [0, 0.68889, 0, 0],
|
||||
@@ -1750,3 +1751,5 @@ module.exports = {
|
||||
"8242": [0, 0.61111, 0, 0],
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = fontMetricsData;
|
||||
|
@@ -17,6 +17,7 @@ const defaultSettings = new Settings({});
|
||||
const defaultOptions = new Options({
|
||||
style: Style.TEXT,
|
||||
size: 5,
|
||||
maxSize: Infinity,
|
||||
});
|
||||
|
||||
const _getBuilt = function(expr, settings) {
|
||||
|
@@ -21,6 +21,7 @@ const getMathML = function(expr, settings) {
|
||||
// Setup the default options
|
||||
const options = new Options({
|
||||
style: startStyle,
|
||||
maxSize: Infinity,
|
||||
});
|
||||
|
||||
const built = buildMathML(parseTree(expr, usedSettings), expr, options);
|
||||
|
Reference in New Issue
Block a user