Fix subsetting of fonts whose names start with "and" not working

Caused by a bug in the font-family parsing routines of cssutils,
workaround by quoting the family name before passing it to cssutils.
This commit is contained in:
Kovid Goyal 2016-12-05 17:07:11 +05:30
parent 9a60dd0da1
commit 353e7c2b36

View File

@ -16,6 +16,7 @@ from .tokenizer import tokenize_grouped
def parse_font_family_tokens(tokens): def parse_font_family_tokens(tokens):
families = [] families = []
current_family = '' current_family = ''
def commit(): def commit():
val = current_family.strip() val = current_family.strip()
if val: if val:
@ -37,19 +38,23 @@ def parse_font_family_tokens(tokens):
commit() commit()
return families return families
def parse_font_family(css_string): def parse_font_family(css_string):
return parse_font_family_tokens(tokenize_grouped(type('')(css_string).strip())) return parse_font_family_tokens(tokenize_grouped(type('')(css_string).strip()))
def serialize_single_font_family(x): def serialize_single_font_family(x):
xl = x.lower() xl = x.lower()
if xl in GENERIC_FAMILIES: if xl in GENERIC_FAMILIES:
if xl == 'sansserif': if xl == 'sansserif':
xl = 'sans-serif' xl = 'sans-serif'
return xl return xl
if SIMPLE_NAME_PAT.match(x) is not None: if SIMPLE_NAME_PAT.match(x) is not None and not x.lower().startswith('and'):
# cssutils dies if a font name starts with and
return x return x
return '"%s"' % x.replace('"', r'\"') return '"%s"' % x.replace('"', r'\"')
def serialize_font_family(families): def serialize_font_family(families):
return ', '.join(map(serialize_single_font_family, families)) return ', '.join(map(serialize_single_font_family, families))
@ -65,6 +70,7 @@ LEGACY_FONT_SPEC = frozenset('caption icon menu message-box small-caption status
GENERIC_FAMILIES = frozenset('serif sans-serif sansserif cursive fantasy monospace'.split()) GENERIC_FAMILIES = frozenset('serif sans-serif sansserif cursive fantasy monospace'.split())
SIMPLE_NAME_PAT = re.compile(r'[a-zA-Z][a-zA-Z0-9_-]*$') SIMPLE_NAME_PAT = re.compile(r'[a-zA-Z][a-zA-Z0-9_-]*$')
def serialize_font(font_dict): def serialize_font(font_dict):
ans = [] ans = []
for x in 'style variant weight stretch'.split(): for x in 'style variant weight stretch'.split():
@ -83,6 +89,7 @@ def serialize_font(font_dict):
ans.append(serialize_font_family(val)) ans.append(serialize_font_family(val))
return ' '.join(ans) return ' '.join(ans)
def parse_font(css_string): def parse_font(css_string):
# See https://www.w3.org/TR/css-fonts-3/#font-prop # See https://www.w3.org/TR/css-fonts-3/#font-prop
style = variant = weight = stretch = size = height = None style = variant = weight = stretch = size = height = None
@ -178,6 +185,7 @@ def parse_font(css_string):
ans['font-family'] = families ans['font-family'] = families
return ans return ans
class FontFaceRule(object): class FontFaceRule(object):
at_keyword = '@font-face' at_keyword = '@font-face'
@ -192,6 +200,7 @@ class FontFaceRule(object):
return ('<{0.__class__.__name__} at {0.line}:{0.column}>' return ('<{0.__class__.__name__} at {0.line}:{0.column}>'
.format(self)) .format(self))
class CSSFonts3Parser(CSS21Parser): class CSSFonts3Parser(CSS21Parser):
''' Parse @font-face rules from the CSS 3 fonts module ''' ''' Parse @font-face rules from the CSS 3 fonts module '''
@ -214,4 +223,3 @@ class CSSFonts3Parser(CSS21Parser):
declarations, decerrors = self.parse_declaration_list(rule.body) declarations, decerrors = self.parse_declaration_list(rule.body)
errors.extend(decerrors) errors.extend(decerrors)
return FontFaceRule(declarations, rule.line, rule.column) return FontFaceRule(declarations, rule.line, rule.column)