text: // @flow
  import { supportedCodepoint } from "./unicodeScripts";
  
  /**
   * 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 the arrays below, in that order.
  //
  // The font metrics are stored in fonts cmsy10, cmsy7, and cmsy5 respsectively.
  // 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 retreived 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],
  };
  
  // 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',
      'Ç': 'C',
      'Ð': 'D',
      'Þ': 'o',
      'å': 'a',
      'ç': 'c',
      'ð': '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;
  };
  
  /**
   * 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`.
   */
  const getCharacterMetrics = function(
      character: string,
      font: string,
  ): ?CharacterMetrics {
      if (!metricMap[font]) {
          throw new Error(`Font metrics not found for font: ${font}.`);
      }
      let ch = character.charCodeAt(0);
      if (character[0] in extraCharacterMap) {
          ch = extraCharacterMap[character[0]].charCodeAt(0);
      }
      let metrics = metricMap[font][ch];
  
      if (!metrics) {
          // We don't typically have font metrics for Asian scripts.
          // So if the character is in a script we support but we
          // dont 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 glpyh
          // 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.
   */
  const getFontMetrics = function(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];
  };
  
  export default {
      getFontMetrics: getFontMetrics,
      getCharacterMetrics: getCharacterMetrics,
  };
  