102 lines
2.7 KiB
JavaScript
102 lines
2.7 KiB
JavaScript
|
'use strict'
|
|||
|
|
|||
|
/**
|
|||
|
* Font RegExp helpers.
|
|||
|
*/
|
|||
|
|
|||
|
const weights = 'bold|bolder|lighter|[1-9]00'
|
|||
|
const styles = 'italic|oblique'
|
|||
|
const variants = 'small-caps'
|
|||
|
const stretches = 'ultra-condensed|extra-condensed|condensed|semi-condensed|semi-expanded|expanded|extra-expanded|ultra-expanded'
|
|||
|
const units = 'px|pt|pc|in|cm|mm|%|em|ex|ch|rem|q'
|
|||
|
const 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 = 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`
|
|||
|
const 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)
|
|||
|
}
|