Edit book: Add a tool to transform HTML tags based on rules (Tools->Transform HTML)

This commit is contained in:
Kovid Goyal 2021-11-11 09:54:20 +05:30
parent 23f3dbd85c
commit 0e9b1c55e6
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 84 additions and 2 deletions

View File

@ -293,7 +293,7 @@ class RuleItem(RuleItemBase): # {{{
text += '<br>' + ACTION_MAP[action['type']].short_text
if action.get('data'):
ad = elided_text(action['data'], font=parent.font(), width=200, pos='right')
text += f'<code>{prepare_string_for_xml(ad)}</code>'
text += f' <code>{prepare_string_for_xml(ad)}</code>'
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))
# }}}

View File

@ -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([])

View File

@ -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)}

View File

@ -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.')):

View File

@ -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)