Starting write support for ftbl and flst boxes. #175

This commit is contained in:
jevans 2014-02-18 20:01:28 -05:00
commit cd606e1f9d
2 changed files with 120 additions and 3 deletions

View file

@ -148,7 +148,8 @@ class Jp2kBox(object):
box = UnknownBox(box_id, offset=start, length=num_bytes,
longname='Unknown')
if fptr.tell() != start + 8:
cpos = fptr.tell()
if not ((cpos == start + 8) or (cpos == start + 16)):
# If the file pointer has advanced, then the KeyError
# ocurred during the parsing of the box.
pass
@ -1122,6 +1123,18 @@ class FragmentListBox(Jp2kBox):
self.length = length
self.offset = offset
def _validate(self):
"""Validate internal correctness."""
if (((len(self.fragment_offset) != len(self.fragment_length)) or
(len(self.fragment_length) != len(self.data_reference)))):
msg = "The lengths of the fragment offsets, fragment lengths, and "
msg += "data reference items must be the same."
raise IOError(msg)
if any([x <= 0 for x in self.fragment_offset]):
raise IOError("Fragment offsets must all be positive.")
if any([x <= 0 for x in self.fragment_length]):
raise IOError("Fragment lengths must all be positive.")
def __repr__(self):
msg = "glymur.jp2box.FragmentListBox()"
return msg
@ -1141,6 +1154,22 @@ class FragmentListBox(Jp2kBox):
return msg
def write(self, fptr):
"""Write a fragment list box to file.
"""
self._validate()
num_items = len(self.fragment_offset)
length = 8 + 2 + num_items * 14
fptr.write(struct.pack('>I', length))
fptr.write(self.box_id.encode())
fptr.write(struct.pack('>H', num_items))
for j in range(num_items):
write_buffer = struct.pack('>QIH',
self.fragment_offset[j],
self.fragment_length[j],
self.data_reference[j])
fptr.write(write_buffer)
@staticmethod
def parse(fptr, offset, length):
"""Parse JPX free box.
@ -1184,10 +1213,11 @@ class FragmentTableBox(Jp2kBox):
longname : str
more verbose description of the box.
"""
def __init__(self, length=0, offset=-1):
def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='ftbl', longname='Fragment Table')
self.length = length
self.offset = offset
self.box = box if box is not None else []
def __repr__(self):
msg = "glymur.jp2box.FragmentTableBox()"
@ -1212,7 +1242,7 @@ class FragmentTableBox(Jp2kBox):
Returns
-------
FreeBox instance
FragmentTableBox instance
"""
box = FragmentTableBox(length=length, offset=offset)
@ -1222,6 +1252,20 @@ class FragmentTableBox(Jp2kBox):
return box
def _validate(self):
"""Self-validate the box before writing."""
box_ids = [box.box_id for box in self.box]
if len(box_ids) != 1 or box_ids[0] != 'flst':
msg = "Fragment table boxes must have a single fragment list "
msg += "box as a child box."
raise IOError(msg)
def write(self, fptr):
"""Write a fragment table box to file.
"""
self._validate()
self._write_superbox(fptr)
class FreeBox(Jp2kBox):

View file

@ -41,6 +41,34 @@ class TestJPXWrap(unittest.TestCase):
def tearDown(self):
os.unlink(self.xmlfile)
def test_ftbl(self):
"""Write a fragment table box."""
# Add a negative test where offset < 0
# Add a negative test where length < 0
# Add a negative test where ref > 0 but no data reference box.
# Add a negative test where more than one flst
# Add negative test where ftbl contained in a superbox.
jp2 = Jp2k(self.jp2file)
boxes = [jp2.box[idx] for idx in [0, 1, 2, 4]]
# The ftyp box must be modified to jpx.
boxes[1].brand = 'jpx '
boxes[1].compatibility_list = ['jp2 ', 'jpxb']
offset = [89]
length = [1132288]
reference = [0]
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
ftbl = glymur.jp2box.FragmentTableBox(box=[flst])
boxes.append(ftbl)
with tempfile.NamedTemporaryFile(suffix=".jpx") as tfile:
jpx = jp2.wrap(tfile.name, boxes=boxes)
self.assertEqual(jpx.box[1].compatibility_list, ['jp2 ', 'jpxb'])
self.assertEqual(jpx.box[-1].box_id, 'ftbl')
self.assertEqual(jpx.box[-1].box[0].box_id, 'flst')
def test_jpxb_compatibility(self):
"""Wrap JP2 to JPX, state jpxb compatibility"""
jp2 = Jp2k(self.jp2file)
@ -194,6 +222,51 @@ class TestJPX(unittest.TestCase):
def tearDown(self):
pass
def test_flst_lens_not_the_same(self):
"""A fragment list box items must be the same length."""
offset = [89]
length = [1132288]
reference = [0, 0]
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile:
flst.write(tfile)
def test_flst_offsets_not_positive(self):
"""A fragment list box offsets must be positive."""
offset = [0]
length = [1132288]
reference = [0]
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile:
flst.write(tfile)
def test_flst_lengths_not_positive(self):
"""A fragment list box lengths must be positive."""
offset = [89]
length = [0]
reference = [0]
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile:
flst.write(tfile)
def test_ftbl_boxes_empty(self):
"""A fragment table box must have at least one child box."""
ftbl = glymur.jp2box.FragmentTableBox()
with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile:
ftbl.write(tfile)
def test_ftbl_child_not_flst(self):
"""A fragment table box can only contain a fragment list."""
free = glymur.jp2box.FreeBox()
ftbl = glymur.jp2box.FragmentTableBox(box=[free])
with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile:
ftbl.write(tfile)
def test_jpx_rreq_mask_length_3(self):
"""There are some JPX files with rreq mask length of 3."""
jpx = Jp2k(self.jpxfile)