mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Code to parse the font shorthand property
This commit is contained in:
parent
f1122ac05f
commit
b0b7a59afd
@ -11,7 +11,7 @@ from tinycss.css21 import CSS21Parser, ParseError
|
||||
from .tokenizer import tokenize_grouped
|
||||
|
||||
|
||||
def parse_font_family(css_string):
|
||||
def parse_font_family_tokens(tokens):
|
||||
families = []
|
||||
current_family = ''
|
||||
def commit():
|
||||
@ -19,7 +19,7 @@ def parse_font_family(css_string):
|
||||
if val:
|
||||
families.append(val)
|
||||
|
||||
for token in tokenize_grouped(css_string.strip()):
|
||||
for token in tokens:
|
||||
if token.type == 'STRING':
|
||||
if current_family:
|
||||
commit()
|
||||
@ -35,6 +35,113 @@ def parse_font_family(css_string):
|
||||
commit()
|
||||
return families
|
||||
|
||||
def parse_font_family(css_string):
|
||||
return parse_font_family_tokens(tokenize_grouped(type('')(css_string).strip()))
|
||||
|
||||
GLOBAL_IDENTS = frozenset('inherit initial unset normal'.split())
|
||||
STYLE_IDENTS = frozenset('italic oblique'.split())
|
||||
VARIANT_IDENTS = frozenset(('small-caps',))
|
||||
WEIGHT_IDENTS = frozenset('bold bolder lighter'.split())
|
||||
STRETCH_IDENTS = frozenset('ultra-condensed extra-condensed condensed semi-condensed semi-expanded expanded extra-expanded ultra-expanded'.split())
|
||||
BEFORE_SIZE_IDENTS = STYLE_IDENTS | VARIANT_IDENTS | WEIGHT_IDENTS | STRETCH_IDENTS
|
||||
SIZE_IDENTS = frozenset('xx-small x-small small medium large x-large xx-large larger smaller'.split())
|
||||
WEIGHT_SIZES = frozenset(map(int, '100 200 300 400 500 600 700 800 900'.split()))
|
||||
LEGACY_FONT_SPEC = frozenset('caption icon menu message-box small-caption status-bar'.split())
|
||||
|
||||
def parse_font(css_string):
|
||||
# See https://www.w3.org/TR/css-fonts-3/#font-prop
|
||||
style = variant = weight = stretch = size = height = None
|
||||
tokens = list(reversed(tuple(tokenize_grouped(type('')(css_string).strip()))))
|
||||
if tokens and tokens[-1].value in LEGACY_FONT_SPEC:
|
||||
return {'font-family':['sans-serif']}
|
||||
while tokens:
|
||||
tok = tokens.pop()
|
||||
if tok.type == 'STRING':
|
||||
tokens.append(tok)
|
||||
break
|
||||
if tok.type == 'INTEGER':
|
||||
if size is None:
|
||||
if weight is None and tok.value in WEIGHT_SIZES:
|
||||
weight = tok.as_css()
|
||||
continue
|
||||
break
|
||||
if height is None:
|
||||
height = tok.as_css()
|
||||
break
|
||||
break
|
||||
if tok.type == 'NUMBER':
|
||||
if size is not None and height is None:
|
||||
height = tok.as_css()
|
||||
break
|
||||
if tok.type == 'DELIM':
|
||||
if tok.value == '/' and size is not None and height is None:
|
||||
continue
|
||||
break
|
||||
if tok.type in ('DIMENSION', 'PERCENTAGE'):
|
||||
if size is None:
|
||||
size = tok.as_css()
|
||||
continue
|
||||
if height is None:
|
||||
height = tok.as_css()
|
||||
break
|
||||
if tok.type == 'IDENT':
|
||||
if tok.value in GLOBAL_IDENTS:
|
||||
if size is not None:
|
||||
if height is None:
|
||||
height = tok.value
|
||||
else:
|
||||
tokens.append(tok)
|
||||
break
|
||||
if style is None:
|
||||
style = tok.value
|
||||
elif variant is None:
|
||||
variant = tok.value
|
||||
elif weight is None:
|
||||
weight = tok.value
|
||||
elif stretch is None:
|
||||
stretch = tok.value
|
||||
elif size is None:
|
||||
size = tok.value
|
||||
elif height is None:
|
||||
height = tok.value
|
||||
break
|
||||
else:
|
||||
tokens.append(tok)
|
||||
break
|
||||
continue
|
||||
if tok.value in BEFORE_SIZE_IDENTS:
|
||||
if size is not None:
|
||||
break
|
||||
if tok.value in STYLE_IDENTS:
|
||||
style = tok.value
|
||||
elif tok.value in VARIANT_IDENTS:
|
||||
variant = tok.value
|
||||
elif tok.value in WEIGHT_IDENTS:
|
||||
weight = tok.value
|
||||
elif tok.value in STRETCH_IDENTS:
|
||||
stretch = tok.value
|
||||
elif tok.value in SIZE_IDENTS:
|
||||
size = tok.value
|
||||
else:
|
||||
tokens.append(tok)
|
||||
break
|
||||
families = parse_font_family_tokens(reversed(tokens))
|
||||
ans = {}
|
||||
if style is not None:
|
||||
ans['font-style'] = style
|
||||
if variant is not None:
|
||||
ans['font-variant'] = variant
|
||||
if weight is not None:
|
||||
ans['font-weight'] = weight
|
||||
if stretch is not None:
|
||||
ans['font-stretch'] = stretch
|
||||
if size is not None:
|
||||
ans['font-size'] = size
|
||||
if height is not None:
|
||||
ans['line-height'] = height
|
||||
if families:
|
||||
ans['font-family'] = families
|
||||
return ans
|
||||
|
||||
class FontFaceRule(object):
|
||||
|
||||
|
@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import,
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from tinycss.fonts3 import CSSFonts3Parser, parse_font_family
|
||||
from tinycss.fonts3 import CSSFonts3Parser, parse_font_family, parse_font
|
||||
from tinycss.tests import BaseTest
|
||||
|
||||
class TestFonts3(BaseTest):
|
||||
@ -48,4 +48,16 @@ class TestFonts3(BaseTest):
|
||||
for single in ('serif', 'sans-serif', 'A B C'):
|
||||
self.ae([single], parse_font_family(single))
|
||||
|
||||
|
||||
def test_parse_font(self):
|
||||
def t(raw, **kw):
|
||||
q = {('line' if k == 'height' else 'font') + '-' + k:v for k, v in kw.iteritems()}
|
||||
self.ae(q, parse_font(raw))
|
||||
t('caption', family=['sans-serif'])
|
||||
t('serif', family=['serif'])
|
||||
t('12pt/14pt sans-serif', size='12pt', height='14pt', family=['sans-serif'])
|
||||
t('80% sans-serif', size='80%', family=['sans-serif'])
|
||||
t('x-large/110% "new century schoolbook", serif', size='x-large', height='110%', family=['new century schoolbook', 'serif'])
|
||||
t('bold italic large Palatino, serif', weight='bold', style='italic', size='large', family=['Palatino', 'serif'])
|
||||
t('normal small-caps 120%/120% fantasy', style='normal', variant='small-caps', size='120%', height='120%', family=['fantasy'])
|
||||
t('condensed oblique 12pt Helvetica Neue, serif', stretch='condensed', style='oblique', size='12pt', family=['Helvetica Neue', 'serif'])
|
||||
t('300 italic 1.3em/1.7em FB Armada, sans-serif', weight='300', style='italic', size='1.3em', height='1.7em', family=['FB Armada', 'sans-serif'])
|
||||
|
Loading…
x
Reference in New Issue
Block a user