From 4fafaa8661fe4352325a1577fbaf0dd4437e8e75 Mon Sep 17 00:00:00 2001 From: jevans Date: Sun, 9 Feb 2014 12:51:28 -0500 Subject: [PATCH] Added set_printoptions, get_printoptions functions. Closes #158 --- CHANGES.txt | 9 +- glymur/__init__.py | 1 + glymur/jp2box.py | 183 ++++++++++++++++--- glymur/test/fixtures.py | 332 +++++++++++++++++++++++++++++++++++ glymur/test/test_printing.py | 162 +++++++---------- 5 files changed, 557 insertions(+), 130 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f70550e..f6c3728 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,10 +1,11 @@ -Feb 08, 2014 - Removed support for Python 2.6. Added write support for JP2 +Feb 09, 2014 - Removed support for Python 2.6. Added write support for JP2 UUID, DataEntryURL, Palette and Component Mapping boxes, JPX Association, NumberList and DataReference boxes. Added read support for JPX free, number list, data reference, fragment - table, and fragment list boxes. Palette box now a 2D numpy - array instead of a list of 1D arrays. JP2 super box constructors - now take optional box list argument. Fixed bug where JPX files + table, and fragment list boxes. Added get_printoptions, + set_printoptions functions. Palette box now a 2D numpy array + instead of a list of 1D arrays. JP2 super box constructors now + take optional box list argument. Fixed bug where JPX files with more than one codestream but advertising jp2 compatibility were not being read. diff --git a/glymur/__init__.py b/glymur/__init__.py index ba17727..5826f8c 100644 --- a/glymur/__init__.py +++ b/glymur/__init__.py @@ -8,6 +8,7 @@ __version__ = version.version from .jp2k import Jp2k from .jp2dump import jp2dump +from .jp2box import get_printoptions, set_printoptions from . import data diff --git a/glymur/jp2box.py b/glymur/jp2box.py index f8a0dc8..d0b6c07 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -14,9 +14,7 @@ References # pylint: disable=C0302,R0903,R0913 from collections import OrderedDict -import copy import datetime -import io import math import os import pprint @@ -24,7 +22,6 @@ import struct import sys import uuid import warnings -import xml import xml.etree.cElementTree as ET import numpy as np @@ -244,6 +241,8 @@ class ColourSpecificationBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg msg += '\n Method: {0}'.format(_METHOD_DISPLAY[self.method]) msg += '\n Precedence: {0}'.format(self.precedence) @@ -501,6 +500,9 @@ class ChannelDefinitionBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + for j in range(len(self.association)): color_type_string = _COLOR_TYPE_MAP_DISPLAY[self.channel_type[j]] if self.association[j] == 0: @@ -591,6 +593,9 @@ class CodestreamHeaderBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + for box in self.box: boxstr = str(box) @@ -655,6 +660,9 @@ class CompositingLayerHeaderBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + for box in self.box: boxstr = str(box) @@ -727,6 +735,9 @@ class ComponentMappingBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + for k in range(len(self.component_index)): if self.mapping_type[k] == 1: @@ -746,7 +757,7 @@ class ComponentMappingBox(Jp2kBox): fptr.write(write_buffer) for j in range(len(self.component_index)): - write_buffer = struct.pack('>HBB', + write_buffer = struct.pack('>HBB', self.component_index[j], self.mapping_type[j], self.palette_index[j]) @@ -812,6 +823,11 @@ class ContiguousCodestreamBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + if _printoptions['codestream'] == False: + return msg + msg += '\n Main header:' for segment in self.main_header.segment: segstr = str(segment) @@ -889,6 +905,9 @@ class DataReferenceBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + for box in self.DR: msg += '\n ' + str(box) return msg @@ -976,7 +995,11 @@ class FileTypeBox(Jp2kBox): return msg def __str__(self): - lst = [Jp2kBox.__str__(self), + msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + + lst = [msg, ' Brand: {0}', ' Compatibility: {1}'] msg = '\n'.join(lst) @@ -1067,6 +1090,9 @@ class FragmentListBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + for j in range(len(self.fragment_offset)): msg += "\n Offset {0}: {1}" msg += "\n Fragment Length {2}: {3}" @@ -1092,13 +1118,13 @@ class FragmentListBox(Jp2kBox): Returns ------- - FreeBox instance + FragmentListBox instance """ read_buffer = fptr.read(2) - nf, = struct.unpack('>H', read_buffer) + num_fragments, = struct.unpack('>H', read_buffer) - read_buffer = fptr.read(nf * 14) - lst = struct.unpack('>' + 'QIH' * nf, read_buffer) + read_buffer = fptr.read(num_fragments * 14) + lst = struct.unpack('>' + 'QIH' * num_fragments, read_buffer) frag_offset = lst[0::3] frag_len = lst[1::3] data_reference = lst[2::3] @@ -1131,6 +1157,9 @@ class FragmentTableBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + for box in self.box: boxstr = str(box) @@ -1191,6 +1220,9 @@ class FreeBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + return msg @staticmethod @@ -1280,6 +1312,10 @@ class ImageHeaderBox(Jp2kBox): return msg def __str__(self): + msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + msg = "{0}" msg += '\n Size: [{1} {2} {3}]' msg += '\n Bitdepth: {4}' @@ -1380,6 +1416,9 @@ class AssociationBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + for box in self.box: boxstr = str(box) @@ -1513,6 +1552,9 @@ class JPEG2000SignatureBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + msg += '\n Signature: {0:02x}{1:02x}{2:02x}{3:02x}' msg = msg.format(self.signature[0], self.signature[1], self.signature[2], self.signature[3]) @@ -1584,16 +1626,15 @@ class PaletteBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + msg += '\n Size: ({0} x {1})'.format(*self.palette.shape) return msg def write(self, fptr): """Write a Palette box to file. """ - # Box length is usual header (8) - # + num entries NE (2) + num columns NC (1) - # + (bps/8, /signed) for each column (3) + bps * NC - # + 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 @@ -1608,7 +1649,7 @@ class PaletteBox(Jp2kBox): fptr.write(write_buffer) bps_signed = [x - 1 for x in self.bits_per_component] - for j, item in enumerate(bps_signed): + for j, _ in enumerate(bps_signed): if self.signed[j]: bps_signed[j] |= 0x80 write_buffer = struct.pack('>' + 'B' * self.palette.shape[1], @@ -1616,13 +1657,10 @@ class PaletteBox(Jp2kBox): fptr.write(write_buffer) if self.bits_per_component[0] <= 8: - dtype = np.uint8 code = 'B' elif self.bits_per_component[0] <= 16: - dtype = np.uint16 code = 'H' elif self.bits_per_component[0] <= 32: - dtype = np.uint32 code = 'I' fmt = '>' + code * self.palette.shape[1] @@ -1820,6 +1858,9 @@ class ReaderRequirementsBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + msg += '\n Standard Features:' for j in range(len(self.standard_flag)): @@ -2044,6 +2085,9 @@ class CaptureResolutionBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + msg += '\n VCR: {0}'.format(self.vertical_resolution) msg += '\n HCR: {0}'.format(self.horizontal_resolution) return msg @@ -2106,6 +2150,9 @@ class DisplayResolutionBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + msg += '\n VDR: {0}'.format(self.vertical_resolution) msg += '\n HDR: {0}'.format(self.horizontal_resolution) return msg @@ -2162,6 +2209,9 @@ class LabelBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + msg += '\n Label: {0}'.format(self.label) return msg @@ -2218,6 +2268,9 @@ class NumberListBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + for j, association in enumerate(self.associations): msg += '\n Association[{0}]: '.format(j) if association == 0: @@ -2315,6 +2368,11 @@ class XMLBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + if _printoptions['xml'] == False: + return msg + xml = self.xml if self.xml is not None: msg += _pretty_print_xml(self.xml) @@ -2416,9 +2474,12 @@ class UUIDListBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + for j, uuid_item in enumerate(self.ulst): msg += '\n UUID[{0}]: {1}'.format(j, uuid_item) - return(msg) + return msg @staticmethod def parse(fptr, offset, length): @@ -2446,7 +2507,7 @@ class UUIDListBox(Jp2kBox): ulst.append(uuid.UUID(bytes=read_buffer)) box = UUIDListBox(ulst, length=length, offset=offset) - return(box) + return box class UUIDInfoBox(Jp2kBox): @@ -2477,7 +2538,6 @@ class UUIDInfoBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - for box in self.box: box_str = str(box) @@ -2485,7 +2545,7 @@ class UUIDInfoBox(Jp2kBox): lst = [('\n ' + x) for x in box_str.split('\n')] msg += ''.join(lst) - return(msg) + return msg @staticmethod def parse(fptr, offset, length): @@ -2561,6 +2621,9 @@ class DataEntryURLBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + msg += '\n ' lines = ['Version: {0}', @@ -2671,15 +2734,30 @@ class UUIDBox(Jp2kBox): return msg.format(repr(self.uuid), len(self.raw_data)) def __str__(self): - msg = '{0}\n UUID: {1}'.format(Jp2kBox.__str__(self), self.uuid) + msg = Jp2kBox.__str__(self) + if _printoptions['short'] == True: + return msg + + msg = '{0}\n UUID: {1}'.format(msg, self.uuid) + if self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'): + msg += ' (XMP)' + elif self.uuid.bytes == b'JpgTiffExif->JP2': + msg += ' (EXIF)' + else: + msg += ' (unknown)' + + if (((_printoptions['xml'] == False) and + (self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')))): + # If it's an XMP UUID, don't print the XML contents. + return msg if self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'): - line = ' (XMP)\n UUID Data: {0}' + line = '\n UUID Data: {0}' msg += line.format(_pretty_print_xml(self.data)) elif self.uuid.bytes == b'JpgTiffExif->JP2': - msg += ' (EXIF)\n UUID Data: {0}'.format(str(self.data)) + msg += '\n UUID Data: {0}'.format(str(self.data)) else: - line = ' (unknown)\n UUID Data: {0} bytes' + line = '\n UUID Data: {0} bytes' msg += line.format(len(self.raw_data)) return msg @@ -2748,3 +2826,58 @@ _BOX_WITH_ID = { 'url ': DataEntryURLBox, 'uuid': UUIDBox, 'xml ': XMLBox} + +_printoptions = {'short': False, 'xml': True, 'codestream': True} + +def set_printoptions(short=False, xml=True, codestream=True): + """Set printing options. + + These options determine the way JPEG 2000 boxes are displayed. + + Parameters + ---------- + short : bool, optional + When True, only the box ID, offset, and length are displayed. Useful + for displaying only the basic structure or skeleton of a JPEG 2000 file. + xml : bool, optional + When False, printing of the XML contents of any XML boxes or UUID XMP + boxes is suppressed. + codestream : bool, optional + When False, printing of the codestream contents is suppressed. + + See also + -------- + get_printoptions + + Examples + -------- + To put back the default options, you can use: + + >>> import glymur + >>> glymur.set_printoptions(short=False, xml=True, codestream=True) + """ + _printoptions['short'] = short + _printoptions['xml'] = xml + _printoptions['codestream'] = codestream + +def get_printoptions(): + """Return the current print options. + + Returns + ------- + print_opts : dict + Dictionary of current print options with keys + + - short : bool + - xml : bool + - codestream : bool + + For a full description of these options, see `set_printoptions`. + + See also + -------- + set_printoptions + """ + return _printoptions + + diff --git a/glymur/test/fixtures.py b/glymur/test/fixtures.py index ff33237..182a04f 100644 --- a/glymur/test/fixtures.py +++ b/glymur/test/fixtures.py @@ -350,3 +350,335 @@ text_gbr_34 = """Colour Specification Box (colr) @ (179, 1339) 'Rendering Intent': 'perceptual', 'Illuminant': array([ 0.96420288, 1. , 0.8249054 ]), 'Creator': 'appl'}""" + + +# Metadata dump of nemo. +nemo_dump_full = r'''JPEG 2000 Signature Box (jP ) @ (0, 12) + Signature: 0d0a870a +File Type Box (ftyp) @ (12, 20) + Brand: jp2 + Compatibility: ['jp2 '] +JP2 Header Box (jp2h) @ (32, 45) + Image Header Box (ihdr) @ (40, 22) + Size: [1456 2592 3] + Bitdepth: 8 + Signed: False + Compression: wavelet + Colorspace Unknown: False + Colour Specification Box (colr) @ (62, 15) + Method: enumerated colorspace + Precedence: 0 + Colorspace: sRGB +UUID Box (uuid) @ (77, 3146) + UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP) + UUID Data: + + + + Google + 2013-02-09T14:47:53 + + + 1 + 72/1 + 72/1 + 2 + HTC + HTC Glacier + 2592 + 1456 + + + 8 + 8 + 8 + + + 2 + 3 + + + 1343036288/4294967295 + 1413044224/4294967295 + + + + + 2748779008/4294967295 + 1417339264/4294967295 + 1288490240/4294967295 + 2576980480/4294967295 + 644245120/4294967295 + 257698032/4294967295 + + + + + 1 + 2528 + 1424 + 353/100 + 0 + 0/1 + WGS-84 + 2013-02-09T14:47:53 + + + 76 + + + 0220 + 0100 + + + 1 + 2 + 3 + 0 + + + 42,20.56N + 71,5.29W + 2013-02-09T19:47:53Z + NETWORK + + + 2013-02-09T14:47:53 + + + + + Glymur + Python XMP Toolkit + + + + + + +Contiguous Codestream Box (jp2c) @ (3223, 1132296) + Main header: + SOC marker segment @ (3231, 0) + SIZ marker segment @ (3233, 47) + Profile: 2 + Reference Grid Height, Width: (1456 x 2592) + Vertical, Horizontal Reference Grid Offset: (0 x 0) + Reference Tile Height, Width: (1456 x 2592) + Vertical, Horizontal Reference Tile Offset: (0 x 0) + Bitdepth: (8, 8, 8) + Signed: (False, False, False) + Vertical, Horizontal Subsampling: ((1, 1), (1, 1), (1, 1)) + COD marker segment @ (3282, 12) + Coding style: + Entropy coder, without partitions + SOP marker segments: False + EPH marker segments: False + Coding style parameters: + Progression order: LRCP + Number of layers: 2 + Multiple component transformation usage: reversible + Number of resolutions: 2 + Code block height, width: (64 x 64) + Wavelet transform: 5-3 reversible + Precinct size: default, 2^15 x 2^15 + Code block context: + Selective arithmetic coding bypass: False + Reset context probabilities on coding pass boundaries: False + Termination on each coding pass: False + Vertically stripe causal context: False + Predictable termination: False + Segmentation symbols: False + QCD marker segment @ (3296, 7) + Quantization style: no quantization, 2 guard bits + Step size: [(0, 8), (0, 9), (0, 9), (0, 10)] + CME marker segment @ (3305, 37) + "Created by OpenJPEG version 2.0.0"''' + +nemo_dump_short = r"""JPEG 2000 Signature Box (jP ) @ (0, 12) +File Type Box (ftyp) @ (12, 20) +JP2 Header Box (jp2h) @ (32, 45) + Image Header Box (ihdr) @ (40, 22) + Colour Specification Box (colr) @ (62, 15) +UUID Box (uuid) @ (77, 3146) +Contiguous Codestream Box (jp2c) @ (3223, 1132296)""" + +nemo_dump_no_xml = '''JPEG 2000 Signature Box (jP ) @ (0, 12) + Signature: 0d0a870a +File Type Box (ftyp) @ (12, 20) + Brand: jp2 + Compatibility: ['jp2 '] +JP2 Header Box (jp2h) @ (32, 45) + Image Header Box (ihdr) @ (40, 22) + Size: [1456 2592 3] + Bitdepth: 8 + Signed: False + Compression: wavelet + Colorspace Unknown: False + Colour Specification Box (colr) @ (62, 15) + Method: enumerated colorspace + Precedence: 0 + Colorspace: sRGB +UUID Box (uuid) @ (77, 3146) + UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP) +Contiguous Codestream Box (jp2c) @ (3223, 1132296) + Main header: + SOC marker segment @ (3231, 0) + SIZ marker segment @ (3233, 47) + Profile: 2 + Reference Grid Height, Width: (1456 x 2592) + Vertical, Horizontal Reference Grid Offset: (0 x 0) + Reference Tile Height, Width: (1456 x 2592) + Vertical, Horizontal Reference Tile Offset: (0 x 0) + Bitdepth: (8, 8, 8) + Signed: (False, False, False) + Vertical, Horizontal Subsampling: ((1, 1), (1, 1), (1, 1)) + COD marker segment @ (3282, 12) + Coding style: + Entropy coder, without partitions + SOP marker segments: False + EPH marker segments: False + Coding style parameters: + Progression order: LRCP + Number of layers: 2 + Multiple component transformation usage: reversible + Number of resolutions: 2 + Code block height, width: (64 x 64) + Wavelet transform: 5-3 reversible + Precinct size: default, 2^15 x 2^15 + Code block context: + Selective arithmetic coding bypass: False + Reset context probabilities on coding pass boundaries: False + Termination on each coding pass: False + Vertically stripe causal context: False + Predictable termination: False + Segmentation symbols: False + QCD marker segment @ (3296, 7) + Quantization style: no quantization, 2 guard bits + Step size: [(0, 8), (0, 9), (0, 9), (0, 10)] + CME marker segment @ (3305, 37) + "Created by OpenJPEG version 2.0.0"''' + +nemo_dump_no_codestream = r"""JPEG 2000 Signature Box (jP ) @ (0, 12) + Signature: 0d0a870a +File Type Box (ftyp) @ (12, 20) + Brand: jp2 + Compatibility: ['jp2 '] +JP2 Header Box (jp2h) @ (32, 45) + Image Header Box (ihdr) @ (40, 22) + Size: [1456 2592 3] + Bitdepth: 8 + Signed: False + Compression: wavelet + Colorspace Unknown: False + Colour Specification Box (colr) @ (62, 15) + Method: enumerated colorspace + Precedence: 0 + Colorspace: sRGB +UUID Box (uuid) @ (77, 3146) + UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP) + UUID Data: + + + + Google + 2013-02-09T14:47:53 + + + 1 + 72/1 + 72/1 + 2 + HTC + HTC Glacier + 2592 + 1456 + + + 8 + 8 + 8 + + + 2 + 3 + + + 1343036288/4294967295 + 1413044224/4294967295 + + + + + 2748779008/4294967295 + 1417339264/4294967295 + 1288490240/4294967295 + 2576980480/4294967295 + 644245120/4294967295 + 257698032/4294967295 + + + + + 1 + 2528 + 1424 + 353/100 + 0 + 0/1 + WGS-84 + 2013-02-09T14:47:53 + + + 76 + + + 0220 + 0100 + + + 1 + 2 + 3 + 0 + + + 42,20.56N + 71,5.29W + 2013-02-09T19:47:53Z + NETWORK + + + 2013-02-09T14:47:53 + + + + + Glymur + Python XMP Toolkit + + + + + + +Contiguous Codestream Box (jp2c) @ (3223, 1132296)""" + +nemo_dump_no_codestream_no_xml = r"""JPEG 2000 Signature Box (jP ) @ (0, 12) + Signature: 0d0a870a +File Type Box (ftyp) @ (12, 20) + Brand: jp2 + Compatibility: ['jp2 '] +JP2 Header Box (jp2h) @ (32, 45) + Image Header Box (ihdr) @ (40, 22) + Size: [1456 2592 3] + Bitdepth: 8 + Signed: False + Compression: wavelet + Colorspace Unknown: False + Colour Specification Box (colr) @ (62, 15) + Method: enumerated colorspace + Precedence: 0 + Colorspace: sRGB +UUID Box (uuid) @ (77, 3146) + UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP) +Contiguous Codestream Box (jp2c) @ (3223, 1132296)""" diff --git a/glymur/test/test_printing.py b/glymur/test/test_printing.py index 3e9d3ab..fa9ab0c 100644 --- a/glymur/test/test_printing.py +++ b/glymur/test/test_printing.py @@ -11,7 +11,6 @@ # pylint: disable=R0904 import os -import re import struct import sys import tempfile @@ -31,104 +30,76 @@ else: import glymur from glymur import Jp2k +from . import fixtures from .fixtures import OPJ_DATA_ROOT, opj_data_file, nemo_xmp_box from .fixtures import text_gbr_27, text_gbr_33, text_gbr_34 @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") -@unittest.skipIf(re.match(r"""1\.[01234]""", glymur.version.openjpeg_version), - "Need at least 1.5 in order to write jp2 files.") -class TestPrintingNeedsLib(unittest.TestCase): - """These tests require the library, mostly in order to just setup the test. - """ - - @classmethod - def setUpClass(cls): - # Setup a plain JP2 file without the two UUID boxes. - jp2file = glymur.data.nemo() - with tempfile.NamedTemporaryFile(suffix='.jp2', delete=False) as tfile: - cls._plain_nemo_file = tfile.name - ijfile = Jp2k(jp2file) - data = ijfile.read(rlevel=1) - ojfile = Jp2k(cls._plain_nemo_file, 'wb') - ojfile.write(data) - - @classmethod - def tearDownClass(cls): - os.unlink(cls._plain_nemo_file) - +class TestPrinting(unittest.TestCase): + """Tests for verifying how printing works.""" def setUp(self): self.jp2file = glymur.data.nemo() self.j2kfile = glymur.data.goodstuff() - # Save the output of dumping nemo.jp2 for more than one test. - lines = ['JPEG 2000 Signature Box (jP ) @ (0, 12)', - ' Signature: 0d0a870a', - 'File Type Box (ftyp) @ (12, 20)', - ' Brand: jp2 ', - " Compatibility: ['jp2 ']", - 'JP2 Header Box (jp2h) @ (32, 45)', - ' Image Header Box (ihdr) @ (40, 22)', - ' Size: [728 1296 3]', - ' Bitdepth: 8', - ' Signed: False', - ' Compression: wavelet', - ' Colorspace Unknown: False', - ' Colour Specification Box (colr) @ (62, 15)', - ' Method: enumerated colorspace', - ' Precedence: 0', - ' Colorspace: sRGB', - 'Contiguous Codestream Box (jp2c) @ (77, 1632355)', - ' Main header:', - ' SOC marker segment @ (85, 0)', - ' SIZ marker segment @ (87, 47)', - ' Profile: 2', - ' Reference Grid Height, Width: (728 x 1296)', - ' Vertical, Horizontal Reference Grid Offset: ' - + '(0 x 0)', - ' Reference Tile Height, Width: (728 x 1296)', - ' Vertical, Horizontal Reference Tile Offset: ' - + '(0 x 0)', - ' Bitdepth: (8, 8, 8)', - ' Signed: (False, False, False)', - ' Vertical, Horizontal Subsampling: ' - + '((1, 1), (1, 1), (1, 1))', - ' COD marker segment @ (136, 12)', - ' Coding style:', - ' Entropy coder, without partitions', - ' SOP marker segments: False', - ' EPH marker segments: False', - ' Coding style parameters:', - ' Progression order: LRCP', - ' Number of layers: 1', - ' Multiple component transformation usage: ' - + 'reversible', - ' Number of resolutions: 6', - ' Code block height, width: (64 x 64)', - ' Wavelet transform: 5-3 reversible', - ' Precinct size: default, 2^15 x 2^15', - ' Code block context:', - ' Selective arithmetic coding bypass: ' - + 'False', - ' Reset context probabilities on ' - + 'coding pass boundaries: False', - ' Termination on each coding pass: False', - ' Vertically stripe causal context: ' - + 'False', - ' Predictable termination: False', - ' Segmentation symbols: False', - ' QCD marker segment @ (150, 19)', - ' Quantization style: no quantization, ' - + '2 guard bits', - ' Step size: [(0, 8), (0, 9), (0, 9), ' - + '(0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), ' - + '(0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), ' - + '(0, 10)]'] - self.expected_plain = '\n'.join(lines) + # Reset printoptions for every test. + glymur.set_printoptions(short=False, xml=True, codestream=True) def tearDown(self): pass + def test_printopt_no_codestr_or_xml(self): + """Verify printed output when codestream=False and xml=False""" + glymur.set_printoptions(codestream=False, xml=False) + with patch('sys.stdout', new=StringIO()) as fake_out: + glymur.jp2dump(self.jp2file) + actual = fake_out.getvalue().strip() + + # Get rid of the filename line, as it is not set in stone. + lst = actual.split('\n') + lst = lst[1:] + actual = '\n'.join(lst) + self.assertEqual(actual, fixtures.nemo_dump_no_codestream_no_xml) + + def test_printoptions_no_codestream(self): + """Verify printed output when codestream=False""" + glymur.set_printoptions(codestream=False) + with patch('sys.stdout', new=StringIO()) as fake_out: + glymur.jp2dump(self.jp2file) + actual = fake_out.getvalue().strip() + + # Get rid of the filename line, as it is not set in stone. + lst = actual.split('\n') + lst = lst[1:] + actual = '\n'.join(lst) + self.assertEqual(actual, fixtures.nemo_dump_no_codestream) + + def test_printoptions_no_xml(self): + """Verify printed output when xml=False""" + glymur.set_printoptions(xml=False) + with patch('sys.stdout', new=StringIO()) as fake_out: + glymur.jp2dump(self.jp2file) + actual = fake_out.getvalue().strip() + + # Get rid of the filename line, as it is not set in stone. + lst = actual.split('\n') + lst = lst[1:] + actual = '\n'.join(lst) + self.assertEqual(actual, fixtures.nemo_dump_no_xml) + + def test_printoptions_short(self): + """Verify printed output when short=True""" + glymur.set_printoptions(short=True) + with patch('sys.stdout', new=StringIO()) as fake_out: + glymur.jp2dump(self.jp2file) + actual = fake_out.getvalue().strip() + + # Get rid of the filename line, as it is not set in stone. + lst = actual.split('\n') + lst = lst[1:] + actual = '\n'.join(lst) + self.assertEqual(actual, fixtures.nemo_dump_short) + def test_asoc_label_box(self): """verify printing of asoc, label boxes""" # Construct a fake file with an asoc and a label box, as @@ -185,18 +156,18 @@ class TestPrintingNeedsLib(unittest.TestCase): def test_jp2dump(self): """basic jp2dump test""" with patch('sys.stdout', new=StringIO()) as fake_out: - glymur.jp2dump(self._plain_nemo_file) + glymur.jp2dump(self.jp2file) actual = fake_out.getvalue().strip() # Get rid of the filename line, as it is not set in stone. lst = actual.split('\n') lst = lst[1:] actual = '\n'.join(lst) - self.assertEqual(actual, self.expected_plain) + self.assertEqual(actual, fixtures.nemo_dump_full) def test_entire_file(self): """verify output from printing entire file""" - j = glymur.Jp2k(self._plain_nemo_file) + j = glymur.Jp2k(self.jp2file) with patch('sys.stdout', new=StringIO()) as fake_out: print(j) actual = fake_out.getvalue().strip() @@ -206,18 +177,7 @@ class TestPrintingNeedsLib(unittest.TestCase): lst = lst[1:] actual = '\n'.join(lst) - self.assertEqual(actual, self.expected_plain) - - -class TestPrinting(unittest.TestCase): - """Test suite for printing where the libraries are not needed""" - - def setUp(self): - # Save sys.stdout. - self.jp2file = glymur.data.nemo() - - def tearDown(self): - pass + self.assertEqual(actual, fixtures.nemo_dump_full) def test_coc_segment(self): """verify printing of COC segment"""