X-Git-Url: https://scm.cri.minesparis.psl.eu/git/Photo.git/blobdiff_plain/b0a7e10b4f32cf74864bb53268ca4d3080f23bc0..6c41809185e322ce2d30e98234f71144f78f06c0:/xmp_jpeg.py?ds=inline diff --git a/xmp_jpeg.py b/xmp_jpeg.py deleted file mode 100755 index e193114..0000000 --- a/xmp_jpeg.py +++ /dev/null @@ -1,256 +0,0 @@ -# -*- coding: utf-8 -*- -####################################################################################### -# Photo is a part of Plinn - http://plinn.org # -# Copyright (C) 2008 Benoît PIN # -# # -# 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. # -####################################################################################### -""" Jpeg plugin for xmp read/write support. - - -""" - -from xmp import XMP -from types import StringType - -class JpegXmpIO(object): - - JPEG_XMP_LEADIN = 'http://ns.adobe.com/xap/1.0/\x00' - JPEG_XMP_LEADIN_LENGTH = len(JPEG_XMP_LEADIN) - - MARKERS = { - 0xFFC0: ("SOF0", "Baseline DCT", True), - 0xFFC1: ("SOF1", "Extended Sequential DCT", True), - 0xFFC2: ("SOF2", "Progressive DCT", True), - 0xFFC3: ("SOF3", "Spatial lossless", True), - 0xFFC4: ("DHT", "Define Huffman table", True), - 0xFFC5: ("SOF5", "Differential sequential DCT", True), - 0xFFC6: ("SOF6", "Differential progressive DCT", True), - 0xFFC7: ("SOF7", "Differential spatial", True), - 0xFFC8: ("JPG", "Extension", False), - 0xFFC9: ("SOF9", "Extended sequential DCT (AC)", True), - 0xFFCA: ("SOF10", "Progressive DCT (AC)", True), - 0xFFCB: ("SOF11", "Spatial lossless DCT (AC)", True), - 0xFFCC: ("DAC", "Define arithmetic coding conditioning", True), - 0xFFCD: ("SOF13", "Differential sequential DCT (AC)", True), - 0xFFCE: ("SOF14", "Differential progressive DCT (AC)", True), - 0xFFCF: ("SOF15", "Differential spatial (AC)", True), - 0xFFD0: ("RST0", "Restart 0", False), - 0xFFD1: ("RST1", "Restart 1", False), - 0xFFD2: ("RST2", "Restart 2", False), - 0xFFD3: ("RST3", "Restart 3", False), - 0xFFD4: ("RST4", "Restart 4", False), - 0xFFD5: ("RST5", "Restart 5", False), - 0xFFD6: ("RST6", "Restart 6", False), - 0xFFD7: ("RST7", "Restart 7", False), - 0xFFD8: ("SOI", "Start of image", False), - 0xFFD9: ("EOI", "End of image", False), - 0xFFDA: ("SOS", "Start of scan", True), - 0xFFDB: ("DQT", "Define quantization table", True), - 0xFFDC: ("DNL", "Define number of lines", True), - 0xFFDD: ("DRI", "Define restart interval", True), - 0xFFDE: ("DHP", "Define hierarchical progression", True), - 0xFFDF: ("EXP", "Expand reference component", True), - 0xFFE0: ("APP0", "Application segment 0", True), - 0xFFE1: ("APP1", "Application segment 1", True), - 0xFFE2: ("APP2", "Application segment 2", True), - 0xFFE3: ("APP3", "Application segment 3", True), - 0xFFE4: ("APP4", "Application segment 4", True), - 0xFFE5: ("APP5", "Application segment 5", True), - 0xFFE6: ("APP6", "Application segment 6", True), - 0xFFE7: ("APP7", "Application segment 7", True), - 0xFFE8: ("APP8", "Application segment 8", True), - 0xFFE9: ("APP9", "Application segment 9", True), - 0xFFEA: ("APP10", "Application segment 10", True), - 0xFFEB: ("APP11", "Application segment 11", True), - 0xFFEC: ("APP12", "Application segment 12", True), - 0xFFED: ("APP13", "Application segment 13", True), - 0xFFEE: ("APP14", "Application segment 14", True), - 0xFFEF: ("APP15", "Application segment 15", True), - 0xFFF0: ("JPG0", "Extension 0", False), - 0xFFF1: ("JPG1", "Extension 1", False), - 0xFFF2: ("JPG2", "Extension 2", False), - 0xFFF3: ("JPG3", "Extension 3", False), - 0xFFF4: ("JPG4", "Extension 4", False), - 0xFFF5: ("JPG5", "Extension 5", False), - 0xFFF6: ("JPG6", "Extension 6", False), - 0xFFF7: ("JPG7", "Extension 7", False), - 0xFFF8: ("JPG8", "Extension 8", False), - 0xFFF9: ("JPG9", "Extension 9", False), - 0xFFFA: ("JPG10", "Extension 10", False), - 0xFFFB: ("JPG11", "Extension 11", False), - 0xFFFC: ("JPG12", "Extension 12", False), - 0xFFFD: ("JPG13", "Extension 13", False), - 0xFFFE: ("COM", "Comment", True) - } - - - @staticmethod - def i16(c,o=0): - return ord(c[o+1]) + (ord(c[o])<<8) - - @staticmethod - def getBlockInfo(marker, f): - start = f.tell() - length = JpegXmpIO.i16(f.read(2)) - - markerInfo = JpegXmpIO.MARKERS[marker] - blockInfo = { 'name' : markerInfo[0] - , 'description' : markerInfo[1] - , 'start' : start - , 'length' : length} - - jump = start + length - f.seek(jump) - - return blockInfo - - @staticmethod - def getBlockInfos(f) : - f.seek(0) - s = f.read(1) - - blockInfos = [] - - while 1: - s = s + f.read(1) - i = JpegXmpIO.i16(s) - - if JpegXmpIO.MARKERS.has_key(i): - name, desciption, handle = JpegXmpIO.MARKERS[i] - - if handle: - blockInfo = JpegXmpIO.getBlockInfo(i, f) - blockInfos.append(blockInfo) - if i == 0xFFDA: # start of scan - break - s = f.read(1) - elif i == 0 or i == 65535: - # padded marker or junk; move on - s = "\xff" - - return blockInfos - - - @staticmethod - def genJpegXmpBlock(uXmpData, paddingSize=2) : - block = u'' - - block += JpegXmpIO.JPEG_XMP_LEADIN - block += XMP.genXMPPacket(uXmpData, paddingSize) - # utf-8 mandatory in jpeg files (xmp specification) - block = block.encode('utf-8') - - length = len(block) + 2 - - # TODO : reduce padding size if this assertion occurs - assert length <= 0xfffd, "Jpeg block too long: %d (max: 0xfffd)" % hex(length) - - chrlength = chr(length >> 8 & 0xff) + chr(length & 0xff) - - block = chrlength + block - - return block - - - - @staticmethod - def read(f) : - - blockInfos = JpegXmpIO.getBlockInfos(f) - app1BlockInfos = [b for b in blockInfos if b['name'] == 'APP1'] - - xmpBlocks = [] - - for info in app1BlockInfos : - f.seek(info['start']) - data = f.read(info['length'])[2:] - if data.startswith(JpegXmpIO.JPEG_XMP_LEADIN) : - xmpBlocks.append(data) - - assert len(xmpBlocks) <= 1, "Multiple xmp block data is not yet supported." - - if len(xmpBlocks) == 1 : - data = xmpBlocks[0] - packet = data[len(JpegXmpIO.JPEG_XMP_LEADIN):] - return packet - else : - return None - - @staticmethod - def write(original, new, uxmp) : - - blockInfos = JpegXmpIO.getBlockInfos(original) - app1BlockInfos = [b for b in blockInfos if b['name'] == 'APP1'] - - xmpBlockInfos = [] - - for info in app1BlockInfos : - original.seek(info['start']) - lead = original.read(JpegXmpIO.JPEG_XMP_LEADIN_LENGTH+2)[2:] - if lead == JpegXmpIO.JPEG_XMP_LEADIN : - xmpBlockInfos.append(info) - - - assert len(xmpBlockInfos) <= 1, "Multiple xmp block data is not yet supported." - - if isinstance(uxmp, StringType) : - uxmp = unicode(uxmp, 'utf-8') - - if len(xmpBlockInfos) == 0 : - blockInfo = [b for b in blockInfos if b['name'] == 'APP13'] - - if not blockInfo : - blockInfo = [b for b in blockInfos if b['name'] == 'APP1'] - - if not blockInfo : - blockInfo = [b for b in blockInfos if b['name'] == 'APP0'] - - if not blockInfo : raise ValueError, "No suitable place to write xmp segment" - - info = blockInfo[0] - print 'create xmp after: %s' % info['name'] - - original.seek(0) - before = original.read(info['start'] + info['length']) - after = original.read() - - jpegBlock = '\xFF\xE1' + JpegXmpIO.genJpegXmpBlock(uxmp) - - else : - info = xmpBlockInfos[0] - - original.seek(0) - before = original.read(info['start']) - - original.seek(info['start'] + info['length']) - after = original.read() - - jpegBlock = JpegXmpIO.genJpegXmpBlock(uxmp) - - new.seek(0) - new.write(before) - new.write(jpegBlock) - new.write(after) - - # if original == new : - # new.seek(0) - # else : - # new.close() - # original.close() - - -XMP.registerReader('image/jpeg', JpegXmpIO.read) -XMP.registerWriter('image/jpeg', JpegXmpIO.write)