# -*- coding:  utf-8 -*-
"""
Test suite for printing.
"""
# Standard library imports ...
import importlib.resources as ir
from io import BytesIO, StringIO
import shutil
import struct
import sys
import unittest
from unittest.mock import patch
from uuid import UUID
import warnings

# Third party imports ...
import numpy as np
import lxml.etree as ET

import glymur
from glymur.codestream import LRCP, WAVELET_XFORM_5X3_REVERSIBLE
from glymur.core import COLOR, RED, GREEN, BLUE, RESTRICTED_ICC_PROFILE
from glymur.jp2box import BitsPerComponentBox, ColourSpecificationBox
from glymur.jp2box import LabelBox, UUIDBox
from glymur import Jp2k
from glymur.lib import openjp2 as opj2
from . import fixtures
from .fixtures import OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG


class TestPrinting(fixtures.TestCommon):
    """
    Tests for verifying how printing works.
    """
    def setUp(self):
        super().setUp()

        # Reset printoptions for every test.
        glymur.reset_option('all')

    def tearDown(self):
        super().tearDown()
        glymur.reset_option('all')

    def test_cap_segment(self):
        """
        Scenario:  Print a CAP segment

        Expected Result:  segment is verified
        """
        htj2k_file = ir.files('tests.data.from-openjpeg') \
                       .joinpath('oj-ht-byte.jph')
        j = glymur.Jp2kr(htj2k_file)
        actual = str(j.codestream.segment[2])
        expected = (
            'CAP marker segment @ (467, 8)\n'
            '    Pcap:  Part 15 (ISO/IEC 15444-15)\n'
            '    Ccap:  (3,)'
        )
        self.assertEqual(actual, expected)

    def test_empty_file(self):
        """
        SCENARIO:  Print the file after with object is constructed, but
        before data is written to it.

        EXPECTED RESULT:  Just the single line.
        """
        filename = self.test_dir_path / 'a.jp2'
        actual = str(Jp2k(filename))
        expected = 'File:  a.jp2'
        self.assertEqual(actual, expected)

    def test_bad_color_specification(self):
        """
        Invalid channel type should not prevent printing.
        """
        path = ir.files('tests.data').joinpath('issue392.jp2')
        with warnings.catch_warnings():
            warnings.simplefilter('ignore')
            str(Jp2k(path))

    def test_palette(self):
        """
        verify printing of pclr box

        Original file tested was input/conformance/file9.jp2
        """
        palette = np.array([[0, 0, 0] for _ in range(256)], dtype=np.uint8)
        bps = (8, 8, 8)
        signed = (False, False, False)
        box = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
                                       signed=signed, length=782, offset=66)
        actual = str(box)
        expected = ('Palette Box (pclr) @ (66, 782)\n'
                    '    Size:  (256 x 3)')
        self.assertEqual(actual, expected)

        glymur.set_option('print.short', True)
        actual = str(box)
        expected = ('Palette Box (pclr) @ (66, 782)')
        self.assertEqual(actual, expected)

    def test_component_mapping_palette(self):
        """
        verify printing of cmap box tied to a palette

        Original file tested was input/conformance/file9.jp2
        """
        cmap = glymur.jp2box.ComponentMappingBox(component_index=(0, 0, 0),
                                                 mapping_type=(1, 1, 1),
                                                 palette_index=(0, 1, 2),
                                                 length=20, offset=848)
        actual = str(cmap)
        expected = ('Component Mapping Box (cmap) @ (848, 20)\n'
                    '    Component 0 ==> palette column 0\n'
                    '    Component 0 ==> palette column 1\n'
                    '    Component 0 ==> palette column 2')
        self.assertEqual(actual, expected)

    def test_component_mapping_non_palette(self):
        """
        verify printing of cmap box where there is no palette
        """
        cmap = glymur.jp2box.ComponentMappingBox(component_index=(0, 1, 2),
                                                 mapping_type=(0, 0, 0),
                                                 palette_index=(0, 0, 0),
                                                 length=20, offset=848)
        actual = str(cmap)
        expected = ('Component Mapping Box (cmap) @ (848, 20)\n'
                    '    Component 0 ==> 0\n'
                    '    Component 1 ==> 1\n'
                    '    Component 2 ==> 2')
        self.assertEqual(actual, expected)

    def test_channel_definition(self):
        """
        verify printing of cdef box

        Original file tested was input/conformance/file2.jp2
        """
        channel_type = [COLOR, COLOR, COLOR]
        association = [BLUE, GREEN, RED]
        cdef = glymur.jp2box.ChannelDefinitionBox(index=[0, 1, 2],
                                                  channel_type=channel_type,
                                                  association=association,
                                                  length=28, offset=81)
        actual = str(cdef)
        expected = ('Channel Definition Box (cdef) @ (81, 28)\n'
                    '    Channel 0 (color) ==> (3)\n'
                    '    Channel 1 (color) ==> (2)\n'
                    '    Channel 2 (color) ==> (1)')
        self.assertEqual(actual, expected)

        glymur.set_option('print.short', True)
        actual = str(cdef)
        expected = ('Channel Definition Box (cdef) @ (81, 28)')
        self.assertEqual(actual, expected)

    def test_xml(self):
        """
        SCENARIO:  JP2 file has an XML box.

        The original test file was input/conformance/file1.jp2

        EXPECTED RESULT:  The string representation of the XML box matches
        expectations.
        """
        s = ir.files('tests.data.conformance') \
              .joinpath('file1_xml.txt') \
              .read_text()
        elt = ET.fromstring(s)
        xml = ET.ElementTree(elt)
        box = glymur.jp2box.XMLBox(xml=xml, length=439, offset=36)
        actual = str(box)
        expected = (
            ir.files('tests.data.conformance')
              .joinpath('file1_xml_box.txt')
              .read_text()
              .rstrip()
        )
        self.assertEqual(actual, expected)

    def test_xml_short_option(self):
        """
        verify printing of XML box when print.xml option set to false
        """
        s = ir.files('tests.data.conformance') \
              .joinpath('file1_xml.txt') \
              .read_text()
        elt = ET.fromstring(s)
        xml = ET.ElementTree(elt)
        box = glymur.jp2box.XMLBox(xml=xml, length=439, offset=36)
        glymur.set_option('print.short', True)

        actual = str(box)
        expected = (
            ir.files('tests.data.conformance')
              .joinpath('file1_xml_box.txt')
              .read_text()
              .rstrip()
              .splitlines()[0]
        )
        self.assertEqual(actual, expected)

    def test_xml_no_xml_option(self):
        """
        verify printing of XML box when print.xml option set to false
        """
        s = ir.files('tests.data.conformance') \
              .joinpath('file1_xml.txt') \
              .read_text()
        elt = ET.fromstring(s)
        xml = ET.ElementTree(elt)
        box = glymur.jp2box.XMLBox(xml=xml, length=439, offset=36)

        glymur.set_option('print.xml', False)
        actual = str(box)
        expected = (
            ir.files('tests.data.conformance')
              .joinpath('file1_xml_box.txt')
              .read_text()
              .rstrip()
              .splitlines()[0]
        )
        self.assertEqual(actual, expected)

    def test_xml_no_xml(self):
        """
        verify printing of XML box when there is no XML
        """
        box = glymur.jp2box.XMLBox()

        actual = str(box)
        expected = ("XML Box (xml ) @ (-1, 0)\n"
                    "    None")
        self.assertEqual(actual, expected)

    def test_uuid(self):
        """
        verify printing of UUID box

        Original test file was text_GBR.jp2
        """
        buuid = UUID('urn:uuid:3a0d0218-0ae9-4115-b376-4bca41ce0e71')
        box = glymur.jp2box.UUIDBox(buuid, b'\x00', 25, 1544)
        actual = str(box)
        expected = (
            'UUID Box (uuid) @ (1544, 25)\n'
            '    UUID:  3a0d0218-0ae9-4115-b376-4bca41ce0e71 (unknown)\n'
            '    UUID Data:  1 bytes')
        self.assertEqual(actual, expected)

    def test_invalid_progression_order(self):
        """
        Should still be able to print even if prog order is invalid.

        Original test file was 2977.pdf.asan.67.2198.jp2
        """
        pargs = (0, 33, 1, 1, 5, 3, 3, 0, 0, None)
        with warnings.catch_warnings():
            warnings.simplefilter('ignore')
            segment = glymur.codestream.CODsegment(*pargs, length=12,
                                                   offset=174)
        actual = str(segment)
        expected = (
            ir.files('tests.data.misc')
              .joinpath('issue186_progression_order.txt')
              .read_text()
              .rstrip()
        )
        self.assertEqual(actual, expected)

    def test_bad_wavelet_transform(self):
        """
        Should still be able to print if wavelet xform is bad, issue195

        Original test file was edf_c2_10025.jp2
        """
        pargs = (0, 0, 0, 0, 0, 0, 0, 0, 2, None)
        with warnings.catch_warnings():
            warnings.simplefilter('ignore')
            segment = glymur.codestream.CODsegment(*pargs, length=0, offset=0)
        str(segment)

    def test_bad_rsiz(self):
        """
        Should still be able to print if rsiz is bad, issue196

        Original test file was edf_c2_1002767.jp2
        """
        kwargs = {'rsiz': 33,
                  'xysiz': (1920, 1080),
                  'xyosiz': (0, 0),
                  'xytsiz': (1920, 1080),
                  'xytosiz': (0, 0),
                  'Csiz': 3,
                  'bitdepth': (12, 12, 12),
                  'signed': (False, False, False),
                  'xyrsiz': ((1, 1, 1), (1, 1, 1)),
                  'length': 47,
                  'offset': 2}
        segment = glymur.codestream.SIZsegment(**kwargs)
        str(segment)

    def test_invalid_approximation(self):
        """
        An invalid approximation value shouldn't cause a printing error.

        Original test file was edf_c2_1015644.jp2
        """
        kwargs = {
            'colorspace': 1,
            'precedence': 2,
            'approximation': 32,
        }
        with warnings.catch_warnings():
            # Get a warning for the bad approximation value when parsing.
            warnings.simplefilter("ignore")
            colr = ColourSpecificationBox(**kwargs)
        actual = str(colr)
        expected = ("Colour Specification Box (colr) @ (-1, 0)\n"
                    "    Method:  enumerated colorspace\n"
                    "    Precedence:  2\n"
                    "    Approximation:  invalid (32)\n"
                    "    Colorspace:  1 (unrecognized)")
        self.assertEqual(actual, expected)

    def test_invalid_colorspace(self):
        """
        SCENARIO:  An invalid colorspace shouldn't cause an error when
        printing.

        EXPECTED RESULT:  No error, although there is a warning.
        """
        with self.assertWarns(UserWarning):
            colr = ColourSpecificationBox(colorspace=276)
        str(colr)

    def test_label_box_short(self):
        """
        Test the short option for the LabelBox
        """
        box = LabelBox('test')
        glymur.set_option('print.short', True)
        actual = str(box)
        expected = "Label Box (lbl ) @ (-1, 0)"
        self.assertEqual(actual, expected)

    def test_bpcc(self):
        """
        BPCC boxes are rare :-)
        """
        bpcc = (5, 5, 5, 1)
        signed = (False, False, True, False)
        box = BitsPerComponentBox(bpcc, signed, length=12, offset=62)
        actual = str(box)

        expected = ("Bits Per Component Box (bpcc) @ (62, 12)\n"
                    "    Bits per component:  (5, 5, 5, 1)\n"
                    "    Signed:  (False, False, True, False)")

        self.assertEqual(actual, expected)

        glymur.set_option('print.short', True)
        actual = str(box)
        self.assertEqual(actual, expected.splitlines()[0])

    def test_cinema_profile(self):
        """
        Should print Cinema 2K when the profile is 3.
        """
        kwargs = {'rsiz': 3,
                  'xysiz': (1920, 1080),
                  'xyosiz': (0, 0),
                  'xytsiz': (1920, 1080),
                  'xytosiz': (0, 0),
                  'Csiz': 3,
                  'bitdepth': (12, 12, 12),
                  'signed': (False, False, False),
                  'xyrsiz': ((1, 1, 1), (1, 1, 1)),
                  'length': 47,
                  'offset': 2}
        segment = glymur.codestream.SIZsegment(**kwargs)
        actual = str(segment)

        expected = (
            "SIZ marker segment @ (2, 47)\n"
            "    Profile:  2K cinema\n"
            "    Reference Grid Height, Width:  (1080 x 1920)\n"
            "    Vertical, Horizontal Reference Grid Offset:  (0 x 0)\n"
            "    Reference Tile Height, Width:  (1080 x 1920)\n"
            "    Vertical, Horizontal Reference Tile Offset:  (0 x 0)\n"
            "    Bitdepth:  (12, 12, 12)\n"
            "    Signed:  (False, False, False)\n"
            "    Vertical, Horizontal Subsampling:  ((1, 1), (1, 1), (1, 1))"
        )

        self.assertEqual(actual, expected)

    def test_version_info(self):
        """Should be able to print(glymur.version.info)"""
        str(glymur.version.info)

        self.assertTrue(True)

    def test_unknown_superbox(self):
        """
        SCENARIO:  An unknown superbox is encountered.

        EXPECTED RESULT:  str should produce a predictable result.
        """
        with open(self.temp_jpx_filename, mode='wb') as tfile:
            with open(self.jpxfile, 'rb') as ifile:
                tfile.write(ifile.read())

            # Add the header for an unknown superbox.
            write_buffer = struct.pack('>I4s', 20, 'grp '.encode())
            tfile.write(write_buffer)

            # Add a free box inside of it.  We won't be able to identify it,
            # but it's there.
            write_buffer = struct.pack('>I4sI', 12, 'free'.encode(), 0)
            tfile.write(write_buffer)
            tfile.flush()

            with warnings.catch_warnings():
                warnings.simplefilter("ignore")
                jpx = Jp2k(tfile.name)

            glymur.set_option('print.short', True)
            actual = str(jpx.box[-1])
            expected = ("Unknown Box (xxxx) @ (1399071, 20)\n"
                        "    Claimed ID:  b'grp '")
            self.assertEqual(actual, expected)

    def test_printoptions_bad_argument(self):
        """Verify error when bad parameter to set_printoptions"""
        with self.assertRaises(KeyError):
            glymur.set_option('hi', 'low')

    @unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
    def test_asoc_label_box(self):
        """
        SCENARIO:  A JPX file has both asoc and labl boxes.

        EXPECTED RESULT:  str representations validate
        """
        # Construct a fake file with an asoc and a label box, as
        # OpenJPEG doesn't have such a file.
        data = glymur.Jp2k(self.jp2file)[::2, ::2]

        # Create a JP2 file with only the basic JP2 boxes.
        vanilla_jp2_file = self.test_dir_path / 'tmp_test.jp2'
        glymur.Jp2k(vanilla_jp2_file, data=data)

        with open(vanilla_jp2_file, mode='rb') as tfile:
            with open(self.temp_jp2_filename, mode='wb') as tfile2:

                # Offset of the codestream is where we start.
                wbuffer = tfile.read(77)
                tfile2.write(wbuffer)

                # read the rest of the file, it's the codestream.
                codestream = tfile.read()

                # Write the asoc superbox.
                # Length = 36, id is 'asoc'.
                wbuffer = struct.pack('>I4s', int(56), b'asoc')
                tfile2.write(wbuffer)

                # Write the contained label box
                wbuffer = struct.pack('>I4s', int(13), b'lbl ')
                tfile2.write(wbuffer)
                tfile2.write('label'.encode())

                # Write the xml box
                # Length = 36, id is 'xml '.
                wbuffer = struct.pack('>I4s', int(35), b'xml ')
                tfile2.write(wbuffer)

                wbuffer = '<test>this is a test</test>'
                wbuffer = wbuffer.encode()
                tfile2.write(wbuffer)

                # Now append the codestream.
                tfile2.write(codestream)
                tfile2.flush()

                jasoc = glymur.Jp2k(tfile2.name)
                actual = str(jasoc.box[3])
                expected = ('Association Box (asoc) @ (77, 56)\n'
                            '    Label Box (lbl ) @ (85, 13)\n'
                            '        Label:  label\n'
                            '    XML Box (xml ) @ (98, 35)\n'
                            '        <test>this is a test</test>')
                self.assertEqual(actual, expected)

    def test_coc_segment(self):
        """verify printing of COC segment"""
        j = glymur.Jp2k(self.jp2file)
        codestream = j.get_codestream(header_only=False)
        actual = str(codestream.segment[6])

        exp = ('COC marker segment @ (210, 9)\n'
               '    Associated component:  1\n'
               '    Coding style for this component:  '
               'Entropy coder, PARTITION = 0\n'
               '    Coding style parameters:\n'
               '        Number of decomposition levels:  1\n'
               '        Code block height, width:  (64 x 64)\n'
               '        Wavelet transform:  5-3 reversible\n'
               '        Precinct size:  [32768, 32768]\n'
               '        Code block context:\n'
               '            Selective arithmetic coding bypass:  False\n'
               '            Reset context probabilities '
               'on coding pass boundaries:  False\n'
               '            Termination on each coding pass:  False\n'
               '            Vertically stripe causal context:  False\n'
               '            Predictable termination:  False\n'
               '            Segmentation symbols:  False')

        self.assertEqual(actual, exp)

    def test_cod_segment_unknown(self):
        """
        Verify printing of transform when it's actually unknown
        """
        scod = 0
        prog_order = LRCP
        num_layers = 2
        mct = 4
        nr = 1
        xcb = ycb = 4
        cstyle = 0
        xform = WAVELET_XFORM_5X3_REVERSIBLE
        precinct_size = None
        length = 12
        offset = 3282
        pargs = (scod, prog_order, num_layers, mct, nr, xcb, ycb, cstyle,
                 xform, precinct_size, length, offset)
        segment = glymur.codestream.CODsegment(*pargs)
        actual = str(segment)
        exp = ('COD marker segment @ (3282, 12)\n'
               '    Coding style:\n'
               '        Entropy coder, without partitions\n'
               '        SOP marker segments:  False\n'
               '        EPH marker segments:  False\n'
               '    Coding style parameters:\n'
               '        Progression order:  LRCP\n'
               '        Number of layers:  2\n'
               '        Multiple component transformation usage:  unknown\n'
               '        Number of decomposition levels:  1\n'
               '        Code block height, width:  (64 x 64)\n'
               '        Wavelet transform:  5-3 reversible\n'
               '        Precinct size:  [32768, 32768]\n'
               '        Code block context:\n'
               '            Selective arithmetic coding bypass:  False\n'
               '            Reset context probabilities on coding '
               'pass boundaries:  False\n'
               '            Termination on each coding pass:  False\n'
               '            Vertically stripe causal context:  False\n'
               '            Predictable termination:  False\n'
               '            Segmentation symbols:  False')

        self.assertEqual(actual, exp)

    def test_cod_segment_irreversible(self):
        """
        Verify printing of irreversible transform
        """
        scod = 0
        prog_order = LRCP
        num_layers = 2
        mct = 2
        nr = 1
        xcb = ycb = 4
        cstyle = 0
        xform = WAVELET_XFORM_5X3_REVERSIBLE
        precinct_size = None
        length = 12
        offset = 3282
        pargs = (scod, prog_order, num_layers, mct, nr, xcb, ycb, cstyle,
                 xform, precinct_size, length, offset)
        segment = glymur.codestream.CODsegment(*pargs)
        actual = str(segment)
        exp = ('COD marker segment @ (3282, 12)\n'
               '    Coding style:\n'
               '        Entropy coder, without partitions\n'
               '        SOP marker segments:  False\n'
               '        EPH marker segments:  False\n'
               '    Coding style parameters:\n'
               '        Progression order:  LRCP\n'
               '        Number of layers:  2\n'
               '        Multiple component transformation usage:  '
               'irreversible\n'
               '        Number of decomposition levels:  1\n'
               '        Code block height, width:  (64 x 64)\n'
               '        Wavelet transform:  5-3 reversible\n'
               '        Precinct size:  [32768, 32768]\n'
               '        Code block context:\n'
               '            Selective arithmetic coding bypass:  False\n'
               '            Reset context probabilities on coding '
               'pass boundaries:  False\n'
               '            Termination on each coding pass:  False\n'
               '            Vertically stripe causal context:  False\n'
               '            Predictable termination:  False\n'
               '            Segmentation symbols:  False')

        self.assertEqual(actual, exp)

    def test_cod_segment(self):
        """verify printing of COD segment"""
        j = glymur.Jp2k(self.jp2file)
        codestream = j.get_codestream()
        actual = str(codestream.segment[2])

        exp = ('COD marker segment @ (136, 12)\n'
               '    Coding style:\n'
               '        Entropy coder, without partitions\n'
               '        SOP marker segments:  False\n'
               '        EPH marker segments:  False\n'
               '    Coding style parameters:\n'
               '        Progression order:  LRCP\n'
               '        Number of layers:  2\n'
               '        Multiple component transformation usage:  '
               'reversible\n'
               '        Number of decomposition levels:  1\n'
               '        Code block height, width:  (64 x 64)\n'
               '        Wavelet transform:  5-3 reversible\n'
               '        Precinct size:  [32768, 32768]\n'
               '        Code block context:\n'
               '            Selective arithmetic coding bypass:  False\n'
               '            Reset context probabilities on coding '
               'pass boundaries:  False\n'
               '            Termination on each coding pass:  False\n'
               '            Vertically stripe causal context:  False\n'
               '            Predictable termination:  False\n'
               '            Segmentation symbols:  False')

        self.assertEqual(actual, exp)

    def test_eoc_segment(self):
        """verify printing of eoc segment"""
        j = glymur.Jp2k(self.jp2file)
        codestream = j.get_codestream(header_only=False)
        actual = str(codestream.segment[-1])

        expected = 'EOC marker segment @ (1132371, 0)'
        self.assertEqual(actual, expected)

    def test_qcc_segment(self):
        """verify printing of qcc segment"""
        j = glymur.Jp2k(self.jp2file)
        codestream = j.get_codestream(header_only=False)
        actual = str(codestream.segment[7])

        expected = ('QCC marker segment @ (221, 8)\n'
                    '    Associated Component:  1\n'
                    '    Quantization style:  no quantization, 2 guard bits\n'
                    '    Step size:  [(0, 8), (0, 9), (0, 9), (0, 10)]')

        self.assertEqual(actual, expected)

    def test_qcd_segment_5x3_transform(self):
        """verify printing of qcd segment"""
        j = glymur.Jp2k(self.jp2file)
        codestream = j.get_codestream()
        actual = str(codestream.segment[3])

        expected = ('QCD marker segment @ (150, 7)\n'
                    '    Quantization style:  no quantization, 2 guard bits\n'
                    '    Step size:  [(0, 8), (0, 9), (0, 9), (0, 10)]')

        self.assertEqual(actual, expected)

    def test_siz_segment(self):
        """verify printing of SIZ segment"""
        j = glymur.Jp2k(self.jp2file)
        actual = str(j.codestream.segment[1])

        exp = ('SIZ marker segment @ (87, 47)\n'
               '    Profile:  no profile\n'
               '    Reference Grid Height, Width:  (1456 x 2592)\n'
               '    Vertical, Horizontal Reference Grid Offset:  (0 x 0)\n'
               '    Reference Tile Height, Width:  (1456 x 2592)\n'
               '    Vertical, Horizontal Reference Tile Offset:  (0 x 0)\n'
               '    Bitdepth:  (8, 8, 8)\n'
               '    Signed:  (False, False, False)\n'
               '    Vertical, Horizontal Subsampling:  '
               '((1, 1), (1, 1), (1, 1))')

        self.assertEqual(actual, exp)

    def test_soc_segment(self):
        """verify printing of SOC segment"""
        j = glymur.Jp2k(self.jp2file)
        codestream = j.get_codestream()
        actual = str(codestream.segment[0])

        expected = 'SOC marker segment @ (85, 0)'
        self.assertEqual(actual, expected)

    def test_sod_segment(self):
        """verify printing of SOD segment"""
        j = glymur.Jp2k(self.jp2file)
        codestream = j.get_codestream(header_only=False)
        actual = str(codestream.segment[10])

        expected = 'SOD marker segment @ (252, 0)'
        self.assertEqual(actual, expected)

    def test_sot_segment(self):
        """verify printing of SOT segment"""
        j = glymur.Jp2k(self.jp2file)
        codestream = j.get_codestream(header_only=False)
        actual = str(codestream.segment[5])

        expected = ('SOT marker segment @ (198, 10)\n'
                    '    Tile part index:  0\n'
                    '    Tile part length:  1132173\n'
                    '    Tile part instance:  0\n'
                    '    Number of tile parts:  1')

        self.assertEqual(actual, expected)

    def test_xmp(self):
        """
        Verify the printing of a UUID/XMP box.
        """
        the_uuid = UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')
        raw_data = (
            ir.files('tests.data.misc')
              .joinpath('simple_rdf.txt')
              .read_text()
              .encode('utf-8')
        )
        ubox = glymur.jp2box.UUIDBox(the_uuid=the_uuid, raw_data=raw_data)

        actual = str(ubox)

        expected = (
            ir.files('tests.data.misc')
              .joinpath('simple_rdf.uuid-box.txt')
              .read_text()
              .rstrip()
        )
        self.assertEqual(actual, expected)

    def test_codestream(self):
        """
        verify printing of entire codestream
        """
        j = glymur.Jp2k(self.jp2file)
        actual = str(j.get_codestream())
        expected = (
            ir.files('tests.data.misc')
              .joinpath('nemo.txt')
              .read_text()
              .rstrip()
        )
        expected = '\n'.join(expected.splitlines()[17:52])
        expected = f'Codestream:\n{expected}'

        self.assertEqual(actual, expected)

    def test_xml_latin1(self):
        """Should be able to print an XMLBox with utf-8 encoding (latin1)."""
        # Seems to be inconsistencies between different versions of python2.x
        # as to what gets printed.
        #
        # 2.7.5 (fedora 19) prints xml entities.
        # 2.7.3 seems to want to print hex escapes.
        text = u"""<flow>Strömung</flow>"""
        xml = ET.parse(StringIO(text))

        xmlbox = glymur.jp2box.XMLBox(xml=xml)
        actual = str(xmlbox)
        expected = ("XML Box (xml ) @ (-1, 0)\n"
                    "    <flow>Strömung</flow>")
        self.assertEqual(actual, expected)

    def test_xml_cyrrilic(self):
        """Should be able to print XMLBox with utf-8 encoding (cyrrillic)."""
        # Seems to be inconsistencies between different versions of python2.x
        # as to what gets printed.
        #
        # 2.7.5 (fedora 19) prints xml entities.
        # 2.7.3 seems to want to print hex escapes.
        text = u"""<country>Россия</country>"""
        xml = ET.parse(StringIO(text))

        xmlbox = glymur.jp2box.XMLBox(xml=xml)
        actual = str(xmlbox)
        expected = ("XML Box (xml ) @ (-1, 0)\n"
                    "    <country>Россия</country>")

        self.assertEqual(actual, expected)

    def test_less_common_boxes(self):
        """verify uinf, ulst, url, res, resd, resc box printing"""
        with open(self.temp_jp2_filename, mode='wb') as tfile:
            with open(self.jp2file, 'rb') as ifile:
                # Everything up until the jp2c box.
                wbuffer = ifile.read(77)
                tfile.write(wbuffer)

                # Write the UINF superbox
                # Length = 50, id is uinf.
                wbuffer = struct.pack('>I4s', int(50), b'uinf')
                tfile.write(wbuffer)

                # Write the ULST box.
                # Length is 26, 1 UUID, hard code that UUID as zeros.
                wbuffer = struct.pack('>I4sHIIII', int(26), b'ulst', int(1),
                                      int(0), int(0), int(0), int(0))
                tfile.write(wbuffer)

                # Write the URL box.
                # Length is 16, version is one byte, flag is 3 bytes, url
                # is the rest.
                wbuffer = struct.pack('>I4sBBBB',
                                      int(16), b'url ',
                                      int(0), int(0), int(0), int(0))
                tfile.write(wbuffer)

                wbuffer = struct.pack('>ssss', b'a', b'b', b'c', b'd')
                tfile.write(wbuffer)

                # Start the resolution superbox.
                wbuffer = struct.pack('>I4s', int(44), b'res ')
                tfile.write(wbuffer)

                # Write the capture resolution box.
                wbuffer = struct.pack('>I4sHHHHBB',
                                      int(18), b'resc',
                                      int(1), int(1), int(1), int(1),
                                      int(0), int(1))
                tfile.write(wbuffer)

                # Write the display resolution box.
                wbuffer = struct.pack('>I4sHHHHBB',
                                      int(18), b'resd',
                                      int(1), int(1), int(1), int(1),
                                      int(1), int(0))
                tfile.write(wbuffer)

                # Get the rest of the input file.
                wbuffer = ifile.read()
                tfile.write(wbuffer)
                tfile.flush()

            jp2k = glymur.Jp2k(tfile.name)
            with patch('sys.stdout', new=StringIO()) as stdout:
                print(jp2k.box[3])
                print(jp2k.box[4])
                actual = stdout.getvalue().strip()
            exp = ('UUIDInfo Box (uinf) @ (77, 50)\n'
                   '    UUID List Box (ulst) @ (85, 26)\n'
                   '        UUID[0]:  00000000-0000-0000-0000-000000000000\n'
                   '    Data Entry URL Box (url ) @ (111, 16)\n'
                   '        Version:  0\n'
                   '        Flag:  0 0 0\n'
                   '        URL:  "abcd"\n'
                   'Resolution Box (res ) @ (127, 44)\n'
                   '    Capture Resolution Box (resc) @ (135, 18)\n'
                   '        VCR:  1.0\n'
                   '        HCR:  10.0\n'
                   '    Display Resolution Box (resd) @ (153, 18)\n'
                   '        VDR:  10.0\n'
                   '        HDR:  1.0')

            self.assertEqual(actual, exp)

            glymur.set_option('print.short', True)
            with patch('sys.stdout', new=StringIO()) as stdout:
                print(jp2k.box[3])
                print(jp2k.box[4])
                actual = stdout.getvalue().strip()
            exp = ('UUIDInfo Box (uinf) @ (77, 50)\n'
                   '    UUID List Box (ulst) @ (85, 26)\n'
                   '    Data Entry URL Box (url ) @ (111, 16)\n'
                   'Resolution Box (res ) @ (127, 44)\n'
                   '    Capture Resolution Box (resc) @ (135, 18)\n'
                   '    Display Resolution Box (resd) @ (153, 18)')

            self.assertEqual(actual, exp)

    def test_flst(self):
        """Verify printing of fragment list box."""
        flst = glymur.jp2box.FragmentListBox([89], [1132288], [0])
        actual = str(flst)
        expected = ("Fragment List Box (flst) @ (-1, 0)\n"
                    "    Offset 0:  89\n"
                    "    Fragment Length 0:  1132288\n"
                    "    Data Reference 0:  0")
        self.assertEqual(actual, expected)

        glymur.set_option('print.short', True)
        actual = str(flst)
        self.assertEqual(actual, expected.splitlines()[0])

    def test_dref(self):
        """Verify printing of data reference box."""

        version = 0
        flag = (0, 0, 0)
        url = "http://readthedocs.glymur.org"
        deu = glymur.jp2box.DataEntryURLBox(version, flag, url)

        dref = glymur.jp2box.DataReferenceBox([deu])
        actual = str(dref)
        expected = ("Data Reference Box (dtbl) @ (-1, 0)\n"
                    "    Data Entry URL Box (url ) @ (-1, 0)\n"
                    "        Version:  0\n"
                    "        Flag:  0 0 0\n"
                    '        URL:  "http://readthedocs.glymur.org"')
        self.assertEqual(actual, expected)

        # Test the short version.
        glymur.set_option('print.short', True)
        actual = str(dref)
        self.assertEqual(actual, 'Data Reference Box (dtbl) @ (-1, 0)')

    def test_empty_dref(self):
        """Verify printing of data reference box with no content."""

        dref = glymur.jp2box.DataReferenceBox()
        actual = str(dref)
        expected = "Data Reference Box (dtbl) @ (-1, 0)"
        self.assertEqual(actual, expected)

    def test_jplh_cgrp(self):
        """Verify printing of compositing layer header box, color group box."""
        jpx = glymur.Jp2k(self.jpxfile)
        actual = str(jpx.box[7])

        expected = (
            "Compositing Layer Header Box (jplh) @ (314227, 31)\n"
            "    Colour Group Box (cgrp) @ (314235, 23)\n"
            "        Colour Specification Box (colr) @ (314243, 15)\n"
            "            Method:  enumerated colorspace\n"
            "            Precedence:  0\n"
            "            Colorspace:  sRGB"
        )

        self.assertEqual(actual, expected)

    def test_free(self):
        """Verify printing of Free box."""
        free = glymur.jp2box.FreeBox()
        actual = str(free)
        self.assertEqual(actual, 'Free Box (free) @ (-1, 0)')

    def test_nlst(self):
        """Verify printing of number list box."""
        assn = (0, 16777216, 33554432, 50331648)
        nlst = glymur.jp2box.NumberListBox(assn)

        actual = str(nlst)
        expected = ("Number List Box (nlst) @ (-1, 0)\n"
                    "    Association[0]:  the rendered result\n"
                    "    Association[1]:  codestream 0\n"
                    "    Association[2]:  compositing layer 0\n"
                    "    Association[3]:  unrecognized")

        self.assertEqual(actual, expected)

    def test_nlst_short(self):
        glymur.set_option('print.short', True)

        assn = (0, 16777216, 33554432)
        nlst = glymur.jp2box.NumberListBox(assn)

        actual = str(nlst)
        expected = "Number List Box (nlst) @ (-1, 0)"
        self.assertEqual(actual, expected)

    def test_ftbl(self):
        """Verify printing of fragment table box."""
        flst = glymur.jp2box.FragmentListBox([89], [1132288], [0])
        ftbl = glymur.jp2box.FragmentTableBox([flst])
        actual = str(ftbl)

        expected = ("Fragment Table Box (ftbl) @ (-1, 0)\n"
                    "    Fragment List Box (flst) @ (-1, 0)\n"
                    "        Offset 0:  89\n"
                    "        Fragment Length 0:  1132288\n"
                    "        Data Reference 0:  0")
        self.assertEqual(actual, expected)

    def test_jpch(self):
        """Verify printing of JPCH box."""
        jpx = glymur.Jp2k(self.jpxfile)
        actual = str(jpx.box[3])
        self.assertEqual(actual, 'Codestream Header Box (jpch) @ (887, 8)')

    def test_exif_uuid(self):
        """
        SCENARIO:  A JP2 file has an Exif UUID box.

        EXPECTED RESULT:  Verify printing of Exif information.
        """
        with open(self.temp_jp2_filename, mode='wb') as tfile:

            with open(self.jp2file, 'rb') as ifptr:
                tfile.write(ifptr.read())

            b = BytesIO()

            # UUID stuff at byte 0
            # Exif leader at byte 24
            # TIFF header at byte 30
            # IFD start at byte 38
            # IFD tags at byte 40
            # tile offsets at byte 88 (40 bytes)
            #    IFD location 58
            # Exif IFD start at byte 128
            #    IFD byte location 98
            # Exif IFD tag at byte 130 (12 bytes)

            # Write L, T, UUID identifier.
            b.write(struct.pack('>I4s', 142, b'uuid'))
            b.write(b'JpgTiffExif->JP2')

            b.write(b'Exif\x00\x00')

            # write the tiff header
            xbuffer = struct.pack('<BBHI', 73, 73, 42, 8)
            b.write(xbuffer)

            # We will write just four tags.
            b.write(struct.pack('<H', 4))

            # The "Make" tag is tag no. 271.
            b.write(struct.pack('<HHII', 256, 4, 1, 256))
            b.write(struct.pack('<HHII', 257, 4, 1, 512))

            b.write(struct.pack('<HHII', 324, 4, 10, 58))

            b.write(struct.pack('<HHII', 34665, 4, 1, 98))

            # write the tile offsets (fake)
            tile_offsets = list(range(0, 100, 10))
            b.write(struct.pack('<' + 'I' * 10, *tile_offsets))

            # start writing the Exif IFD.

            # We will write just one tag.
            b.write(struct.pack('<H', 1))

            b.write(struct.pack('<HHI4s', 271, 2, 3, b'HTC\x00'))
            b.flush()

            tfile.write(b.getvalue())
            tfile.flush()

            j = glymur.Jp2k(tfile.name)

            actual = str(j.box[-1])

        if sys.version_info[1] >= 12:
            expected = (
                "UUID Box (uuid) @ (1132373, 142)\n"
                "    UUID:  4a706754-6966-6645-7869-662d3e4a5032 (EXIF)\n"
                "    UUID Data:  OrderedDict([   ('ImageWidth', 256),\n"
                "                    ('ImageLength', 512),\n"
                "                    (   'TileOffsets',\n"
                "                        "
                "array([ 0, 10, 20, ..., 70, 80, 90], dtype=uint32)),\n"
                "                    ('ExifTag', OrderedDict({'Make': 'HTC'}))])"  # noqa : E501
            )
        else:
            expected = (
                "UUID Box (uuid) @ (1132373, 142)\n"
                "    UUID:  4a706754-6966-6645-7869-662d3e4a5032 (EXIF)\n"
                "    UUID Data:  OrderedDict([   ('ImageWidth', 256),\n"
                "                    ('ImageLength', 512),\n"
                "                    (   'TileOffsets',\n"
                "                        "
                "array([ 0, 10, 20, ..., 70, 80, 90], dtype=uint32)),\n"
                "                    ('ExifTag', OrderedDict([('Make', 'HTC')]))])"  # noqa : E501
            )
        # Numpy 2.x adds shape to string representation
        self.assertEqual(actual.replace("shape=(10,), ", ""), expected)

    def test_crg(self):
        """verify printing of CRG segment"""
        crg = glymur.codestream.CRGsegment((65535,), (32767,), 6, 87)
        actual = str(crg)
        expected = ('CRG marker segment @ (87, 6)\n'
                    '    Vertical, Horizontal offset:  (0.50, 1.00)')
        self.assertEqual(actual, expected)

    def test_rgn(self):
        """
        verify printing of RGN segment
        """
        segment = glymur.codestream.RGNsegment(0, 0, 7, 5, 310)
        actual = str(segment)
        expected = ('RGN marker segment @ (310, 5)\n'
                    '    Associated component:  0\n'
                    '    ROI style:  0\n'
                    '    Parameter:  7')
        self.assertEqual(actual, expected)

    def test_sop(self):
        """
        verify printing of SOP segment
        """
        segment = glymur.codestream.SOPsegment(15, 4, 12836)
        actual = str(segment)
        expected = ('SOP marker segment @ (12836, 4)\n'
                    '    Nsop:  15')
        self.assertEqual(actual, expected)

    def test_cme(self):
        """
        Test printing a CME or comment marker segment.

        Originally tested with input/conformance/p0_02.j2k
        """
        buffer = "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode('latin-1')
        segment = glymur.codestream.CMEsegment(1, buffer, 45, 85)
        actual = str(segment)
        expected = ('CME marker segment @ (85, 45)\n'
                    '    "Creator: AV-J2K (c) 2000,2001 Algo Vision"')
        self.assertEqual(actual, expected)

    def test_plt_segment(self):
        """
        verify printing of PLT segment

        Originally tested with input/conformance/p0_07.j2k
        """
        pkt_lengths = [9, 122, 19, 30, 27, 9, 41, 62, 18, 29, 261,
                       55, 82, 299, 93, 941, 951, 687, 1729, 1443, 1008, 2168,
                       2188, 2223]
        segment = glymur.codestream.PLTsegment(0, pkt_lengths, 38, 7871146)

        actual = str(segment)

        exp = ('PLT marker segment @ (7871146, 38)\n'
               '    Index:  0\n'
               '    Iplt:  [9, 122, 19, 30, 27, 9, 41, 62, 18, 29, 261,'
               ' 55, 82, 299, 93, 941, 951, 687, 1729, 1443, 1008, 2168,'
               ' 2188, 2223]')
        self.assertEqual(actual, exp)

    def test_invalid_pod_segment(self):
        """
        SCENARIO:  A progression order parameter is out of range.

        EXPECTED RESPONSE:  Should not error out.  The invalid progression
        order should be clearly displayed.
        """
        params = (0, 0, 1, 33, 128, 1, 0, 128, 1, 33, 257, 16)
        segment = glymur.codestream.PODsegment(params, 20, 878)
        actual = str(segment)

        expected = (
            'POD marker segment @ (878, 20)\n'
            '    Progression change 0:\n'
            '        Resolution index start:  0\n'
            '        Component index start:  0\n'
            '        Layer index end:  1\n'
            '        Resolution index end:  33\n'
            '        Component index end:  128\n'
            '        Progression order:  RLCP\n'
            '    Progression change 1:\n'
            '        Resolution index start:  0\n'
            '        Component index start:  128\n'
            '        Layer index end:  1\n'
            '        Resolution index end:  33\n'
            '        Component index end:  257\n'
            '        Progression order:  invalid value: 16'
        )

        self.assertEqual(actual, expected)

    def test_pod_segment(self):
        """
        verify printing of POD segment

        Original test file was input/conformance/p0_13.j2k
        """
        params = (0, 0, 1, 33, 128, 1, 0, 128, 1, 33, 257, 4)
        segment = glymur.codestream.PODsegment(params, 20, 878)
        actual = str(segment)

        expected = ('POD marker segment @ (878, 20)\n'
                    '    Progression change 0:\n'
                    '        Resolution index start:  0\n'
                    '        Component index start:  0\n'
                    '        Layer index end:  1\n'
                    '        Resolution index end:  33\n'
                    '        Component index end:  128\n'
                    '        Progression order:  RLCP\n'
                    '    Progression change 1:\n'
                    '        Resolution index start:  0\n'
                    '        Component index start:  128\n'
                    '        Layer index end:  1\n'
                    '        Resolution index end:  33\n'
                    '        Component index end:  257\n'
                    '        Progression order:  CPRL')

        self.assertEqual(actual, expected)

    def test_ppm_segment(self):
        """
        verify printing of PPM segment

        Original file tested was input/conformance/p1_03.j2k
        """
        segment = glymur.codestream.PPMsegment(0, b'\0' * 43709, 43712, 213)
        actual = str(segment)

        expected = ('PPM marker segment @ (213, 43712)\n'
                    '    Index:  0\n'
                    '    Data:  43709 uninterpreted bytes')

        self.assertEqual(actual, expected)

    def test_ppt_segment(self):
        """
        verify printing of ppt segment

        Original file tested was input/conformance/p1_06.j2k
        """
        segment = glymur.codestream.PPTsegment(0, b'\0' * 106, 109, 155)
        actual = str(segment)

        expected = ('PPT marker segment @ (155, 109)\n'
                    '    Index:  0\n'
                    '    Packet headers:  106 uninterpreted bytes')

        self.assertEqual(actual, expected)

    def test_tlm_segment(self):
        """
        verify printing of TLM segment

        Original file tested was input/conformance/p0_15.j2k
        """
        segment = glymur.codestream.TLMsegment(
            0,
            (0, 1, 2, 3, 4, 5, 6, 7),
            (4267, 2117, 4080, 2081, 1000, 100, 150, 400),
            28, 268
        )
        actual = str(segment)

        expected = (
            'TLM marker segment @ (268, 28)\n'
            '    Index:  0\n'
            '    Tile number:  [0 1 2 ... 5 6 7]\n'
            '    Length:  [4267 2117 4080 ...  100  150  400]'
        )

        self.assertEqual(actual, expected)

    def test_differing_subsamples(self):
        """
        verify printing of SIZ with different subsampling... Issue 86.
        """
        kwargs = {'rsiz': 1,
                  'xysiz': (1024, 1024),
                  'xyosiz': (0, 0),
                  'xytsiz': (1024, 1024),
                  'xytosiz': (0, 0),
                  'Csiz': 4,
                  'bitdepth': (8, 8, 8, 8),
                  'signed': (False, False, False, False),
                  'xyrsiz': ((1, 1, 2, 2), (1, 1, 2, 2)),
                  'length': 50,
                  'offset': 2}
        segment = glymur.codestream.SIZsegment(**kwargs)
        actual = str(segment)
        exp = ('SIZ marker segment @ (2, 50)\n'
               '    Profile:  0\n'
               '    Reference Grid Height, Width:  (1024 x 1024)\n'
               '    Vertical, Horizontal Reference Grid Offset:  (0 x 0)\n'
               '    Reference Tile Height, Width:  (1024 x 1024)\n'
               '    Vertical, Horizontal Reference Tile Offset:  (0 x 0)\n'
               '    Bitdepth:  (8, 8, 8, 8)\n'
               '    Signed:  (False, False, False, False)\n'
               '    Vertical, Horizontal Subsampling:  '
               '((1, 1), (1, 1), (2, 2), (2, 2))')
        self.assertEqual(actual, exp)

    def test_issue182(self):
        """
        SCENARIO: Print a component mapping box.

        This is a regression test.

        Original file tested was input/nonregression/mem-b2ace68c-1381.jp2

        EXPECTED RESULT:  Format strings like %d should not appear in the
        output.
        """
        cmap = glymur.jp2box.ComponentMappingBox(component_index=(0, 0, 0, 0),
                                                 mapping_type=(1, 1, 1, 1),
                                                 palette_index=(0, 1, 2, 3),
                                                 length=24, offset=130)
        actual = str(cmap)
        expected = ("Component Mapping Box (cmap) @ (130, 24)\n"
                    "    Component 0 ==> palette column 0\n"
                    "    Component 0 ==> palette column 1\n"
                    "    Component 0 ==> palette column 2\n"
                    "    Component 0 ==> palette column 3")
        self.assertEqual(actual, expected)

        glymur.set_option('print.short', True)
        actual = str(cmap)
        expected = expected.splitlines()[0]
        self.assertEqual(actual, expected)

    def test_issue183(self):
        """
        SCENARIO:  An ICC profile is present in a ColourSpecificationBox, but
        it is invalid.

        EXPECTED RESULT:  The printed representation validates.
        """
        colr = ColourSpecificationBox(method=RESTRICTED_ICC_PROFILE,
                                      icc_profile=None, length=12, offset=62)

        actual = str(colr)
        expected = ("Colour Specification Box (colr) @ (62, 12)\n"
                    "    Method:  restricted ICC profile\n"
                    "    Precedence:  0\n"
                    "    ICC Profile:  None")

        self.assertEqual(actual, expected)

    def test_icc_profile(self):
        """
        SCENARIO:  print a colr box with an ICC profile

        EXPECTED RESULT:  validate the string representation
        """
        path = ir.files('tests.data.from-openjpeg').joinpath('text_GBR.jp2')
        with self.assertWarns(UserWarning):
            # The brand is wrong, this is JPX, not JP2.
            j = Jp2k(path)

        box = j.box[3].box[1]
        actual = str(box)
        # Don't bother verifying the OrderedDict part of the colr box.
        # OrderedDicts are brittle print-wise.
        actual = actual.split('\n')[:5]
        actual = '\n'.join(actual)
        expected = (
            "Colour Specification Box (colr) @ (179, 1339)\n"
            "    Method:  any ICC profile\n"
            "    Precedence:  2\n"
            "    Approximation:  "
            "accurately represents correct colorspace definition\n"
            "    ICC Profile:"
        )
        self.assertEqual(actual, expected)

    def test_rreq(self):
        """
        verify printing of reader requirements box

        Original file tested was text_GBR.jp2
        """

        fuam = 0xffff
        dcm = 0xf8f0
        standard_flag = 1, 5, 12, 18, 44
        standard_mask = 0x8000, 0x4080, 0x2040, 0x1020, 0x810
        vendor_feature = [UUID('{3a0d0218-0ae9-4115-b376-4bca41ce0e71}')]
        vendor_feature.append(UUID('{47c92ccc-d1a1-4581-b904-38bb5467713b}'))
        vendor_feature.append(UUID('{bc45a774-dd50-4ec6-a9f6-f3a137f47e90}'))
        vendor_feature.append(UUID('{d7c8c5ef-951f-43b2-8757-042500f538e8}'))
        vendor_mask = 0,
        box = glymur.jp2box.ReaderRequirementsBox(fuam, dcm, standard_flag,
                                                  standard_mask,
                                                  vendor_feature, vendor_mask,
                                                  length=109, offset=40)
        actual = str(box)
        expected = (
            ir.files('tests.data.from-openjpeg')
              .joinpath('text_GBR_rreq.txt')
              .read_text()
              .rstrip()
        )
        self.assertEqual(actual, expected)

        glymur.set_option('print.short', True)
        actual = str(box)
        expected = expected.splitlines()[0]
        self.assertEqual(actual, expected)

    def test_bom(self):
        """
        Byte order markers are illegal in UTF-8.  Issue 185

        Original test file was input/nonregression/issue171.jp2
        """
        fptr = BytesIO()

        s = "<?xpacket begin='\ufeff' id='W5M0MpCehiHzreSzNTczkc9d'?>"
        s += "<stuff>goes here</stuff>"
        s += "<?xpacket end='w'?>"
        data = s.encode('utf-8')
        fptr.write(data)
        fptr.seek(0)

        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            box = glymur.jp2box.XMLBox.parse(fptr, 0, 8 + len(data))
            # No need to verify, it's enough that we don't error out.
            str(box)

    @unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
    def test_precincts(self):
        """
        SCENARIO:  print the first COD segment

        EXPECTED RESULT:  the precinct information validates predetermined
        values
        """
        data = Jp2k(self.jp2file)[:]
        j = Jp2k(self.temp_j2k_filename, data=data, psizes=[(128, 128)] * 3)

        # Should be three layers.
        codestream = j.get_codestream()

        actual = str(codestream.segment[2])
        expected = (
            ir.files('tests.data.misc')
              .joinpath('multiple_precinct_size.txt')
              .read_text()
              .rstrip()
        )
        self.assertEqual(actual, expected)

    def test_old_short_option(self):
        """
        Verify printing with deprecated set_printoptions "short"
        """
        jp2 = Jp2k(self.jp2file)
        with warnings.catch_warnings():
            warnings.simplefilter('ignore')
            glymur.set_printoptions(short=True)

        actual = str(jp2)

        # Get rid of leading "File" line, as that is volatile.
        actual = '\n'.join(actual.splitlines()[1:])

        expected = (
            ir.files('tests.data.misc')
              .joinpath('nemo_dump_short.txt')
              .read_text()
              .rstrip()
        )
        self.assertEqual(actual, expected)

        with warnings.catch_warnings():
            warnings.simplefilter('ignore')
            opt = glymur.get_printoptions()['short']
        self.assertTrue(opt)

    def test_suppress_xml_old_option(self):
        """
        Verify printing with xml suppressed, deprecated method
        """
        jp2 = Jp2k(self.jp2file)
        with warnings.catch_warnings():
            warnings.simplefilter('ignore')
            glymur.set_printoptions(xml=False)

        actual = str(jp2)

        # Get rid of leading "File" line, as that is volatile.
        actual = '\n'.join(actual.splitlines()[1:])

        # shave off the XML and non-main-header segments
        expected = (
            ir.files('tests.data.misc')
              .joinpath('nemo_dump_no_xml.txt')
              .read_text()
              .rstrip()
        )
        self.assertEqual(actual, expected)
        self.assertEqual(actual, expected)

        with warnings.catch_warnings():
            warnings.simplefilter('ignore')
            opt = glymur.get_printoptions()['xml']
        self.assertFalse(opt)

    def test_suppress_xml(self):
        """
        Verify printing with xml suppressed
        """
        s = ir.files('tests.data.conformance') \
              .joinpath('file1_xml.txt') \
              .read_text()
        elt = ET.fromstring(s)
        xml = ET.ElementTree(elt)
        box = glymur.jp2box.XMLBox(xml=xml, length=439, offset=36)

        shutil.copyfile(self.jp2file, self.temp_jp2_filename)
        jp2 = Jp2k(self.temp_jp2_filename)
        jp2.append(box)

        glymur.set_option('print.xml', False)

        actual = str(jp2)

        # Get rid of the file line, that's kind of volatile.
        actual = '\n'.join(actual.splitlines()[1:])
        expected = (
            ir.files('tests.data.misc')
              .joinpath('appended_xml_box.txt')
              .read_text()
              .rstrip()
        )

        self.assertEqual(actual, expected)

        opt = glymur.get_option('print.xml')
        self.assertFalse(opt)

    def test_suppress_codestream_old_option(self):
        """
        Verify printing with codestream suppressed, deprecated
        """
        jp2 = Jp2k(self.jp2file)
        with warnings.catch_warnings():
            warnings.simplefilter('ignore')
            glymur.set_printoptions(codestream=False)

        actual = str(jp2)

        # Get rid of the file line, that's kind of volatile.
        actual = '\n'.join(actual.splitlines()[1:])

        expected = (
            ir.files('tests.data.misc')
              .joinpath('nemo_dump_no_codestream.txt')
              .read_text()
              .rstrip()
        )
        self.assertEqual(actual, expected)

        with warnings.catch_warnings():
            warnings.simplefilter('ignore')
            opt = glymur.get_printoptions()['codestream']
        self.assertFalse(opt)

    def test_suppress_codestream(self):
        """
        Verify printing with codestream suppressed
        """
        jp2 = Jp2k(self.jp2file)
        glymur.set_option('print.codestream', False)

        # Get rid of the file line
        actual = '\n'.join(str(jp2).splitlines()[1:])

        expected = (
            ir.files('tests.data.misc')
              .joinpath('nemo_dump_no_codestream.txt')
              .read_text()
              .rstrip()
        )
        self.assertEqual(actual, expected)

        opt = glymur.get_option('print.codestream')
        self.assertFalse(opt)

    def test_full_codestream(self):
        """
        Verify printing with the full blown codestream
        """
        jp2 = Jp2k(self.jp2file)
        glymur.set_option('parse.full_codestream', True)

        # Get rid of the file line
        actual = '\n'.join(str(jp2).splitlines()[1:])

        expected = (
            ir.files('tests.data.misc')
              .joinpath('nemo.txt')
              .read_text()
              .rstrip()
        )
        self.assertEqual(actual, expected)

        opt = glymur.get_option('print.codestream')
        self.assertTrue(opt)

    def test_reserved_marker(self):
        """
        SCENARIO:  print a marker segment with a reserver marker value

        EXPECTED RESULT:  validate the string representation
        """
        path = ir.files('tests.data.conformance').joinpath('p0_02.j2k')
        j = Jp2k(path)
        actual = str(j.codestream.segment[6])
        expected = '0xff30 marker segment @ (132, 0)'
        self.assertEqual(actual, expected)

    def test_scalar_implicit_quantization_file(self):
        path = ir.files('tests.data.conformance').joinpath('p0_03.j2k')
        j = Jp2k(path)
        actual = str(j.codestream.segment[3])
        self.assertIn('scalar implicit', actual)

    def test_scalar_explicit_quantization_file(self):
        path = ir.files('tests.data.conformance').joinpath('p0_06.j2k')
        j = Jp2k(path)
        actual = str(j.codestream.segment[3])
        self.assertIn('scalar explicit', actual)

    def test_non_default_precinct_size(self):
        path = ir.files('tests.data.conformance').joinpath('p1_07.j2k')
        j = Jp2k(path)
        actual = str(j.codestream.segment[3])
        expected = (
            ir.files('tests.data.conformance')
              .joinpath('p1_07.txt')
              .read_text()
              .rstrip()
        )
        self.assertEqual(actual, expected)

    def test_jph_rsiz(self):
        """
        Scenario:  parse a JPH file, print the SIZ segment

        Expected result:  no warnings, the output is verified
        """
        path = ir.files('tests.data.from-openjpeg').joinpath('oj-ht-byte.jph')

        with warnings.catch_warnings():
            warnings.simplefilter('error')
            j = Jp2k(path)
            actual = str(j.codestream.segment[1])

        expected = (
            ir.files('tests.data.from-openjpeg')
              .joinpath('jph_siz.txt')
              .read_text()
              .rstrip()
        )
        self.assertEqual(actual, expected)


