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.base import OEB_STYLES, XHTML
|
||||||
from calibre.ebooks.oeb.normalize_css import normalizers, DEFAULTS
|
from calibre.ebooks.oeb.normalize_css import normalizers, DEFAULTS
|
||||||
from calibre.ebooks.oeb.stylizer import media_ok, INHERITED
|
from calibre.ebooks.oeb.stylizer import media_ok, INHERITED
|
||||||
|
from tinycss.fonts3 import serialize_font_family, parse_font_family
|
||||||
|
|
||||||
_html_css_stylesheet = None
|
_html_css_stylesheet = None
|
||||||
|
|
||||||
@ -116,6 +117,9 @@ class Values(tuple):
|
|||||||
def normalize_style_declaration(decl, sheet_name):
|
def normalize_style_declaration(decl, sheet_name):
|
||||||
ans = {}
|
ans = {}
|
||||||
for prop in iterdeclaration(decl):
|
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)
|
ans[prop.name] = Values(prop.propertyValue, sheet_name, prop.priority)
|
||||||
return ans
|
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.base import OEB_DOCS, OEB_STYLES
|
||||||
from calibre.ebooks.oeb.polish.check.base import BaseError, WARN
|
from calibre.ebooks.oeb.polish.check.base import BaseError, WARN
|
||||||
from calibre.ebooks.oeb.polish.container import OEB_FONTS
|
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.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 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):
|
class InvalidFont(BaseError):
|
||||||
|
|
||||||
HELP = _('This font could not be processed. It most likely will'
|
HELP = _('This font could not be processed. It most likely will'
|
||||||
' not work in an ebook reader, either')
|
' 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):
|
def fix_sheet(sheet, css_name, font_name):
|
||||||
changed = False
|
changed = False
|
||||||
for rule in sheet.cssRules:
|
for rule in sheet.cssRules:
|
||||||
if rule.type in (CSSRule.FONT_FACE_RULE, CSSRule.STYLE_RULE):
|
if rule.type in (CSSRule.FONT_FACE_RULE, CSSRule.STYLE_RULE):
|
||||||
if fix_declaration(rule.style, css_name, font_name):
|
changed = change_font_in_declaration(rule.style, css_name, font_name) or changed
|
||||||
changed = True
|
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
class FontAliasing(BaseError):
|
class FontAliasing(BaseError):
|
||||||
@ -92,7 +61,7 @@ class FontAliasing(BaseError):
|
|||||||
changed = True
|
changed = True
|
||||||
for elem in container.parsed(name).xpath('//*[@style and contains(@style, "font-family")]'):
|
for elem in container.parsed(name).xpath('//*[@style and contains(@style, "font-family")]'):
|
||||||
style = container.parse_css(elem.get('style'), is_declaration=True)
|
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', ' '))
|
elem.set('style', force_unicode(style.cssText, 'utf-8').replace('\n', ' '))
|
||||||
container.dirty(name)
|
container.dirty(name)
|
||||||
changed = True
|
changed = True
|
||||||
|
@ -6,10 +6,9 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
__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.polish.container import OEB_STYLES, OEB_DOCS
|
||||||
from calibre.ebooks.oeb.normalize_css import normalize_font
|
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):
|
def unquote(x):
|
||||||
if x and len(x) > 1 and x[0] == x[-1] and x[0] in ('"', "'"):
|
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]
|
font_families = [unquote(x) for x in f]
|
||||||
f = style.getProperty('font-family')
|
f = style.getProperty('font-family')
|
||||||
if f is not None:
|
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:
|
for f in font_families:
|
||||||
families[f] = families.get(f, False)
|
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:
|
elif rule.type == rule.FONT_FACE_RULE:
|
||||||
ff = rule.style.getProperty('font-family')
|
ff = rule.style.getProperty('font-family')
|
||||||
if ff is not None:
|
if ff is not None:
|
||||||
for f in ff.propertyValue:
|
for f in parse_font_family(ff.propertyValue.cssText):
|
||||||
families[f.value] = True
|
families[f] = True
|
||||||
|
|
||||||
def font_family_data(container):
|
def font_family_data(container):
|
||||||
families = {}
|
families = {}
|
||||||
@ -58,43 +57,30 @@ def font_family_data(container):
|
|||||||
font_family_data_from_declaration(style, families)
|
font_family_data_from_declaration(style, families)
|
||||||
return 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):
|
def change_font_in_declaration(style, old_name, new_name=None):
|
||||||
changed = False
|
changed = False
|
||||||
for x in ('font', 'font-family'):
|
ff = style.getProperty('font-family')
|
||||||
prop = style.getProperty(x)
|
if ff is not None:
|
||||||
if prop is not None:
|
fams = parse_font_family(ff.propertyValue.cssText)
|
||||||
changed |= change_font_family_in_property(style, prop, old_name, new_name)
|
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
|
return changed
|
||||||
|
|
||||||
def remove_embedded_font(container, sheet, rule, sheet_name):
|
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:
|
elif rule.type == rule.FONT_FACE_RULE:
|
||||||
ff = rule.style.getProperty('font-family')
|
ff = rule.style.getProperty('font-family')
|
||||||
if ff is not None:
|
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:
|
if old_name in families:
|
||||||
changed = True
|
changed = True
|
||||||
removals.append(rule)
|
removals.append(rule)
|
||||||
|
@ -16,6 +16,7 @@ import regex
|
|||||||
from calibre.ebooks.oeb.base import XHTML
|
from calibre.ebooks.oeb.base import XHTML
|
||||||
from calibre.ebooks.oeb.polish.cascade import iterrules, resolve_styles, iterdeclaration
|
from calibre.ebooks.oeb.polish.cascade import iterrules, resolve_styles, iterdeclaration
|
||||||
from calibre.utils.icu import ord_string, safe_chr
|
from calibre.utils.icu import ord_string, safe_chr
|
||||||
|
from tinycss.fonts3 import parse_font_family
|
||||||
|
|
||||||
def normalize_font_properties(font):
|
def normalize_font_properties(font):
|
||||||
w = font.get('font-weight', None)
|
w = font.get('font-weight', None)
|
||||||
@ -191,7 +192,7 @@ class StatsCollector(object):
|
|||||||
cssdict = {}
|
cssdict = {}
|
||||||
for prop in iterdeclaration(rule.style):
|
for prop in iterdeclaration(rule.style):
|
||||||
if prop.name == 'font-family':
|
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-'):
|
elif prop.name.startswith('font-'):
|
||||||
cssdict[prop.name] = prop.propertyValue[0].value
|
cssdict[prop.name] = prop.propertyValue[0].value
|
||||||
elif prop.name == 'src':
|
elif prop.name == 'src':
|
||||||
|
Loading…
x
Reference in New Issue
Block a user