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

283 lines
8.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @flow
import {supportedCodepoint} from "./unicodeScripts";
import type {Mode} from "./types";
/**
* This file contains metrics regarding fonts and individual symbols. The sigma
* and xi variables, as well as the metricMap map contain data extracted from
* TeX, TeX font metrics, and the TTF files. These data are then exposed via the
* `metrics` variable and the getCharacterMetrics function.
*/
// In TeX, there are actually three sets of dimensions, one for each of
// textstyle (size index 5 and higher: >=9pt), scriptstyle (size index 3 and 4:
// 7-8pt), and scriptscriptstyle (size index 1 and 2: 5-6pt). These are
// provided in the arrays below, in that order.
//
// The font metrics are stored in fonts cmsy10, cmsy7, and cmsy5 respectively.
// This was determined by running the following script:
//
// latex -interaction=nonstopmode \
// '\documentclass{article}\usepackage{amsmath}\begin{document}' \
// '$a$ \expandafter\show\the\textfont2' \
// '\expandafter\show\the\scriptfont2' \
// '\expandafter\show\the\scriptscriptfont2' \
// '\stop'
//
// The metrics themselves were retrieved using the following commands:
//
// tftopl cmsy10
// tftopl cmsy7
// tftopl cmsy5
//
// The output of each of these commands is quite lengthy. The only part we
// care about is the FONTDIMEN section. Each value is measured in EMs.
const sigmasAndXis = {
slant: [0.250, 0.250, 0.250], // sigma1
space: [0.000, 0.000, 0.000], // sigma2
stretch: [0.000, 0.000, 0.000], // sigma3
shrink: [0.000, 0.000, 0.000], // sigma4
xHeight: [0.431, 0.431, 0.431], // sigma5
quad: [1.000, 1.171, 1.472], // sigma6
extraSpace: [0.000, 0.000, 0.000], // sigma7
num1: [0.677, 0.732, 0.925], // sigma8
num2: [0.394, 0.384, 0.387], // sigma9
num3: [0.444, 0.471, 0.504], // sigma10
denom1: [0.686, 0.752, 1.025], // sigma11
denom2: [0.345, 0.344, 0.532], // sigma12
sup1: [0.413, 0.503, 0.504], // sigma13
sup2: [0.363, 0.431, 0.404], // sigma14
sup3: [0.289, 0.286, 0.294], // sigma15
sub1: [0.150, 0.143, 0.200], // sigma16
sub2: [0.247, 0.286, 0.400], // sigma17
supDrop: [0.386, 0.353, 0.494], // sigma18
subDrop: [0.050, 0.071, 0.100], // sigma19
delim1: [2.390, 1.700, 1.980], // sigma20
delim2: [1.010, 1.157, 1.420], // sigma21
axisHeight: [0.250, 0.250, 0.250], // sigma22
// These font metrics are extracted from TeX by using tftopl on cmex10.tfm;
// they correspond to the font parameters of the extension fonts (family 3).
// See the TeXbook, page 441. In AMSTeX, the extension fonts scale; to
// match cmex7, we'd use cmex7.tfm values for script and scriptscript
// values.
defaultRuleThickness: [0.04, 0.049, 0.049], // xi8; cmex7: 0.049
bigOpSpacing1: [0.111, 0.111, 0.111], // xi9
bigOpSpacing2: [0.166, 0.166, 0.166], // xi10
bigOpSpacing3: [0.2, 0.2, 0.2], // xi11
bigOpSpacing4: [0.6, 0.611, 0.611], // xi12; cmex7: 0.611
bigOpSpacing5: [0.1, 0.143, 0.143], // xi13; cmex7: 0.143
// The \sqrt rule width is taken from the height of the surd character.
// Since we use the same font at all sizes, this thickness doesn't scale.
sqrtRuleThickness: [0.04, 0.04, 0.04],
// This value determines how large a pt is, for metrics which are defined
// in terms of pts.
// This value is also used in katex.less; if you change it make sure the
// values match.
ptPerEm: [10.0, 10.0, 10.0],
// 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
// metrics, including height, depth, italic correction, and skew (kern from the
// character to the corresponding \skewchar)
// This map is generated via `make metrics`. It should not be changed manually.
import metricMap from "./fontMetricsData";
// These are very rough approximations. We default to Times New Roman which
// should have Latin-1 and Cyrillic characters, but may not depending on the
// operating system. The metrics do not account for extra height from the
// accents. In the case of Cyrillic characters which have both ascenders and
// descenders we prefer approximations with ascenders, primarily to prevent
// the fraction bar or root line from intersecting the glyph.
// TODO(kevinb) allow union of multiple glyph metrics for better accuracy.
const extraCharacterMap = {
// Latin-1
'Å': 'A',
'Ð': 'D',
'Þ': 'o',
'å': 'a',
'ð': 'd',
'þ': 'o',
// Cyrillic
'А': 'A',
'Б': 'B',
'В': 'B',
'Г': 'F',
'Д': 'A',
'Е': 'E',
'Ж': 'K',
'З': '3',
'И': 'N',
'Й': 'N',
'К': 'K',
'Л': 'N',
'М': 'M',
'Н': 'H',
'О': 'O',
'П': 'N',
'Р': 'P',
'С': 'C',
'Т': 'T',
'У': 'y',
'Ф': 'O',
'Х': 'X',
'Ц': 'U',
'Ч': 'h',
'Ш': 'W',
'Щ': 'W',
'Ъ': 'B',
'Ы': 'X',
'Ь': 'B',
'Э': '3',
'Ю': 'X',
'Я': 'R',
'а': 'a',
'б': 'b',
'в': 'a',
'г': 'r',
'д': 'y',
'е': 'e',
'ж': 'm',
'з': 'e',
'и': 'n',
'й': 'n',
'к': 'n',
'л': 'n',
'м': 'm',
'н': 'n',
'о': 'o',
'п': 'n',
'р': 'p',
'с': 'c',
'т': 'o',
'у': 'y',
'ф': 'b',
'х': 'x',
'ц': 'n',
'ч': 'n',
'ш': 'w',
'щ': 'w',
'ъ': 'a',
'ы': 'm',
'ь': 'a',
'э': 'e',
'ю': 'm',
'я': 'r',
};
export type CharacterMetrics = {
depth: number;
height: number;
italic: number;
skew: number;
width: number;
};
export type MetricMap = {
[string]: number[]
}
/**
* This function adds new font metrics to default metricMap
* It can also override existing metrics
*/
export function setFontMetrics(fontName: string, metrics: MetricMap) {
metricMap[fontName] = metrics;
}
/**
* This function is a convenience function for looking up information in the
* 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`.
*/
export function getCharacterMetrics(
character: string,
font: string,
mode: Mode,
): ?CharacterMetrics {
if (!metricMap[font]) {
throw new Error(`Font metrics not found for font: ${font}.`);
}
let ch = character.charCodeAt(0);
let metrics = metricMap[font][ch];
if (!metrics && character[0] in extraCharacterMap) {
ch = extraCharacterMap[character[0]].charCodeAt(0);
metrics = metricMap[font][ch];
}
if (!metrics && mode === 'text') {
// We don't typically have font metrics for Asian scripts.
// But since we support them in text mode, we need to return
// some sort of metrics.
// So if the character is in a script we support but we
// don't have metrics for it, just use the metrics for
// the Latin capital letter M. This is close enough because
// we (currently) only care about the height of the glyph
// not its width.
if (supportedCodepoint(ch)) {
metrics = metricMap[font][77]; // 77 is the charcode for 'M'
}
}
if (metrics) {
return {
depth: metrics[0],
height: metrics[1],
italic: metrics[2],
skew: metrics[3],
width: metrics[4],
};
}
}
type FontSizeIndex = 0 | 1 | 2;
export type FontMetrics = {
cssEmPerMu: number,
[string]: number,
};
const fontMetricsBySizeIndex: {[FontSizeIndex]: FontMetrics} = {};
/**
* Get the font metrics for a given size.
*/
export function getGlobalMetrics(size: number): FontMetrics {
let sizeIndex: FontSizeIndex;
if (size >= 5) {
sizeIndex = 0;
} else if (size >= 3) {
sizeIndex = 1;
} else {
sizeIndex = 2;
}
if (!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];
}
}
}
return fontMetricsBySizeIndex[sizeIndex];
}