Merge branch 'issue165' into devel

This commit is contained in:
John Evans 2014-02-11 16:16:48 -05:00
commit 1a4d6a54c6
4 changed files with 77 additions and 34 deletions

View file

@ -1837,10 +1837,10 @@ class ReaderRequirementsBox(Jp2kBox):
Jp2kBox.__init__(self, box_id='rreq', longname='Reader Requirements') Jp2kBox.__init__(self, box_id='rreq', longname='Reader Requirements')
self.fuam = fuam self.fuam = fuam
self.dcm = dcm self.dcm = dcm
self.standard_flag = standard_flag self.standard_flag = tuple(standard_flag)
self.standard_mask = standard_mask self.standard_mask = tuple(standard_mask)
self.vendor_feature = vendor_feature self.vendor_feature = tuple(vendor_feature)
self.vendor_mask = vendor_mask self.vendor_mask = tuple(vendor_mask)
self.length = length self.length = length
self.offset = offset self.offset = offset
@ -1902,6 +1902,9 @@ class ReaderRequirementsBox(Jp2kBox):
read_buffer = fptr.read(1) read_buffer = fptr.read(1)
mask_length, = struct.unpack('>B', read_buffer) mask_length, = struct.unpack('>B', read_buffer)
if mask_length == 3:
return _parse_rreq3(fptr, length, offset)
# Fully Understands Aspect Mask # Fully Understands Aspect Mask
# Decodes Completely Mask # Decodes Completely Mask
read_buffer = fptr.read(2 * mask_length) read_buffer = fptr.read(2 * mask_length)
@ -1911,6 +1914,7 @@ class ReaderRequirementsBox(Jp2kBox):
# The mask length tells us the format string to use when unpacking # The mask length tells us the format string to use when unpacking
# from the buffer read from file. # from the buffer read from file.
try: try:
mask_format = {1: 'B', 2: 'H', 4: 'I', 8: 'Q'}[mask_length] mask_format = {1: 'B', 2: 'H', 4: 'I', 8: 'Q'}[mask_length]
fuam, dcm = struct.unpack('>' + mask_format * 2, read_buffer) fuam, dcm = struct.unpack('>' + mask_format * 2, read_buffer)
@ -1931,6 +1935,62 @@ class ReaderRequirementsBox(Jp2kBox):
return box return box
def _parse_rreq3(fptr, length, offset):
"""Parse a reader requirements box. Special case when mask length is 3."""
# Fully Understands Aspect Mask
# Decodes Completely Mask
read_buffer = fptr.read(2 * 3)
fuam = dcm = standard_flag = standard_mask = []
vendor_feature = vendor_mask = []
# The mask length tells us the format string to use when unpacking
# from the buffer read from file.
lst = struct.unpack('>BBBBBB', read_buffer)
fuam = lst[0] << 16 | lst[1] << 8 | lst[2]
dcm = lst[3] << 16 | lst[4] << 8 | lst[5]
read_buffer = fptr.read(2)
num_standard_features, = struct.unpack('>H', read_buffer)
fmt = '>' + 'HBBB' * num_standard_features
read_buffer = fptr.read(num_standard_features * 5)
lst = struct.unpack(fmt, read_buffer)
standard_flag = lst[0::4]
standard_mask = []
for j in range(num_standard_features):
items = lst[slice(j * 4 + 1, j * 4 + 4)]
mask = items[0] << 16 | items[1] << 8 | items[2]
standard_mask.append(mask)
read_buffer = fptr.read(2)
num_vendor_features, = struct.unpack('>H', read_buffer)
fmt = '>' + 'HBBB' * num_vendor_features
read_buffer = fptr.read(num_vendor_features * 5)
lst = struct.unpack(fmt, read_buffer)
# Each vendor feature consists of a 16-byte UUID plus a mask whose
# length is specified by, you guessed it, "mask_length".
entry_length = 16 + 3
read_buffer = fptr.read(num_vendor_features * entry_length)
vendor_feature = []
vendor_mask = []
for j in range(num_vendor_features):
ubuffer = read_buffer[j * entry_length:(j + 1) * entry_length]
vendor_feature.append(uuid.UUID(bytes=ubuffer[0:16]))
lst = struct.unpack('>BBB', ubuffer[16:])
vmask = lst[0] << 16 | lst[1] << 8 | lst[2]
vendor_mask.append(vmask)
box = ReaderRequirementsBox(fuam, dcm, standard_flag, standard_mask,
vendor_feature, vendor_mask,
length=length, offset=offset)
return box
def _parse_standard_flag(fptr, mask_length): def _parse_standard_flag(fptr, mask_length):
"""Construct standard flag, standard mask data from the file. """Construct standard flag, standard mask data from the file.

View file

