diff --git a/glymur/jp2box.py b/glymur/jp2box.py index e358eab..4810834 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -164,12 +164,32 @@ class Jp2kBox(object): # Call the proper parser for the given box with ID "T". try: box = _BOX_WITH_ID[box_id].parse(fptr, start, num_bytes) - except KeyError: + except KeyError as err: msg = 'Unrecognized box ({0}) encountered.'.format(box_id) warnings.warn(msg) box = Jp2kBox(box_id, offset=start, length=num_bytes, longname='Unknown box') + if fptr.tell() != start + 8: + # If the file pointer has advanced, then the KeyError + # ocurred during the parsing of the box. + pass + else: + # Could it be a superbox with recognizable child boxes? + # Peek ahead to see. + pos = fptr.tell() + read_buffer = fptr.read(8) + sub_length, sub_id = struct.unpack('>I4s', read_buffer) + sub_id = sub_id.decode('utf-8') + + # Regardless of whether or not we recognize the box, rewind back + # to properly advance to the next box. + fptr.seek(pos) + + # Now process any child boxes if we actually did recognize it. + if sub_id in _BOX_WITH_ID.keys(): + box.box = box.parse_superbox(fptr) + superbox.append(box) # Position to the start of the next box. diff --git a/glymur/test/test_jp2box_jpx.py b/glymur/test/test_jp2box_jpx.py index 8b5e075..6624291 100644 --- a/glymur/test/test_jp2box_jpx.py +++ b/glymur/test/test_jp2box_jpx.py @@ -4,6 +4,7 @@ Test suite specifically targeting JPX box layout. """ import os +import shutil import struct import sys import tempfile @@ -202,6 +203,25 @@ class TestJPX(unittest.TestCase): self.assertEqual(jpx.box[2].standard_flag, (5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20)) + @unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.") + def test_unknown_superbox(self): + """Verify that we can handle an unknown superbox.""" + with tempfile.NamedTemporaryFile(suffix='.jpx') as tfile: + with open(self.jpxfile, 'rb') as ifile: + tfile.write(ifile.read()) + + # Add the header for an unknwon superbox. + write_buffer = struct.pack('>I4s', 20, 'grp '.encode()) + tfile.write(write_buffer) + write_buffer = struct.pack('>I4sI', 12, 'free'.encode(), 0) + tfile.write(write_buffer) + tfile.flush() + + with self.assertWarns(UserWarning): + jpx = Jp2k(tfile.name) + self.assertEqual(jpx.box[-1].box_id, 'grp ') + self.assertEqual(jpx.box[-1].box[0].box_id, 'free') + def test_free_box(self): """Verify that we can handle a free box.""" j = Jp2k(self.jpxfile)