mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Edit book: Remove unused CSS: Add an option to merge CSS rules that have identical properties
This commit is contained in:
parent
40e5b7124f
commit
2283b6114e
@ -80,33 +80,38 @@ def merge_identical_properties(sheet):
|
||||
properties_map = defaultdict(list)
|
||||
|
||||
def declaration_key(declaration):
|
||||
items = []
|
||||
for prop in declaration.getProperties():
|
||||
val = prop.propertyValue.value
|
||||
name = prop.name
|
||||
items.append((name, val))
|
||||
items.sort(key=itemgetter(0))
|
||||
return tuple(items)
|
||||
return tuple(sorted(
|
||||
((prop.name, prop.propertyValue.value) for prop in declaration.getProperties()),
|
||||
key=itemgetter(0)
|
||||
))
|
||||
|
||||
for rule in sheet.cssRules.rulesOfType(CSSRule.STYLE_RULE):
|
||||
properties_map[declaration_key(rule.style)].append(rule)
|
||||
for idx, rule in enumerate(sheet.cssRules):
|
||||
if rule.type == CSSRule.STYLE_RULE:
|
||||
properties_map[declaration_key(rule.style)].append((idx, rule))
|
||||
|
||||
removals = []
|
||||
num_merged = 0
|
||||
for rule_group in properties_map.values():
|
||||
if len(rule_group) < 2:
|
||||
continue
|
||||
selectors = rule_group[0].selectorList
|
||||
num_merged += len(rule_group)
|
||||
selectors = rule_group[0][1].selectorList
|
||||
seen = {s.selectorText for s in selectors}
|
||||
rules = iter(rule_group)
|
||||
next(rules)
|
||||
for rule in rules:
|
||||
for idx, rule in rules:
|
||||
removals.append(idx)
|
||||
for s in rule.selectorList:
|
||||
q = s.selectorText
|
||||
if q not in seen:
|
||||
seen.add(q)
|
||||
selectors.append(s)
|
||||
for idx in sorted(removals, reverse=True):
|
||||
sheet.cssRules.pop(idx)
|
||||
return num_merged
|
||||
|
||||
|
||||
def remove_unused_css(container, report=None, remove_unused_classes=False, merge_rules=False):
|
||||
def remove_unused_css(container, report=None, remove_unused_classes=False, merge_rules=False, merge_rules_with_identical_properties=False):
|
||||
'''
|
||||
Remove all unused CSS rules from the book. An unused CSS rule is one that does not match any actual content.
|
||||
|
||||
@ -123,13 +128,19 @@ def remove_unused_css(container, report=None, remove_unused_classes=False, merge
|
||||
pass
|
||||
sheets = {name:safe_parse(name) for name, mt in iteritems(container.mime_map) if mt in OEB_STYLES}
|
||||
sheets = {k:v for k, v in iteritems(sheets) if v is not None}
|
||||
num_merged = 0
|
||||
num_merged = num_rules_merged = 0
|
||||
if merge_rules:
|
||||
for name, sheet in iteritems(sheets):
|
||||
num = merge_identical_selectors(sheet)
|
||||
if num:
|
||||
container.dirty(name)
|
||||
num_merged += num
|
||||
if merge_rules_with_identical_properties:
|
||||
for name, sheet in iteritems(sheets):
|
||||
num = merge_identical_properties(sheet)
|
||||
if num:
|
||||
container.dirty(name)
|
||||
num_rules_merged += num
|
||||
import_map = {name:get_imported_sheets(name, container, sheets) for name in sheets}
|
||||
if remove_unused_classes:
|
||||
class_map = {name:{icu_lower(x) for x in classes_in_rule_list(sheet.cssRules)} for name, sheet in iteritems(sheets)}
|
||||
@ -151,6 +162,11 @@ def remove_unused_css(container, report=None, remove_unused_classes=False, merge
|
||||
if num:
|
||||
num_merged += num
|
||||
container.dirty(name)
|
||||
if merge_rules_with_identical_properties:
|
||||
num = merge_identical_properties(sheet)
|
||||
if num:
|
||||
num_rules_merged += num
|
||||
container.dirty(name)
|
||||
if remove_unused_classes:
|
||||
used_classes |= {icu_lower(x) for x in classes_in_rule_list(sheet.cssRules)}
|
||||
imports = get_imported_sheets(name, container, sheets, sheet=sheet)
|
||||
@ -201,7 +217,7 @@ def remove_unused_css(container, report=None, remove_unused_classes=False, merge
|
||||
[sheet.cssRules.remove(r) for r in unused_rules]
|
||||
container.dirty(name)
|
||||
|
||||
num_changes = num_of_removed_rules + num_merged + num_of_removed_classes
|
||||
num_changes = num_of_removed_rules + num_merged + num_of_removed_classes + num_rules_merged
|
||||
if num_changes > 0:
|
||||
if num_of_removed_rules > 0:
|
||||
report(ngettext('Removed one unused CSS style rule', 'Removed {} unused CSS style rules',
|
||||
@ -210,8 +226,11 @@ def remove_unused_css(container, report=None, remove_unused_classes=False, merge
|
||||
report(ngettext('Removed one unused class from the HTML', 'Removed {} unused classes from the HTML',
|
||||
num_of_removed_classes).format(num_of_removed_classes))
|
||||
if num_merged > 0:
|
||||
report(ngettext('Merged one CSS style rule', 'Merged {} CSS style rules',
|
||||
report(ngettext('Merged one CSS style rule with identical selectors', 'Merged {} CSS style rules with identical selectors',
|
||||
num_merged).format(num_merged))
|
||||
if num_rules_merged > 0:
|
||||
report(ngettext('Merged one CSS style rule with identical properties', 'Merged {} CSS style rules with identical properties',
|
||||
num_rules_merged).format(num_rules_merged))
|
||||
if num_of_removed_rules == 0:
|
||||
report(_('No unused CSS style rules found'))
|
||||
if remove_unused_classes and num_of_removed_classes == 0:
|
||||
|
@ -43,6 +43,7 @@ ALL_OPTS = {
|
||||
CUSTOMIZATION = {
|
||||
'remove_unused_classes': False,
|
||||
'merge_identical_selectors': False,
|
||||
'merge_rules_with_identical_properties': False,
|
||||
}
|
||||
|
||||
SUPPORTED = {'EPUB', 'AZW3'}
|
||||
@ -233,7 +234,11 @@ def polish_one(ebook, opts, report, customization=None):
|
||||
if opts.remove_unused_css:
|
||||
rt(_('Removing unused CSS rules'))
|
||||
if remove_unused_css(
|
||||
ebook, report, remove_unused_classes=customization['remove_unused_classes'], merge_rules=customization['merge_identical_selectors']):
|
||||
ebook, report,
|
||||
remove_unused_classes=customization['remove_unused_classes'],
|
||||
merge_rules=customization['merge_identical_selectors'],
|
||||
merge_rules_with_identical_properties=customization['merge_rules_with_identical_properties']
|
||||
):
|
||||
changed = True
|
||||
report('')
|
||||
|
||||
|
@ -53,6 +53,8 @@ d['inline_spell_check'] = True
|
||||
d['custom_themes'] = {}
|
||||
d['remove_unused_classes'] = False
|
||||
d['merge_identical_selectors'] = False
|
||||
d['merge_identical_selectors'] = False
|
||||
d['merge_rules_with_identical_properties'] = False
|
||||
d['global_book_toolbar'] = [
|
||||
'new-file', 'open-book', 'save-book', None, 'global-undo', 'global-redo', 'create-checkpoint', None, 'donate', 'user-manual']
|
||||
d['global_tools_toolbar'] = [
|
||||
|
@ -54,6 +54,14 @@ def customize_remove_unused_css(name, parent, ans):
|
||||
'Merge CSS rules in the same stylesheet that have identical selectors.'
|
||||
' Note that in rare cases merging can result in a change to the effective styling'
|
||||
' of the book, so use with care.'))
|
||||
d.p = p = QCheckBox(_('Merge CSS rules with identical properties'))
|
||||
p.setChecked(tprefs['merge_rules_with_identical_properties'])
|
||||
l.addWidget(p)
|
||||
d.la4 = label('<span style="font-size:small; font-style: italic">' + _(
|
||||
'Merge CSS rules in the same stylesheet that have identical properties.'
|
||||
' Note that in rare cases merging can result in a change to the effective styling'
|
||||
' of the book, so use with care.'))
|
||||
|
||||
d.bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
d.l.addWidget(d.bb)
|
||||
d.bb.rejected.connect(d.reject)
|
||||
@ -62,6 +70,7 @@ def customize_remove_unused_css(name, parent, ans):
|
||||
raise Abort()
|
||||
ans['remove_unused_classes'] = tprefs['remove_unused_classes'] = c.isChecked()
|
||||
ans['merge_identical_selectors'] = tprefs['merge_identical_selectors'] = m.isChecked()
|
||||
ans['merge_rules_with_identical_properties'] = tprefs['merge_rules_with_identical_properties'] = p.isChecked()
|
||||
|
||||
|
||||
def get_customization(action, name, parent):
|
||||
|
Loading…
x
Reference in New Issue
Block a user