class TestJp2dump(fixtures.TestCommon):
    """Tests for verifying how jp2dump console script works."""
    def setUp(self):
        super().setUp()

        # Reset printoptions for every test.
        glymur.reset_option('all')

    def tearDown(self):
        super().tearDown()
        glymur.reset_option('all')

    def test_default_component_parameters(self):
        """printing default image component parameters"""
        icpt = glymur.lib.openjp2.ImageComptParmType()
        with patch('sys.stdout', new=StringIO()) as fake_out:
            print(icpt)
            actual = fake_out.getvalue().strip()
        expected = (
            "<class 'glymur.lib.openjp2.ImageComptParmType'>:\n"
            "    dx: 0\n"
            "    dy: 0\n"
            "    w: 0\n"
            "    h: 0\n"
            "    x0: 0\n"
            "    y0: 0\n"
            "    prec: 0\n"
            "    bpp: 0\n"
            "    sgnd: 0")
        self.assertEqual(actual, expected)

    def test_default_image_type(self):
        """printing default image type"""
        it = glymur.lib.openjp2.ImageType()
        with patch('sys.stdout', new=StringIO()) as fake_out:
            print(it)
            actual = fake_out.getvalue().strip()

        expected = (
            "<class 'glymur\\.lib\\.openjp2\\.ImageType'>:\n"
            "    x0: 0\n"
            "    y0: 0\n"
            "    x1: 0\n"
            "    y1: 0\n"
            "    numcomps: 0\n"
            "    color_space: 0\n"
            "    icc_profile_buf: "
            "<(glymur\\.lib\\.openjp2|ctypes(\\.wintypes)?)\\.LP_c_ubyte "
            "object at 0x[0-9A-Fa-f]*>\n"
            "    icc_profile_len: 0")
        self.assertRegex(actual, expected)

    @unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
    def test_image_comp_type(self):
        obj = opj2.ImageCompType()
        actual = str(obj)
        expected = (
            "<class 'glymur\\.lib\\.openjp2\\.ImageCompType'>:\n"
            "    dx: 0\n"
            "    dy: 0\n"
            "    w: 0\n"
            "    h: 0\n"
            "    x0: 0\n"
            "    y0: 0\n"
            "    prec: 0\n"
            "    bpp: 0\n"
            "    sgnd: 0\n"
            "    resno_decoded: 0\n"
            "    factor: 0\n"
            "    data: "
            "<(glymur\\.lib\\.openjp2|ctypes(\\.wintypes)?)\\.LP_c_(int|long) "
            "object at 0x[a-fA-F0-9]+>\n"
            "    alpha: 0\n"
        )
        self.assertRegex(actual, expected)

    def test_xmp_uuid_short(self):
        """
        SCENARIO:  Append an XMP UUID box to an existing JP2 file, print it
        with the short option.

        EXPECTED RESULT:  strings are validated
        """
        the_uuid = UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')
        raw_data = (
            ir.files('tests.data.misc')
              .joinpath('simple_rdf.txt')
              .read_text()
              .encode('utf-8')
        )

        shutil.copyfile(self.jp2file, self.temp_jp2_filename)

        jp2 = Jp2k(self.temp_jp2_filename)
        ubox = glymur.jp2box.UUIDBox(the_uuid=the_uuid, raw_data=raw_data)
        jp2.append(ubox)

        glymur.set_option('print.short', True)

        actual = str(jp2.box[-1])
        expected = 'UUID Box (uuid) @ (1132373, 434)'

        self.assertEqual(actual, expected)

        # now invoke print.xml, answer should be nearly the same.
        glymur.reset_option('all')
        glymur.set_option('print.xml', False)

        actual = str(jp2.box[-1])
        expected = 'UUID Box (uuid) @ (1132373, 434)\n    UUID:  be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)'  # noqa : E501

        self.assertEqual(actual, expected)

    def test__print_malformed_exif_uuid(self):
        """
        SCENARIO:  Parse a JpgTiffExif->Jp2 UUID that is not only missing the
        'EXIF\0\0' lead-in, but even the TIFF header is malformed.  Then print
        it.

        EXPECTED RESULT:  a UUIDBox string showing that it is invalid
        """
        box_data = ir.files('tests.data.misc') \
                     .joinpath('issue549.dat') \
                     .read_bytes()
        bf = BytesIO(box_data[:16] + box_data[20:])
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            box = UUIDBox.parse(bf, 0, 37700)

        actual = str(box)
        expected = [
            "UUID Box (uuid) @ (0, 37700)",
            "    UUID:  4a706754-6966-6645-7869-662d3e4a5032 (EXIF)",
            "    UUID Data:  Invalid Exif UUID",
        ]
        expected = '\n'.join(expected)

        self.assertEqual(actual, expected)
