mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-31 14:33:54 -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.
|
# Misc.
|
||||||
'text_indent', 'text_align', 'line_height', 'direction', 'background_color',
|
'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):
|
def __init__(self, pPr=None):
|
||||||
@ -324,7 +324,7 @@ class ParagraphStyle(object):
|
|||||||
for s in XPath('./w:pStyle[@w:val]')(pPr):
|
for s in XPath('./w:pStyle[@w:val]')(pPr):
|
||||||
self.linked_style = get(s, 'w:val')
|
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
|
self._css = None
|
||||||
|
|
||||||
@ -368,7 +368,7 @@ class ParagraphStyle(object):
|
|||||||
if self.line_height not in {inherit, '1'}:
|
if self.line_height not in {inherit, '1'}:
|
||||||
c['line-height'] = self.line_height
|
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)
|
val = getattr(self, x)
|
||||||
if val is not inherit:
|
if val is not inherit:
|
||||||
if x == 'font_size':
|
if x == 'font_size':
|
||||||
|
@ -317,51 +317,63 @@ class Styles(object):
|
|||||||
def cascade(self, layers):
|
def cascade(self, layers):
|
||||||
self.body_font_family = 'serif'
|
self.body_font_family = 'serif'
|
||||||
self.body_font_size = '10pt'
|
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():
|
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]
|
char_styles = [self.resolve_run(r) for r in runs]
|
||||||
block_style = self.resolve_paragraph(p)
|
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:
|
for s in char_styles:
|
||||||
if s.font_family is not inherit:
|
if s.text_decoration == 'none':
|
||||||
c[s.font_family] += 1
|
# 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:
|
if c:
|
||||||
family = c.most_common(1)[0][0]
|
val = c.most_common(1)[0][0]
|
||||||
block_style.font_family = family
|
for s in block_styles:
|
||||||
for s in char_styles:
|
oval = getattr(s, prop)
|
||||||
if s.font_family == family:
|
if oval is inherit:
|
||||||
s.font_family = 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]
|
block_styles = tuple(self.resolve_paragraph(p) for p in layers)
|
||||||
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 = [self.resolve_paragraph(p) for p in layers]
|
ff = promote_most_common(block_styles, 'font_family', self.body_font_family)
|
||||||
c = Counter()
|
if ff is not None:
|
||||||
for s in block_styles:
|
self.body_font_family = ff
|
||||||
if s.font_family is not inherit:
|
|
||||||
c[s.font_family] += 1
|
|
||||||
|
|
||||||
if c:
|
fs = promote_most_common(block_styles, 'font_size', int(self.body_font_size[:2]))
|
||||||
self.body_font_family = family = c.most_common(1)[0][0]
|
if fs is not None:
|
||||||
for s in block_styles:
|
self.body_font_size = '%.3gpt' % fs
|
||||||
if s.font_family == family:
|
|
||||||
s.font_family = inherit
|
|
||||||
|
|
||||||
c = Counter()
|
color = promote_most_common(block_styles, 'color', self.body_color)
|
||||||
for s in block_styles:
|
if color is not None:
|
||||||
if s.font_size is not inherit:
|
self.body_color = color
|
||||||
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
|
|
||||||
|
|
||||||
def resolve_numbering(self, numbering):
|
def resolve_numbering(self, numbering):
|
||||||
# When a numPr element appears inside a paragraph style, the lvl info
|
# 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)
|
ef = self.fonts.embed_fonts(dest_dir, docx)
|
||||||
prefix = textwrap.dedent(
|
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 }
|
ul, ol, p { margin: 0; padding: 0 }
|
||||||
|
|
||||||
@ -419,7 +431,7 @@ class Styles(object):
|
|||||||
|
|
||||||
dl.notes dd:last-of-type { page-break-after: avoid }
|
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:
|
if ef:
|
||||||
prefix = ef + '\n' + prefix
|
prefix = ef + '\n' + prefix
|
||||||
|
|
||||||
@ -430,3 +442,4 @@ class Styles(object):
|
|||||||
ans.append('.%s {\n%s\n}\n' % (cls, b.rstrip(';')))
|
ans.append('.%s {\n%s\n}\n' % (cls, b.rstrip(';')))
|
||||||
return prefix + '\n' + '\n'.join(ans)
|
return prefix + '\n' + '\n'.join(ans)
|
||||||
|
|
||||||
|
|
||||||
|
@ -325,6 +325,7 @@ class Convert(object):
|
|||||||
try:
|
try:
|
||||||
hl = hl_xpath(x)[0]
|
hl = hl_xpath(x)[0]
|
||||||
self.link_map[hl].append(span)
|
self.link_map[hl].append(span)
|
||||||
|
x.set('is-link', '1')
|
||||||
except IndexError:
|
except IndexError:
|
||||||
current_hyperlink = None
|
current_hyperlink = None
|
||||||
dest.append(span)
|
dest.append(span)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user