mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-08-11 09:13:57 -04:00
Use the new font family parsing code in the rest of the container infrastucture
This commit is contained in:
parent
75690f41f8
commit
6bb8a2eb7e
@ -18,6 +18,7 @@ from calibre.ebooks.css_transform_rules import all_properties
|
||||
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
|
||||
|
||||
@ -116,6 +117,9 @@ class Values(tuple):
|
||||
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
|
||||
|
||||
|
@ -12,52 +12,21 @@ from calibre import force_unicode
|
||||
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):
|
||||
@ -92,7 +61,7 @@ class FontAliasing(BaseError):
|
||||
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
|
||||
|
@ -6,10 +6,9 @@ from __future__ import (unicode_literals, division, absolute_import,
|
||||
__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 ('"', "'"):
|
||||
@ -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)
|
||||
@ -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 = {}
|
||||
@ -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):
|
||||
@ -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)
|
||||
|
@ -16,6 +16,7 @@ import regex
|
||||
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)
|
||||
@ -191,7 +192,7 @@ class StatsCollector(object):
|
||||
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':
|
||||
|
Loading…
x
Reference in New Issue
Block a user