parse-font.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. 'use strict'
  2. /**
  3. * Font RegExp helpers.
  4. */
  5. const weights = 'bold|bolder|lighter|[1-9]00'
  6. const styles = 'italic|oblique'
  7. const variants = 'small-caps'
  8. const stretches = 'ultra-condensed|extra-condensed|condensed|semi-condensed|semi-expanded|expanded|extra-expanded|ultra-expanded'
  9. const units = 'px|pt|pc|in|cm|mm|%|em|ex|ch|rem|q'
  10. const string = '\'([^\']+)\'|"([^"]+)"|[\\w\\s-]+'
  11. // [ [ <‘font-style’> || <font-variant-css21> || <‘font-weight’> || <‘font-stretch’> ]?
  12. // <‘font-size’> [ / <‘line-height’> ]? <‘font-family’> ]
  13. // https://drafts.csswg.org/css-fonts-3/#font-prop
  14. const weightRe = new RegExp(`(${weights}) +`, 'i')
  15. const styleRe = new RegExp(`(${styles}) +`, 'i')
  16. const variantRe = new RegExp(`(${variants}) +`, 'i')
  17. const stretchRe = new RegExp(`(${stretches}) +`, 'i')
  18. const sizeFamilyRe = new RegExp(
  19. `([\\d\\.]+)(${units}) *((?:${string})( *, *(?:${string}))*)`)
  20. /**
  21. * Cache font parsing.
  22. */
  23. const cache = {}
  24. const defaultHeight = 16 // pt, common browser default
  25. /**
  26. * Parse font `str`.
  27. *
  28. * @param {String} str
  29. * @return {Object} Parsed font. `size` is in device units. `unit` is the unit
  30. * appearing in the input string.
  31. * @api private
  32. */
  33. module.exports = str => {
  34. // Cached
  35. if (cache[str]) return cache[str]
  36. // Try for required properties first.
  37. const sizeFamily = sizeFamilyRe.exec(str)
  38. if (!sizeFamily) return // invalid
  39. // Default values and required properties
  40. const font = {
  41. weight: 'normal',
  42. style: 'normal',
  43. stretch: 'normal',
  44. variant: 'normal',
  45. size: parseFloat(sizeFamily[1]),
  46. unit: sizeFamily[2],
  47. family: sizeFamily[3].replace(/["']/g, '').replace(/ *, */g, ',')
  48. }
  49. // Optional, unordered properties.
  50. let weight, style, variant, stretch
  51. // Stop search at `sizeFamily.index`
  52. const substr = str.substring(0, sizeFamily.index)
  53. if ((weight = weightRe.exec(substr))) font.weight = weight[1]
  54. if ((style = styleRe.exec(substr))) font.style = style[1]
  55. if ((variant = variantRe.exec(substr))) font.variant = variant[1]
  56. if ((stretch = stretchRe.exec(substr))) font.stretch = stretch[1]
  57. // Convert to device units. (`font.unit` is the original unit)
  58. // TODO: ch, ex
  59. switch (font.unit) {
  60. case 'pt':
  61. font.size /= 0.75
  62. break
  63. case 'pc':
  64. font.size *= 16
  65. break
  66. case 'in':
  67. font.size *= 96
  68. break
  69. case 'cm':
  70. font.size *= 96.0 / 2.54
  71. break
  72. case 'mm':
  73. font.size *= 96.0 / 25.4
  74. break
  75. case '%':
  76. // TODO disabled because existing unit tests assume 100
  77. // font.size *= defaultHeight / 100 / 0.75
  78. break
  79. case 'em':
  80. case 'rem':
  81. font.size *= defaultHeight / 0.75
  82. break
  83. case 'q':
  84. font.size *= 96 / 25.4 / 4
  85. break
  86. }
  87. return (cache[str] = font)
  88. }