mirror of
https://github.com/gryf/ebook-converter.git
synced 2026-01-05 18:54:11 +01:00
300 lines
9.6 KiB
Python
300 lines
9.6 KiB
Python
from collections import OrderedDict
|
|
from ebook_converter.ebooks.docx.block_styles import ( # noqa
|
|
inherit, simple_color, LINE_STYLES, simple_float, binary_property, read_shd)
|
|
|
|
|
|
__license__ = 'GPL v3'
|
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
|
|
|
# Read from XML {{{
|
|
|
|
|
|
def read_text_border(parent, dest, XPath, get):
|
|
border_color = border_style = border_width = padding = inherit
|
|
elems = XPath('./w:bdr')(parent)
|
|
if elems and elems[0].attrib:
|
|
border_color = simple_color('auto')
|
|
border_style = 'none'
|
|
border_width = 1
|
|
for elem in elems:
|
|
color = get(elem, 'w:color')
|
|
if color is not None:
|
|
border_color = simple_color(color)
|
|
style = get(elem, 'w:val')
|
|
if style is not None:
|
|
border_style = LINE_STYLES.get(style, 'solid')
|
|
space = get(elem, 'w:space')
|
|
if space is not None:
|
|
try:
|
|
padding = float(space)
|
|
except (ValueError, TypeError):
|
|
pass
|
|
sz = get(elem, 'w:sz')
|
|
if sz is not None:
|
|
# we dont care about art borders (they are only used for page borders)
|
|
try:
|
|
# A border of less than 1pt is not rendered by WebKit
|
|
border_width = min(96, max(8, float(sz))) / 8
|
|
except (ValueError, TypeError):
|
|
pass
|
|
|
|
setattr(dest, 'border_color', border_color)
|
|
setattr(dest, 'border_style', border_style)
|
|
setattr(dest, 'border_width', border_width)
|
|
setattr(dest, 'padding', padding)
|
|
|
|
|
|
def read_color(parent, dest, XPath, get):
|
|
ans = inherit
|
|
for col in XPath('./w:color[@w:val]')(parent):
|
|
val = get(col, 'w:val')
|
|
if not val:
|
|
continue
|
|
ans = simple_color(val)
|
|
setattr(dest, 'color', ans)
|
|
|
|
|
|
def convert_highlight_color(val):
|
|
return {
|
|
'darkBlue': '#000080', 'darkCyan': '#008080', 'darkGray': '#808080',
|
|
'darkGreen': '#008000', 'darkMagenta': '#800080', 'darkRed': '#800000', 'darkYellow': '#808000',
|
|
'lightGray': '#c0c0c0'}.get(val, val)
|
|
|
|
|
|
def read_highlight(parent, dest, XPath, get):
|
|
ans = inherit
|
|
for col in XPath('./w:highlight[@w:val]')(parent):
|
|
val = get(col, 'w:val')
|
|
if not val:
|
|
continue
|
|
if not val or val == 'none':
|
|
val = 'transparent'
|
|
else:
|
|
val = convert_highlight_color(val)
|
|
ans = val
|
|
setattr(dest, 'highlight', ans)
|
|
|
|
|
|
def read_lang(parent, dest, XPath, get):
|
|
ans = inherit
|
|
for col in XPath('./w:lang[@w:val]')(parent):
|
|
val = get(col, 'w:val')
|
|
if not val:
|
|
continue
|
|
try:
|
|
code = int(val, 16)
|
|
except (ValueError, TypeError):
|
|
ans = val
|
|
else:
|
|
from ebook_converter.ebooks.docx.lcid import lcid
|
|
val = lcid.get(code, None)
|
|
if val:
|
|
ans = val
|
|
setattr(dest, 'lang', ans)
|
|
|
|
|
|
def read_letter_spacing(parent, dest, XPath, get):
|
|
ans = inherit
|
|
for col in XPath('./w:spacing[@w:val]')(parent):
|
|
val = simple_float(get(col, 'w:val'), 0.05)
|
|
if val is not None:
|
|
ans = val
|
|
setattr(dest, 'letter_spacing', ans)
|
|
|
|
|
|
def read_underline(parent, dest, XPath, get):
|
|
ans = inherit
|
|
for col in XPath('./w:u[@w:val]')(parent):
|
|
val = get(col, 'w:val')
|
|
if val:
|
|
ans = val if val == 'none' else 'underline'
|
|
setattr(dest, 'text_decoration', ans)
|
|
|
|
|
|
def read_vert_align(parent, dest, XPath, get):
|
|
ans = inherit
|
|
for col in XPath('./w:vertAlign[@w:val]')(parent):
|
|
val = get(col, 'w:val')
|
|
if val and val in {'baseline', 'subscript', 'superscript'}:
|
|
ans = val
|
|
setattr(dest, 'vert_align', ans)
|
|
|
|
|
|
def read_position(parent, dest, XPath, get):
|
|
ans = inherit
|
|
for col in XPath('./w:position[@w:val]')(parent):
|
|
val = get(col, 'w:val')
|
|
try:
|
|
ans = float(val)/2.0
|
|
except Exception:
|
|
pass
|
|
setattr(dest, 'position', ans)
|
|
|
|
|
|
def read_font(parent, dest, XPath, get):
|
|
ff = inherit
|
|
for col in XPath('./w:rFonts')(parent):
|
|
val = get(col, 'w:asciiTheme')
|
|
if val:
|
|
val = '|%s|' % val
|
|
else:
|
|
val = get(col, 'w:ascii')
|
|
if val:
|
|
ff = val
|
|
setattr(dest, 'font_family', ff)
|
|
for col in XPath('./w:sz[@w:val]')(parent):
|
|
val = simple_float(get(col, 'w:val'), 0.5)
|
|
if val is not None:
|
|
setattr(dest, 'font_size', val)
|
|
return
|
|
setattr(dest, 'font_size', inherit)
|
|
|
|
|
|
def read_font_cs(parent, dest, XPath, get):
|
|
ff = inherit
|
|
for col in XPath('./w:rFonts')(parent):
|
|
val = get(col, 'w:csTheme')
|
|
if val:
|
|
val = '|%s|' % val
|
|
else:
|
|
val = get(col, 'w:cs')
|
|
if val:
|
|
ff = val
|
|
setattr(dest, 'cs_font_family', ff)
|
|
for col in XPath('./w:szCS[@w:val]')(parent):
|
|
val = simple_float(get(col, 'w:val'), 0.5)
|
|
if val is not None:
|
|
setattr(dest, 'font_size', val)
|
|
return
|
|
setattr(dest, 'cs_font_size', inherit)
|
|
|
|
# }}}
|
|
|
|
|
|
class RunStyle(object):
|
|
|
|
all_properties = {
|
|
'b', 'bCs', 'caps', 'cs', 'dstrike', 'emboss', 'i', 'iCs', 'imprint',
|
|
'rtl', 'shadow', 'smallCaps', 'strike', 'vanish', 'webHidden',
|
|
|
|
'border_color', 'border_style', 'border_width', 'padding', 'color', 'highlight', 'background_color',
|
|
'letter_spacing', 'font_size', 'text_decoration', 'vert_align', 'lang', 'font_family', 'position',
|
|
'cs_font_size', 'cs_font_family'
|
|
}
|
|
|
|
toggle_properties = {
|
|
'b', 'bCs', 'caps', 'emboss', 'i', 'iCs', 'imprint', 'shadow', 'smallCaps', 'strike', 'vanish',
|
|
}
|
|
|
|
def __init__(self, namespace, rPr=None):
|
|
self.namespace = namespace
|
|
self.linked_style = None
|
|
if rPr is None:
|
|
for p in self.all_properties:
|
|
setattr(self, p, inherit)
|
|
else:
|
|
X, g = namespace.XPath, namespace.get
|
|
for p in (
|
|
'b', 'bCs', 'caps', 'cs', 'dstrike', 'emboss', 'i', 'iCs', 'imprint', 'rtl', 'shadow',
|
|
'smallCaps', 'strike', 'vanish', 'webHidden',
|
|
):
|
|
setattr(self, p, binary_property(rPr, p, X, g))
|
|
|
|
read_font(rPr, self, X, g)
|
|
read_font_cs(rPr, self, X, g)
|
|
read_text_border(rPr, self, X, g)
|
|
read_color(rPr, self, X, g)
|
|
read_highlight(rPr, self, X, g)
|
|
read_shd(rPr, self, X, g)
|
|
read_letter_spacing(rPr, self, X, g)
|
|
read_underline(rPr, self, X, g)
|
|
read_vert_align(rPr, self, X, g)
|
|
read_position(rPr, self, X, g)
|
|
read_lang(rPr, self, X, g)
|
|
|
|
for s in X('./w:rStyle[@w:val]')(rPr):
|
|
self.linked_style = g(s, 'w:val')
|
|
|
|
self._css = None
|
|
|
|
def update(self, other):
|
|
for prop in self.all_properties:
|
|
nval = getattr(other, prop)
|
|
if nval is not inherit:
|
|
setattr(self, prop, nval)
|
|
if other.linked_style is not None:
|
|
self.linked_style = other.linked_style
|
|
|
|
def resolve_based_on(self, parent):
|
|
for p in self.all_properties:
|
|
val = getattr(self, p)
|
|
if val is inherit:
|
|
setattr(self, p, getattr(parent, p))
|
|
|
|
def get_border_css(self, ans):
|
|
for x in ('color', 'style', 'width'):
|
|
val = getattr(self, 'border_'+x)
|
|
if x == 'width' and val is not inherit:
|
|
val = '%.3gpt' % val
|
|
if val is not inherit:
|
|
ans['border-%s' % x] = val
|
|
|
|
def clear_border_css(self):
|
|
for x in ('color', 'style', 'width'):
|
|
setattr(self, 'border_'+x, inherit)
|
|
|
|
@property
|
|
def css(self):
|
|
if self._css is None:
|
|
c = self._css = OrderedDict()
|
|
td = set()
|
|
if self.text_decoration is not inherit:
|
|
td.add(self.text_decoration)
|
|
if self.strike and self.strike is not inherit:
|
|
td.add('line-through')
|
|
if self.dstrike and self.dstrike is not inherit:
|
|
td.add('line-through')
|
|
if td:
|
|
c['text-decoration'] = ' '.join(td)
|
|
if self.caps is True:
|
|
c['text-transform'] = 'uppercase'
|
|
if self.i is True:
|
|
c['font-style'] = 'italic'
|
|
if self.shadow and self.shadow is not inherit:
|
|
c['text-shadow'] = '2px 2px'
|
|
if self.smallCaps is True:
|
|
c['font-variant'] = 'small-caps'
|
|
if self.vanish is True or self.webHidden is True:
|
|
c['display'] = 'none'
|
|
|
|
self.get_border_css(c)
|
|
if self.padding is not inherit:
|
|
c['padding'] = '%.3gpt' % self.padding
|
|
|
|
for x in ('color', 'background_color'):
|
|
val = getattr(self, x)
|
|
if val is not inherit:
|
|
c[x.replace('_', '-')] = val
|
|
|
|
for x in ('letter_spacing', 'font_size'):
|
|
val = getattr(self, x)
|
|
if val is not inherit:
|
|
c[x.replace('_', '-')] = '%.3gpt' % val
|
|
|
|
if self.position is not inherit:
|
|
c['vertical-align'] = '%.3gpt' % self.position
|
|
|
|
if self.highlight is not inherit and self.highlight != 'transparent':
|
|
c['background-color'] = self.highlight
|
|
|
|
if self.b:
|
|
c['font-weight'] = 'bold'
|
|
|
|
if self.font_family is not inherit:
|
|
c['font-family'] = self.font_family
|
|
|
|
return self._css
|
|
|
|
def same_border(self, other):
|
|
return self.get_border_css({}) == other.get_border_css({})
|