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