Skip to content

Commit

Permalink
Use the new font family parsing code in the rest of the container inf…
Browse files Browse the repository at this point in the history
…rastucture
  • Loading branch information
kovidgoyal committed Jun 30, 2016
1 parent 75690f4 commit 6bb8a2e
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 77 deletions.
4 changes: 4 additions & 0 deletions src/calibre/ebooks/oeb/polish/cascade.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from calibre.ebooks.oeb.base import OEB_STYLES, XHTML
from calibre.ebooks.oeb.normalize_css import normalizers, DEFAULTS
from calibre.ebooks.oeb.stylizer import media_ok, INHERITED
from tinycss.fonts3 import serialize_font_family, parse_font_family

_html_css_stylesheet = None

Expand Down Expand Up @@ -116,6 +117,9 @@ def cssText(self):
def normalize_style_declaration(decl, sheet_name):
ans = {}
for prop in iterdeclaration(decl):
if prop.name == 'font-family':
# Needed because of https://bitbucket.org/cthedot/cssutils/issues/66/incorrect-handling-of-spaces-in-font
prop.propertyValue.cssText = serialize_font_family(parse_font_family(prop.propertyValue.cssText))
ans[prop.name] = Values(prop.propertyValue, sheet_name, prop.priority)
return ans

Expand Down
39 changes: 4 additions & 35 deletions src/calibre/ebooks/oeb/polish/check/fonts.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,52 +12,21 @@
from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES
from calibre.ebooks.oeb.polish.check.base import BaseError, WARN
from calibre.ebooks.oeb.polish.container import OEB_FONTS
from calibre.ebooks.oeb.polish.fonts import change_font_family_value
from calibre.ebooks.oeb.polish.pretty import pretty_script_or_style
from calibre.ebooks.oeb.polish.fonts import change_font_in_declaration
from calibre.utils.fonts.utils import get_all_font_names
from tinycss.fonts3 import parse_font_family, parse_font, serialize_font_family, serialize_font
from tinycss.fonts3 import parse_font_family

class InvalidFont(BaseError):

HELP = _('This font could not be processed. It most likely will'
' not work in an ebook reader, either')

def fix_property(prop, css_name, font_name):
changed = False
ff = prop.propertyValue
for i in xrange(ff.length):
val = ff.item(i)
if hasattr(val.value, 'lower') and val.value.lower() == css_name.lower():
change_font_family_value(val, font_name)
changed = True
return changed

def fix_declaration(style, css_name, font_name):
changed = False
ff = style.getProperty('font-family')
if ff is not None:
fams = parse_font_family(ff.propertyValue.cssText)
nfams = [font_name if x == css_name else x for x in fams]
if fams != nfams:
ff.propertyValue.cssText = serialize_font_family(nfams)
changed = True
ff = style.getProperty('font')
if ff is not None:
props = parse_font(ff.propertyValue.cssText)
fams = props.get('font-family') or []
nfams = [font_name if x == css_name else x for x in fams]
if fams != nfams:
props['font-family'] = nfams
ff.propertyValue.cssText = serialize_font(props)
changed = True
return changed

def fix_sheet(sheet, css_name, font_name):
changed = False
for rule in sheet.cssRules:
if rule.type in (CSSRule.FONT_FACE_RULE, CSSRule.STYLE_RULE):
if fix_declaration(rule.style, css_name, font_name):
changed = True
changed = change_font_in_declaration(rule.style, css_name, font_name) or changed
return changed

class FontAliasing(BaseError):
Expand Down Expand Up @@ -92,7 +61,7 @@ def __call__(self, container):
changed = True
for elem in container.parsed(name).xpath('//*[@style and contains(@style, "font-family")]'):
style = container.parse_css(elem.get('style'), is_declaration=True)
if fix_declaration(style, self.css_name, self.font_name):
if change_font_in_declaration(style, self.css_name, self.font_name):
elem.set('style', force_unicode(style.cssText, 'utf-8').replace('\n', ' '))
container.dirty(name)
changed = True
Expand Down
68 changes: 27 additions & 41 deletions src/calibre/ebooks/oeb/polish/fonts.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
__license__ = 'GPL v3'
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'

import re

from calibre.ebooks.oeb.polish.container import OEB_STYLES, OEB_DOCS
from calibre.ebooks.oeb.normalize_css import normalize_font
from tinycss.fonts3 import parse_font_family, parse_font, serialize_font_family, serialize_font

