Merge branch 'devel' into issue182
This commit is contained in:
commit
baecdad9b2
6 changed files with 87 additions and 35 deletions
|
|
@ -673,6 +673,18 @@ class Codestream(object):
|
||||||
msg = msg.format(j, subsampling[0], subsampling[1])
|
msg = msg.format(j, subsampling[0], subsampling[1])
|
||||||
warnings.warn(msg)
|
warnings.warn(msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
num_tiles_x = (xysiz[0] - xyosiz[0]) / (xytsiz[0] - xytosiz[0])
|
||||||
|
num_tiles_y = (xysiz[1] - xyosiz[1]) / (xytsiz[1] - xytosiz[1])
|
||||||
|
except ZeroDivisionError as err:
|
||||||
|
warnings.warn("Invalid tile dimensions.")
|
||||||
|
else:
|
||||||
|
numtiles = math.ceil(num_tiles_x) * math.ceil(num_tiles_y)
|
||||||
|
if numtiles > 65535:
|
||||||
|
msg = "Invalid number of tiles ({0}).".format(numtiles)
|
||||||
|
warnings.warn(msg)
|
||||||
|
|
||||||
|
|
||||||
kwargs = {'rsiz': rsiz,
|
kwargs = {'rsiz': rsiz,
|
||||||
'xysiz': xysiz,
|
'xysiz': xysiz,
|
||||||
'xyosiz': xyosiz,
|
'xyosiz': xyosiz,
|
||||||
|
|
@ -1514,14 +1526,6 @@ class SIZsegment(Segment):
|
||||||
lst.append(bitdepth - 1)
|
lst.append(bitdepth - 1)
|
||||||
self.ssiz = tuple(lst)
|
self.ssiz = tuple(lst)
|
||||||
|
|
||||||
num_tiles_x = (self.xsiz - self.xosiz) / (self.xtsiz - self.xtosiz)
|
|
||||||
num_tiles_y = (self.ysiz - self.yosiz) / (self.ytsiz - self.ytosiz)
|
|
||||||
numtiles = math.ceil(num_tiles_x) * math.ceil(num_tiles_y)
|
|
||||||
if numtiles > 65535:
|
|
||||||
msg = "Invalid number of tiles ({0}).".format(numtiles)
|
|
||||||
warnings.warn(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = "glymur.codestream.SIZsegment(rsiz={rsiz}, xysiz={xysiz}, "
|
msg = "glymur.codestream.SIZsegment(rsiz={rsiz}, xysiz={xysiz}, "
|
||||||
msg += "xyosiz={xyosiz}, xytsiz={xytsiz}, xytosiz={xytosiz}, "
|
msg += "xyosiz={xyosiz}, xytsiz={xytsiz}, xytosiz={xytosiz}, "
|
||||||
|
|
|
||||||
|
|
@ -359,13 +359,16 @@ class ColourSpecificationBox(Jp2kBox):
|
||||||
else:
|
else:
|
||||||
# 2.7 has trouble pretty-printing ordered dicts so we just have
|
# 2.7 has trouble pretty-printing ordered dicts so we just have
|
||||||
# to print as a regular dict in this case.
|
# to print as a regular dict in this case.
|
||||||
if sys.hexversion < 0x03000000:
|
if self.icc_profile is None:
|
||||||
icc_profile = dict(self.icc_profile)
|
msg += '\n ICC Profile: None'
|
||||||
else:
|
else:
|
||||||
icc_profile = self.icc_profile
|
if sys.hexversion < 0x03000000:
|
||||||
dispvalue = pprint.pformat(icc_profile)
|
icc_profile = dict(self.icc_profile)
|
||||||
lines = [' ' * 8 + y for y in dispvalue.split('\n')]
|
else:
|
||||||
msg += '\n ICC Profile:\n{0}'.format('\n'.join(lines))
|
icc_profile = self.icc_profile
|
||||||
|
dispvalue = pprint.pformat(icc_profile)
|
||||||
|
lines = [' ' * 8 + y for y in dispvalue.split('\n')]
|
||||||
|
msg += '\n ICC Profile:\n{0}'.format('\n'.join(lines))
|
||||||
|
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
@ -2628,19 +2631,22 @@ class XMLBox(Jp2kBox):
|
||||||
read_buffer = fptr.read(num_bytes)
|
read_buffer = fptr.read(num_bytes)
|
||||||
try:
|
try:
|
||||||
text = read_buffer.decode('utf-8')
|
text = read_buffer.decode('utf-8')
|
||||||
except UnicodeDecodeError as ude:
|
except UnicodeDecodeError as err:
|
||||||
# Possibly bad string of bytes to begin with.
|
# Possibly bad string of bytes to begin with.
|
||||||
# Try to search for <?xml and go from there.
|
# Try to search for <?xml and go from there.
|
||||||
decl_start = read_buffer.find(b'<?xml')
|
decl_start = read_buffer.find(b'<?xml')
|
||||||
if decl_start > -1:
|
if decl_start <= -1:
|
||||||
text = read_buffer[decl_start:].decode('utf-8')
|
msg = 'A problem was encountered while parsing an XML box:'
|
||||||
else:
|
msg += '\n\n\t"{0}"\n\nNo XML was retrieved.'
|
||||||
raise
|
warnings.warn(msg.format(str(err)))
|
||||||
|
return XMLBox(xml=None, length=length, offset=offset)
|
||||||
|
|
||||||
|
text = read_buffer[decl_start:].decode('utf-8')
|
||||||
|
|
||||||
# Let the user know that the XML box was problematic.
|
# Let the user know that the XML box was problematic.
|
||||||
msg = 'A UnicodeDecodeError was encountered parsing an XML box at '
|
msg = 'A UnicodeDecodeError was encountered parsing an XML box at '
|
||||||
msg += 'byte position {0} ({1}), but the XML was still recovered.'
|
msg += 'byte position {0} ({1}), but the XML was still recovered.'
|
||||||
msg = msg.format(offset, ude.reason)
|
msg = msg.format(offset, err.reason)
|
||||||
warnings.warn(msg, UserWarning)
|
warnings.warn(msg, UserWarning)
|
||||||
|
|
||||||
# Strip out any trailing nulls, as they can foul up XML parsing.
|
# Strip out any trailing nulls, as they can foul up XML parsing.
|
||||||
|
|
|
||||||
|
|
@ -582,3 +582,8 @@ issue_182_cmap = """Component Mapping Box (cmap) @ (130, 24)
|
||||||
Component 0 ==> palette column 0
|
Component 0 ==> palette column 0
|
||||||
Component 1 ==> palette column 0
|
Component 1 ==> palette column 0
|
||||||
Component 2 ==> 2"""
|
Component 2 ==> 2"""
|
||||||
|
|
||||||
|
issue_183_colr = """Colour Specification Box (colr) @ (62, 12)
|
||||||
|
Method: restricted ICC profile
|
||||||
|
Precedence: 0
|
||||||
|
ICC Profile: None"""
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,12 @@ import struct
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
import warnings
|
||||||
|
|
||||||
from glymur import Jp2k
|
from glymur import Jp2k
|
||||||
import glymur
|
import glymur
|
||||||
|
|
||||||
try:
|
from .fixtures import opj_data_file, OPJ_DATA_ROOT
|
||||||
DATA_ROOT = os.environ['OPJ_DATA_ROOT']
|
|
||||||
except KeyError:
|
|
||||||
DATA_ROOT = None
|
|
||||||
except:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
class TestCodestream(unittest.TestCase):
|
class TestCodestream(unittest.TestCase):
|
||||||
"""Test suite for unusual codestream cases."""
|
"""Test suite for unusual codestream cases."""
|
||||||
|
|
@ -34,7 +29,21 @@ class TestCodestream(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@unittest.skipIf(DATA_ROOT is None,
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
|
def test_tile_height_is_zero(self):
|
||||||
|
"""Zero tile height should not cause an exception."""
|
||||||
|
filename = opj_data_file('input/nonregression/2539.pdf.SIGFPE.706.1712.jp2')
|
||||||
|
if sys.hexversion < 0x03000000:
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
Jp2k(filename)
|
||||||
|
else:
|
||||||
|
with self.assertWarns(UserWarning):
|
||||||
|
Jp2k(filename)
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||||
def test_reserved_marker_segment(self):
|
def test_reserved_marker_segment(self):
|
||||||
|
|
@ -45,7 +54,7 @@ class TestCodestream(unittest.TestCase):
|
||||||
#
|
#
|
||||||
# Let's inject a reserved marker segment into a file that
|
# Let's inject a reserved marker segment into a file that
|
||||||
# we know something about to make sure we can still parse it.
|
# we know something about to make sure we can still parse it.
|
||||||
filename = os.path.join(DATA_ROOT, 'input/conformance/p0_01.j2k')
|
filename = os.path.join(OPJ_DATA_ROOT, 'input/conformance/p0_01.j2k')
|
||||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||||
with open(filename, 'rb') as ifile:
|
with open(filename, 'rb') as ifile:
|
||||||
# Everything up until the first QCD marker.
|
# Everything up until the first QCD marker.
|
||||||
|
|
@ -67,7 +76,7 @@ class TestCodestream(unittest.TestCase):
|
||||||
self.assertEqual(codestream.segment[2].length, 3)
|
self.assertEqual(codestream.segment[2].length, 3)
|
||||||
self.assertEqual(codestream.segment[2].data, b'\x00')
|
self.assertEqual(codestream.segment[2].data, b'\x00')
|
||||||
|
|
||||||
@unittest.skipIf(DATA_ROOT is None,
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||||
"Uses features introduced in 3.2.")
|
"Uses features introduced in 3.2.")
|
||||||
|
|
@ -77,7 +86,7 @@ class TestCodestream(unittest.TestCase):
|
||||||
# Let's inject a marker segment whose marker does not appear to
|
# Let's inject a marker segment whose marker does not appear to
|
||||||
# be valid. We still parse the file, but warn about the offending
|
# be valid. We still parse the file, but warn about the offending
|
||||||
# marker.
|
# marker.
|
||||||
filename = os.path.join(DATA_ROOT, 'input/conformance/p0_01.j2k')
|
filename = os.path.join(OPJ_DATA_ROOT, 'input/conformance/p0_01.j2k')
|
||||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||||
with open(filename, 'rb') as ifile:
|
with open(filename, 'rb') as ifile:
|
||||||
# Everything up until the first QCD marker.
|
# Everything up until the first QCD marker.
|
||||||
|
|
@ -100,11 +109,11 @@ class TestCodestream(unittest.TestCase):
|
||||||
self.assertEqual(codestream.segment[2].length, 3)
|
self.assertEqual(codestream.segment[2].length, 3)
|
||||||
self.assertEqual(codestream.segment[2].data, b'\x00')
|
self.assertEqual(codestream.segment[2].data, b'\x00')
|
||||||
|
|
||||||
@unittest.skipIf(DATA_ROOT is None,
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
def test_psot_is_zero(self):
|
def test_psot_is_zero(self):
|
||||||
"""Psot=0 in SOT is perfectly legal. Issue #78."""
|
"""Psot=0 in SOT is perfectly legal. Issue #78."""
|
||||||
filename = os.path.join(DATA_ROOT,
|
filename = os.path.join(OPJ_DATA_ROOT,
|
||||||
'input/nonregression/123.j2c')
|
'input/nonregression/123.j2c')
|
||||||
j = Jp2k(filename)
|
j = Jp2k(filename)
|
||||||
codestream = j.get_codestream(header_only=False)
|
codestream = j.get_codestream(header_only=False)
|
||||||
|
|
@ -125,11 +134,11 @@ class TestCodestream(unittest.TestCase):
|
||||||
self.assertEqual(codestream.segment[1].ssiz, (7, 7, 7))
|
self.assertEqual(codestream.segment[1].ssiz, (7, 7, 7))
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(DATA_ROOT is None,
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
def test_siz_segment_ssiz_signed(self):
|
def test_siz_segment_ssiz_signed(self):
|
||||||
"""ssiz attribute to be removed in future release"""
|
"""ssiz attribute to be removed in future release"""
|
||||||
filename = os.path.join(DATA_ROOT, 'input/conformance/p0_03.j2k')
|
filename = os.path.join(OPJ_DATA_ROOT, 'input/conformance/p0_03.j2k')
|
||||||
j = Jp2k(filename)
|
j = Jp2k(filename)
|
||||||
codestream = j.get_codestream()
|
codestream = j.get_codestream()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ from glymur.jp2box import ColourSpecificationBox, ContiguousCodestreamBox
|
||||||
from glymur.jp2box import FileTypeBox, ImageHeaderBox, JP2HeaderBox
|
from glymur.jp2box import FileTypeBox, ImageHeaderBox, JP2HeaderBox
|
||||||
from glymur.jp2box import JPEG2000SignatureBox
|
from glymur.jp2box import JPEG2000SignatureBox
|
||||||
|
|
||||||
|
from .fixtures import OPJ_DATA_ROOT, opj_data_file
|
||||||
|
|
||||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||||
class TestXML(unittest.TestCase):
|
class TestXML(unittest.TestCase):
|
||||||
|
|
@ -93,6 +94,22 @@ class TestXML(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
os.unlink(self.xmlfile)
|
os.unlink(self.xmlfile)
|
||||||
|
|
||||||
|
def test_invalid_utf8(self):
|
||||||
|
"""Bad byte sequence that cannot be parsed."""
|
||||||
|
filename = opj_data_file(os.path.join('input',
|
||||||
|
'nonregression',
|
||||||
|
'26ccf3651020967f7778238ef5af08af.SIGFPE.d25.527.jp2'))
|
||||||
|
if sys.hexversion < 0x03000000:
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
else:
|
||||||
|
with self.assertWarns(UserWarning):
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
|
||||||
|
self.assertIsNone(jp2.box[3].box[1].box[1].xml)
|
||||||
|
|
||||||
|
|
||||||
def test_negative_file_and_xml(self):
|
def test_negative_file_and_xml(self):
|
||||||
"""The XML should come from only one source."""
|
"""The XML should come from only one source."""
|
||||||
xml_object = ET.parse(self.xmlfile)
|
xml_object = ET.parse(self.xmlfile)
|
||||||
|
|
|
||||||
|
|
@ -920,6 +920,17 @@ class TestPrinting(unittest.TestCase):
|
||||||
actual = fake_out.getvalue().strip()
|
actual = fake_out.getvalue().strip()
|
||||||
self.assertEqual(actual, fixtures.issue_182_cmap)
|
self.assertEqual(actual, fixtures.issue_182_cmap)
|
||||||
|
|
||||||
|
def test_issue183(self):
|
||||||
|
filename = opj_data_file('input/nonregression/orb-blue10-lin-jp2.jp2')
|
||||||
|
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
# Ignore warning about bad pclr box.
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jp2 = Jp2k(filename)
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
print(jp2.box[2].box[1])
|
||||||
|
actual = fake_out.getvalue().strip()
|
||||||
|
self.assertEqual(actual, fixtures.issue_183_colr)
|
||||||
|
|
||||||
@unittest.skipIf(sys.hexversion < 0x03000000,
|
@unittest.skipIf(sys.hexversion < 0x03000000,
|
||||||
"Ordered dicts not printing well in 2.7")
|
"Ordered dicts not printing well in 2.7")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue