From 8470b1794d3c1fcc84991cde150569d2031b3133 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 19 Jun 2016 09:40:13 +0530 Subject: [PATCH] Implement --- src/calibre/ebooks/metadata/opf3.py | 42 +++++++++++++++++++++++- src/calibre/ebooks/metadata/opf3_test.py | 14 +++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/calibre/ebooks/metadata/opf3.py b/src/calibre/ebooks/metadata/opf3.py index 9c9a4d9c08..52ca95eede 100644 --- a/src/calibre/ebooks/metadata/opf3.py +++ b/src/calibre/ebooks/metadata/opf3.py @@ -12,8 +12,9 @@ from lxml import etree from calibre.ebooks.metadata import check_isbn from calibre.ebooks.metadata.book.base import Metadata -from calibre.ebooks.metadata.utils import parse_opf, pretty_print_opf, ensure_unique +from calibre.ebooks.metadata.utils import parse_opf, pretty_print_opf, ensure_unique, normalize_languages from calibre.ebooks.oeb.base import OPF2_NSMAP, OPF, DC +from calibre.utils.localization import canonicalize_lang # Utils {{{ # http://www.idpf.org/epub/vocab/package/pfx/ @@ -31,6 +32,13 @@ reserved_prefixes = { _xpath_cache = {} _re_cache = {} +def uniq(vals): + ''' Remove all duplicates from vals, while preserving order. ''' + vals = vals or () + seen = set() + seen_add = seen.add + return list(x for x in vals if x not in seen and not seen_add(x)) + def XPath(x): try: return _xpath_cache[x] @@ -268,6 +276,36 @@ def set_title(root, prefixes, refines, title, title_sort=None): main_title.text = title or None ts = [refdef('file-as', title_sort)] if title_sort else () set_refines(main_title, refines, refdef('title-type', 'main'), *ts) + for m in XPath('./opf:metadata/opf:meta[@name="calibre:title_sort"]')(root): + remove_element(m, refines) + +# }}} + +# Languages {{{ +def read_languages(root, prefixes, refines): + ans = [] + for lang in XPath('./opf:metadata/dc:language')(root): + val = canonicalize_lang((lang.text or '').strip()) + if val and val not in ans and val != 'und': + ans.append(val) + return uniq(ans) + +def set_languages(root, prefixes, refines, languages): + opf_languages = [] + for lang in XPath('./opf:metadata/dc:language')(root): + remove_element(lang, refines) + val = (lang.text or '').strip() + if val: + opf_languages.append(val) + languages = filter(lambda x: x and x != 'und', normalize_languages(opf_languages, languages)) + if not languages: + # EPUB spec says dc:language is required + languages = ['und'] + metadata = XPath('./opf:metadata')(root)[0] + for lang in uniq(languages): + l = metadata.makeelement(DC('language')) + l.text = lang + metadata.append(l) # }}} @@ -284,6 +322,7 @@ def read_metadata(root): ans.set_identifiers(ids) ans.title = read_title(root, prefixes, refines) or ans.title ans.title_sort = read_title_sort(root, prefixes, refines) or ans.title_sort + ans.languages = read_languages(root, prefixes, refines) or ans.languages return ans @@ -295,6 +334,7 @@ def apply_metadata(root, mi, cover_prefix='', cover_data=None, apply_null=False, prefixes, refines = read_prefixes(root), read_refines(root) set_identifiers(root, prefixes, refines, mi.identifiers, force_identifiers=force_identifiers) set_title(root, prefixes, refines, mi.title, mi.title_sort) + set_languages(root, prefixes, refines, mi.languages) pretty_print_opf(root) diff --git a/src/calibre/ebooks/metadata/opf3_test.py b/src/calibre/ebooks/metadata/opf3_test.py index 6ca638df44..e1655498b0 100644 --- a/src/calibre/ebooks/metadata/opf3_test.py +++ b/src/calibre/ebooks/metadata/opf3_test.py @@ -12,7 +12,7 @@ from lxml import etree from calibre.ebooks.metadata.opf3 import ( parse_prefixes, reserved_prefixes, expand_prefix, read_identifiers, read_metadata, set_identifiers, XPath, set_application_id, read_title, - read_refines, set_title, read_title_sort + read_refines, set_title, read_title_sort, read_languages, set_languages ) TEMPLATE = '''{metadata}''' # noqa @@ -90,6 +90,18 @@ class TestOPF3(unittest.TestCase): self.ae(st(root, 'abc'), 'abc') # }}} + def test_languages(self): # {{{ + def rl(root): + return read_languages(root, reserved_prefixes, read_refines(root)) + def st(root, languages): + set_languages(root, reserved_prefixes, read_refines(root), languages) + return rl(root) + root = self.get_opf('''en-USfr''') + self.ae(['eng', 'fra'], rl(root)) + self.ae(st(root, ['de', 'de', 'es']), ['deu', 'spa']) + self.ae(st(root, []), []) + + # }}} # Run tests {{{ class TestRunner(unittest.main):