+++ /dev/null
-# -*- coding: utf-8 -*-
-#######################################################################################
-# Photo is a part of Plinn - http://plinn.org #
-# Copyright © 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. #
-#######################################################################################
-#
-#
-
-from types import StringTypes
-from logging import getLogger
-import re
-console = getLogger('Photo.xmp')
-
-class XMP(object) :
- XMP_HEADER = u'<?xpacket begin="\ufeff" id="W5M0MpCehiHzreSzNTczkc9d"?>'
- XMP_HEADER_PATTERN = u'''<\?xpacket begin=['"]\ufeff['"] id=['"]W5M0MpCehiHzreSzNTczkc9d['"][^\?]*\?>'''
- XMP_PADDING_LINE = u'\u0020' * 63 + u'\n'
- XMP_TRAILER = u'<?xpacket end="w"?>'
-
- _readers = {}
- _writers = {}
-
-
-
- def __init__(self, file, content_type='image/jpeg', encoding='utf-8') :
- try :
- self.reader = self._readers[content_type]
- except KeyError:
- raise NotImplementedError, "%r content type not supported by XMP" % content_type
-
- try :
- self.writer = self._writers[content_type]
- except KeyError :
- self.writer = None
- console.info('XMP file opened on read-only mode.')
-
- self.file = file
- self.encoding = encoding
- self.xmp = None
- self._open()
-
-
- def __del__(self) :
- try :
- self.file.close()
- except :
- pass
-
-
- def _open(self) :
-
- if type(self.file) in StringTypes :
- self.file = file(self.file)
-
- packet = self.reader(self.file)
-
- if packet is not None :
- # tests / unwrap
- reEncodedHeader = re.compile(self.XMP_HEADER_PATTERN.encode(self.encoding))
- m = reEncodedHeader.match(packet)
- if m is None :
- console.warn('XMP packet wrapper not found')
- self.xmp = packet
- return
-
- xmp = packet[m.end():]
- trailer = self.XMP_TRAILER[:-6].encode(self.encoding) # TODO handle read-only mode
- trailerPos = xmp.find(trailer)
- assert trailerPos != -1, "No xmp trailer found"
-
- xmp = xmp[:trailerPos]
- xmp = xmp.strip()
- self.xmp = xmp
- else :
- self.xmp = None
-
- def save(self, f=None):
- original = self.file
- if f :
- if type(f) in StringTypes :
- new = file(f, 'w')
- else :
- new = f
- elif f is None :
- new = self.file
-
- self.writer(original, new, self.xmp)
-
-
- def getXMP(self) :
- return self.xmp
-
-
- def setXMP(self, xmp) :
- self.xmp = xmp
-
- #
- # xmp utils
- #
-
- @staticmethod
- def getXmpPadding(size) :
- # size of trailer in kB
- return (XMP.XMP_PADDING_LINE * 32 * size)
-
-
- @staticmethod
- def genXMPPacket(uXmpData, paddingSize):
- packet = u''
-
- packet += XMP.XMP_HEADER
- packet += uXmpData
- packet += XMP.getXmpPadding(paddingSize)
- packet += XMP.XMP_TRAILER
-
- return packet
-
-
-
- #
- # content type registry stuff
- #
-
-
- @classmethod
- def registerReader(cls, content_type, reader) :
- cls._readers[content_type] = reader
-
- @classmethod
- def registerWriter(cls, content_type, writer) :
- cls._writers[content_type] = writer
-
- @classmethod
- def registerWrapper(cls, content_type, wrapper) :
- """ Registers specific wrapper to prepare data
- for embedding xmp into specific content_type file.
- """
- pass
-
-
-
-def test() :
- from xml.dom.minidom import parse
- data = parse('new.xmp').documentElement.toxml()
-
- def test1() :
- original = 'original.jpg'
- modified = 'modified.jpg'
-
- x = XMP(original)
- x.setXMP(data)
- x.save(modified)
-
- def test2() :
- from cStringIO import StringIO
- sio = StringIO()
- sio.write(file('modified.jpg').read())
- sio.seek(0)
-
- x = XMP(sio)
- x.setXMP(data)
- x.save()
-
- f2 = open('modified2.jpg', 'w')
- f2.write(sio.read())
- f2.close()
-
-
- test1()
- test2()
-
-
-
-if __name__ == '__main__' :
- test()