From 55a01cbc39186f111e919c9f1fa2ab8272eab5b0 Mon Sep 17 00:00:00 2001 From: John Evans Date: Wed, 19 Mar 2014 15:15:55 -0400 Subject: [PATCH] Not decoding box ID anymore. #197 We don't actually use the box ID as parsed from the file, so there was no need to decode it. This means that unrecognized box IDs are actually kept as bytes, but no big deal. The code is simplified a bit because of this. --- glymur/_uuid_io.py | 2 +- glymur/jp2box.py | 76 ++++++++++++++++++---------------- glymur/test/test_jp2box_jpx.py | 2 +- glymur/test/test_jp2k.py | 17 +++++++- glymur/test/test_opj_suite.py | 2 +- glymur/test/test_printing.py | 2 +- 6 files changed, 61 insertions(+), 40 deletions(-) diff --git a/glymur/_uuid_io.py b/glymur/_uuid_io.py index 399fa99..8cd5bcf 100644 --- a/glymur/_uuid_io.py +++ b/glymur/_uuid_io.py @@ -43,7 +43,7 @@ def tiff_header(read_buffer): endian = '>' else: msg = "Bad byte order indication: {0}".format(read_buffer[6:8]) - raise RuntimeError(msg) + raise IOError(msg) _, offset = struct.unpack(endian + 'HI', read_buffer[8:14]) diff --git a/glymur/jp2box.py b/glymur/jp2box.py index f3f0776..723b5a3 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -177,6 +177,13 @@ class Jp2kBox(object): """ try: box = _BOX_WITH_ID[box_id].parse(fptr, start, num_bytes) + + except UnicodeDecodeError: + msg = 'Unrecognized box ({0}) encountered.'.format(box_id) + warnings.warn(msg) + box = UnknownBox(' ', offset=start, length=num_bytes, + longname='Unknown') + except KeyError: msg = 'Unrecognized box ({0}) encountered.'.format(box_id) warnings.warn(msg) @@ -194,7 +201,6 @@ class Jp2kBox(object): pos = fptr.tell() read_buffer = fptr.read(8) _, 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. @@ -230,16 +236,12 @@ class Jp2kBox(object): break read_buffer = fptr.read(8) - try: - (box_length, box_id) = struct.unpack('>I4s', read_buffer) - except Exception as err: + if len(read_buffer) < 8: msg = "Extra bytes at end of file ignored." warnings.warn(msg) return superbox - if sys.hexversion >= 0x03000000: - box_id = box_id.decode('utf-8') - + (box_length, box_id) = struct.unpack('>I4s', read_buffer) if box_length == 0: # The length of the box is presumed to last until the end of # the file. Compute the effective length of the box. @@ -2998,7 +3000,11 @@ class UUIDBox(Jp2kBox): try: self._parse_raw_data() - except RuntimeError as error: + except KeyError as error: + # Such as when an Exif tag is unrecognized. + warnings.warn(str(error)) + except IOError as error: + # Such as when Exif byte order is unrecognized. warnings.warn(str(error)) def _parse_raw_data(self): @@ -3088,33 +3094,33 @@ class UUIDBox(Jp2kBox): # Map each box ID to the corresponding class. _BOX_WITH_ID = { - 'asoc': AssociationBox, - 'cdef': ChannelDefinitionBox, - 'cmap': ComponentMappingBox, - 'colr': ColourSpecificationBox, - 'dtbl': DataReferenceBox, - 'ftyp': FileTypeBox, - 'ihdr': ImageHeaderBox, - 'jP ': JPEG2000SignatureBox, - 'jpch': CodestreamHeaderBox, - 'jplh': CompositingLayerHeaderBox, - 'jp2c': ContiguousCodestreamBox, - 'free': FreeBox, - 'flst': FragmentListBox, - 'ftbl': FragmentTableBox, - 'jp2h': JP2HeaderBox, - 'lbl ': LabelBox, - 'nlst': NumberListBox, - 'pclr': PaletteBox, - 'res ': ResolutionBox, - 'resc': CaptureResolutionBox, - 'resd': DisplayResolutionBox, - 'rreq': ReaderRequirementsBox, - 'uinf': UUIDInfoBox, - 'ulst': UUIDListBox, - 'url ': DataEntryURLBox, - 'uuid': UUIDBox, - 'xml ': XMLBox} + b'asoc': AssociationBox, + b'cdef': ChannelDefinitionBox, + b'cmap': ComponentMappingBox, + b'colr': ColourSpecificationBox, + b'dtbl': DataReferenceBox, + b'ftyp': FileTypeBox, + b'ihdr': ImageHeaderBox, + b'jP ': JPEG2000SignatureBox, + b'jpch': CodestreamHeaderBox, + b'jplh': CompositingLayerHeaderBox, + b'jp2c': ContiguousCodestreamBox, + b'free': FreeBox, + b'flst': FragmentListBox, + b'ftbl': FragmentTableBox, + b'jp2h': JP2HeaderBox, + b'lbl ': LabelBox, + b'nlst': NumberListBox, + b'pclr': PaletteBox, + b'res ': ResolutionBox, + b'resc': CaptureResolutionBox, + b'resd': DisplayResolutionBox, + b'rreq': ReaderRequirementsBox, + b'uinf': UUIDInfoBox, + b'ulst': UUIDListBox, + b'url ': DataEntryURLBox, + b'uuid': UUIDBox, + b'xml ': XMLBox} _printoptions = {'short': False, 'xml': True, 'codestream': True} diff --git a/glymur/test/test_jp2box_jpx.py b/glymur/test/test_jp2box_jpx.py index f8e9db8..f71f409 100644 --- a/glymur/test/test_jp2box_jpx.py +++ b/glymur/test/test_jp2box_jpx.py @@ -402,7 +402,7 @@ class TestJPX(unittest.TestCase): with self.assertWarns(UserWarning): jpx = Jp2k(tfile.name) - self.assertEqual(jpx.box[-1].box_id, 'grp ') + self.assertEqual(jpx.box[-1].box_id, b'grp ') self.assertEqual(jpx.box[-1].box[0].box_id, 'free') def test_free_box(self): diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index 10363ab..222cde8 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -758,7 +758,22 @@ class TestJp2k_2_1(unittest.TestCase): class TestJp2kOpjDataRoot(unittest.TestCase): """These tests should be run by just about all configuration.""" - @unittest.skipIf(sys.hexversion < 0x03000000, "Test requires Python 3.3+") + def test_undecodeable_box_id(self): + """Should warn in case of undecodeable box ID but not error out.""" + filename = opj_data_file('input/nonregression/edf_c2_1013627.jp2') + if sys.hexversion < 0x03000000: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + jp2 = Jp2k(filename) + else: + with self.assertWarns(UserWarning): + jp2 = Jp2k(filename) + + # Now make sure we got all of the boxes. Ignore the last, which was + # bad. + box_ids = [box.box_id for box in jp2.box[:-1]] + self.assertEqual(box_ids, ['jP ', 'ftyp', 'jp2h', 'jp2c']) + def test_invalid_approximation(self): """Should warn in case of bad ftyp brand.""" filename = opj_data_file('input/nonregression/edf_c2_1000290.jp2') diff --git a/glymur/test/test_opj_suite.py b/glymur/test/test_opj_suite.py index 2a49b5d..40bfa68 100644 --- a/glymur/test/test_opj_suite.py +++ b/glymur/test/test_opj_suite.py @@ -5482,7 +5482,7 @@ class TestSuiteDump(unittest.TestCase): jp2 = Jp2k(jfile) ids = [box.box_id for box in jp2.box] - self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', 'XML ', 'jp2c']) + self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', b'XML ', 'jp2c']) ids = [box.box_id for box in jp2.box[2].box] self.assertEqual(ids, ['ihdr', 'colr']) diff --git a/glymur/test/test_printing.py b/glymur/test/test_printing.py index 7a17e4e..5cd8347 100644 --- a/glymur/test/test_printing.py +++ b/glymur/test/test_printing.py @@ -78,7 +78,7 @@ class TestPrinting(unittest.TestCase): with patch('sys.stdout', new=StringIO()) as fake_out: print(jpx.box[-1]) actual = fake_out.getvalue().strip() - lines = ['Unknown Box (grp ) @ (695609, 20)', + lines = ["Unknown Box (b'grp ') @ (695609, 20)", ' Free Box (free) @ (695617, 12)'] expected = '\n'.join(lines) self.assertEqual(actual, expected)