From 221297ad49b77ca614e117c16338564ec10fec1f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Mar 2015 09:51:50 +0530 Subject: [PATCH] Serialize styles to docx --- src/calibre/ebooks/docx/writer/TODO | 1 + src/calibre/ebooks/docx/writer/from_html.py | 6 ++++ src/calibre/ebooks/docx/writer/styles.py | 35 ++++++++++++--------- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/calibre/ebooks/docx/writer/TODO b/src/calibre/ebooks/docx/writer/TODO index 563957497f..200e7ea794 100644 --- a/src/calibre/ebooks/docx/writer/TODO +++ b/src/calibre/ebooks/docx/writer/TODO @@ -7,4 +7,5 @@ Tables Lists Embed Fonts RTL text +Lang support in run styles diff --git a/src/calibre/ebooks/docx/writer/from_html.py b/src/calibre/ebooks/docx/writer/from_html.py index ad66fcfb10..946c3a22d3 100644 --- a/src/calibre/ebooks/docx/writer/from_html.py +++ b/src/calibre/ebooks/docx/writer/from_html.py @@ -67,6 +67,9 @@ class TextRun(object): def serialize(self, p): r = p.makeelement(w('r')) p.append(r) + rpr = r.makeelement(w('rPr')) + rpr.append(rpr.makeelement(w('rStyle'), **{w('val'):self.style.id})) + r.append(rpr) for text, preserve_whitespace in self.texts: if text is None: r.append(r.makeelement(w('br'), **{w('clear'):preserve_whitespace})) @@ -120,6 +123,7 @@ class Block(object): p.append(ppr) if self.keep_next: ppr.append(ppr.makeelement(w('keepNext'))) + ppr.append(ppr.makeelement(w('pStyle'), **{w('val'):self.style.id})) for run in self.runs: run.serialize(p) @@ -140,6 +144,7 @@ class Convert(object): for item in self.oeb.spine: self.process_item(item) + self.styles_manager.finalize(self.blocks) self.write() def process_item(self, item): @@ -235,3 +240,4 @@ class Convert(object): ) ) ) + self.styles_manager.serialize(self.docx.styles) diff --git a/src/calibre/ebooks/docx/writer/styles.py b/src/calibre/ebooks/docx/writer/styles.py index d3cd6e2040..450789bc83 100644 --- a/src/calibre/ebooks/docx/writer/styles.py +++ b/src/calibre/ebooks/docx/writer/styles.py @@ -14,6 +14,7 @@ from lxml import etree from calibre.ebooks import parse_css_length from calibre.ebooks.docx.names import namespaces from calibre.ebooks.docx.writer.utils import convert_color, int_or_zero +from calibre.utils.icu import numeric_sort_key from tinycss.css21 import CSS21Parser css_parser = CSS21Parser() @@ -168,7 +169,7 @@ class TextStyle(DOCXStyle): val = getattr(self, attr) if self is normal_style or getattr(normal_style, attr) != val: for suffix in ('', 'Cs'): - style.append(makeelement(style, 'sz' + suffix, val=vmap(val))) + style.append(makeelement(style, name + suffix, val=vmap(val))) def check_attr(attr): val = getattr(self, attr) @@ -201,7 +202,7 @@ class TextStyle(DOCXStyle): if val: style.append(makeelement(style, 'vertAlign', val=val)) - bdr = self.serialize_borders(makeelement(style, 'bdr', normal_style)) + bdr = self.serialize_borders(makeelement(style, 'bdr'), normal_style) if len(bdr): style.append(bdr) @@ -267,8 +268,8 @@ class BlockStyle(DOCXStyle): val = int(css_val * 240 * mult) spacing.set(w('line'), str(val)) else: - spacing.set(w('line'), (0 if self.css_line_height == 'normal' else str(self.line_height))) - spacing.set(w('lineRule', 'exactly')) + spacing.set(w('line'), str(0 if self.css_line_height == 'normal' else self.line_height)) + spacing.set(w('lineRule'), 'exactly') if spacing.attrib: style.append(spacing) @@ -309,7 +310,7 @@ class BlockStyle(DOCXStyle): style.append(makeelement(style, 'jc', val=self.text_align)) if (self is normal_style and self.page_break_before) or self.page_break_before != normal_style.page_break_before: - style.append(makeelement(style, 'pageBreakBefore', bmap(self.page_break_before))) + style.append(makeelement(style, 'pageBreakBefore', val=bmap(self.page_break_before))) if (self is normal_style and self.keep_lines) or self.keep_lines != normal_style.keep_lines: style.append(makeelement(style, 'keepLines', bmap(self.keep_lines))) @@ -352,17 +353,23 @@ class StylesManager(object): run_rmap[run.style].append(run) for i, (block_style, count) in enumerate(block_counts.most_common()): if i == 0: - normal_block_style = block_style - normal_block_style.id = 'BlockNormal' - normal_block_style.name = 'Normal' + self.normal_block_style = block_style + block_style.id = 'ParagraphNormal' + block_style.name = 'Normal' else: - block_style.id = 'Block%d' % i + block_style.id = 'Paragraph%d' % i block_style.name = 'Paragraph %d' % i for i, (text_style, count) in enumerate(run_counts.most_common()): if i == 0: - normal_text_style = text_style - normal_text_style.id = 'TextNormal' - normal_text_style.name = 'Normal' + self.normal_text_style = text_style + text_style.id = 'TextNormal' + text_style.name = 'Normal' else: - block_style.id = 'Text%d' % i - block_style.name = 'Text %d' % i + text_style.id = 'Text%d' % i + text_style.name = 'Text %d' % i + + def serialize(self, styles): + for style in sorted(self.block_styles, key=lambda s:(s is not self.normal_block_style, numeric_sort_key(s.id))): + style.serialize(styles, self.normal_block_style) + for style in sorted(self.text_styles, key=lambda s:(s is not self.normal_text_style, numeric_sort_key(s.id))): + style.serialize(styles, self.normal_text_style)