UNPKG

3.9 kBJavaScriptView Raw
1// @flow
2
3/**
4 * This file does conversion between units. In particular, it provides
5 * calculateSize to convert other units into ems.
6 */
7
8import ParseError from "./ParseError";
9import Options from "./Options";
10
11// This table gives the number of TeX pts in one of each *absolute* TeX unit.
12// Thus, multiplying a length by this number converts the length from units
13// into pts. Dividing the result by ptPerEm gives the number of ems
14// *assuming* a font size of ptPerEm (normal size, normal style).
15const ptPerUnit = {
16 // https://en.wikibooks.org/wiki/LaTeX/Lengths and
17 // https://tex.stackexchange.com/a/8263
18 "pt": 1, // TeX point
19 "mm": 7227 / 2540, // millimeter
20 "cm": 7227 / 254, // centimeter
21 "in": 72.27, // inch
22 "bp": 803 / 800, // big (PostScript) points
23 "pc": 12, // pica
24 "dd": 1238 / 1157, // didot
25 "cc": 14856 / 1157, // cicero (12 didot)
26 "nd": 685 / 642, // new didot
27 "nc": 1370 / 107, // new cicero (12 new didot)
28 "sp": 1 / 65536, // scaled point (TeX's internal smallest unit)
29 // https://tex.stackexchange.com/a/41371
30 "px": 803 / 800, // \pdfpxdimen defaults to 1 bp in pdfTeX and LuaTeX
31};
32
33// Dictionary of relative units, for fast validity testing.
34const relativeUnit = {
35 "ex": true,
36 "em": true,
37 "mu": true,
38};
39
40export type Measurement = {| number: number, unit: string |};
41
42/**
43 * Determine whether the specified unit (either a string defining the unit
44 * or a "size" parse node containing a unit field) is valid.
45 */
46export const validUnit = function(unit: string | Measurement): boolean {
47 if (typeof unit !== "string") {
48 unit = unit.unit;
49 }
50 return (unit in ptPerUnit || unit in relativeUnit || unit === "ex");
51};
52
53/*
54 * Convert a "size" parse node (with numeric "number" and string "unit" fields,
55 * as parsed by functions.js argType "size") into a CSS em value for the
56 * current style/scale. `options` gives the current options.
57 */
58export const calculateSize = function(
59 sizeValue: Measurement, options: Options): number {
60 let scale;
61 if (sizeValue.unit in ptPerUnit) {
62 // Absolute units
63 scale = ptPerUnit[sizeValue.unit] // Convert unit to pt
64 / options.fontMetrics().ptPerEm // Convert pt to CSS em
65 / options.sizeMultiplier; // Unscale to make absolute units
66 } else if (sizeValue.unit === "mu") {
67 // `mu` units scale with scriptstyle/scriptscriptstyle.
68 scale = options.fontMetrics().cssEmPerMu;
69 } else {
70 // Other relative units always refer to the *textstyle* font
71 // in the current size.
72 let unitOptions;
73 if (options.style.isTight()) {
74 // isTight() means current style is script/scriptscript.
75 unitOptions = options.havingStyle(options.style.text());
76 } else {
77 unitOptions = options;
78 }
79 // TODO: In TeX these units are relative to the quad of the current
80 // *text* font, e.g. cmr10. KaTeX instead uses values from the
81 // comparably-sized *Computer Modern symbol* font. At 10pt, these
82 // match. At 7pt and 5pt, they differ: cmr7=1.138894, cmsy7=1.170641;
83 // cmr5=1.361133, cmsy5=1.472241. Consider $\scriptsize a\kern1emb$.
84 // TeX \showlists shows a kern of 1.13889 * fontsize;
85 // KaTeX shows a kern of 1.171 * fontsize.
86 if (sizeValue.unit === "ex") {
87 scale = unitOptions.fontMetrics().xHeight;
88 } else if (sizeValue.unit === "em") {
89 scale = unitOptions.fontMetrics().quad;
90 } else {
91 throw new ParseError("Invalid unit: '" + sizeValue.unit + "'");
92 }
93 if (unitOptions !== options) {
94 scale *= unitOptions.sizeMultiplier / options.sizeMultiplier;
95 }
96 }
97 return Math.min(sizeValue.number * scale, options.maxSize);
98};