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