Merge branch 'issue204' into devel

This commit is contained in:
John Evans 2014-03-19 16:53:54 -04:00
commit 8a448600d2
3 changed files with 66 additions and 44 deletions

View file

@ -316,9 +316,6 @@ class ColourSpecificationBox(Jp2kBox):
self.method = method
self.precedence = precedence
if approximation not in (0, 1, 2, 3, 4):
warnings.warn("Invalid approximation: {0}".format(approximation))
self.approximation = approximation
self.colorspace = colorspace
@ -326,14 +323,19 @@ class ColourSpecificationBox(Jp2kBox):
self.length = length
self.offset = offset
self._validate()
self._validate(writing=False)
def _validate(self):
def _validate(self, writing=False):
"""Verify that the box obeys the specifications."""
if self.colorspace is not None and self.icc_profile is not None:
raise IOError("colorspace and icc_profile cannot both be set.")
msg = "Colorspace and icc_profile cannot both be set."
self._dispatch_validation_error(msg, writing=writing)
if self.method not in (1, 2, 3, 4):
raise IOError("Invalid method.")
msg = "Invalid method.".format(self.method)
self._dispatch_validation_error(msg, writing=writing)
if self.approximation not in (0, 1, 2, 3, 4):
msg = "Invalid approximation: {0}".format(self.approximation)
self._dispatch_validation_error(msg, writing=writing)
def _write_validate(self):
"""In addition to constructor validation steps, run validation steps
@ -341,15 +343,15 @@ class ColourSpecificationBox(Jp2kBox):
if self.colorspace is None:
msg = "Writing Colour Specification boxes without enumerated "
msg += "colorspaces is not supported at this time."
raise IOError(msg)
self._dispatch_validation_error(msg, writing=True)
if self.icc_profile is None:
if self.colorspace not in [SRGB, GREYSCALE, YCC]:
msg = "Colorspace should correspond to one of SRGB, GREYSCALE, "
msg += "or YCC."
raise IOError(msg)
self._dispatch_validation_error(msg, writing=True)
self._validate()
self._validate(writing=True)
def __repr__(self):
@ -607,15 +609,15 @@ class ChannelDefinitionBox(Jp2kBox):
self.channel_type = tuple(channel_type)
self.association = tuple(association)
self.__dict__.update(**kwargs)
self._validate()
self._validate(writing=False)
def _validate(self):
def _validate(self, writing=False):
"""Verify that the box obeys the specifications."""
# channel type and association must be specified.
if not ((len(self.index) == len(self.channel_type)) and
(len(self.channel_type) == len(self.association))):
msg = "Length of channel definition box inputs must be the same."
raise IOError(msg)
self._dispatch_validation_error(msg, writing=writing)
# channel types must be one of 0, 1, 2, 65535
if any(x not in [0, 1, 2, 65535] for x in self.channel_type):
@ -624,7 +626,7 @@ class ChannelDefinitionBox(Jp2kBox):
msg += " 1 - opacity\n"
msg += " 2 - premultiplied opacity\n"
msg += " 65535 - unspecified"
raise IOError(msg)
self._dispatch_validation_error(msg, writing=writing)
def __str__(self):
@ -651,7 +653,7 @@ class ChannelDefinitionBox(Jp2kBox):
def write(self, fptr):
"""Write a channel definition box to file.
"""
self._validate()
self._validate(writing=True)
num_components = len(self.association)
fptr.write(struct.pack('>I', 8 + 2 + num_components * 6))
fptr.write('cdef'.encode('utf-8'))
@ -993,23 +995,23 @@ class DataReferenceBox(Jp2kBox):
self.DR = data_entry_url_boxes
self.length = length
self.offset = offset
self._validate()
self._validate(writing=False)
def _validate(self):
def _validate(self, writing=False):
"""Verify that the box obeys the specifications."""
for box in self.DR:
if box.box_id != 'url ':
msg = 'All child boxes of a data reference box must be data '
msg += 'entry URL boxes.'
raise IOError(msg)
self._dispatch_validation_error(msg, writing=writing)
def _write_validate(self):
"""Verify that the box obeys the specifications for writing.
"""
if len(self.DR) == 0:
msg = "A data reference box cannot be empty when written to a file."
raise IOError(msg)
self._validate()
self._dispatch_validation_error(msg, writing=True)
self._validate(writing=True)
def write(self, fptr):
"""Write a Data Reference box to file.
@ -1227,18 +1229,21 @@ class FragmentListBox(Jp2kBox):
self.data_reference = data_reference
self.length = length
self.offset = offset
self._validate(writing=False)
def _validate(self):
def _validate(self, writing=False):
"""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)
self._dispatch_validation_error(msg, writing=writing)
if any([x <= 0 for x in self.fragment_offset]):
raise IOError("Fragment offsets must all be positive.")
msg = "Fragment offsets must all be positive."
self._dispatch_validation_error(msg, writing=writing)
if any([x <= 0 for x in self.fragment_length]):
raise IOError("Fragment lengths must all be positive.")
msg = "Fragment lengths must all be positive."
self._dispatch_validation_error(msg, writing=writing)
def __repr__(self):
msg = "glymur.jp2box.FragmentListBox()"
@ -1262,7 +1267,7 @@ class FragmentListBox(Jp2kBox):
def write(self, fptr):
"""Write a fragment list box to file.
"""
self._validate()
self._validate(writing=True)
num_items = len(self.fragment_offset)
length = 8 + 2 + num_items * 14
fptr.write(struct.pack('>I', length))
@ -1357,18 +1362,18 @@ class FragmentTableBox(Jp2kBox):
return box
def _validate(self):
def _validate(self, writing=False):
"""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)
self._dispatch_validation_error(msg, writing=writing)
def write(self, fptr):
"""Write a fragment table box to file.
"""
self._validate()
self._validate(writing=True)
self._write_superbox(fptr)
@ -1779,15 +1784,15 @@ class PaletteBox(Jp2kBox):
self.signed = signed
self.length = length
self.offset = offset
self._validate()
self._validate(writing=False)
def _validate(self):
def _validate(self, writing=False):
"""Verify that the box obeys the specifications."""
if ((len(self.bits_per_component) != len(self.signed)) or
(len(self.signed) != self.palette.shape[1])):
msg = "The length of the 'bits_per_component' and the 'signed' "
msg += "members must equal the number of columns of the palette."
raise IOError(msg)
self._dispatch_validation_error(msg, writing=writing)
def __repr__(self):
msg = "glymur.jp2box.PaletteBox({0}, bits_per_component={1}, "
@ -1807,7 +1812,7 @@ class PaletteBox(Jp2kBox):
def write(self, fptr):
"""Write a Palette box to file.
"""
self._validate()
self._validate(writing=True)
bytes_per_row = sum(self.bits_per_component) / 8
bytes_per_palette = bytes_per_row * self.palette.shape[0]
box_length = 8 + 3 + self.palette.shape[1] + bytes_per_palette

