Starting write support for ftbl and flst boxes. #175
This commit is contained in:
parent
5e76d1cf39
commit
cd606e1f9d
2 changed files with 120 additions and 3 deletions
|
|
@ -148,7 +148,8 @@ class Jp2kBox(object):
|
||||||
box = UnknownBox(box_id, offset=start, length=num_bytes,
|
box = UnknownBox(box_id, offset=start, length=num_bytes,
|
||||||
longname='Unknown')
|
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
|
# If the file pointer has advanced, then the KeyError
|
||||||
# ocurred during the parsing of the box.
|
# ocurred during the parsing of the box.
|
||||||
pass
|
pass
|
||||||
|
|
@ -1122,6 +1123,18 @@ class FragmentListBox(Jp2kBox):
|
||||||
self.length = length
|
self.length = length
|
||||||
self.offset = offset
|
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):
|
def __repr__(self):
|
||||||
msg = "glymur.jp2box.FragmentListBox()"
|
msg = "glymur.jp2box.FragmentListBox()"
|
||||||
return msg
|
return msg
|
||||||
|
|
@ -1141,6 +1154,22 @@ class FragmentListBox(Jp2kBox):
|
||||||
|
|
||||||
return msg
|
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
|
@staticmethod
|
||||||
def parse(fptr, offset, length):
|
def parse(fptr, offset, length):
|
||||||
"""Parse JPX free box.
|
"""Parse JPX free box.
|
||||||
|
|
@ -1184,10 +1213,11 @@ class FragmentTableBox(Jp2kBox):
|
||||||
longname : str
|
longname : str
|
||||||
more verbose description of the box.
|
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')
|
Jp2kBox.__init__(self, box_id='ftbl', longname='Fragment Table')
|
||||||
self.length = length
|
self.length = length
|
||||||
self.offset = offset
|
self.offset = offset
|
||||||
|
self.box = box if box is not None else []
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = "glymur.jp2box.FragmentTableBox()"
|
msg = "glymur.jp2box.FragmentTableBox()"
|
||||||
|
|
@ -1212,7 +1242,7 @@ class FragmentTableBox(Jp2kBox):
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
FreeBox instance
|
FragmentTableBox instance
|
||||||
"""
|
"""
|
||||||
box = FragmentTableBox(length=length, offset=offset)
|
box = FragmentTableBox(length=length, offset=offset)
|
||||||
|
|
||||||
|
|
@ -1222,6 +1252,20 @@ class FragmentTableBox(Jp2kBox):
|
||||||
|
|
||||||
return box
|
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):
|
class FreeBox(Jp2kBox):
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,34 @@ class TestJPXWrap(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
os.unlink(self.xmlfile)
|
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):
|
def test_jpxb_compatibility(self):
|
||||||
"""Wrap JP2 to JPX, state jpxb compatibility"""
|
"""Wrap JP2 to JPX, state jpxb compatibility"""
|
||||||
jp2 = Jp2k(self.jp2file)
|
jp2 = Jp2k(self.jp2file)
|
||||||
|
|
@ -194,6 +222,51 @@ class TestJPX(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
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):
|
def test_jpx_rreq_mask_length_3(self):
|
||||||
"""There are some JPX files with rreq mask length of 3."""
|
"""There are some JPX files with rreq mask length of 3."""
|
||||||
jpx = Jp2k(self.jpxfile)
|
jpx = Jp2k(self.jpxfile)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue