in packages/core/src/text/font-props.ts [30:135]
static from(cssFontProp: string) {
// Cached
if (cache[cssFontProp]) return cache[cssFontProp]
let state = states.VARIATION,
buffer = ''
const result: FontProps = {
'font-family': [],
'font-size': '',
}
// eslint-disable-next-line no-cond-assign
for (let c: string, i = 0; c = cssFontProp.charAt(i); i += 1) {
if (state === states.BEFORE_FONT_FAMILY && (c === '"' || c === '\'')) {
let index = i + 1
// consume the entire string
do {
index = cssFontProp.indexOf(c, index) + 1
if (!index) {
// If a string is not closed by a ' or " return null.
throw new TypeError(`Parse error: string is not closed by a ' or "'`)
}
} while (cssFontProp.charAt(index - 2) === '\\')
result['font-family'].push(cssFontProp.slice(i, index))
i = index - 1
state = states.FONT_FAMILY
buffer = ''
} else if (state === states.FONT_FAMILY && c === ',') {
state = states.BEFORE_FONT_FAMILY
buffer = ''
} else if (state === states.BEFORE_FONT_FAMILY && c === ',') {
const identifier = FontProps.parseIdentifier(buffer)
if (identifier) {
result['font-family'].push(identifier)
}
buffer = ''
} else if (state === states.AFTER_OBLIQUE && c === ' ') {
if (/^(?:\+|-)?(?:[0-9]*\.)?[0-9]+(?:deg|grad|rad|turn)$/.test(buffer)) {
result['font-style'] += ' ' + buffer
buffer = ''
} else {
// The 'oblique' token was not followed by an angle.
// Backtrack to allow the token to be parsed as VARIATION
i -= 1
}
state = states.VARIATION
} else if (state === states.VARIATION && (c === ' ' || c === '/')) {
if (/^(?:(?:xx|x)-large|(?:xx|s)-small|small|large|medium)$/.test(buffer) ||
/^(?:larg|small)er$/.test(buffer) ||
/^(?:\+|-)?(?:[0-9]*\.)?[0-9]+(?:em|ex|ch|rem|vh|vw|vmin|vmax|px|mm|cm|in|pt|pc|%)$/.test(buffer)) {
state = c === '/' ? states.LINE_HEIGHT : states.BEFORE_FONT_FAMILY
result['font-size'] = buffer
} else if (/^italic$/.test(buffer)) {
result['font-style'] = buffer
} else if (/^oblique$/.test(buffer)) {
result['font-style'] = buffer
state = states.AFTER_OBLIQUE
} else if (/^small-caps$/.test(buffer)) {
result['font-variant'] = buffer
} else if (/^(?:bold(?:er)?|lighter)$/.test(buffer)) {
result['font-weight'] = buffer
} else if (/^[+-]?(?:[0-9]*\.)?[0-9]+(?:e[+-]?(?:0|[1-9][0-9]*))?$/.test(buffer)) {
const num = parseFloat(buffer)
if (num >= 1 && num <= 1000) {
result['font-weight'] = buffer
}
} else if (/^(?:(?:ultra|extra|semi)-)?(?:condensed|expanded)$/.test(buffer)) {
result['font-stretch'] = buffer
}
buffer = ''
} else if (state === states.LINE_HEIGHT && c === ' ') {
if (/^(?:\+|-)?([0-9]*\.)?[0-9]+(?:em|ex|ch|rem|vh|vw|vmin|vmax|px|mm|cm|in|pt|pc|%)?$/.test(buffer)) {
result['line-height'] = buffer
}
state = states.BEFORE_FONT_FAMILY
buffer = ''
} else {
buffer += c
}
}
// This is for the case where a string was specified followed by
// an identifier, but without a separating comma.
if (state === states.FONT_FAMILY && !/^\s*$/.test(buffer)) {
throw new TypeError(`Parse font-faimly error: '${buffer}'`)
}
if (state === states.BEFORE_FONT_FAMILY) {
const identifier = FontProps.parseIdentifier(buffer)
if (identifier) {
result['font-family'].push(identifier)
}
}
if (!result['font-size'] || result['font-family'].length === 0) {
throw new TypeError(`Can not resolve '${cssFontProp}'`)
}
return (cache[cssFontProp] = result)
}