From 5c1998610f9bd34582a77873d24167b39a28d620 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Aug 2013 17:34:42 +0530 Subject: [PATCH 1/6] Conversion: Handle shorthand CSS properties Conversion: Fix bugs in the handling of shorthand CSS properties such as font or border, which could cause some properties to not be resolved correctly during conversion. --- src/calibre/ebooks/oeb/normalize_css.py | 269 ++++++++++++++++++++++++ src/calibre/ebooks/oeb/stylizer.py | 152 +------------ 2 files changed, 280 insertions(+), 141 deletions(-) create mode 100644 src/calibre/ebooks/oeb/normalize_css.py diff --git a/src/calibre/ebooks/oeb/normalize_css.py b/src/calibre/ebooks/oeb/normalize_css.py new file mode 100644 index 0000000000..cadb58b5cb --- /dev/null +++ b/src/calibre/ebooks/oeb/normalize_css.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2013, Kovid Goyal ' + +from future_builtins import zip +from functools import wraps + +try: + from cssutils.css import PropertyValue +except ImportError: + raise RuntimeError('You need cssutils >= 0.9.9 for calibre') +from cssutils import profile as cssprofiles + +DEFAULTS = {'azimuth': 'center', 'background-attachment': 'scroll', # {{{ + 'background-color': 'transparent', 'background-image': 'none', + 'background-position': '0% 0%', 'background-repeat': 'repeat', + 'border-bottom-color': 'currentColor', 'border-bottom-style': 'none', + 'border-bottom-width': 'medium', 'border-collapse': 'separate', + 'border-left-color': 'currentColor', 'border-left-style': 'none', + 'border-left-width': 'medium', 'border-right-color': 'currentColor', + 'border-right-style': 'none', 'border-right-width': 'medium', + 'border-spacing': 0, 'border-top-color': 'currentColor', + 'border-top-style': 'none', 'border-top-width': 'medium', 'bottom': + 'auto', 'caption-side': 'top', 'clear': 'none', 'clip': 'auto', + 'color': 'black', 'content': 'normal', 'counter-increment': 'none', + 'counter-reset': 'none', 'cue-after': 'none', 'cue-before': 'none', + 'cursor': 'auto', 'direction': 'ltr', 'display': 'inline', + 'elevation': 'level', 'empty-cells': 'show', 'float': 'none', + 'font-family': 'serif', 'font-size': 'medium', 'font-style': + 'normal', 'font-variant': 'normal', 'font-weight': 'normal', + 'height': 'auto', 'left': 'auto', 'letter-spacing': 'normal', + 'line-height': 'normal', 'list-style-image': 'none', + 'list-style-position': 'outside', 'list-style-type': 'disc', + 'margin-bottom': 0, 'margin-left': 0, 'margin-right': 0, + 'margin-top': 0, 'max-height': 'none', 'max-width': 'none', + 'min-height': 0, 'min-width': 0, 'orphans': '2', + 'outline-color': 'invert', 'outline-style': 'none', + 'outline-width': 'medium', 'overflow': 'visible', 'padding-bottom': + 0, 'padding-left': 0, 'padding-right': 0, 'padding-top': 0, + 'page-break-after': 'auto', 'page-break-before': 'auto', + 'page-break-inside': 'auto', 'pause-after': 0, 'pause-before': + 0, 'pitch': 'medium', 'pitch-range': '50', 'play-during': 'auto', + 'position': 'static', 'quotes': u"'“' '”' '‘' '’'", 'richness': + '50', 'right': 'auto', 'speak': 'normal', 'speak-header': 'once', + 'speak-numeral': 'continuous', 'speak-punctuation': 'none', + 'speech-rate': 'medium', 'stress': '50', 'table-layout': 'auto', + 'text-align': 'auto', 'text-decoration': 'none', 'text-indent': + 0, 'text-transform': 'none', 'top': 'auto', 'unicode-bidi': + 'normal', 'vertical-align': 'baseline', 'visibility': 'visible', + 'voice-family': 'default', 'volume': 'medium', 'white-space': + 'normal', 'widows': '2', 'width': 'auto', 'word-spacing': 'normal', + 'z-index': 'auto'} +# }}} + +EDGES = ('top', 'right', 'bottom', 'left') + +def normalize_edge(name, cssvalue): + style = {} + if isinstance(cssvalue, PropertyValue): + primitives = [v.cssText for v in cssvalue] + else: + primitives = [cssvalue.cssText] + if len(primitives) == 1: + value, = primitives + values = (value, value, value, value) + elif len(primitives) == 2: + vert, horiz = primitives + values = (vert, horiz, vert, horiz) + elif len(primitives) == 3: + top, horiz, bottom = primitives + values = (top, horiz, bottom, horiz) + else: + values = primitives[:4] + if '-' in name: + l, _, r = name.partition('-') + for edge, value in zip(EDGES, values): + style['%s-%s-%s' % (l, edge, r)] = value + else: + for edge, value in zip(EDGES, values): + style['%s-%s' % (name, edge)] = value + return style + + +def simple_normalizer(prefix, names, check_inherit=True): + composition = tuple('%s-%s' %(prefix, n) for n in names) + @wraps(normalize_simple_composition) + def wrapper(name, cssvalue): + return normalize_simple_composition(name, cssvalue, composition, check_inherit=check_inherit) + return wrapper + + +def normalize_simple_composition(name, cssvalue, composition, check_inherit=True): + if check_inherit and cssvalue.cssText == 'inherit': + style = {k:'inherit' for k in composition} + else: + style = {k:DEFAULTS[k] for k in composition} + try: + primitives = [v.cssText for v in cssvalue] + except TypeError: + primitives = [cssvalue.cssText] + while primitives: + value = primitives.pop() + for key in composition: + if cssprofiles.validate(key, value): + style[key] = value + break + return style + +font_composition = ('font-style', 'font-variant', 'font-weight', 'font-size', 'line-height', 'font-family') + +def normalize_font(name, cssvalue): + # See https://developer.mozilla.org/en-US/docs/Web/CSS/font + composition = font_composition + val = cssvalue.cssText + if val == 'inherit': + return {k:'inherit' for k in composition} + if val in {'caption', 'icon', 'menu', 'message-box', 'small-caption', 'status-bar'}: + return {k:DEFAULTS[k] for k in composition} + try: + primitives = list(v.cssText for v in cssvalue) + except TypeError: + primitives = [cssvalue.cssText] + if len(primitives) < 2: + return {} # Mandatory to define both font size and font family + style = {k:DEFAULTS[k] for k in composition} + style['font-family'] = primitives.pop() + if len(primitives) > 1 and cssprofiles.validate('line-height', primitives[-1]) and cssprofiles.validate('font-size', primitives[-2]): + style['line-height'], style['font-size'] = primitives.pop(), primitives.pop() + else: + val = primitives.pop() + if not cssprofiles.validate('font-size', val): + return {} + style['font-size'] = val + composition = composition[:3] + while primitives: + value = primitives.pop() + for key in composition: + if cssprofiles.validate(key, value): + style[key] = value + break + return style + +def normalize_border(name, cssvalue): + style = normalizers['border-' + EDGES[0]]('border-' + EDGES[0], cssvalue) + vals = style.copy() + for edge in EDGES[1:]: + style.update({k.replace(EDGES[0], edge):v for k, v in vals.iteritems()}) + return style + +normalizers = { + 'list-style': simple_normalizer('list-style', ('type', 'position', 'image')), + 'font': normalize_font, + 'border': normalize_border, +} + +for x in ('margin', 'padding', 'border-style', 'border-width', 'border-color'): + normalizers[x] = normalize_edge + +for x in EDGES: + name = 'border-' + x + normalizers[name] = simple_normalizer(name, ('color', 'style', 'width'), check_inherit=False) + +def test_normalization(): + import unittest + from cssutils import parseStyle + + class TestNormalization(unittest.TestCase): + longMessage = True + maxDiff = None + + def test_font_normalization(self): + def font_dict(expected): + ans = {k:DEFAULTS[k] for k in font_composition} + ans.update(expected) + return ans + + for raw, expected in { + 'some_font': {}, 'none': {}, 'inherit':{k:'inherit' for k in font_composition}, + '1.2pt/1.4 A_Font': {'font-family':'A_Font', 'font-size':'1.2pt', 'line-height':'1.4'}, + 'bad font': {}, '10% serif': {'font-family':'serif', 'font-size':'10%'}, + 'bold italic large serif': {'font-family':'serif', 'font-weight':'bold', 'font-style':'italic', 'font-size':'large'}, + 'bold italic small-caps larger/normal serif': + {'font-family':'serif', 'font-weight':'bold', 'font-style':'italic', 'font-size':'larger', + 'line-height':'normal', 'font-variant':'small-caps'}, + }.iteritems(): + val = tuple(parseStyle('font: %s' % raw, validate=False))[0].cssValue + style = normalizers['font']('font', val) + if expected: + self.assertDictEqual(font_dict(expected), style) + else: + self.assertDictEqual(expected, style) + + def test_border_normalization(self): + def border_edge_dict(expected, edge='right'): + ans = {'border-%s-%s' % (edge, x): DEFAULTS['border-%s-%s' % (edge, x)] for x in ('style', 'width', 'color')} + for x, v in expected.iteritems(): + ans['border-%s-%s' % (edge, x)] = v + return ans + def border_dict(expected): + ans = {} + for edge in EDGES: + ans.update(border_edge_dict(expected, edge)) + return ans + def border_val_dict(expected, val='color'): + ans = {'border-%s-%s' % (edge, val): DEFAULTS['border-%s-%s' % (edge, val)] for edge in EDGES} + for edge in EDGES: + ans['border-%s-%s' % (edge, val)] = expected + return ans + + for raw, expected in { + 'solid 1px red': {'color':'red', 'width':'1px', 'style':'solid'}, + '1px': {'width': '1px'}, '#aaa': {'color': '#aaa'}, + '2em groove': {'width':'2em', 'style':'groove'}, + }.iteritems(): + for edge in EDGES: + br = 'border-%s' % edge + val = tuple(parseStyle('%s: %s' % (br, raw), validate=False))[0].cssValue + self.assertDictEqual(border_edge_dict(expected, edge), normalizers[br](br, val)) + + for raw, expected in { + 'solid 1px red': {'color':'red', 'width':'1px', 'style':'solid'}, + '1px': {'width': '1px'}, '#aaa': {'color': '#aaa'}, + 'thin groove': {'width':'thin', 'style':'groove'}, + }.iteritems(): + val = tuple(parseStyle('%s: %s' % ('border', raw), validate=False))[0].cssValue + self.assertDictEqual(border_dict(expected), normalizers['border']('border', val)) + + for name, val in { + 'width': '10%', 'color': 'rgb(0, 1, 1)', 'style': 'double', + }.iteritems(): + cval = tuple(parseStyle('border-%s: %s' % (name, val), validate=False))[0].cssValue + self.assertDictEqual(border_val_dict(val, name), normalizers['border-'+name]('border-'+name, cval)) + + def test_edge_normalization(self): + def edge_dict(prefix, expected): + return {'%s-%s' % (prefix, edge) : x for edge, x in zip(EDGES, expected)} + for raw, expected in { + '2px': ('2px', '2px', '2px', '2px'), + '1em 2em': ('1em', '2em', '1em', '2em'), + '1em 2em 3em': ('1em', '2em', '3em', '2em'), + '1 2 3 4': ('1', '2', '3', '4'), + }.iteritems(): + for prefix in ('margin', 'padding'): + cval = tuple(parseStyle('%s: %s' % (prefix, raw), validate=False))[0].cssValue + self.assertDictEqual(edge_dict(prefix, expected), normalizers[prefix](prefix, cval)) + + def test_list_style_normalization(self): + def ls_dict(expected): + ans = {'list-style-%s' % x : DEFAULTS['list-style-%s' % x] for x in ('type', 'image', 'position')} + for k, v in expected.iteritems(): + ans['list-style-%s' % k] = v + return ans + for raw, expected in { + 'url(http://www.example.com/images/list.png)': {'image': 'url(http://www.example.com/images/list.png)'}, + 'inside square': {'position':'inside', 'type':'square'}, + 'upper-roman url(img) outside': {'position':'outside', 'type':'upper-roman', 'image':'url(img)'}, + }.iteritems(): + cval = tuple(parseStyle('list-style: %s' % raw, validate=False))[0].cssValue + self.assertDictEqual(ls_dict(expected), normalizers['list-style']('list-style', cval)) + + tests = unittest.defaultTestLoader.loadTestsFromTestCase(TestNormalization) + unittest.TextTestRunner(verbosity=4).run(tests) + +if __name__ == '__main__': + test_normalization() diff --git a/src/calibre/ebooks/oeb/stylizer.py b/src/calibre/ebooks/oeb/stylizer.py index 626af21b97..7a559ca15c 100644 --- a/src/calibre/ebooks/oeb/stylizer.py +++ b/src/calibre/ebooks/oeb/stylizer.py @@ -8,15 +8,11 @@ from __future__ import with_statement __license__ = 'GPL v3' __copyright__ = '2008, Marshall T. Vandegrift ' -import os, itertools, re, logging, copy, unicodedata +import os, re, logging, copy, unicodedata from weakref import WeakKeyDictionary from xml.dom import SyntaxErr as CSSSyntaxError from cssutils.css import (CSSStyleRule, CSSPageRule, CSSFontFaceRule, cssproperties) -try: - from cssutils.css import PropertyValue -except ImportError: - raise RuntimeError('You need cssutils >= 0.9.9 for calibre') from cssutils import (profile as cssprofiles, parseString, parseStyle, log as cssutils_log, CSSParser, profiles, replaceUrls) from lxml import etree @@ -24,8 +20,8 @@ from cssselect import HTMLTranslator from calibre import force_unicode from calibre.ebooks import unit_convert -from calibre.ebooks.oeb.base import XHTML, XHTML_NS, CSS_MIME, OEB_STYLES -from calibre.ebooks.oeb.base import XPNSMAP, xpath, urlnormalize +from calibre.ebooks.oeb.base import XHTML, XHTML_NS, CSS_MIME, OEB_STYLES, XPNSMAP, xpath, urlnormalize +from calibre.ebooks.oeb.normalize_css import DEFAULTS, normalizers cssutils_log.setLevel(logging.WARN) @@ -54,46 +50,6 @@ INHERITED = set(['azimuth', 'border-collapse', 'border-spacing', 'visibility', 'voice-family', 'volume', 'white-space', 'widows', 'word-spacing']) -DEFAULTS = {'azimuth': 'center', 'background-attachment': 'scroll', - 'background-color': 'transparent', 'background-image': 'none', - 'background-position': '0% 0%', 'background-repeat': 'repeat', - 'border-bottom-color': ':color', 'border-bottom-style': 'none', - 'border-bottom-width': 'medium', 'border-collapse': 'separate', - 'border-left-color': ':color', 'border-left-style': 'none', - 'border-left-width': 'medium', 'border-right-color': ':color', - 'border-right-style': 'none', 'border-right-width': 'medium', - 'border-spacing': 0, 'border-top-color': ':color', - 'border-top-style': 'none', 'border-top-width': 'medium', 'bottom': - 'auto', 'caption-side': 'top', 'clear': 'none', 'clip': 'auto', - 'color': 'black', 'content': 'normal', 'counter-increment': 'none', - 'counter-reset': 'none', 'cue-after': 'none', 'cue-before': 'none', - 'cursor': 'auto', 'direction': 'ltr', 'display': 'inline', - 'elevation': 'level', 'empty-cells': 'show', 'float': 'none', - 'font-family': 'serif', 'font-size': 'medium', 'font-style': - 'normal', 'font-variant': 'normal', 'font-weight': 'normal', - 'height': 'auto', 'left': 'auto', 'letter-spacing': 'normal', - 'line-height': 'normal', 'list-style-image': 'none', - 'list-style-position': 'outside', 'list-style-type': 'disc', - 'margin-bottom': 0, 'margin-left': 0, 'margin-right': 0, - 'margin-top': 0, 'max-height': 'none', 'max-width': 'none', - 'min-height': 0, 'min-width': 0, 'orphans': '2', - 'outline-color': 'invert', 'outline-style': 'none', - 'outline-width': 'medium', 'overflow': 'visible', 'padding-bottom': - 0, 'padding-left': 0, 'padding-right': 0, 'padding-top': 0, - 'page-break-after': 'auto', 'page-break-before': 'auto', - 'page-break-inside': 'auto', 'pause-after': 0, 'pause-before': - 0, 'pitch': 'medium', 'pitch-range': '50', 'play-during': 'auto', - 'position': 'static', 'quotes': u"'“' '”' '‘' '’'", 'richness': - '50', 'right': 'auto', 'speak': 'normal', 'speak-header': 'once', - 'speak-numeral': 'continuous', 'speak-punctuation': 'none', - 'speech-rate': 'medium', 'stress': '50', 'table-layout': 'auto', - 'text-align': 'auto', 'text-decoration': 'none', 'text-indent': - 0, 'text-transform': 'none', 'top': 'auto', 'unicode-bidi': - 'normal', 'vertical-align': 'baseline', 'visibility': 'visible', - 'voice-family': 'default', 'volume': 'medium', 'white-space': - 'normal', 'widows': '2', 'width': 'auto', 'word-spacing': 'normal', - 'z-index': 'auto'} - FONT_SIZE_NAMES = set(['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large']) @@ -421,14 +377,11 @@ class Stylizer(object): style = {} for prop in cssstyle: name = prop.name - if name in ('margin', 'padding'): - style.update(self._normalize_edge(prop.cssValue, name)) - elif name == 'font': - style.update(self._normalize_font(prop.cssValue)) - elif name == 'list-style': - style.update(self._normalize_list_style(prop.cssValue)) + normalizer = normalizers.get(name, None) + if normalizer is not None: + style.update(normalizer(name, prop.cssValue)) elif name == 'text-align': - style.update(self._normalize_text_align(prop.cssValue)) + style['text-align'] = self._apply_text_align(prop.value) else: style[name] = prop.value if 'font-size' in style: @@ -441,93 +394,10 @@ class Stylizer(object): style['font-size'] = "%dpt" % self.profile.fnames[size] return style - def _normalize_edge(self, cssvalue, name): - style = {} - if isinstance(cssvalue, PropertyValue): - primitives = [v.cssText for v in cssvalue] - else: - primitives = [cssvalue.cssText] - if len(primitives) == 1: - value, = primitives - values = [value, value, value, value] - elif len(primitives) == 2: - vert, horiz = primitives - values = [vert, horiz, vert, horiz] - elif len(primitives) == 3: - top, horiz, bottom = primitives - values = [top, horiz, bottom, horiz] - else: - values = primitives[:4] - edges = ('top', 'right', 'bottom', 'left') - for edge, value in itertools.izip(edges, values): - style["%s-%s" % (name, edge)] = value - return style - - def _normalize_list_style(self, cssvalue): - composition = ('list-style-type', 'list-style-position', - 'list-style-image') - style = {} - if cssvalue.cssText == 'inherit': - for key in composition: - style[key] = 'inherit' - else: - try: - primitives = [v.cssText for v in cssvalue] - except TypeError: - primitives = [cssvalue.cssText] - primitives.reverse() - value = primitives.pop() - for key in composition: - if cssprofiles.validate(key, value): - style[key] = value - if not primitives: - break - value = primitives.pop() - for key in composition: - if key not in style: - style[key] = DEFAULTS[key] - - return style - - def _normalize_text_align(self, cssvalue): - style = {} - text = cssvalue.cssText - if text == 'inherit': - style['text-align'] = 'inherit' - else: - if text in ('left', 'justify') and self.opts.change_justification in ('left', 'justify'): - val = self.opts.change_justification - style['text-align'] = val - else: - style['text-align'] = text - return style - - def _normalize_font(self, cssvalue): - composition = ('font-style', 'font-variant', 'font-weight', - 'font-size', 'line-height', 'font-family') - style = {} - if cssvalue.cssText == 'inherit': - for key in composition: - style[key] = 'inherit' - else: - try: - primitives = [v.cssText for v in cssvalue] - except TypeError: - primitives = [cssvalue.cssText] - primitives.reverse() - value = primitives.pop() - for key in composition: - if cssprofiles.validate(key, value): - style[key] = value - if not primitives: - break - value = primitives.pop() - for key in composition: - if key not in style: - val = ('inherit' if key in {'font-family', 'font-size'} - else 'normal') - style[key] = val - return style + def _apply_text_align(self, text): + if text in ('left', 'justify') and self.opts.change_justification in ('left', 'justify'): + text = self.opts.change_justification + return text def style(self, element): try: From 622306be962e61d7a00cd1d0a42fdef43f03f945 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Aug 2013 18:03:02 +0530 Subject: [PATCH 2/6] ... --- src/calibre/ebooks/oeb/normalize_css.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/oeb/normalize_css.py b/src/calibre/ebooks/oeb/normalize_css.py index cadb58b5cb..dea3826c87 100644 --- a/src/calibre/ebooks/oeb/normalize_css.py +++ b/src/calibre/ebooks/oeb/normalize_css.py @@ -164,7 +164,7 @@ for x in EDGES: name = 'border-' + x normalizers[name] = simple_normalizer(name, ('color', 'style', 'width'), check_inherit=False) -def test_normalization(): +def test_normalization(): # {{{ import unittest from cssutils import parseStyle @@ -264,6 +264,7 @@ def test_normalization(): tests = unittest.defaultTestLoader.loadTestsFromTestCase(TestNormalization) unittest.TextTestRunner(verbosity=4).run(tests) +# }}} if __name__ == '__main__': test_normalization() From cdbf10ffabfc61339610707671d8552fd618af59 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Sep 2013 10:20:40 +0530 Subject: [PATCH 3/6] pep8 --- src/calibre/ebooks/conversion/plugins/mobi_output.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/calibre/ebooks/conversion/plugins/mobi_output.py b/src/calibre/ebooks/conversion/plugins/mobi_output.py index b5dc6f17d5..0bdb26162c 100644 --- a/src/calibre/ebooks/conversion/plugins/mobi_output.py +++ b/src/calibre/ebooks/conversion/plugins/mobi_output.py @@ -148,7 +148,6 @@ class MOBIOutput(OutputFormatPlugin): self.oeb.manifest.remove(self.oeb.manifest.hrefs[x.href]) x.href = articles_[0].href - for sec in sections: articles[id(sec)] = [] for a in list(sec): @@ -179,7 +178,7 @@ class MOBIOutput(OutputFormatPlugin): mobi_type = opts.mobi_file_type if self.is_periodical: - mobi_type = 'old' # Amazon does not support KF8 periodicals + mobi_type = 'old' # Amazon does not support KF8 periodicals create_kf8 = mobi_type in ('new', 'both') remove_html_cover(self.oeb, self.log) @@ -188,12 +187,10 @@ class MOBIOutput(OutputFormatPlugin): self.check_for_periodical() if create_kf8: - # Split on pagebreaks so that the resulting KF8 works better with - # calibre's viewer, which does not support CSS page breaks + # Split on pagebreaks so that the resulting KF8 is faster to load from calibre.ebooks.oeb.transforms.split import Split Split()(self.oeb, self.opts) - kf8 = self.create_kf8(resources, for_joint=mobi_type=='both' ) if create_kf8 else None if mobi_type == 'new': From a4f2288976736d76c61ffdb2f74732b1073767a9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Sep 2013 10:25:13 +0530 Subject: [PATCH 4/6] pep8 --- src/calibre/ebooks/conversion/plugins/oeb_output.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/calibre/ebooks/conversion/plugins/oeb_output.py b/src/calibre/ebooks/conversion/plugins/oeb_output.py index 86d3b76a8c..19468425b1 100644 --- a/src/calibre/ebooks/conversion/plugins/oeb_output.py +++ b/src/calibre/ebooks/conversion/plugins/oeb_output.py @@ -18,7 +18,6 @@ class OEBOutput(OutputFormatPlugin): recommendations = set([('pretty_print', True, OptionRecommendation.HIGH)]) - def convert(self, oeb_book, output_path, input_plugin, opts, log): from urllib import unquote from lxml import etree @@ -62,7 +61,7 @@ class OEBOutput(OutputFormatPlugin): f.write(str(item)) item.unload_data_from_memory(memory=path) - def workaround_nook_cover_bug(self, root): # {{{ + def workaround_nook_cover_bug(self, root): # {{{ cov = root.xpath('//*[local-name() = "meta" and @name="cover" and' ' @content != "cover"]') @@ -96,7 +95,7 @@ class OEBOutput(OutputFormatPlugin): cov.set('content', 'cover') # }}} - def workaround_pocketbook_cover_bug(self, root): # {{{ + def workaround_pocketbook_cover_bug(self, root): # {{{ m = root.xpath('//*[local-name() = "manifest"]/*[local-name() = "item" ' ' and @id="cover"]') if len(m) == 1: @@ -106,7 +105,7 @@ class OEBOutput(OutputFormatPlugin): p.insert(0, m) # }}} - def migrate_lang_code(self, root): # {{{ + def migrate_lang_code(self, root): # {{{ from calibre.utils.localization import lang_as_iso639_1 for lang in root.xpath('//*[local-name() = "language"]'): clc = lang_as_iso639_1(lang.text) From c6491d03a66b23e62ffe2881e0fe96223353bad1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Sep 2013 11:52:11 +0530 Subject: [PATCH 5/6] pep8 --- src/calibre/ebooks/mobi/writer8/main.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/calibre/ebooks/mobi/writer8/main.py b/src/calibre/ebooks/mobi/writer8/main.py index 564dc5521c..efdfb00ba8 100644 --- a/src/calibre/ebooks/mobi/writer8/main.py +++ b/src/calibre/ebooks/mobi/writer8/main.py @@ -48,8 +48,8 @@ class KF8Writer(object): self.toc_adder = TOCAdder(oeb, opts) self.used_images = set() self.resources = resources - self.flows = [None] # First flow item is reserved for the text - self.records = [None] # Placeholder for zeroth record + self.flows = [None] # First flow item is reserved for the text + self.records = [None] # Placeholder for zeroth record self.log('\tGenerating KF8 markup...') self.dup_data() @@ -145,7 +145,7 @@ class KF8Writer(object): cssutils.replaceUrls(sheet, replacer, ignoreImportRules=True) def extract_css_into_flows(self): - inlines = defaultdict(list) # Ensure identical