diff --git a/src/calibre/ebooks/metadata/opf.xml b/src/calibre/ebooks/metadata/opf.xml index f8aa09b64e..2c9127ed87 100644 --- a/src/calibre/ebooks/metadata/opf.xml +++ b/src/calibre/ebooks/metadata/opf.xml @@ -16,9 +16,9 @@ ${mi.comments} ${mi.publisher} ${mi.isbn} - ${mi.series} - ${mi.series_index} - ${mi.rating} + + + ${tag} diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index ed0340e4a8..a62838d932 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -392,8 +392,8 @@ class MetadataField(object): def __set__(self, obj, val): elem = obj.get_metadata_element(self.name) if elem is None: - elem = obj.create_metadata_element(self.name, ns='dc' if self.is_dc else 'opf') - elem.text = unicode(val) + elem = obj.create_metadata_element(self.name, is_dc=self.is_dc) + obj.set_text(elem, unicode(val)) class OPF(object): MIMETYPE = 'application/oebps-package+xml' @@ -403,16 +403,18 @@ class OPF(object): 'dc' : "http://purl.org/dc/elements/1.1/", 'opf' : "http://www.idpf.org/2007/opf", } + META = '{%s}meta' % NAMESPACES['opf'] xpn = NAMESPACES.copy() xpn.pop(None) xpn['re'] = 'http://exslt.org/regular-expressions' XPath = functools.partial(etree.XPath, namespaces=xpn) + CONTENT = XPath('self::*[re:match(name(), "meta$", "i")]/@content') TEXT = XPath('string()') metadata_path = XPath('descendant::*[re:match(name(), "metadata", "i")]') - metadata_elem_path = XPath('descendant::*[re:match(name(), $name, "i")]') - series_path = XPath('descendant::*[re:match(name(), "series$", "i")]') + metadata_elem_path = XPath('descendant::*[re:match(name(), $name, "i") or (re:match(name(), "^meta$", "i") and re:match(@name, $name, "i"))]') + series_path = XPath('descendant::*[re:match(name(), "series$", "i") or (re:match(name(), "^meta$", "i") and re:match(@name, "series$", "i"))]') authors_path = XPath('descendant::*[re:match(name(), "creator", "i") and (@role="aut" or @opf:role="aut" or (not(@role) and not(@opf:role)))]') bkp_path = XPath('descendant::*[re:match(name(), "contributor", "i") and (@role="bkp" or @opf:role="bkp")]') tags_path = XPath('descendant::*[re:match(name(), "subject", "i")]') @@ -497,7 +499,13 @@ class OPF(object): def get_text(self, elem): - return u''.join(self.TEXT(elem)) + return u''.join(self.CONTENT(elem) or self.TEXT(elem)) + + def set_text(self, elem, content): + if elem.tag == self.META: + elem.attib['content'] = content + else: + elem.text = content def itermanifest(self): return self.manifest_path(self.root) @@ -611,9 +619,9 @@ class OPF(object): for elem in remove: self.metadata.remove(elem) for author in val: - elem = self.create_metadata_element('creator', ns='dc', - attrib={'{%s}role'%self.NAMESPACES['opf']:'aut'}) - elem.text = author + attrib = {'{%s}role'%self.NAMESPACES['opf']: 'aut'} + elem = self.create_metadata_element('creator', attrib=attrib) + self.set_text(elem, author) return property(fget=fget, fset=fset) @@ -650,8 +658,8 @@ class OPF(object): for tag in list(self.tags_path(self.metadata)): self.metadata.remove(tag) for tag in val: - elem = self.create_metadata_element('subject', ns='dc') - elem.text = unicode(tag) + elem = self.create_metadata_element('subject') + self.set_text(elem, unicode(tag)) return property(fget=fget, fset=fset) @@ -660,14 +668,15 @@ class OPF(object): def fget(self): for match in self.isbn_path(self.metadata): - return match.text if match.text else None + return self.get_text(match) or None def fset(self, val): matches = self.isbn_path(self.metadata) if not matches: - matches = [self.create_metadata_element('identifier', ns='dc', - attrib={'{%s}scheme'%self.NAMESPACES['opf']:'ISBN'})] - matches[0].text = unicode(val) + attrib = {'{%s}scheme'%self.NAMESPACES['opf']: 'ISBN'} + matches = [self.create_metadata_element('identifier', + attrib=attrib)] + self.set_text(matches[0], unicode(val)) return property(fget=fget, fset=fset) @@ -676,14 +685,15 @@ class OPF(object): def fget(self): for match in self.application_id_path(self.metadata): - return match.text if match.text else None + return self.get_text(match) or None def fset(self, val): matches = self.application_id_path(self.metadata) if not matches: - matches = [self.create_metadata_element('identifier', ns='dc', - attrib={'{%s}scheme'%self.NAMESPACES['opf']:'calibre'})] - matches[0].text = unicode(val) + attrib = {'{%s}scheme'%self.NAMESPACES['opf']: 'calibre'} + matches = [self.create_metadata_element('identifier', + attrib=attrib)] + self.set_text(matches[0], unicode(val)) return property(fget=fget, fset=fset) @@ -693,13 +703,13 @@ class OPF(object): def fget(self): for match in self.series_path(self.metadata): - return match.text if match.text else None + return self.get_text(match) or None def fset(self, val): matches = self.series_path(self.metadata) if not matches: - matches = [self.create_metadata_element('series')] - matches[0].text = unicode(val) + matches = [self.create_metadata_element('series', is_dc=False)] + self.set_text(matches[0], unicode(val)) return property(fget=fget, fset=fset) @@ -710,14 +720,15 @@ class OPF(object): def fget(self): for match in self.bkp_path(self.metadata): - return match.text if match.text else None + return self.get_text(match) or None def fset(self, val): matches = self.bkp_path(self.metadata) if not matches: - matches = [self.create_metadata_element('contributor', ns='dc', - attrib={'{%s}role'%self.NAMESPACES['opf']:'bkp'})] - matches[0].text = unicode(val) + attrib = {'{%s}role'%self.NAMESPACES['opf']: 'bkp'} + matches = [self.create_metadata_element('contributor', + attrib=attrib)] + self.set_text(matches[0], unicode(val)) return property(fget=fget, fset=fset) @@ -783,9 +794,15 @@ class OPF(object): if matches: return matches[-1] - def create_metadata_element(self, name, attrib=None, ns='opf'): - elem = etree.SubElement(self.metadata, '{%s}%s'%(self.NAMESPACES[ns], name), - attrib=attrib, nsmap=self.NAMESPACES) + def create_metadata_element(self, name, attrib=None, is_dc=True): + if is_dc: + name = '{%s}%s' % (self.NAMESPACES['dc'], name) + else: + attrib = attrib or {} + attrib['name'] = name + name = '{%s}%s' % (self.NAMESPACES['opf'], 'meta') + elem = etree.SubElement(self.metadata, name, attrib=attrib, + nsmap=self.NAMESPACES) elem.tail = '\n' return elem