mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
DOCX: Improve style cascading
1) Add cascading for colors 2) Remove unnecessary text-decoration:none entries 3) Only cascade font properties if they are the same for all runs in a block. 4) Set sensible defaults for cascaded properties that are never specified.
This commit is contained in:
parent
cc81908994
commit
9961ada770
@ -300,7 +300,7 @@ class ParagraphStyle(object):
|
||||
|
||||
# Misc.
|
||||
'text_indent', 'text_align', 'line_height', 'direction', 'background_color',
|
||||
'numbering', 'font_family', 'font_size', 'frame',
|
||||
'numbering', 'font_family', 'font_size', 'color', 'frame',
|
||||
)
|
||||
|
||||
def __init__(self, pPr=None):
|
||||
@ -324,7 +324,7 @@ class ParagraphStyle(object):
|
||||
for s in XPath('./w:pStyle[@w:val]')(pPr):
|
||||
self.linked_style = get(s, 'w:val')
|
||||
|
||||
self.font_family = self.font_size = inherit
|
||||
self.font_family = self.font_size = self.color = inherit
|
||||
|
||||
self._css = None
|
||||
|
||||
@ -368,7 +368,7 @@ class ParagraphStyle(object):
|
||||
if self.line_height not in {inherit, '1'}:
|
||||
c['line-height'] = self.line_height
|
||||
|
||||
for x in ('text_indent', 'text_align', 'background_color', 'font_family', 'font_size'):
|
||||
for x in ('text_indent', 'text_align', 'background_color', 'font_family', 'font_size', 'color'):
|
||||
val = getattr(self, x)
|
||||
if val is not inherit:
|
||||
if x == 'font_size':
|
||||
|
@ -317,51 +317,63 @@ class Styles(object):
|
||||
def cascade(self, layers):
|
||||
self.body_font_family = 'serif'
|
||||
self.body_font_size = '10pt'
|
||||
self.body_color = 'black'
|
||||
|
||||
def promote_property(char_styles, block_style, prop):
|
||||
vals = {getattr(s, prop) for s in char_styles}
|
||||
if len(vals) == 1:
|
||||
# All the character styles have the same value
|
||||
for s in char_styles:
|
||||
setattr(s, prop, inherit)
|
||||
setattr(block_style, prop, next(iter(vals)))
|
||||
|
||||
for p, runs in layers.iteritems():
|
||||
has_links = '1' in {r.get('is-link', None) for r in runs}
|
||||
char_styles = [self.resolve_run(r) for r in runs]
|
||||
block_style = self.resolve_paragraph(p)
|
||||
c = Counter()
|
||||
for prop in ('font_family', 'font_size', 'color'):
|
||||
if has_links and prop == 'color':
|
||||
# We cannot promote color as browser rendering engines will
|
||||
# override the link color setting it to blue, unless the
|
||||
# color is specified on the link element itself
|
||||
continue
|
||||
promote_property(char_styles, block_style, prop)
|
||||
for s in char_styles:
|
||||
if s.font_family is not inherit:
|
||||
c[s.font_family] += 1
|
||||
if s.text_decoration == 'none':
|
||||
# The default text decoration is 'none'
|
||||
s.text_decoration = inherit
|
||||
|
||||
def promote_most_common(block_styles, prop, default):
|
||||
c = Counter()
|
||||
for s in block_styles:
|
||||
val = getattr(s, prop)
|
||||
if val is not inherit:
|
||||
c[val] += 1
|
||||
val = None
|
||||
if c:
|
||||
family = c.most_common(1)[0][0]
|
||||
block_style.font_family = family
|
||||
for s in char_styles:
|
||||
if s.font_family == family:
|
||||
s.font_family = inherit
|
||||
val = c.most_common(1)[0][0]
|
||||
for s in block_styles:
|
||||
oval = getattr(s, prop)
|
||||
if oval is inherit:
|
||||
if default != val:
|
||||
setattr(s, prop, default)
|
||||
elif oval == val:
|
||||
setattr(s, prop, inherit)
|
||||
return val
|
||||
|
||||
sizes = [s.font_size for s in char_styles if s.font_size is not inherit]
|
||||
if sizes:
|
||||
sz = block_style.font_size = sizes[0]
|
||||
for s in char_styles:
|
||||
if s.font_size == sz:
|
||||
s.font_size = inherit
|
||||
block_styles = tuple(self.resolve_paragraph(p) for p in layers)
|
||||
|
||||
block_styles = [self.resolve_paragraph(p) for p in layers]
|
||||
c = Counter()
|
||||
for s in block_styles:
|
||||
if s.font_family is not inherit:
|
||||
c[s.font_family] += 1
|
||||
ff = promote_most_common(block_styles, 'font_family', self.body_font_family)
|
||||
if ff is not None:
|
||||
self.body_font_family = ff
|
||||
|
||||
if c:
|
||||
self.body_font_family = family = c.most_common(1)[0][0]
|
||||
for s in block_styles:
|
||||
if s.font_family == family:
|
||||
s.font_family = inherit
|
||||
fs = promote_most_common(block_styles, 'font_size', int(self.body_font_size[:2]))
|
||||
if fs is not None:
|
||||
self.body_font_size = '%.3gpt' % fs
|
||||
|
||||
c = Counter()
|
||||
for s in block_styles:
|
||||
if s.font_size is not inherit:
|
||||
c[s.font_size] += 1
|
||||
|
||||
if c:
|
||||
sz = c.most_common(1)[0][0]
|
||||
for s in block_styles:
|
||||
if s.font_size == sz:
|
||||
s.font_size = inherit
|
||||
self.body_font_size = '%.3gpt' % sz
|
||||
color = promote_most_common(block_styles, 'color', self.body_color)
|
||||
if color is not None:
|
||||
self.body_color = color
|
||||
|
||||
def resolve_numbering(self, numbering):
|
||||
# When a numPr element appears inside a paragraph style, the lvl info
|
||||
@ -403,7 +415,7 @@ class Styles(object):
|
||||
ef = self.fonts.embed_fonts(dest_dir, docx)
|
||||
prefix = textwrap.dedent(
|
||||
'''\
|
||||
body { font-family: %s; font-size: %s }
|
||||
body { font-family: %s; font-size: %s; color: %s }
|
||||
|
||||
ul, ol, p { margin: 0; padding: 0 }
|
||||
|
||||
@ -419,7 +431,7 @@ class Styles(object):
|
||||
|
||||
dl.notes dd:last-of-type { page-break-after: avoid }
|
||||
|
||||
''') % (self.body_font_family, self.body_font_size)
|
||||
''') % (self.body_font_family, self.body_font_size, self.body_color)
|
||||
if ef:
|
||||
prefix = ef + '\n' + prefix
|
||||
|
||||
@ -430,3 +442,4 @@ class Styles(object):
|
||||
ans.append('.%s {\n%s\n}\n' % (cls, b.rstrip(';')))
|
||||
return prefix + '\n' + '\n'.join(ans)
|
||||
|
||||
|
||||
|
@ -325,6 +325,7 @@ class Convert(object):
|
||||
try:
|
||||
hl = hl_xpath(x)[0]
|
||||
self.link_map[hl].append(span)
|
||||
x.set('is-link', '1')
|
||||
except IndexError:
|
||||
current_hyperlink = None
|
||||
dest.append(span)
|
||||
|
Loading…
x
Reference in New Issue
Block a user