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 |
|
8 | import ParseError from "./ParseError";
|
9 | import 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).
|
15 | const 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.
|
34 | const relativeUnit = {
|
35 | "ex": true,
|
36 | "em": true,
|
37 | "mu": true,
|
38 | };
|
39 |
|
40 | export 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 | */
|
46 | export 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 | */
|
58 | export 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 | };
|