+++ /dev/null
-# -*- coding: utf-8 -*-
-#######################################################################################
-# Photo is a part of Plinn - http://plinn.org #
-# Copyright (C) 2008 Benoît PIN <benoit.pin@ensmp.fr> #
-# #
-# This program is free software; you can redistribute it and/or #
-# modify it under the terms of the GNU General Public License #
-# as published by the Free Software Foundation; either version 2 #
-# of the License, or (at your option) any later version. #
-# #
-# This program is distributed in the hope that it will be useful, #
-# but WITHOUT ANY WARRANTY; without even the implied warranty of #
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
-# GNU General Public License for more details. #
-# #
-# You should have received a copy of the GNU General Public License #
-# along with this program; if not, write to the Free Software #
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #
-#######################################################################################
-""" XMP generation utilities.
-
-
-
-"""
-
-from libxml2 import newNode, parseDoc, treeError
-# prefix <-> namespaces mappings as defined in the official xmp documentation
-from standards.xmp import namespaces as xmpNs2Prefix
-from standards.xmp import prefix2Ns as xmpPrefix2Ns
-
-TIFF_ORIENTATIONS = {1 : (0, False)
- ,2 : (0, True)
- ,3 : (180, False)
- ,4 : (180, True)
- ,5 : (90, True)
- ,6 : (90, False)
- ,7 : (270, True)
- ,8 : (270, False)}
-
-def _getRDFArrayValues(node, arrayType):
- values = []
- for element in iterElementChilds(node):
- if element.name == arrayType and element.ns().content == 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' :
- for value in iterElementChilds(element):
- if value.name == 'li':
- values.append(value.content)
- return tuple(values)
- else :
- raise ValueError("No %s found" % arrayType )
-
-def getBagValues(node):
- return _getRDFArrayValues(node, 'Bag')
-
-def getSeqValues(node):
- return _getRDFArrayValues(node, 'Seq')
-
-
-def createRDFAlt(surrounded, defaultText, rootIndex):
- """
- returns (as libxml2 node):
- <surrounded>
- <rdf:Alt>
- <rdf:li xml:lang="x-default">defaultText</rdf:li>
- </rdf:Alt>
- <surrounded>
- """
- docNs = rootIndex.getDocumentNs()
- rdfPrefix = docNs['http://www.w3.org/1999/02/22-rdf-syntax-ns#']
- normalizedPrefix, name = surrounded.split(':')
- ns = xmpPrefix2Ns[normalizedPrefix]
- actualPrefix = docNs[ns]
-
- surrounded = newNode('%s:%s' % (actualPrefix, name))
- alt = newNode('%s:Alt' % rdfPrefix)
- li = newNode('%s:li' % rdfPrefix)
- li.newProp('xml:lang', 'x-default')
- li.setContent(defaultText)
-
- reduce(lambda a, b: a.addChild(b), (surrounded, alt, li))
-
- return surrounded
-
-
-def createRDFBag(surrounded, values, rootIndex):
- """
- returns (as libxml2 node):
- <surrounded>
- <rdf:Bag>
- <rdf:li>values[0]</rdf:li>
- ...
- <rdf:li>values[n]</rdf:li>
- </rdf:Bag>
- <surrounded>
- """
- return _createRDFArray(surrounded, values, False, rootIndex)
-
-def createRDFSeq(surrounded, values, rootIndex):
- """
- returns (as libxml2 node):
- <surrounded>
- <rdf:Seq>
- <rdf:li>values[0]</rdf:li>
- ...
- <rdf:li>values[n]</rdf:li>
- </rdf:Seq>
- <surrounded>
- """
- return _createRDFArray(surrounded, values, True, rootIndex)
-
-def _createRDFArray(surrounded, values, ordered, rootIndex):
- docNs = rootIndex.getDocumentNs()
- rdfPrefix = docNs['http://www.w3.org/1999/02/22-rdf-syntax-ns#']
- normalizedPrefix, name = surrounded.split(':')
- ns = xmpPrefix2Ns[normalizedPrefix]
- actualPrefix = docNs[ns]
-
-
- surrounded = newNode('%s:%s' % (actualPrefix, name))
- if ordered is True :
- array = newNode('%s:Seq' % rdfPrefix)
- elif ordered is False :
- array = newNode('%s:Bag' % rdfPrefix)
- else :
- raise ValueError("'ordered' parameter must be a boolean value")
-
- surrounded.addChild(array)
-
- for v in values :
- li = newNode('%s:li' % rdfPrefix)
- li.setContent(v)
- array.addChild(li)
-
- return surrounded
-
-def createEmptyXmpDoc() :
- emptyDocument = """
-<x:xmpmeta xmlns:x='adobe:ns:meta/'>
- <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <rdf:Description/>
- </rdf:RDF>
-</x:xmpmeta>
- """
- d = parseDoc(emptyDocument)
- return d
-
-def getPathIndex(doc) :
- root = doc.getRootElement()
- index = PathIndex(root)
- return index
-
-
-class PathIndex :
- """\
- Class used to provide a convenient tree access to xmp properties by paths.
- Issues about namespaces and prefixes are normalized during the object
- instanciation. Ns prefixes used to access elements are those recommended in the
- official xmp documentation from Adobe.
- """
-
- def __init__(self, element, parent=None) :
- self.unique = True
- self.element = element
- self.parent = parent
-
- elementNs = element.ns().content
- elementPrefix = element.ns().name
- recommendedPrefix = xmpNs2Prefix.get(elementNs, elementPrefix)
-
- self.name = '%s:%s' % (recommendedPrefix, element.name)
- self.namespace = elementNs
- self.prefix = elementPrefix
- self._index = {}
-
- for prop in iterElementProperties(element) :
- self.addChildIndex(prop)
-
- for child in iterElementChilds(element) :
- self.addChildIndex(child)
-
- if self.parent is None:
- self.nsDeclarations = self._namespaceDeclarations()
-
- def addChildIndex(self, child) :
- ns = child.ns()
- if not ns :
- return
-
- childNs = ns.content
- childPrefix = ns.name
- childRecommendedPrefix = xmpNs2Prefix.get(childNs, childPrefix)
- childName = '%s:%s' % (childRecommendedPrefix, child.name)
-
- if not self._index.has_key(childName) :
- self._index[childName] = PathIndex(child, parent=self)
- else :
- childIndex = self._index[childName]
- childIndex.unique = False
- for prop in iterElementProperties(child) :
- childIndex.addChildIndex(prop)
-
- for c in iterElementChilds(child) :
- childIndex.addChildIndex(c)
-
- self._index[childName].parent = self
- return self._index[childName]
-
- def _namespaceDeclarations(self) :
- """\
- returns ns / prefix pairs as found in xmp packet
- """
- namespaces = {}
- namespaces[self.namespace] = self.prefix
- for child in self._index.values() :
- for namespace, prefix in child._namespaceDeclarations().items() :
- if namespaces.has_key(namespace) :
- assert namespaces[namespace] == prefix, \
- "using several prefix for the same namespace is forbidden "\
- "in this implementation"
- else :
- namespaces[namespace] = prefix
- return namespaces
-
- def getDocumentNs(self) :
- root = self.getRootIndex()
- return root.nsDeclarations
-
- def exists(self, path) :
- o = self
- for part in path.split('/') :
- if o._index.has_key(part) :
- o = o._index[part]
- else :
- return False
- return True
-
- def __getitem__(self, path) :
- o = self
- try :
- for part in path.split('/') :
- if part == '.' :
- continue
- elif part == '..' :
- o = o.parent
- o = o._index[part]
- except ValueError :
- raise KeyError, path
- return o
-
- def get(self, path, default=None) :
- try :
- return self[path]
- except KeyError :
- return default
-
- def getRootIndex(self) :
- root = self
- while root.parent is not None :
- root = root.parent
- return root
-
- def createChildAndIndex(self, name, rdfType, nsDeclarationElement) :
- recommandedPrefix, name = name.split(':', 1)
-
- if rdfType == 'prop' :
- try :
- node = self.element.newProp(name, '')
- except treeError :
- raise ValueError, (self.element, name)
- else :
- node = newNode(name)
- self.element.addChild(node)
-
- # bind namespace to new node
- uri = xmpPrefix2Ns[recommandedPrefix]
- docNamespaces = self.getDocumentNs()
- if not docNamespaces.has_key(uri) :
- try :
- ns = nsDeclarationElement.newNs(uri, recommandedPrefix)
- except treeError :
- raise ValueError, (uri, prefix, self.element, list(nsDeclarationElement.nsDefs()))
- docNamespaces[uri] = recommandedPrefix
- else :
- actualPrefix = docNamespaces[uri]
- try :
- ns = self.element.searchNs(None, actualPrefix)
- except treeError:
- # cas d'un xmp verbeux : le nouvel élément n'est pas ajouté
- # dans le rdf:Description du ns correspondant
- # (après tout, ce n'est pas une obligation)
- # => on ajoute le ns
- ns = nsDeclarationElement.newNs(uri, actualPrefix)
-
-
- node.setNs(ns)
- return self.addChildIndex(node)
-
- def getOrCreate(self, path, rdfType, preferedNsDeclaration='rdf:RDF/rdf:Description') :
- parts = path.split('/')
-
- if not parts :
- return self
-
- name = parts[-1]
- parts = parts[:-1]
- root = self.getRootIndex()
- nsDeclarationElement = root[preferedNsDeclaration].element
-
- parent = self
- for p in parts :
- child = parent._index.get(p, None)
- if child is None :
- child = parent.createChildAndIndex(p, None, nsDeclarationElement)
- parent = child
-
- child = parent._index.get(name, None)
- if child is None :
- child = parent.createChildAndIndex(name, rdfType, nsDeclarationElement)
-
- return child
-
- def __str__(self) :
- out = []
- pr = out.append
- path = [self.name]
- parent = self.parent
- while parent :
- path.append(parent.name)
- parent = parent.parent
- path.reverse()
- path = '/'.join(path)
- pr(path)
- pr(self.name)
- pr(self.namespace)
- pr(str(self.unique))
- pr('-------')
-
- for child in self._index.values() :
- pr(str(child))
-
- return '\n'.join(out)
-
-def iterElementChilds(parent) :
- child = parent.children
- while child :
- if child.type == 'element' :
- yield child
- child = child.next
-
-def iterElementProperties(element) :
- prop = element.properties
- while prop :
- if prop.type == 'attribute' :
- yield prop
- prop = prop.next