mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Handle formatting tags in metadata when geenrating covers -- they must not be interpreted as formatting instructions
This commit is contained in:
parent
e20672cf45
commit
9963e4263e
@ -6,7 +6,7 @@ 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, random
|
import re, random, unicodedata
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from math import ceil, sqrt, cos, sin, atan2
|
from math import ceil, sqrt, cos, sin, atan2
|
||||||
@ -46,11 +46,11 @@ cprefs.defaults['subtitle_template'] = '''{series:'test($, strcat("<i>", $, "</i
|
|||||||
cprefs.defaults['footer_template'] = r'''program:
|
cprefs.defaults['footer_template'] = r'''program:
|
||||||
# Show at most two authors, on separate lines.
|
# Show at most two authors, on separate lines.
|
||||||
authors = field('authors');
|
authors = field('authors');
|
||||||
num = count(authors, ' & ');
|
num = count(authors, ' & ');
|
||||||
authors = sublist(authors, 0, 2, ' & ');
|
authors = sublist(authors, 0, 2, ' & ');
|
||||||
authors = list_re(authors, ' & ', '(.+)', '<b>\1');
|
authors = list_re(authors, ' & ', '(.+)', '<b>\1');
|
||||||
authors = re(authors, ' & ', '<br>');
|
authors = re(authors, ' & ', '<br>');
|
||||||
re(authors, '&&', '&')
|
re(authors, '&&', '&')
|
||||||
'''
|
'''
|
||||||
Prefs = namedtuple('Prefs', ' '.join(sorted(cprefs.defaults)))
|
Prefs = namedtuple('Prefs', ' '.join(sorted(cprefs.defaults)))
|
||||||
# }}}
|
# }}}
|
||||||
@ -121,8 +121,8 @@ class Block(object):
|
|||||||
self.leading = fm.leading()
|
self.leading = fm.leading()
|
||||||
self.line_spacing = fm.lineSpacing()
|
self.line_spacing = fm.lineSpacing()
|
||||||
for text in text.split('<br>') if text else ():
|
for text in text.split('<br>') if text else ():
|
||||||
text, formats = parse_text_formatting(text)
|
text, formats = parse_text_formatting(sanitize(text))
|
||||||
l = QTextLayout(text, font, img)
|
l = QTextLayout(unescape_formatting(text), font, img)
|
||||||
l.setAdditionalFormats(formats)
|
l.setAdditionalFormats(formats)
|
||||||
to = QTextOption(align)
|
to = QTextOption(align)
|
||||||
to.setWrapMode(QTextOption.WrapAtWordBoundaryOrAnywhere)
|
to.setWrapMode(QTextOption.WrapAtWordBoundaryOrAnywhere)
|
||||||
@ -181,7 +181,7 @@ class Block(object):
|
|||||||
|
|
||||||
def layout_text(prefs, img, title, subtitle, footer, max_height, style):
|
def layout_text(prefs, img, title, subtitle, footer, max_height, style):
|
||||||
width = img.width() - 2 * style.hmargin
|
width = img.width() - 2 * style.hmargin
|
||||||
title, subtitle, footer = map(normalize, (title, subtitle, footer))
|
title, subtitle, footer = title, subtitle, footer
|
||||||
title_font = QFont(prefs.title_font_family or 'Liberation Serif')
|
title_font = QFont(prefs.title_font_family or 'Liberation Serif')
|
||||||
title_font.setPixelSize(prefs.title_font_size)
|
title_font.setPixelSize(prefs.title_font_size)
|
||||||
title_block = Block(title, width, title_font, img, max_height, style.TITLE_ALIGN)
|
title_block = Block(title, width, title_font, img, max_height, style.TITLE_ALIGN)
|
||||||
@ -206,23 +206,35 @@ def layout_text(prefs, img, title, subtitle, footer, max_height, style):
|
|||||||
|
|
||||||
# Format text using templates {{{
|
# Format text using templates {{{
|
||||||
def sanitize(s):
|
def sanitize(s):
|
||||||
return clean_xml_chars(clean_ascii_chars(force_unicode(s or '')))
|
return unicodedata.normalize('NFC', clean_xml_chars(clean_ascii_chars(force_unicode(s or ''))))
|
||||||
|
|
||||||
_formatter = None
|
_formatter = None
|
||||||
_template_cache = {}
|
_template_cache = {}
|
||||||
|
|
||||||
|
def escape_formatting(val):
|
||||||
|
return val.replace('&', '&').replace('<', '<').replace('>', '>')
|
||||||
|
|
||||||
|
def unescape_formatting(val):
|
||||||
|
return val.replace('<', '<').replace('>', '>').replace('&', '&')
|
||||||
|
|
||||||
|
class Formatter(SafeFormat):
|
||||||
|
|
||||||
|
def get_value(self, orig_key, args, kwargs):
|
||||||
|
ans = SafeFormat.get_value(self, orig_key, args, kwargs)
|
||||||
|
return escape_formatting(ans)
|
||||||
|
|
||||||
def formatter():
|
def formatter():
|
||||||
global _formatter
|
global _formatter
|
||||||
if _formatter is None:
|
if _formatter is None:
|
||||||
_formatter = SafeFormat()
|
_formatter = Formatter()
|
||||||
return _formatter
|
return _formatter
|
||||||
|
|
||||||
def format_fields(mi, prefs):
|
def format_fields(mi, prefs):
|
||||||
f = formatter()
|
f = formatter()
|
||||||
def safe_format(field):
|
def safe_format(field):
|
||||||
return sanitize(f.safe_format(
|
return f.safe_format(
|
||||||
getattr(prefs, field), mi, _('Template error'), mi, template_cache=_template_cache
|
getattr(prefs, field), mi, _('Template error'), mi, template_cache=_template_cache
|
||||||
))
|
)
|
||||||
return map(safe_format, ('title_template', 'subtitle_template', 'footer_template'))
|
return map(safe_format, ('title_template', 'subtitle_template', 'footer_template'))
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
@ -240,12 +252,6 @@ def preserve_fields(obj, fields):
|
|||||||
else:
|
else:
|
||||||
setattr(obj, f, val)
|
setattr(obj, f, val)
|
||||||
|
|
||||||
def normalize(x):
|
|
||||||
if isinstance(x, unicode):
|
|
||||||
import unicodedata
|
|
||||||
x = unicodedata.normalize('NFC', x)
|
|
||||||
return x
|
|
||||||
|
|
||||||
def format_text(mi, prefs):
|
def format_text(mi, prefs):
|
||||||
with preserve_fields(mi, 'authors formatted_series_index'):
|
with preserve_fields(mi, 'authors formatted_series_index'):
|
||||||
mi.authors = [a for a in mi.authors if a != _('Unknown')]
|
mi.authors = [a for a in mi.authors if a != _('Unknown')]
|
||||||
@ -456,9 +462,12 @@ def load_styles(prefs, respect_disabled=True):
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def generate_cover(mi, prefs=None, as_qimage=False):
|
def init_environment():
|
||||||
ensure_app()
|
ensure_app()
|
||||||
load_builtin_fonts()
|
load_builtin_fonts()
|
||||||
|
|
||||||
|
def generate_cover(mi, prefs=None, as_qimage=False):
|
||||||
|
init_environment()
|
||||||
prefs = prefs or cprefs
|
prefs = prefs or cprefs
|
||||||
prefs = {k:prefs.get(k) for k in cprefs.defaults}
|
prefs = {k:prefs.get(k) for k in cprefs.defaults}
|
||||||
prefs = Prefs(**prefs)
|
prefs = Prefs(**prefs)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user