From 244660ec9e19429d2e8856ad497c4cee220d4bb6 Mon Sep 17 00:00:00 2001 From: jevans Date: Tue, 28 May 2013 20:04:23 -0400 Subject: [PATCH 1/3] Can brute-force write a file such that exiv2 can read it. --- glymur/test/__init__.py | 1 + glymur/test/test_xmp.py | 84 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 glymur/test/test_xmp.py diff --git a/glymur/test/__init__.py b/glymur/test/__init__.py index 9677af5..f396e49 100644 --- a/glymur/test/__init__.py +++ b/glymur/test/__init__.py @@ -5,3 +5,4 @@ from .test_printing import TestPrinting as printing from .test_opj_suite import TestSuite as suite from .test_opj_suite_write import TestSuiteWrite as suitew from .test_opj_suite_neg import TestSuiteNegative as suiteneg +from .test_xmp import TestSuite as xmp diff --git a/glymur/test/test_xmp.py b/glymur/test/test_xmp.py new file mode 100644 index 0000000..14b3b6f --- /dev/null +++ b/glymur/test/test_xmp.py @@ -0,0 +1,84 @@ +import os +import struct +import sys +import tempfile +import uuid +import unittest +if sys.hexversion <= 0x03030000: + from mock import patch +else: + from unittest.mock import patch +import warnings +from xml.etree import cElementTree as ET + +import pkg_resources + +from glymur import Jp2k +from glymur.lib import openjp2 as opj2 +import glymur + +lines = [' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' Rubbertree' + + ' ' + + ' ' + + ' ' + + ' \n' + + ''] +packet = '\n'.join(lines) + +class TestSuite(unittest.TestCase): + + @classmethod + def setUpClass(cls): + jp2file = pkg_resources.resource_filename(glymur.__name__, + "data/nemo.jp2") + with tempfile.NamedTemporaryFile(suffix='.jp2', delete=False) as tfile: + cls._xmp_file = tfile.name + with open(jp2file, 'rb') as ifile: + # Everything up until the jp2c box. + buffer = ifile.read(77) + tfile.write(buffer) + + # Write the uuid box with bad xml + # Length = 4 + 4 + 16 + length of packet, id is 'xml '. + L = 4 + 4 + 16 + len(packet.encode()) + buffer = struct.pack('>I4s', int(L), b'uuid') + tfile.write(buffer) + tfile.write(b'\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8') + tfile.write(b'\x9c\x71\x99\x94\x91\xe3\xaf\xac') + tfile.write(packet.encode()) + + # Get the rest of the input file. + buffer = ifile.read() + tfile.write(buffer) + tfile.flush() + + @classmethod + def tearDownClass(cls): + os.unlink(cls._xmp_file) + + def setUp(self): + self.jp2file = pkg_resources.resource_filename(glymur.__name__, + "data/nemo.jp2") + + def tearDown(self): + pass + + def test_basic_xmp(self): + b = b'\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8\x9c\x71\x99\x94\x91\xe3\xaf\xac' + xmp_uuid = uuid.UUID(bytes=b) + jp2 = Jp2k(self._xmp_file) + self.assertEqual(jp2.box[3].uuid, xmp_uuid) + + +if __name__ == "__main__": + unittest.main() From e359f3f66690bf4d58fb834748e94135e74cf02c Mon Sep 17 00:00:00 2001 From: jevans Date: Thu, 30 May 2013 20:31:58 -0400 Subject: [PATCH 2/3] Added exif and xmp uuids from original jpeg. --- glymur/data/nemo.jp2 | Bin 1133504 -> 1136554 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/glymur/data/nemo.jp2 b/glymur/data/nemo.jp2 index 1b97d0dc85cb4722b6b04f2973e03583a0d25048..2bb8638b80ce3f116bd5f2197a40198c0dfc7e5c 100644 GIT binary patch delta 3137 zcmcJR&ui0Q7{{Na?S6FA`Rn8lh*41nZPKhm8`=)->NfSAvK8hvYqKq^ZEBj?t_O=k zaCn*`h~QyYMcB!cpdP$=@Lv$Tco6&-eBLD6(z(&Nu7S7j_j%rLp3n0>(fnOAFZrtgPu@uXwu|7{lN!|a}z}+uc-!1VocLp`v6>C zbIRPI*7aVy9Z{0Rz&Z=3Fm(#;L04Zv%{+HJ4C2EtoHmoR|h*FyjySC7+|@sY&Xh%8F7nBZb@Iet|h9oaLdpud2A7BAvVZzgdK)eat@2{3>b z1_IQG)1p>7v}yarke&aHg#vDV4)Z~9@pxrq9N4FyP|K7%Ji zSYsUZIbfSMK8FE)hH=qibewcQf5f@Lgd5{9?EKdrCy${WpLBP@-ACPhZeliaEe>#f zHZwJax#?_f?(WQO&$RIV0R2V!34UsP|Mu$bjbCpbyngxo=jQw5SiPd;msOLSR~NN% zivRQRGtX&-6hAjgL{iD9OWI_^P-h#{xqM?epD6HS>2R_x*GrX>YARg4R4iBJdWu&H z`n)QmrfGraY>T;^;_s8@JuaggDi@E&qhbWV`iByq4s%JPuprNj-*jtWl;W36vmy&Z ztyYWHMxwg0D8v$pgdj?SBt_5>sjikyr5-6)FVGTBjd8V_H?)eW>1B@AROa;+GsW|G z&e7sc+F_}(UA21-Ib*1bsT(<6FQ)j#;%aHd;04;Q;P#96 z7q$mLn`rkm+Zo!Q`=IP!#5cbqif?|GD6M=i#P|JMOZbSql>P0U^8Had<#$T)$L}7+ z!`}tzbpD>oUb&0nJ!;h*@qJTTncm0;{}3o%VyoWfca^fm{1>v#-?ALS{x4y z59Q$e!=bd}cbj7K_k9t6oMfxogw#a}*1}$>LB1bJUWRYVZ$nom!za8(-@c;k- delta 81 zcmZ3r-u1wA*9m^j7Ch}1Jd8lh1jNih%mT!$K+Fcj>_E%`#GF9P1;pGy3{uYv#C$-^ N55xl7EqDZrya9uu6X5^= From b0b08d537ca79fd67c302577025a7e9f69fc7c6b Mon Sep 17 00:00:00 2001 From: jevans Date: Thu, 30 May 2013 20:32:37 -0400 Subject: [PATCH 3/3] Recognizing XMP uuid data as xml. --- glymur/jp2box.py | 94 +++++++++++++++++++++++------------ glymur/jp2k.py | 2 +- glymur/test/__init__.py | 1 - glymur/test/test_callbacks.py | 2 +- glymur/test/test_jp2k.py | 32 ++++++++---- glymur/test/test_printing.py | 55 +++++++++++++------- glymur/test/test_xmp.py | 84 ------------------------------- 7 files changed, 121 insertions(+), 149 deletions(-) delete mode 100644 glymur/test/test_xmp.py diff --git a/glymur/jp2box.py b/glymur/jp2box.py index 47f7bda..a435f67 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -1288,37 +1288,11 @@ class XMLBox(Jp2kBox): Jp2kBox.__init__(self, id='', longname='XML') self.__dict__.update(**kwargs) - def _indent(self, elem, level=0): - """recipe for pretty printing XML. Please see - - http://effbot.org/zone/element-lib.htm#prettyprint - """ - i = "\n" + level * " " - if len(elem): - if not elem.text or not elem.text.strip(): - elem.text = i + " " - if not elem.tail or not elem.tail.strip(): - elem.tail = i - for elem in elem: - self._indent(elem, level + 1) - if not elem.tail or not elem.tail.strip(): - elem.tail = i - else: - if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i - def __str__(self): msg = Jp2kBox.__str__(self) xml = self.xml if self.xml is not None: - xml = copy.deepcopy(self.xml) - self._indent(xml) - xmltext = ET.tostring(xml).decode('utf-8') - - # Indent it a bit. - lst = [(' ' + x) for x in xmltext.split('\n')] - xml = '\n'.join(lst) - msg += '\n{0}'.format(xml) + msg += _pretty_print_xml(self.xml) else: msg += '\n {0}'.format(xml) return msg @@ -1581,17 +1555,30 @@ class UUIDBox(Jp2kBox): more verbose description of the box. uuid : uuid.UUID 16-byte UUID - data : bytes - Vendor-specific UUID data. + data : bytes or ElementTree.Element + Vendor-specific UUID data. XMP UUIDs are interpreted as standard XML. """ def __init__(self, **kwargs): Jp2kBox.__init__(self, id='', longname='UUID') self.__dict__.update(**kwargs) def __str__(self): - msg = Jp2kBox.__str__(self) - msg += '\n UUID: {0}'.format(self.uuid) - msg += '\n UUID Data: {0} bytes'.format(len(self.data)) + msg = '{0}\n' + msg += ' UUID: {1}{2}\n' + msg += ' UUID Data: {3}' + + if self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'): + uuid_type = ' (XMP)' + uuid_data = _pretty_print_xml(self.data) + else: + uuid_type = '' + uuid_data = '{0} bytes'.format(len(self.data)) + + msg = msg.format(Jp2kBox.__str__(self), + self.uuid, + uuid_type, + uuid_data) + return msg @staticmethod @@ -1622,7 +1609,17 @@ class UUIDBox(Jp2kBox): n = offset + length - f.tell() buffer = f.read(n) - kwargs['data'] = buffer + if kwargs['uuid'] == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'): + # XMP data. Parse as XML. Seems to be a difference between + # ElementTree in version 2.7 and 3.3. + if sys.hexversion < 0x03000000: + parser = ET.XMLParser(encoding='utf-8') + kwargs['data'] = ET.fromstringlist(buffer, parser=parser) + else: + text = buffer.decode('utf-8') + kwargs['data'] = ET.fromstring(text) + else: + kwargs['data'] = buffer box = UUIDBox(**kwargs) return box @@ -1649,3 +1646,34 @@ _box_with_id = { 'url ': DataEntryURLBox, 'uuid': UUIDBox, 'xml ': XMLBox} + +def _indent(elem, level=0): + """Recipe for pretty printing XML. Please see + + http://effbot.org/zone/element-lib.htm#prettyprint + """ + i = "\n" + level * " " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for elem in elem: + _indent(elem, level + 1) + if not elem.tail or not elem.tail.strip(): + elem.tail = i + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = i + +def _pretty_print_xml(xml, level=0): + """Pretty print XML data. + """ + xml = copy.deepcopy(xml) + _indent(xml, level=level) + xmltext = ET.tostring(xml).decode('utf-8') + + # Indent it a bit. + lst = [(' ' + x) for x in xmltext.split('\n')] + xml = '\n'.join(lst) + return '\n{0}'.format(xml) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 6eb23fd..a4ba10a 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -634,7 +634,7 @@ class Jp2k(Jp2kBox): >>> jp = glymur.Jp2k(jfile) >>> codestream = jp.get_codestream() >>> print(codestream.segment[1]) - SIZ marker segment @ (87, 47) + SIZ marker segment @ (3137, 47) Profile: 2 Reference Grid Height, Width: (1456 x 2592) Vertical, Horizontal Reference Grid Offset: (0 x 0) diff --git a/glymur/test/__init__.py b/glymur/test/__init__.py index f396e49..9677af5 100644 --- a/glymur/test/__init__.py +++ b/glymur/test/__init__.py @@ -5,4 +5,3 @@ from .test_printing import TestPrinting as printing from .test_opj_suite import TestSuite as suite from .test_opj_suite_write import TestSuiteWrite as suitew from .test_opj_suite_neg import TestSuiteNegative as suiteneg -from .test_xmp import TestSuite as xmp diff --git a/glymur/test/test_callbacks.py b/glymur/test/test_callbacks.py index 1c70743..aae4ddc 100644 --- a/glymur/test/test_callbacks.py +++ b/glymur/test/test_callbacks.py @@ -53,7 +53,7 @@ class TestCallbacks(unittest.TestCase): d = j.read(reduce=3, verbose=True, area=(0, 0, 512, 1024)) actual = sys.stdout.getvalue().strip() - lines = ['[INFO] Start to read j2k main header (85).', + lines = ['[INFO] Start to read j2k main header (3135).', '[INFO] Main header has been correctly decoded.', '[INFO] Setting decoding area to 0,0,1024,512', '[INFO] Header of tile 0 / 17 has been read.', diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index ae6af19..4606414 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -155,7 +155,7 @@ class TestJp2k(unittest.TestCase): jp2k = Jp2k(self.jp2file) # top-level boxes - self.assertEqual(len(jp2k.box), 4) + self.assertEqual(len(jp2k.box), 6) self.assertEqual(jp2k.box[0].id, 'jP ') self.assertEqual(jp2k.box[0].offset, 0) @@ -172,9 +172,17 @@ class TestJp2k(unittest.TestCase): self.assertEqual(jp2k.box[2].length, 45) self.assertEqual(jp2k.box[2].longname, 'JP2 Header') - self.assertEqual(jp2k.box[3].id, 'jp2c') + self.assertEqual(jp2k.box[3].id, 'uuid') self.assertEqual(jp2k.box[3].offset, 77) - self.assertEqual(jp2k.box[3].length, 1133427) + self.assertEqual(jp2k.box[3].length, 638) + + self.assertEqual(jp2k.box[4].id, 'uuid') + self.assertEqual(jp2k.box[4].offset, 715) + self.assertEqual(jp2k.box[4].length, 2412) + + self.assertEqual(jp2k.box[5].id, 'jp2c') + self.assertEqual(jp2k.box[5].offset, 3127) + self.assertEqual(jp2k.box[5].length, 1133427) # jp2h super box self.assertEqual(len(jp2k.box[2].box), 2) @@ -216,7 +224,7 @@ class TestJp2k(unittest.TestCase): with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with open(self.jp2file, 'rb') as ifile: # Everything up until the jp2c box. - buffer = ifile.read(77) + buffer = ifile.read(3127) tfile.write(buffer) # The L field must be 1 in order to signal the presence of the @@ -237,9 +245,9 @@ class TestJp2k(unittest.TestCase): jp2k = Jp2k(tfile.name) - self.assertEqual(jp2k.box[3].id, 'jp2c') - self.assertEqual(jp2k.box[3].offset, 77) - self.assertEqual(jp2k.box[3].length, 1133427 + 8) + self.assertEqual(jp2k.box[5].id, 'jp2c') + self.assertEqual(jp2k.box[5].offset, 3127) + self.assertEqual(jp2k.box[5].length, 1133427 + 8) def test_L_is_zero(self): # Verify that boxes with the L field as zero are correctly read. @@ -544,17 +552,19 @@ class TestJp2k(unittest.TestCase): with open(self.jp2file, 'rb') as fp: data = fp.read() with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: - tfile.write(data[0:129]) + # Codestream starts at byte 3127. SIZ marker at 3137. + # COD marker at 3186. Subsampling at 3180. + tfile.write(data[0:3179]) # Make the DY bytes of the SIZ segment zero. That means that # a subsampling factor is zero, which is illegal. tfile.write(b'\x00') - tfile.write(data[130:132]) + tfile.write(data[3180:3182]) tfile.write(b'\x00') - tfile.write(data[134:136]) + tfile.write(data[3184:3186]) tfile.write(b'\x00') - tfile.write(data[136:]) + tfile.write(data[3186:]) tfile.flush() with warnings.catch_warnings(): warnings.simplefilter("ignore") diff --git a/glymur/test/test_printing.py b/glymur/test/test_printing.py index 299085d..0278d63 100644 --- a/glymur/test/test_printing.py +++ b/glymur/test/test_printing.py @@ -47,10 +47,26 @@ class TestPrinting(unittest.TestCase): ' Method: enumerated colorspace', ' Precedence: 0', ' Colorspace: sRGB', - 'Contiguous Codestream Box (jp2c) @ (77, 1133427)', + 'UUID Box (uuid) @ (77, 638)', + ' UUID: 4a706754-6966-6645-7869-662d3e4a5032', + ' UUID Data: 614 bytes', + 'UUID Box (uuid) @ (715, 2412)', + ' UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)', + ' UUID Data: ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + 'Contiguous Codestream Box (jp2c) @ (3127, 1133427)', ' Main header:', - ' SOC marker segment @ (85, 0)', - ' SIZ marker segment @ (87, 47)', + ' SOC marker segment @ (3135, 0)', + ' SIZ marker segment @ (3137, 47)', ' Profile: 2', ' Reference Grid Height, Width: (1456 x 2592)', ' Vertical, Horizontal Reference Grid Offset: ' @@ -62,7 +78,7 @@ class TestPrinting(unittest.TestCase): ' Signed: (False, False, False)', ' Vertical, Horizontal Subsampling: ' + '((1, 1), (1, 1), (1, 1))', - ' COD marker segment @ (136, 12)', + ' COD marker segment @ (3186, 12)', ' Coding style:', ' Entropy coder, without partitions', ' SOP marker segments: False', @@ -86,7 +102,7 @@ class TestPrinting(unittest.TestCase): + 'False', ' Predictable termination: False', ' Segmentation symbols: False', - ' QCD marker segment @ (150, 19)', + ' QCD marker segment @ (3200, 19)', ' Quantization style: no quantization, ' + '2 guard bits', ' Step size: [(0, 8), (0, 9), (0, 9), ' @@ -98,10 +114,13 @@ class TestPrinting(unittest.TestCase): def tearDown(self): # Restore stdout. sys.stdout = self.stdout + #import pdb; pdb.set_trace() def test_jp2dump(self): glymur.jp2dump(self.jp2file) actual = sys.stdout.getvalue().strip() + self.actual = actual + self.expected = self.expectedNemo self.assertEqual(actual, self.expectedNemo) def test_COC_segment(self): @@ -110,7 +129,7 @@ class TestPrinting(unittest.TestCase): print(codestream.segment[5]) actual = sys.stdout.getvalue().strip() - lines = ['COC marker segment @ (183, 9)', + lines = ['COC marker segment @ (3233, 9)', ' Associated component: 1', ' Coding style for this component: ' + 'Entropy coder, PARTITION = 0', @@ -136,7 +155,7 @@ class TestPrinting(unittest.TestCase): print(codestream.segment[2]) actual = sys.stdout.getvalue().strip() - lines = ['COD marker segment @ (136, 12)', + lines = ['COD marker segment @ (3186, 12)', ' Coding style:', ' Entropy coder, without partitions', ' SOP marker segments: False', @@ -226,7 +245,7 @@ class TestPrinting(unittest.TestCase): print(codestream.segment[-1]) actual = sys.stdout.getvalue().strip() - lines = ['EOC marker segment @ (1133502, 0)'] + lines = ['EOC marker segment @ (1136552, 0)'] expected = '\n'.join(lines) self.assertEqual(actual, expected) @@ -313,7 +332,7 @@ class TestPrinting(unittest.TestCase): print(codestream.segment[6]) actual = sys.stdout.getvalue().strip() - lines = ['QCC marker segment @ (194, 20)', + lines = ['QCC marker segment @ (3244, 20)', ' Associated Component: 1', ' Quantization style: no quantization, 2 guard bits', ' Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), ' @@ -329,7 +348,7 @@ class TestPrinting(unittest.TestCase): print(codestream.segment[3]) actual = sys.stdout.getvalue().strip() - lines = ['QCD marker segment @ (150, 19)', + lines = ['QCD marker segment @ (3200, 19)', ' Quantization style: no quantization, 2 guard bits', ' Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), ' + '(0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), ' @@ -344,7 +363,7 @@ class TestPrinting(unittest.TestCase): print(codestream.segment[1]) actual = sys.stdout.getvalue().strip() - lines = ['SIZ marker segment @ (87, 47)', + lines = ['SIZ marker segment @ (3137, 47)', ' Profile: 2', ' Reference Grid Height, Width: (1456 x 2592)', ' Vertical, Horizontal Reference Grid Offset: (0 x 0)', @@ -364,7 +383,7 @@ class TestPrinting(unittest.TestCase): print(codestream.segment[0]) actual = sys.stdout.getvalue().strip() - lines = ['SOC marker segment @ (85, 0)'] + lines = ['SOC marker segment @ (3135, 0)'] expected = '\n'.join(lines) self.assertEqual(actual, expected) @@ -374,7 +393,7 @@ class TestPrinting(unittest.TestCase): print(codestream.segment[9]) actual = sys.stdout.getvalue().strip() - lines = ['SOD marker segment @ (249, 0)'] + lines = ['SOD marker segment @ (3299, 0)'] expected = '\n'.join(lines) self.assertEqual(actual, expected) @@ -384,7 +403,7 @@ class TestPrinting(unittest.TestCase): print(codestream.segment[4]) actual = sys.stdout.getvalue().strip() - lines = ['SOT marker segment @ (171, 10)', + lines = ['SOT marker segment @ (3221, 10)', ' Tile part index: 0', ' Tile part length: 78629', ' Tile part instance: 0', @@ -422,8 +441,8 @@ class TestPrinting(unittest.TestCase): actual = sys.stdout.getvalue().strip() lst = ['Codestream:', - ' SOC marker segment @ (85, 0)', - ' SIZ marker segment @ (87, 47)', + ' SOC marker segment @ (3135, 0)', + ' SIZ marker segment @ (3137, 47)', ' Profile: 2', ' Reference Grid Height, Width: (1456 x 2592)', ' Vertical, Horizontal Reference Grid Offset: (0 x 0)', @@ -433,7 +452,7 @@ class TestPrinting(unittest.TestCase): ' Signed: (False, False, False)', ' Vertical, Horizontal Subsampling: ' + '((1, 1), (1, 1), (1, 1))', - ' COD marker segment @ (136, 12)', + ' COD marker segment @ (3186, 12)', ' Coding style:', ' Entropy coder, without partitions', ' SOP marker segments: False', @@ -455,7 +474,7 @@ class TestPrinting(unittest.TestCase): ' Vertically stripe causal context: False', ' Predictable termination: False', ' Segmentation symbols: False', - ' QCD marker segment @ (150, 19)', + ' QCD marker segment @ (3200, 19)', ' Quantization style: no quantization, ' + '2 guard bits', ' Step size: [(0, 8), (0, 9), (0, 9), ' diff --git a/glymur/test/test_xmp.py b/glymur/test/test_xmp.py deleted file mode 100644 index 14b3b6f..0000000 --- a/glymur/test/test_xmp.py +++ /dev/null @@ -1,84 +0,0 @@ -import os -import struct -import sys -import tempfile -import uuid -import unittest -if sys.hexversion <= 0x03030000: - from mock import patch -else: - from unittest.mock import patch -import warnings -from xml.etree import cElementTree as ET - -import pkg_resources - -from glymur import Jp2k -from glymur.lib import openjp2 as opj2 -import glymur - -lines = [' ' - + ' ' - + ' ' - + ' ' - + ' ' - + ' ' - + ' Rubbertree' - + ' ' - + ' ' - + ' ' - + ' \n' - + ''] -packet = '\n'.join(lines) - -class TestSuite(unittest.TestCase): - - @classmethod - def setUpClass(cls): - jp2file = pkg_resources.resource_filename(glymur.__name__, - "data/nemo.jp2") - with tempfile.NamedTemporaryFile(suffix='.jp2', delete=False) as tfile: - cls._xmp_file = tfile.name - with open(jp2file, 'rb') as ifile: - # Everything up until the jp2c box. - buffer = ifile.read(77) - tfile.write(buffer) - - # Write the uuid box with bad xml - # Length = 4 + 4 + 16 + length of packet, id is 'xml '. - L = 4 + 4 + 16 + len(packet.encode()) - buffer = struct.pack('>I4s', int(L), b'uuid') - tfile.write(buffer) - tfile.write(b'\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8') - tfile.write(b'\x9c\x71\x99\x94\x91\xe3\xaf\xac') - tfile.write(packet.encode()) - - # Get the rest of the input file. - buffer = ifile.read() - tfile.write(buffer) - tfile.flush() - - @classmethod - def tearDownClass(cls): - os.unlink(cls._xmp_file) - - def setUp(self): - self.jp2file = pkg_resources.resource_filename(glymur.__name__, - "data/nemo.jp2") - - def tearDown(self): - pass - - def test_basic_xmp(self): - b = b'\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8\x9c\x71\x99\x94\x91\xe3\xaf\xac' - xmp_uuid = uuid.UUID(bytes=b) - jp2 = Jp2k(self._xmp_file) - self.assertEqual(jp2.box[3].uuid, xmp_uuid) - - -if __name__ == "__main__": - unittest.main()