View file

@ -340,6 +340,7 @@ class TestChannelDefinition(unittest.TestCase):
with self.assertRaises((IOError, OSError)):
j2k.wrap(tfile.name, boxes=boxes)
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
def test_bad_type(self):
"""Channel types are limited to 0, 1, 2, 65535
Should reject if not all of index, channel_type, association the
@ -347,17 +348,18 @@ class TestChannelDefinition(unittest.TestCase):
"""
channel_type = (COLOR, COLOR, 3)
association = (RED, GREEN, BLUE)
with self.assertRaises(IOError):
with self.assertWarns(UserWarning):
glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
association=association)
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
def test_wrong_lengths(self):
"""Should reject if not all of index, channel_type, association the
same length.
"""
channel_type = (COLOR, COLOR)
association = (RED, GREEN, BLUE)
with self.assertRaises(IOError):
with self.assertWarns(UserWarning):
glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
association=association)
@ -443,19 +445,21 @@ class TestColourSpecificationBox(unittest.TestCase):
self.assertEqual(colr.colorspace, glymur.core.SRGB)
self.assertIsNone(colr.icc_profile)
@unittest.skipIf(sys.hexversion < 0x03030000, "Requires 3.3+")
def test_colr_with_cspace_and_icc(self):
"""Colour specification boxes can't have both."""
with self.assertRaises((OSError, IOError)):
with self.assertWarns(UserWarning):
colorspace = glymur.core.SRGB
rawb = b'\x01\x02\x03\x04'
glymur.jp2box.ColourSpecificationBox(colorspace=colorspace,
icc_profile=rawb)
@unittest.skipIf(sys.hexversion < 0x03030000, "Requires 3.3+")
def test_colr_with_bad_method(self):
"""colr must have a valid method field"""
colorspace = glymur.core.SRGB
method = -1
with self.assertRaises(IOError):
with self.assertWarns(UserWarning):
glymur.jp2box.ColourSpecificationBox(colorspace=colorspace,
method=method)
@ -490,21 +494,23 @@ class TestPaletteBox(unittest.TestCase):
def tearDown(self):
pass
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
def test_mismatched_bitdepth_signed(self):
"""bitdepth and signed arguments must have equal length"""
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint8)
bps = (8, 8, 8)
signed = (False, False)
with self.assertRaises(IOError):
with self.assertWarns(UserWarning):
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
signed=signed)
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
def test_mismatched_signed_palette(self):
"""bitdepth and signed arguments must have equal length"""
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint8)
bps = (8, 8, 8, 8)
signed = (False, False, False, False)
with self.assertRaises(IOError):
with self.assertWarns(UserWarning):
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
signed=signed)