def unquote(x):
if x and len(x) > 1 and x[0] == x[-1] and x[0] in ('"', "'"):
Expand All @@ -25,7 +24,7 @@ def font_family_data_from_declaration(style, families):
font_families = [unquote(x) for x in f]
f = style.getProperty('font-family')
if f is not None:
font_families = [x.value for x in f.propertyValue]
font_families = parse_font_family(f.propertyValue.cssText)

for f in font_families:
families[f] = families.get(f, False)
Expand All @@ -37,8 +36,8 @@ def font_family_data_from_sheet(sheet, families):
elif rule.type == rule.FONT_FACE_RULE:
ff = rule.style.getProperty('font-family')
if ff is not None:
for f in ff.propertyValue:
families[f.value] = True
for f in parse_font_family(ff.propertyValue.cssText):
families[f] = True

def font_family_data(container):
families = {}
Expand All @@ -58,43 +57,30 @@ def font_family_data(container):
font_family_data_from_declaration(style, families)
return families

def change_font_family_value(cssvalue, new_name):
# If cssvalue.type == 'IDENT' cssutils will not serialize the font
# name properly (it will not enclose it in quotes). So we
# use the following hack (setting an internal property of the
# Value class)
cssvalue.value = new_name
cssvalue._type = 'STRING'

def change_font_family_in_property(style, prop, old_name, new_name=None):
changed = False
families = {x.value for x in prop.propertyValue}
_dummy_family = 'd7d81cf1-1c8c-4993-b788-e1ab596c0f1f'
if new_name and new_name in families:
new_name = None # new name already exists in this property, so simply remove old_name
for val in prop.propertyValue:
if val.value == old_name:
change_font_family_value(val, new_name or _dummy_family)
changed = True
if changed and not new_name:
# Remove dummy family, cssutils provides no clean way to do this, so we
# roundtrip via cssText
pat = re.compile(r'''['"]{0,1}%s['"]{0,1}\s*,{0,1}''' % _dummy_family)
repl = pat.sub('', prop.propertyValue.cssText).strip().rstrip(',').strip()
if repl:
prop.propertyValue.cssText = repl
if prop.name == 'font' and not prop.validate():
style.removeProperty(prop.name) # no families left in font:
else:
style.removeProperty(prop.name)
return changed

def change_font_in_declaration(style, old_name, new_name=None):
changed = False
for x in ('font', 'font-family'):
prop = style.getProperty(x)
if prop is not None:
changed |= change_font_family_in_property(style, prop, old_name, new_name)
ff = style.getProperty('font-family')
if ff is not None:
fams = parse_font_family(ff.propertyValue.cssText)
nfams = filter(None, [new_name if x == old_name else x for x in fams])
if fams != nfams:
if nfams:
ff.propertyValue.cssText = serialize_font_family(nfams)
else:
style.removeProperty(ff.name)
changed = True
ff = style.getProperty('font')
if ff is not None:
props = parse_font(ff.propertyValue.cssText)
fams = props.get('font-family') or []
nfams = filter(None, [new_name if x == old_name else x for x in fams])
if fams != nfams:
props['font-family'] = nfams
if nfams:
ff.propertyValue.cssText = serialize_font(props)
else:
style.removeProperty(ff.name)
changed = True
return changed

def remove_embedded_font(container, sheet, rule, sheet_name):
Expand All @@ -118,7 +104,7 @@ def change_font_in_sheet(container, sheet, old_name, new_name, sheet_name):
elif rule.type == rule.FONT_FACE_RULE:
ff = rule.style.getProperty('font-family')
if ff is not None:
families = {x.value for x in ff.propertyValue}
families = {x for x in parse_font_family(ff.propertyValue.cssText)}
if old_name in families:
changed = True
removals.append(rule)
Expand Down
3 changes: 2 additions & 1 deletion src/calibre/ebooks/oeb/polish/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from calibre.ebooks.oeb.base import XHTML
from calibre.ebooks.oeb.polish.cascade import iterrules, resolve_styles, iterdeclaration
from calibre.utils.icu import ord_string, safe_chr
from tinycss.fonts3 import parse_font_family

def normalize_font_properties(font):
w = font.get('font-weight', None)
Expand Down Expand Up @@ -191,7 +192,7 @@ def collect_font_face_rules(self, container, processed, spine_name, sheet, sheet
cssdict = {}
for prop in iterdeclaration(rule.style):
if prop.name == 'font-family':
cssdict['font-family'] = [icu_lower(x.value) for x in prop.propertyValue]
cssdict['font-family'] = [icu_lower(x) for x in parse_font_family(prop.propertyValue.cssText)]
elif prop.name.startswith('font-'):
cssdict[prop.name] = prop.propertyValue[0].value
elif prop.name == 'src':
Expand Down

0 comments on commit 6bb8a2e

Please sign in to comment.