MOBI Output: Fix handling of white-space:pre-wrap

MOBI Output: Fix text marked with white-space:pre-wrap causing the
Kindle to break lines at arbitrary points inside words. Fixes #1240235 [Private bug](https://bugs.launchpad.net/calibre/+bug/1240235)
This commit is contained in:
Kovid Goyal 2013-10-22 20:46:24 +05:30
parent c9a88a1a80
commit f0e64e56a2

View File

@ -75,6 +75,7 @@ class FormatState(object):
self.strikethrough = False self.strikethrough = False
self.underline = False self.underline = False
self.preserve = False self.preserve = False
self.pre_wrap = False
self.family = 'serif' self.family = 'serif'
self.bgcolor = 'transparent' self.bgcolor = 'transparent'
self.fgcolor = 'black' self.fgcolor = 'black'
@ -88,6 +89,7 @@ class FormatState(object):
and self.bold == other.bold \ and self.bold == other.bold \
and self.href == other.href \ and self.href == other.href \
and self.preserve == other.preserve \ and self.preserve == other.preserve \
and self.pre_wrap == other.pre_wrap \
and self.family == other.family \ and self.family == other.family \
and self.bgcolor == other.bgcolor \ and self.bgcolor == other.bgcolor \
and self.fgcolor == other.fgcolor \ and self.fgcolor == other.fgcolor \
@ -136,8 +138,14 @@ class MobiMLizer(object):
return "%dpt" % int(round(ptsize)) return "%dpt" % int(round(ptsize))
return "%dem" % int(round(ptsize / embase)) return "%dem" % int(round(ptsize / embase))
def preize_text(self, text): def preize_text(self, text, pre_wrap=False):
text = unicode(text).replace(u' ', u'\xa0') text = unicode(text)
if pre_wrap:
# Replace n consecutive spaces with n-1 NBSP + space
text = re.sub(r' {2,}', lambda m:(u'\xa0'*(len(m.group())-1) + u' '), text)
else:
text = text.replace(u' ', u'\xa0')
text = text.replace('\r\n', '\n') text = text.replace('\r\n', '\n')
text = text.replace('\r', '\n') text = text.replace('\r', '\n')
lines = text.split('\n') lines = text.split('\n')
@ -283,7 +291,7 @@ class MobiMLizer(object):
bstate.inline = inline bstate.inline = inline
bstate.istate = istate bstate.istate = istate
inline = bstate.inline inline = bstate.inline
content = self.preize_text(text) if istate.preserve else [text] content = self.preize_text(text, pre_wrap=istate.pre_wrap) if istate.preserve or istate.pre_wrap else [text]
for item in content: for item in content:
if isinstance(item, basestring): if isinstance(item, basestring):
if len(inline) == 0: if len(inline) == 0:
@ -377,7 +385,8 @@ class MobiMLizer(object):
istate.italic = True if style['font-style'] == 'italic' else False istate.italic = True if style['font-style'] == 'italic' else False
weight = style['font-weight'] weight = style['font-weight']
istate.bold = weight in ('bold', 'bolder') or asfloat(weight) > 400 istate.bold = weight in ('bold', 'bolder') or asfloat(weight) > 400
istate.preserve = (style['white-space'] in ('pre', 'pre-wrap')) istate.preserve = style['white-space'] == 'pre'
istate.pre_wrap = style['white-space'] == 'pre-wrap'
istate.bgcolor = style['background-color'] istate.bgcolor = style['background-color']
istate.fgcolor = style['color'] istate.fgcolor = style['color']
istate.strikethrough = style.effective_text_decoration == 'line-through' istate.strikethrough = style.effective_text_decoration == 'line-through'
@ -487,7 +496,7 @@ class MobiMLizer(object):
elem.tail = u'\u201d' + t elem.tail = u'\u201d' + t
text = None text = None
if elem.text: if elem.text:
if istate.preserve: if istate.preserve or istate.pre_wrap:
text = elem.text text = elem.text
elif (len(elem) > 0 and isspace(elem.text) and hasattr(elem[0].tag, 'rpartition') and elif (len(elem) > 0 and isspace(elem.text) and hasattr(elem[0].tag, 'rpartition') and
elem[0].tag.rpartition('}')[-1] not in INLINE_TAGS): elem[0].tag.rpartition('}')[-1] not in INLINE_TAGS):
@ -545,7 +554,7 @@ class MobiMLizer(object):
self.mobimlize_elem(child, stylizer, bstate, istates) self.mobimlize_elem(child, stylizer, bstate, istates)
tail = None tail = None
if child.tail: if child.tail:
if istate.preserve: if istate.preserve or istate.pre_wrap:
tail = child.tail tail = child.tail
elif bstate.para is None and isspace(child.tail): elif bstate.para is None and isspace(child.tail):
tail = None tail = None