From d690bf2aa4e2a01f165ef5e53059403e9dcbbc2d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 4 May 2017 13:47:46 +0530 Subject: [PATCH] DOCX Output: When the input document contains paragraphs inside a block with a background color preserve the background color in the resulting paragraphs in the DOCX document. See #1683188 (Losing all bookmarks while converting azw3 files to docx) --- src/calibre/ebooks/docx/writer/from_html.py | 15 ++++++++++++--- src/calibre/ebooks/docx/writer/styles.py | 18 +++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/calibre/ebooks/docx/writer/from_html.py b/src/calibre/ebooks/docx/writer/from_html.py index 135d64afde..4238871150 100644 --- a/src/calibre/ebooks/docx/writer/from_html.py +++ b/src/calibre/ebooks/docx/writer/from_html.py @@ -135,7 +135,7 @@ class TextRun(object): class Block(object): - def __init__(self, namespace, styles_manager, links_manager, html_block, style, is_table_cell=False, float_spec=None, is_list_item=False): + def __init__(self, namespace, styles_manager, links_manager, html_block, style, is_table_cell=False, float_spec=None, is_list_item=False, parent_bg=None): self.namespace = namespace self.bookmarks = set() self.list_tag = (html_block, style) if is_list_item else None @@ -148,7 +148,7 @@ class Block(object): if float_spec is not None: float_spec.blocks.append(self) self.html_style = style - self.style = styles_manager.create_block_style(style, html_block, is_table_cell=is_table_cell) + self.style = styles_manager.create_block_style(style, html_block, is_table_cell=is_table_cell, parent_bg=parent_bg) self.styles_manager, self.links_manager = styles_manager, links_manager self.keep_next = False self.runs = [] @@ -278,10 +278,19 @@ class Blocks(object): self.current_block = None def start_new_block(self, html_block, style, is_table_cell=False, float_spec=None, is_list_item=False): + parent_bg = None + if html_block is not None: + p = html_block.getparent() + b = self.html_tag_start_blocks.get(p) + if b is not None: + ps = self.styles_manager.styles_for_html_blocks.get(p) + if ps is not None and ps.background_color is not None: + parent_bg = ps.background_color self.end_current_block() self.current_block = Block( self.namespace, self.styles_manager, self.links_manager, html_block, style, - is_table_cell=is_table_cell, float_spec=float_spec, is_list_item=is_list_item) + is_table_cell=is_table_cell, float_spec=float_spec, is_list_item=is_list_item, + parent_bg=parent_bg) self.html_tag_start_blocks[html_block] = self.current_block self.open_html_blocks.add(html_block) return self.current_block diff --git a/src/calibre/ebooks/docx/writer/styles.py b/src/calibre/ebooks/docx/writer/styles.py index fc8a00bd91..396c87b8f4 100644 --- a/src/calibre/ebooks/docx/writer/styles.py +++ b/src/calibre/ebooks/docx/writer/styles.py @@ -147,10 +147,13 @@ class DOCXStyle(object): def __init__(self, namespace): self.namespace = namespace self.w = lambda x: '{%s}%s' % (namespace.namespaces['w'], x) - self._hash = hash(tuple( - getattr(self, x) for x in self.ALL_PROPS)) self.id = self.name = None self.next_style = None + self.calculate_hash() + + def calculate_hash(self): + self._hash = hash(tuple( + getattr(self, x) for x in self.ALL_PROPS)) def makeelement(self, parent, name, **attrs): return parent.makeelement(self.w(name), **{self.w(k):v for k, v in attrs.iteritems()}) @@ -180,6 +183,7 @@ class DOCXStyle(object): styles.append(style) return style + LINE_STYLES = { 'none' : 'none', 'hidden': 'none', @@ -481,7 +485,7 @@ class BlockStyle(DOCXStyle): [x%edge for edge in border_edges for x in border_props] ) - def __init__(self, namespace, css, html_block, is_table_cell=False): + def __init__(self, namespace, css, html_block, is_table_cell=False, parent_bg=None): read_css_block_borders(self, css) if is_table_cell: for edge in border_edges: @@ -507,6 +511,8 @@ class BlockStyle(DOCXStyle): except (TypeError, ValueError): self.line_height = max(0, int(1.2 * css.fontSize * 20)) self.background_color = None if is_table_cell else convert_color(css['background-color']) + if not is_table_cell and self.background_color is None: + self.background_color = parent_bg try: self.text_align = {'start':'left', 'left':'left', 'end':'right', 'right':'right', 'center':'center', 'justify':'both', 'centre':'center'}.get( css['text-align'].lower(), 'left') @@ -623,6 +629,7 @@ class StylesManager(object): self.document_lang = lang_as_iso639_1(document_lang) or 'en' self.log = log self.block_styles, self.text_styles = {}, {} + self.styles_for_html_blocks = {} def create_text_style(self, css_style, is_parent_style=False): ans = TextStyle(self.namespace, css_style, is_parent_style=is_parent_style) @@ -633,13 +640,14 @@ class StylesManager(object): ans = existing return ans - def create_block_style(self, css_style, html_block, is_table_cell=False): - ans = BlockStyle(self.namespace, css_style, html_block, is_table_cell=is_table_cell) + def create_block_style(self, css_style, html_block, is_table_cell=False, parent_bg=None): + ans = BlockStyle(self.namespace, css_style, html_block, is_table_cell=is_table_cell, parent_bg=parent_bg) existing = self.block_styles.get(ans, None) if existing is None: self.block_styles[ans] = ans else: ans = existing + self.styles_for_html_blocks[html_block] = ans return ans def finalize(self, all_blocks):