View file

@ -8,6 +8,8 @@ import struct
import sys
import tempfile
import unittest
import warnings
import lxml.etree as ET
import glymur
@ -207,13 +209,14 @@ class TestJPXWrap(unittest.TestCase):
with self.assertRaises(IOError):
jp2.wrap(tfile.name, boxes=boxes)
@unittest.skipIf(sys.hexversion < 0x03000000, "Needs unittest in 3.x.")
def test_deurl_child_of_dtbl(self):
"""Data reference boxes can only contain data entry url boxes."""
jp2 = Jp2k(self.jp2file)
boxes = [jp2.box[idx] for idx in [0, 1, 2, 4]]
ftyp = glymur.jp2box.FileTypeBox()
with self.assertRaises(IOError):
with self.assertWarns(UserWarning):
dref = glymur.jp2box.DataReferenceBox([ftyp])
# Try to get around it by appending the ftyp box after creation.
@ -337,7 +340,9 @@ class TestJPX(unittest.TestCase):
offset = [89]
length = [1132288]
reference = [0, 0]
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
with warnings.catch_warnings():
warnings.simplefilter("ignore")
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile:
flst.write(tfile)
@ -347,8 +352,10 @@ class TestJPX(unittest.TestCase):
offset = [0]
length = [1132288]
reference = [0]
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
with self.assertRaises(IOError):
with warnings.catch_warnings():
warnings.simplefilter("ignore")
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
with self.assertRaises((IOError, OSError)):
with tempfile.TemporaryFile() as tfile:
flst.write(tfile)
@ -357,14 +364,18 @@ class TestJPX(unittest.TestCase):
offset = [89]
length = [0]
reference = [0]
flst = glymur.jp2box.FragmentListBox(offset, length, reference)
with warnings.catch_warnings():
warnings.simplefilter("ignore")
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 warnings.catch_warnings():
warnings.simplefilter("ignore")
ftbl = glymur.jp2box.FragmentTableBox()
with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile:
ftbl.write(tfile)