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()