mirror of
https://github.com/gryf/ebook-converter.git
synced 2026-02-19 16:25:55 +01:00
Here is the first batch of modules, which are needed for converting several formats to LRF. Some of the logic has been change, more cleanups will follow.
303 lines
9.7 KiB
Python
303 lines
9.7 KiB
Python
#!/usr/bin/env python2
|
|
# vim:fileencoding=utf-8
|
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
|
|
|
__license__ = 'GPL v3'
|
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
|
|
|
from collections import OrderedDict
|
|
from ebook_converter.ebooks.docx.block_styles import ( # noqa
|
|
inherit, simple_color, LINE_STYLES, simple_float, binary_property, read_shd)
|
|
|
|
# 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({})
|