diff --git a/src/calibre/gui2/comments_editor.py b/src/calibre/gui2/comments_editor.py
index 2ce53d30ed..7797711922 100644
--- a/src/calibre/gui2/comments_editor.py
+++ b/src/calibre/gui2/comments_editor.py
@@ -6,6 +6,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera
import os
import re
import weakref
+from collections import defaultdict
from html5_parser import parse
from lxml import html
@@ -24,7 +25,102 @@ from calibre.gui2 import NO_URL_FORMATTING, choose_files, error_dialog, gprefs
from calibre.gui2.widgets import LineEditECM
from calibre.utils.config import tweaks
from calibre.utils.imghdr import what
-from polyglot.builtins import unicode_type
+from polyglot.builtins import filter, iteritems, itervalues, unicode_type
+
+
+def parse_style(style):
+ props = filter(None, (x.strip() for x in style.split(';')))
+ ans = {}
+ for prop in props:
+ try:
+ k, v = prop.split(':', 1)
+ except Exception:
+ continue
+ ans[k.strip().lower()] = v.strip()
+ return ans
+
+
+liftable_props = ('font-style', 'font-weight', 'font-family', 'font-size')
+
+
+def lift_styles(tag, style_map):
+ common_props = None
+ has_text = bool(tag.text)
+ child_styles = []
+ for child in tag.iterchildren('*'):
+ if child.tail:
+ has_text = True
+ style = style_map[child]
+ child_styles.append(style)
+ if common_props is None:
+ common_props = style.copy()
+ else:
+ for k, v in tuple(iteritems(common_props)):
+ if style.get(k) != v:
+ del common_props[k]
+ if not has_text and common_props:
+ lifted_props = []
+ tag_style = style_map[tag]
+ for k in liftable_props:
+ if k in common_props:
+ lifted_props.append(k)
+ tag_style[k] = common_props[k]
+ if lifted_props:
+ for style in child_styles:
+ for k in lifted_props:
+ del style[k]
+
+
+def filter_qt_styles(style):
+ for k in tuple(style):
+ if k.startswith('-qt-'):
+ del style[k]
+
+
+def remove_margins(tag, style):
+ ml, mr, mt, mb = (style.pop('margin-' + k, None) for k in 'left right top bottom'.split())
+ is_blockquote = ml == mr and ml and ml != '0px'
+ if is_blockquote:
+ tag.tag = 'blockquote'
+
+
+def remove_zero_indents(style):
+ ti = style.get('text-indent')
+ if ti == '0px':
+ del style['text-indent']
+
+
+def remove_heading_font_styles(tag, style):
+ lvl = int(tag.tag[1:])
+ expected_size = (None, 'xx-large', 'x-large', 'large', None, 'small', 'x-small')[lvl]
+ if style.get('font-size', 1) == expected_size:
+ del style['font-size']
+ if style.get('font-weight') == '600':
+ del style['font-weight']
+
+
+def cleanup_qt_markup(root):
+ from calibre.ebooks.docx.cleanup import lift
+ style_map = defaultdict(dict)
+ for tag in root.xpath('//*[@style]'):
+ style_map[tag] = parse_style(tag.get('style'))
+ block_tags = root.xpath('//body/*')
+ for tag in block_tags:
+ lift_styles(tag, style_map)
+ tag_style = style_map[tag]
+ remove_margins(tag, tag_style)
+ remove_zero_indents(tag_style)
+ if tag.tag.startswith('h'):
+ remove_heading_font_styles(tag, tag_style)
+ for style in itervalues(style_map):
+ filter_qt_styles(style)
+ for tag, style in iteritems(style_map):
+ if style:
+ tag.set('style', '; '.join('{}: {}'.format(k, v) for k, v in iteritems(style)))
+ else:
+ tag.attrib.pop('style', None)
+ for span in root.xpath('//span[not(@style)]'):
+ lift(span)
class EditorWidget(QTextEdit, LineEditECM): # {{{
@@ -278,9 +374,6 @@ class EditorWidget(QTextEdit, LineEditECM): # {{{
self.setTextCursor(c)
self.focus_self()
- def font_size_for_heading(self, level):
- return int(self.em_size * (1, 2, 1.5, 1.17, 1, 0.83, 0.67)[max(0, min(level, 6))])
-
def level_for_block_type(self, name):
if name == 'blockquote':
return 0
@@ -300,9 +393,8 @@ class EditorWidget(QTextEdit, LineEditECM): # {{{
bcf = c.blockCharFormat()
lvl = self.level_for_block_type(name)
hmargin = 0
- font = self.font()
- font.setBold(bool(lvl))
- font.setPixelSize(self.font_size_for_heading(lvl))
+ wt = 75 if lvl else None
+ adjust = (0, 3, 2, 1, 0, -1, -1)[lvl]
if name == 'blockquote':
hmargin = self.em_size * 3
pos = None
@@ -313,8 +405,12 @@ class EditorWidget(QTextEdit, LineEditECM): # {{{
bf.setLeftMargin(hmargin), bf.setRightMargin(bf.leftMargin())
bf.setTopMargin(self.vmargin_for_block_type(name)), bf.setBottomMargin(bf.topMargin())
bf.setHeadingLevel(lvl)
- bcf.setFont(font, QTextCharFormat.FontPropertiesSpecifiedOnly)
- cf.setFont(font, QTextCharFormat.FontPropertiesSpecifiedOnly)
+ if adjust:
+ bcf.setProperty(QTextCharFormat.FontSizeAdjustment, adjust)
+ cf.setProperty(QTextCharFormat.FontSizeAdjustment, adjust)
+ if wt:
+ bcf.setProperty(QTextCharFormat.FontWeight, wt)
+ cf.setProperty(QTextCharFormat.FontWeight, wt)
c.setBlockCharFormat(bcf)
c.mergeCharFormat(cf)
c.mergeBlockFormat(bf)
@@ -452,14 +548,16 @@ class EditorWidget(QTextEdit, LineEditECM): # {{{
if not check and 'He hadn't
+ w.html = '''
Test blockquote
He hadn't set out to have an affair, much less a long-term, devoted one.''' app.exec_() # print w.html