fontMetrics.js (7301B)
1 /* eslint no-unused-vars:0 */ 2 3 const Style = require("./Style"); 4 const cjkRegex = require("./unicodeRegexes").cjkRegex; 5 6 /** 7 * This file contains metrics regarding fonts and individual symbols. The sigma 8 * and xi variables, as well as the metricMap map contain data extracted from 9 * TeX, TeX font metrics, and the TTF files. These data are then exposed via the 10 * `metrics` variable and the getCharacterMetrics function. 11 */ 12 13 // In TeX, there are actually three sets of dimensions, one for each of 14 // textstyle, scriptstyle, and scriptscriptstyle. These are provided in the 15 // the arrays below, in that order. 16 // 17 // The font metrics are stored in fonts cmsy10, cmsy7, and cmsy5 respsectively. 18 // This was determined by running the folllowing script: 19 // 20 // latex -interaction=nonstopmode \ 21 // '\documentclass{article}\usepackage{amsmath}\begin{document}' \ 22 // '$a$ \expandafter\show\the\textfont2' \ 23 // '\expandafter\show\the\scriptfont2' \ 24 // '\expandafter\show\the\scriptscriptfont2' \ 25 // '\stop' 26 // 27 // The metrics themselves were retreived using the following commands: 28 // 29 // tftopl cmsy10 30 // tftopl cmsy7 31 // tftopl cmsy5 32 // 33 // The output of each of these commands is quite lengthy. The only part we 34 // care about is the FONTDIMEN section. Each value is measured in EMs. 35 const sigmas = { 36 slant: [0.250, 0.250, 0.250], // sigma1 37 space: [0.000, 0.000, 0.000], // sigma2 38 stretch: [0.000, 0.000, 0.000], // sigma3 39 shrink: [0.000, 0.000, 0.000], // sigma4 40 xHeight: [0.431, 0.431, 0.431], // sigma5 41 quad: [1.000, 1.171, 1.472], // sigma6 42 extraSpace: [0.000, 0.000, 0.000], // sigma7 43 num1: [0.677, 0.732, 0.925], // sigma8 44 num2: [0.394, 0.384, 0.387], // sigma9 45 num3: [0.444, 0.471, 0.504], // sigma10 46 denom1: [0.686, 0.752, 1.025], // sigma11 47 denom2: [0.345, 0.344, 0.532], // sigma12 48 sup1: [0.413, 0.503, 0.504], // sigma13 49 sup2: [0.363, 0.431, 0.404], // sigma14 50 sup3: [0.289, 0.286, 0.294], // sigma15 51 sub1: [0.150, 0.143, 0.200], // sigma16 52 sub2: [0.247, 0.286, 0.400], // sigma17 53 supDrop: [0.386, 0.353, 0.494], // sigma18 54 subDrop: [0.050, 0.071, 0.100], // sigma19 55 delim1: [2.390, 1.700, 1.980], // sigma20 56 delim2: [1.010, 1.157, 1.420], // sigma21 57 axisHeight: [0.250, 0.250, 0.250], // sigma22 58 }; 59 60 // These font metrics are extracted from TeX by using 61 // \font\a=cmex10 62 // \showthe\fontdimenX\a 63 // where X is the corresponding variable number. These correspond to the font 64 // parameters of the extension fonts (family 3). See the TeXbook, page 441. 65 const xi1 = 0; 66 const xi2 = 0; 67 const xi3 = 0; 68 const xi4 = 0; 69 const xi5 = 0.431; 70 const xi6 = 1; 71 const xi7 = 0; 72 const xi8 = 0.04; 73 const xi9 = 0.111; 74 const xi10 = 0.166; 75 const xi11 = 0.2; 76 const xi12 = 0.6; 77 const xi13 = 0.1; 78 79 // This value determines how large a pt is, for metrics which are defined in 80 // terms of pts. 81 // This value is also used in katex.less; if you change it make sure the values 82 // match. 83 const ptPerEm = 10.0; 84 85 // The space between adjacent `|` columns in an array definition. From 86 // `\showthe\doublerulesep` in LaTeX. 87 const doubleRuleSep = 2.0 / ptPerEm; 88 89 /** 90 * This is just a mapping from common names to real metrics 91 */ 92 const metrics = { 93 defaultRuleThickness: xi8, 94 bigOpSpacing1: xi9, 95 bigOpSpacing2: xi10, 96 bigOpSpacing3: xi11, 97 bigOpSpacing4: xi12, 98 bigOpSpacing5: xi13, 99 ptPerEm: ptPerEm, 100 doubleRuleSep: doubleRuleSep, 101 }; 102 103 // This map contains a mapping from font name and character code to character 104 // metrics, including height, depth, italic correction, and skew (kern from the 105 // character to the corresponding \skewchar) 106 // This map is generated via `make metrics`. It should not be changed manually. 107 const metricMap = require("./fontMetricsData"); 108 109 // These are very rough approximations. We default to Times New Roman which 110 // should have Latin-1 and Cyrillic characters, but may not depending on the 111 // operating system. The metrics do not account for extra height from the 112 // accents. In the case of Cyrillic characters which have both ascenders and 113 // descenders we prefer approximations with ascenders, primarily to prevent 114 // the fraction bar or root line from intersecting the glyph. 115 // TODO(kevinb) allow union of multiple glyph metrics for better accuracy. 116 const extraCharacterMap = { 117 // Latin-1 118 'À': 'A', 119 'Á': 'A', 120 'Â': 'A', 121 'Ã': 'A', 122 'Ä': 'A', 123 'Å': 'A', 124 'Æ': 'A', 125 'Ç': 'C', 126 'È': 'E', 127 'É': 'E', 128 'Ê': 'E', 129 'Ë': 'E', 130 'Ì': 'I', 131 'Í': 'I', 132 'Î': 'I', 133 'Ï': 'I', 134 'Ð': 'D', 135 'Ñ': 'N', 136 'Ò': 'O', 137 'Ó': 'O', 138 'Ô': 'O', 139 'Õ': 'O', 140 'Ö': 'O', 141 'Ø': 'O', 142 'Ù': 'U', 143 'Ú': 'U', 144 'Û': 'U', 145 'Ü': 'U', 146 'Ý': 'Y', 147 'Þ': 'o', 148 'ß': 'B', 149 'à': 'a', 150 'á': 'a', 151 'â': 'a', 152 'ã': 'a', 153 'ä': 'a', 154 'å': 'a', 155 'æ': 'a', 156 'ç': 'c', 157 'è': 'e', 158 'é': 'e', 159 'ê': 'e', 160 'ë': 'e', 161 'ì': 'i', 162 'í': 'i', 163 'î': 'i', 164 'ï': 'i', 165 'ð': 'd', 166 'ñ': 'n', 167 'ò': 'o', 168 'ó': 'o', 169 'ô': 'o', 170 'õ': 'o', 171 'ö': 'o', 172 'ø': 'o', 173 'ù': 'u', 174 'ú': 'u', 175 'û': 'u', 176 'ü': 'u', 177 'ý': 'y', 178 'þ': 'o', 179 'ÿ': 'y', 180 181 // Cyrillic 182 'А': 'A', 183 'Б': 'B', 184 'В': 'B', 185 'Г': 'F', 186 'Д': 'A', 187 'Е': 'E', 188 'Ж': 'K', 189 'З': '3', 190 'И': 'N', 191 'Й': 'N', 192 'К': 'K', 193 'Л': 'N', 194 'М': 'M', 195 'Н': 'H', 196 'О': 'O', 197 'П': 'N', 198 'Р': 'P', 199 'С': 'C', 200 'Т': 'T', 201 'У': 'y', 202 'Ф': 'O', 203 'Х': 'X', 204 'Ц': 'U', 205 'Ч': 'h', 206 'Ш': 'W', 207 'Щ': 'W', 208 'Ъ': 'B', 209 'Ы': 'X', 210 'Ь': 'B', 211 'Э': '3', 212 'Ю': 'X', 213 'Я': 'R', 214 'а': 'a', 215 'б': 'b', 216 'в': 'a', 217 'г': 'r', 218 'д': 'y', 219 'е': 'e', 220 'ж': 'm', 221 'з': 'e', 222 'и': 'n', 223 'й': 'n', 224 'к': 'n', 225 'л': 'n', 226 'м': 'm', 227 'н': 'n', 228 'о': 'o', 229 'п': 'n', 230 'р': 'p', 231 'с': 'c', 232 'т': 'o', 233 'у': 'y', 234 'ф': 'b', 235 'х': 'x', 236 'ц': 'n', 237 'ч': 'n', 238 'ш': 'w', 239 'щ': 'w', 240 'ъ': 'a', 241 'ы': 'm', 242 'ь': 'a', 243 'э': 'e', 244 'ю': 'm', 245 'я': 'r', 246 }; 247 248 /** 249 * This function is a convenience function for looking up information in the 250 * metricMap table. It takes a character as a string, and a style. 251 * 252 * Note: the `width` property may be undefined if fontMetricsData.js wasn't 253 * built using `Make extended_metrics`. 254 */ 255 const getCharacterMetrics = function(character, style) { 256 let ch = character.charCodeAt(0); 257 if (character[0] in extraCharacterMap) { 258 ch = extraCharacterMap[character[0]].charCodeAt(0); 259 } else if (cjkRegex.test(character[0])) { 260 ch = 'M'.charCodeAt(0); 261 } 262 const metrics = metricMap[style][ch]; 263 if (metrics) { 264 return { 265 depth: metrics[0], 266 height: metrics[1], 267 italic: metrics[2], 268 skew: metrics[3], 269 width: metrics[4], 270 }; 271 } 272 }; 273 274 module.exports = { 275 metrics: metrics, 276 sigmas: sigmas, 277 getCharacterMetrics: getCharacterMetrics, 278 };