diff --git a/src/calibre/ebooks/oeb/transforms/flatcss.py b/src/calibre/ebooks/oeb/transforms/flatcss.py index d3c490567e..343e87d507 100644 --- a/src/calibre/ebooks/oeb/transforms/flatcss.py +++ b/src/calibre/ebooks/oeb/transforms/flatcss.py @@ -19,6 +19,7 @@ from calibre.ebooks.oeb.base import (XHTML, XHTML_NS, CSS_MIME, OEB_STYLES, namespace, barename, XPath) from calibre.ebooks.oeb.stylizer import Stylizer from calibre.utils.filenames import ascii_filename, ascii_text +from calibre.utils.icu import numeric_sort_key COLLAPSE = re.compile(r'[ \t\r\n\v]+') STRIPNUM = re.compile(r'[-0-9]+$') @@ -459,7 +460,7 @@ class CSSFlattener(object): keep_classes = set() if cssdict: - items = sorted(cssdict.items()) + items = sorted(cssdict.iteritems()) css = u';\n'.join(u'%s: %s' % (key, val) for key, val in items) classes = node.get('class', '').strip() or 'calibre' klass = ascii_text(STRIPNUM.sub('', classes.split()[0].replace('_', ''))) @@ -577,7 +578,7 @@ class CSSFlattener(object): body = html.find(XHTML('body')) fsize = self.context.dest.fbase self.flatten_node(body, stylizer, names, styles, pseudo_styles, fsize, item.id) - items = sorted([(key, val) for (val, key) in styles.items()]) + items = sorted(((key, val) for (val, key) in styles.iteritems()), key=lambda x:numeric_sort_key(x[0])) # :hover must come after link and :active must come after :hover psels = sorted(pseudo_styles.iterkeys(), key=lambda x : {'hover':1, 'active':2}.get(x, 0)) diff --git a/src/calibre/utils/icu.py b/src/calibre/utils/icu.py index 1f54a04646..27168bc618 100644 --- a/src/calibre/utils/icu.py +++ b/src/calibre/utils/icu.py @@ -12,7 +12,7 @@ from functools import partial from calibre.constants import plugins from calibre.utils.config_base import tweaks -_icu = _collator = _primary_collator = _sort_collator = None +_icu = _collator = _primary_collator = _sort_collator = _numeric_collator = None _locale = None _none = u'' @@ -90,6 +90,29 @@ def icu_sort_key(collator, obj): obj = obj.replace(b'\0', b'') return _sort_collator.sort_key(obj) +def numeric_collator(): + global _numeric_collator + _numeric_collator = _collator.clone() + _numeric_collator.strength = _icu.UCOL_SECONDARY + _numeric_collator.numeric = True + return _numeric_collator + +def numeric_sort_key(obj): + 'Uses natural sorting for numbers inside strings so something2 will sort before something10' + if not obj: + return _none2 + try: + try: + return _numeric_collator.sort_key(obj) + except AttributeError: + return numeric_collator().sort_key(obj) + except TypeError: + if isinstance(obj, unicode): + obj = obj.replace(u'\0', u'') + else: + obj = obj.replace(b'\0', b'') + return _numeric_collator.sort_key(obj) + def icu_change_case(upper, locale, obj): func = _icu.upper if upper else _icu.lower try: