Merge branch 'issue194' into devel

This commit is contained in:
John Evans 2014-03-19 11:24:06 -04:00
commit 2a75fcfff2
6 changed files with 52 additions and 11 deletions

View file

@ -85,6 +85,17 @@ class Jp2kBox(object):
msg += " @ ({0}, {1})".format(self.offset, self.length) msg += " @ ({0}, {1})".format(self.offset, self.length)
return msg return msg
def _dispatch_validation_error(self, msg, writing=False):
"""Issue either a warning or an error depending on circumstance.
If writing to file, then error out, as we do not wish to create bad
JP2 files. If reading, then we should be more lenient and just warn.
"""
if writing:
raise IOError(msg)
else:
warnings.warn(msg)
def write(self, _): def write(self, _):
"""Must be implemented in a subclass. """Must be implemented in a subclass.
""" """
@ -1102,6 +1113,7 @@ class FileTypeBox(Jp2kBox):
self.compatibility_list = compatibility_list self.compatibility_list = compatibility_list
self.length = length self.length = length
self.offset = offset self.offset = offset
self._validate(writing=False)
def __repr__(self): def __repr__(self):
msg = "glymur.jp2box.FileTypeBox(brand='{0}', minor_version={1}, " msg = "glymur.jp2box.FileTypeBox(brand='{0}', minor_version={1}, "
@ -1123,22 +1135,23 @@ class FileTypeBox(Jp2kBox):
return msg return msg
def _validate(self): def _validate(self, writing=False):
"""Validate the box before writing to file.""" """Validate the box before writing to file."""
if self.brand not in ['jp2 ', 'jpx ']: if self.brand not in ['jp2 ', 'jpx ']:
msg = "The file type brand must be either 'jp2 ' or 'jpx '." msg = "The file type brand must be either 'jp2 ' or 'jpx '."
raise IOError(msg) self._dispatch_validation_error(msg, writing=writing)
valid_cls = ['jp2 ', 'jpx ', 'jpxb'] valid_cls = ['jp2 ', 'jpx ', 'jpxb']
for item in self.compatibility_list: for item in self.compatibility_list:
if item not in valid_cls: if item not in valid_cls:
msg = "The file type compatibility list item '{0}' is not " msg = "The file type compatibility list item '{0}' is not "
msg += "valid: valid entries are {1}" msg += "valid: valid entries are {1}"
raise IOError(msg.format(item, valid_cls)) msg = msg.format(item, valid_cls)
self._dispatch_validation_error(msg, writing=writing)
def write(self, fptr): def write(self, fptr):
"""Write a File Type box to file. """Write a File Type box to file.
""" """
self._validate() self._validate(writing=True)
length = 16 + 4*len(self.compatibility_list) length = 16 + 4*len(self.compatibility_list)
fptr.write(struct.pack('>I', length)) fptr.write(struct.pack('>I', length))
fptr.write('ftyp'.encode()) fptr.write('ftyp'.encode())

View file

@ -9,6 +9,7 @@ import datetime
import os import os
import sys import sys
import unittest import unittest
import warnings
import numpy as np import numpy as np
@ -30,7 +31,10 @@ class TestICC(unittest.TestCase):
def test_file5(self): def test_file5(self):
"""basic ICC profile""" """basic ICC profile"""
filename = opj_data_file('input/conformance/file5.jp2') filename = opj_data_file('input/conformance/file5.jp2')
j = Jp2k(filename) with warnings.catch_warnings():
# The file has a bad compatibility list entry. Not important here.
warnings.simplefilter("ignore")
j = Jp2k(filename)
profile = j.box[2].box[1].icc_profile profile = j.box[2].box[1].icc_profile
self.assertEqual(profile['Size'], 546) self.assertEqual(profile['Size'], 546)
self.assertEqual(profile['Preferred CMM Type'], 0) self.assertEqual(profile['Preferred CMM Type'], 0)

View file

@ -373,14 +373,19 @@ class TestFileTypeBox(unittest.TestCase):
def test_brand_unknown(self): def test_brand_unknown(self):
"""A ftyp box brand must be 'jp2 ' or 'jpx '.""" """A ftyp box brand must be 'jp2 ' or 'jpx '."""
ftyp = glymur.jp2box.FileTypeBox(brand='jp3') with warnings.catch_warnings():
warnings.simplefilter("ignore")
ftyp = glymur.jp2box.FileTypeBox(brand='jp3')
with self.assertRaises(IOError): with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile: with tempfile.TemporaryFile() as tfile:
ftyp.write(tfile) ftyp.write(tfile)
def test_cl_entry_unknown(self): def test_cl_entry_unknown(self):
"""A ftyp box cl list can only contain 'jp2 ', 'jpx ', or 'jpxb'.""" """A ftyp box cl list can only contain 'jp2 ', 'jpx ', or 'jpxb'."""
ftyp = glymur.jp2box.FileTypeBox(compatibility_list=['jp3']) with warnings.catch_warnings():
# Bad compatibility list item.
warnings.simplefilter("ignore")
ftyp = glymur.jp2box.FileTypeBox(compatibility_list=['jp3'])
with self.assertRaises(IOError): with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile: with tempfile.TemporaryFile() as tfile:
ftyp.write(tfile) ftyp.write(tfile)

View file

@ -758,6 +758,13 @@ class TestJp2k_2_1(unittest.TestCase):
class TestJp2kOpjDataRoot(unittest.TestCase): class TestJp2kOpjDataRoot(unittest.TestCase):
"""These tests should be run by just about all configuration.""" """These tests should be run by just about all configuration."""
@unittest.skipIf(sys.hexversion < 0x03000000, "Test requires Python 3.3+")
def test_invalid_approximation(self):
"""Should warn in case of bad ftyp brand."""
filename = opj_data_file('input/nonregression/edf_c2_1000290.jp2')
with self.assertWarns(UserWarning):
jp2 = Jp2k(filename)
@unittest.skipIf(sys.hexversion < 0x03000000, "Test requires Python 3.3+") @unittest.skipIf(sys.hexversion < 0x03000000, "Test requires Python 3.3+")
def test_invalid_approximation(self): def test_invalid_approximation(self):
"""Should warn in case of invalid approximation.""" """Should warn in case of invalid approximation."""
@ -817,7 +824,10 @@ class TestJp2kOpjDataRoot(unittest.TestCase):
# This file has the components physically reversed. The cmap box # This file has the components physically reversed. The cmap box
# tells the decoder how to order them, but this flag prevents that. # tells the decoder how to order them, but this flag prevents that.
filename = opj_data_file('input/conformance/file2.jp2') filename = opj_data_file('input/conformance/file2.jp2')
j = Jp2k(filename) with warnings.catch_warnings():
# The file has a bad compatibility list entry. Not important here.
warnings.simplefilter("ignore")
j = Jp2k(filename)
ycbcr = j.read() ycbcr = j.read()
crcby = j.read(ignore_pclr_cmap_cdef=True) crcby = j.read(ignore_pclr_cmap_cdef=True)

View file

@ -327,7 +327,10 @@ class TestSuite(unittest.TestCase):
def test_ETS_JP2_file1(self): def test_ETS_JP2_file1(self):
jfile = opj_data_file('input/conformance/file1.jp2') jfile = opj_data_file('input/conformance/file1.jp2')
jp2k = Jp2k(jfile) with warnings.catch_warnings():
# Bad compatibility list item.
warnings.simplefilter("ignore")
jp2k = Jp2k(jfile)
jpdata = jp2k.read() jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768, 3)) self.assertEqual(jpdata.shape, (512, 768, 3))
@ -3114,7 +3117,10 @@ class TestSuiteDump(unittest.TestCase):
def test_NR_file1_dump(self): def test_NR_file1_dump(self):
jfile = opj_data_file('input/conformance/file1.jp2') jfile = opj_data_file('input/conformance/file1.jp2')
jp2 = Jp2k(jfile) with warnings.catch_warnings():
# Bad compatibility list item.
warnings.simplefilter("ignore")
jp2 = Jp2k(jfile)
ids = [box.box_id for box in jp2.box] ids = [box.box_id for box in jp2.box]
self.assertEqual(ids, ['jP ', 'ftyp', 'xml ', 'jp2h', 'xml ', self.assertEqual(ids, ['jP ', 'ftyp', 'xml ', 'jp2h', 'xml ',

View file

@ -863,7 +863,10 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
def test_channel_definition(self): def test_channel_definition(self):
"""verify printing of cdef box""" """verify printing of cdef box"""
filename = opj_data_file('input/conformance/file2.jp2') filename = opj_data_file('input/conformance/file2.jp2')
j = glymur.Jp2k(filename) with warnings.catch_warnings():
# Bad compatibility list item.
warnings.simplefilter("ignore")
j = glymur.Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out: with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[2].box[2]) print(j.box[2].box[2])
actual = fake_out.getvalue().strip() actual = fake_out.getvalue().strip()