@ -580,9 +580,6 @@ class TestWrap(unittest.TestCase):
def test_jpx_to_jp2(self): def test_jpx_to_jp2(self):
"""basic test for rewrapping a jpx file""" """basic test for rewrapping a jpx file"""
with warnings.catch_warnings():
# This file has a rreq mask length that we do not recognize.
warnings.simplefilter("ignore")
jpx = Jp2k(self.jpxfile) jpx = Jp2k(self.jpxfile)
idx = [0, 1, 3, 6] idx = [0, 1, 3, 6]
boxes = [jpx.box[idx] for idx in [0, 1, 3, 6]] boxes = [jpx.box[idx] for idx in [0, 1, 3, 6]]

View file

@ -135,7 +135,6 @@ class TestJPXWrap(unittest.TestCase):
jpx = jp2.wrap(tfile.name, boxes=boxes) jpx = jp2.wrap(tfile.name, boxes=boxes)
@unittest.skipIf(sys.hexversion < 0x03000000, "Warning assert on 2.x.")
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
class TestJPX(unittest.TestCase): class TestJPX(unittest.TestCase):
"""Test suite for other JPX boxes.""" """Test suite for other JPX boxes."""
@ -146,21 +145,17 @@ class TestJPX(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
def test_rreq_box_strange_mask_length(self): def test_jpx_rreq_mask_length_3(self):
"""The standard says that the mask length should be 1, 2, 4, or 8.""" """There are some JPX files with rreq mask length of 3."""
with warnings.catch_warnings(): jpx = Jp2k(self.jpxfile)
# This file has a rreq mask length that we do not recognize. self.assertEqual(jpx.box[2].box_id, 'rreq')
warnings.simplefilter("ignore") self.assertEqual(type(jpx.box[2]),
j = Jp2k(self.jpxfile)
self.assertEqual(j.box[2].box_id, 'rreq')
self.assertEqual(type(j.box[2]),
glymur.jp2box.ReaderRequirementsBox) glymur.jp2box.ReaderRequirementsBox)
self.assertEqual(jpx.box[2].standard_flag,
(5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20))
def test_free_box(self): def test_free_box(self):
"""Verify that we can handle a free box.""" """Verify that we can handle a free box."""
with warnings.catch_warnings():
# This file has a rreq mask length that we do not recognize.
warnings.simplefilter("ignore")
j = Jp2k(self.jpxfile) j = Jp2k(self.jpxfile)
self.assertEqual(j.box[16].box[0].box_id, 'free') self.assertEqual(j.box[16].box[0].box_id, 'free')
self.assertEqual(type(j.box[16].box[0]), glymur.jp2box.FreeBox) self.assertEqual(type(j.box[16].box[0]), glymur.jp2box.FreeBox)
@ -183,9 +178,6 @@ class TestJPX(unittest.TestCase):
tfile.flush() tfile.flush()
with warnings.catch_warnings():
# This file has a rreq mask length that we do not recognize.
warnings.simplefilter("ignore")
jpx = Jp2k(tfile.name) jpx = Jp2k(tfile.name)
self.assertEqual(jpx.box[-1].box_id, 'dtbl') self.assertEqual(jpx.box[-1].box_id, 'dtbl')
@ -212,7 +204,6 @@ class TestJPX(unittest.TestCase):
tfile.flush() tfile.flush()
with self.assertWarns(UserWarning):
jpx = Jp2k(tfile.name) jpx = Jp2k(tfile.name)
self.assertEqual(jpx.box[-1].box_id, 'ftbl') self.assertEqual(jpx.box[-1].box_id, 'ftbl')
@ -223,9 +214,6 @@ class TestJPX(unittest.TestCase):
def test_nlst(self): def test_nlst(self):
"""Verify that we can handle a free box.""" """Verify that we can handle a free box."""
with warnings.catch_warnings():
# This file has a rreq mask length that we do not recognize.
warnings.simplefilter("ignore")
j = Jp2k(self.jpxfile) j = Jp2k(self.jpxfile)
self.assertEqual(j.box[16].box[1].box[0].box_id, 'nlst') self.assertEqual(j.box[16].box[1].box[0].box_id, 'nlst')
self.assertEqual(type(j.box[16].box[1].box[0]), self.assertEqual(type(j.box[16].box[1].box[0]),

View file

@ -379,8 +379,6 @@ class TestJp2k(unittest.TestCase):
def test_jpx_mult_codestreams_jp2_brand(self): def test_jpx_mult_codestreams_jp2_brand(self):
"""Read JPX codestream when jp2-compatible.""" """Read JPX codestream when jp2-compatible."""
# The file in question has multiple codestreams. # The file in question has multiple codestreams.
with warnings.catch_warnings():
warnings.simplefilter("ignore")
jpx = Jp2k(self.jpxfile) jpx = Jp2k(self.jpxfile)
data = jpx.read() data = jpx.read()
if re.match(r"""1\.[0123]""", glymur.version.openjpeg_version): if re.match(r"""1\.[0123]""", glymur.version.openjpeg_version):