From 0e9b1c55e6b6a1e611aa700519d7d17763d59d89 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 11 Nov 2021 09:54:20 +0530 Subject: [PATCH] Edit book: Add a tool to transform HTML tags based on rules (Tools->Transform HTML) --- src/calibre/gui2/html_transform_rules.py | 20 ++++++++++- src/calibre/gui2/tag_mapper.py | 16 ++++++++- src/calibre/gui2/tweak_book/__init__.py | 1 + src/calibre/gui2/tweak_book/boss.py | 46 ++++++++++++++++++++++++ src/calibre/gui2/tweak_book/ui.py | 3 ++ 5 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/html_transform_rules.py b/src/calibre/gui2/html_transform_rules.py index df5cac89f1..89e5b1a9d8 100644 --- a/src/calibre/gui2/html_transform_rules.py +++ b/src/calibre/gui2/html_transform_rules.py @@ -293,7 +293,7 @@ class RuleItem(RuleItemBase): # {{{ text += '
' + ACTION_MAP[action['type']].short_text if action.get('data'): ad = elided_text(action['data'], font=parent.font(), width=200, pos='right') - text += f'{prepare_string_for_xml(ad)}' + text += f' {prepare_string_for_xml(ad)}' except Exception: import traceback traceback.print_exc() @@ -336,6 +336,24 @@ class RulesDialog(RulesDialogBase): # {{{ PREFS_OBJECT_NAME = 'html-transform-rules' RulesClass = Rules TesterClass = Tester + + def extra_bottom_widget(self): + self.scope_cb = cb = QComboBox() + cb.addItem(_('Current HTML file'), 'current') + cb.addItem(_('All HTML files'), 'all') + cb.addItem(_('Open HTML files'), 'open') + cb.addItem(_('Selected HTML files'), 'selected') + return cb + + @property + def transform_scope(self): + return self.scope_cb.currentData() + + @transform_scope.setter + def transform_scope(self, val): + idx = self.scope_cb.findData(val) + self.scope_cb.setCurrentIndex(max(0, idx)) + # }}} diff --git a/src/calibre/gui2/tag_mapper.py b/src/calibre/gui2/tag_mapper.py index 452419bb75..47d649e3ae 100644 --- a/src/calibre/gui2/tag_mapper.py +++ b/src/calibre/gui2/tag_mapper.py @@ -495,7 +495,13 @@ class RulesDialog(Dialog, SaveLoadMixin): self.l = l = QVBoxLayout(self) self.edit_widget = w = self.RulesClass(self) l.addWidget(w) - l.addWidget(self.bb) + ebw = self.extra_bottom_widget() + if ebw is None: + l.addWidget(self.bb) + else: + self.h = h = QHBoxLayout() + l.addLayout(h) + h.addWidget(ebw), h.addStretch(10), h.addWidget(self.bb) self.save_button = b = self.bb.addButton(_('&Save'), QDialogButtonBox.ButtonRole.ActionRole) b.setToolTip(_('Save this ruleset for later re-use')) b.clicked.connect(self.save_ruleset) @@ -507,6 +513,9 @@ class RulesDialog(Dialog, SaveLoadMixin): self.test_button = b = self.bb.addButton(_('&Test rules'), QDialogButtonBox.ButtonRole.ActionRole) b.clicked.connect(self.test_rules) + def extra_bottom_widget(self): + pass + @property def rules(self): return self.edit_widget.rules @@ -518,6 +527,11 @@ class RulesDialog(Dialog, SaveLoadMixin): def test_rules(self): self.TesterClass(self.rules, self).exec_() + def sizeHint(self): + ans = super().sizeHint() + ans.setWidth(ans.width() + 100) + return ans + if __name__ == '__main__': app = Application([]) diff --git a/src/calibre/gui2/tweak_book/__init__.py b/src/calibre/gui2/tweak_book/__init__.py index e9f833c3d8..aff8506ecc 100644 --- a/src/calibre/gui2/tweak_book/__init__.py +++ b/src/calibre/gui2/tweak_book/__init__.py @@ -85,6 +85,7 @@ d['file_list_shows_full_pathname'] = False d['auto_link_stylesheets'] = True d['check_external_link_anchors'] = True d['remove_ncx'] = True +d['html_transform_scope'] = 'current' del d ucase_map = {l:string.ascii_uppercase[i] for i, l in enumerate(string.ascii_lowercase)} diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index a2633402a1..1f2e5c1646 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -74,6 +74,7 @@ from polyglot.urllib import urlparse _diff_dialogs = [] last_used_transform_rules = [] +last_used_html_transform_rules = [] def get_container(*args, **kwargs): @@ -626,6 +627,51 @@ class Boss(QObject): self.rewind_savepoint() show_report(changed, self.current_metadata.title, report, parent or self.gui, self.show_current_diff) + def transform_html(self): + global last_used_html_transform_rules + if not self.ensure_book(_('You must first open a book in order to transform styles.')): + return + from calibre.gui2.html_transform_rules import RulesDialog + from calibre.ebooks.html_transform_rules import transform_container + d = RulesDialog(self.gui) + d.rules = last_used_html_transform_rules + d.transform_scope = scope = tprefs['html_transform_scope'] + ret = d.exec_() + last_used_html_transform_rules = d.rules + tprefs.set('html_transform_scope', d.transform_scope) + if ret != QDialog.DialogCode.Accepted: + return + + cc = current_container() + names = () + if scope == 'current': + if not self.currently_editing or cc.mime_map.get(self.currently_editing) not in OEB_DOCS: + return error_dialog(self.gui, _('No HTML file'), _('Not currently editing an HTML file'), show=True) + names = (self.currently_editing,) + elif scope == 'open': + names = tuple(name for name in editors if cc.mime_map.get(name) in OEB_DOCS) + if not names: + return error_dialog(self.gui, _('No HTML files'), _('Not currently editing any HTML files'), show=True) + elif scope == 'selected': + names = tuple(name for name in self.gui.file_list.file_list.selected_names if cc.mime_map.get(name) in OEB_DOCS) + if not names: + return error_dialog(self.gui, _('No HTML files'), _('No HTML files are currently selected in the File browser'), show=True) + with BusyCursor(): + self.add_savepoint(_('Before HTML transformation')) + try: + changed = transform_container(cc, last_used_html_transform_rules, names) + except: + self.rewind_savepoint() + raise + if changed: + self.apply_container_update_to_gui() + if not changed: + self.rewind_savepoint() + info_dialog(self.gui, _('No changes'), _( + 'No HTML was changed.'), show=True) + return + self.show_current_diff() + def transform_styles(self): global last_used_transform_rules if not self.ensure_book(_('You must first open a book in order to transform styles.')): diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index 3b266a2da8..bbc27f0200 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -500,6 +500,8 @@ class Main(MainWindow): 'Compress images losslessly')) self.action_transform_styles = treg('wizard.png', _('Transform &styles'), self.boss.transform_styles, 'transform-styles', (), _( 'Transform styles used in the book')) + self.action_transform_html = treg('wizard.png', _('Transform &HTML'), self.boss.transform_html, 'transform-html', (), _( + 'Transform HTML used in the book')) self.action_get_ext_resources = treg('download-metadata.png', _('Download external &resources'), self.boss.get_external_resources, 'get-external-resources', (), _( 'Download external resources in the book (images/stylesheets/etc/ that are not included in the book)')) @@ -666,6 +668,7 @@ class Main(MainWindow): e.addAction(self.action_smarten_punctuation) e.addAction(self.action_remove_unused_css) e.addAction(self.action_transform_styles) + e.addAction(self.action_transform_html) e.addAction(self.action_fix_html_all) e.addAction(self.action_pretty_all) e.addAction(self.action_rationalize_folders)