From 368c355560127afc0edc8797a838a06614f7695f Mon Sep 17 00:00:00 2001 From: John Evans Date: Sun, 20 Oct 2013 07:52:28 -0400 Subject: [PATCH 01/13] Added __repr__ for JPEG2000SignatureBox, FileTypeBox. --- glymur/jp2box.py | 10 ++++++++++ glymur/test/test_jp2box.py | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/glymur/jp2box.py b/glymur/jp2box.py index bf88e4d..c3f2b63 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -801,6 +801,13 @@ class FileTypeBox(Jp2kBox): self.length = length self.offset = offset + def __repr__(self): + msg = "glymur.jp2box.FileTypeBox(brand='{0}', minor_version={1}, " + msg += "compatibility_list={2})" + msg = msg.format(self.brand, self.minor_version, + self.compatibility_list) + return msg + def __str__(self): lst = [Jp2kBox.__str__(self), ' Brand: {0}', @@ -1141,6 +1148,9 @@ class JPEG2000SignatureBox(Jp2kBox): self.length = length self.offset = offset + def __repr__(self): + return 'glymur.jp2box.JPEG2000SignatureBox()' + def __str__(self): msg = Jp2kBox.__str__(self) msg += '\n Signature: {0:02x}{1:02x}{2:02x}{3:02x}' diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index ef62460..617a93a 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -677,6 +677,10 @@ class TestJp2Boxes(unittest.TestCase): jp2k = glymur.jp2box.JPEG2000SignatureBox() self.assertEqual(jp2k.signature, (13, 10, 135, 10)) + # Test the representation instantiation. + newbox = eval(repr(jp2k)) + self.assertTrue(isinstance(newbox, glymur.jp2box.JPEG2000SignatureBox)) + def test_default_ftyp(self): """Should be able to instantiate a FileTypeBox""" ftyp = glymur.jp2box.FileTypeBox() @@ -684,6 +688,13 @@ class TestJp2Boxes(unittest.TestCase): self.assertEqual(ftyp.minor_version, 0) self.assertEqual(ftyp.compatibility_list, ['jp2 ']) + # Test the representation instantiation. + newbox = eval(repr(ftyp)) + self.assertTrue(isinstance(newbox, glymur.jp2box.FileTypeBox)) + self.assertEqual(newbox.brand, 'jp2 ') + self.assertEqual(newbox.minor_version, 0) + self.assertEqual(newbox.compatibility_list, ['jp2 ']) + def test_default_ihdr(self): """Should be able to instantiate an image header box.""" ihdr = glymur.jp2box.ImageHeaderBox(height=512, width=256, From cdd00d2c2d5936dd7e0e5bc5596fe63e0b19f8f7 Mon Sep 17 00:00:00 2001 From: John Evans Date: Wed, 23 Oct 2013 06:01:48 -0400 Subject: [PATCH 02/13] added __repr__ for colr, cdef, ihdr boxes. #133 --- glymur/jp2box.py | 45 ++++++++++++++++++++---- glymur/test/test_jp2box.py | 71 ++++++++++++++++++++++++++++++++------ 2 files changed, 99 insertions(+), 17 deletions(-) diff --git a/glymur/jp2box.py b/glymur/jp2box.py index c3f2b63..b7f385f 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -206,6 +206,17 @@ class ColourSpecificationBox(Jp2kBox): self.length = length self.offset = offset + def __repr__(self): + msg = "glymur.jp2box.ColourSpecificationBox(" + msg += "method={0}, precedence={1}, approximation={2}, colorspace={3}, " + msg += "icc_profile={4})" + msg = msg.format(self.method, + self.precedence, + self.approximation, + self.colorspace, + self.icc_profile) + return msg + def __str__(self): msg = Jp2kBox.__str__(self) @@ -426,12 +437,12 @@ class ChannelDefinitionBox(Jp2kBox): offset of the box from the start of the file. longname : str more verbose description of the box. - index : int + index : list number of the channel. Defaults to monotonically increasing sequence, i.e. [0, 1, 2, ...] - channel_type : int + channel_type : list type of the channel - association : int + association : list index of the associated color """ def __init__(self, index=None, channel_type=None, association=None, @@ -458,9 +469,9 @@ class ChannelDefinitionBox(Jp2kBox): msg += " 65535 - unspecified" raise IOError(msg) - self.index = index - self.channel_type = channel_type - self.association = association + self.index = tuple(index) + self.channel_type = tuple(channel_type) + self.association = tuple(association) self.__dict__.update(**kwargs) def __str__(self): @@ -475,6 +486,12 @@ class ChannelDefinitionBox(Jp2kBox): msg = msg.format(self.index[j], color_type_string, assn) return msg + def __repr__(self): + msg = "glymur.jp2box.ChannelDefinitionBox(" + msg += "index={0}, channel_type={1}, association={2})" + msg = msg.format(self.index, self.channel_type, self.association) + return msg + def write(self, fptr): """Write a channel definition box to file. """ @@ -921,6 +938,22 @@ class ImageHeaderBox(Jp2kBox): self.length = length self.offset = offset + def __repr__(self): + msg = "glymur.jp2box.ImageHeaderBox(" + msg += "{height}, {width}, num_components={num_components}, " + msg += "signed={signed}, bits_per_component={bits_per_component}, " + msg += "compression={compression}, " + msg += "colorspace_unknown={colorspace_unknown}, " + msg += "ip_provided={ip_provided})" + msg = msg.format(height=self.height, width=self.width, + num_components=self.num_components, + signed=self.signed, + bits_per_component=self.bits_per_component, + compression=self.compression, + colorspace_unknown=self.colorspace_unknown, + ip_provided=self.ip_provided) + return msg + def __str__(self): msg = Jp2kBox.__str__(self) msg = "{0}" diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index 617a93a..c6de126 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -677,10 +677,6 @@ class TestJp2Boxes(unittest.TestCase): jp2k = glymur.jp2box.JPEG2000SignatureBox() self.assertEqual(jp2k.signature, (13, 10, 135, 10)) - # Test the representation instantiation. - newbox = eval(repr(jp2k)) - self.assertTrue(isinstance(newbox, glymur.jp2box.JPEG2000SignatureBox)) - def test_default_ftyp(self): """Should be able to instantiate a FileTypeBox""" ftyp = glymur.jp2box.FileTypeBox() @@ -688,13 +684,6 @@ class TestJp2Boxes(unittest.TestCase): self.assertEqual(ftyp.minor_version, 0) self.assertEqual(ftyp.compatibility_list, ['jp2 ']) - # Test the representation instantiation. - newbox = eval(repr(ftyp)) - self.assertTrue(isinstance(newbox, glymur.jp2box.FileTypeBox)) - self.assertEqual(newbox.brand, 'jp2 ') - self.assertEqual(newbox.minor_version, 0) - self.assertEqual(newbox.compatibility_list, ['jp2 ']) - def test_default_ihdr(self): """Should be able to instantiate an image header box.""" ihdr = glymur.jp2box.ImageHeaderBox(height=512, width=256, @@ -720,6 +709,66 @@ class TestJp2Boxes(unittest.TestCase): self.assertIsNone(box.main_header) +class TestRepr(unittest.TestCase): + """Tests for __repr__ methods.""" + def test_default_jp2k(self): + """Should be able to eval a JPEG2000SignatureBox""" + jp2k = glymur.jp2box.JPEG2000SignatureBox() + + # Test the representation instantiation. + newbox = eval(repr(jp2k)) + self.assertTrue(isinstance(newbox, glymur.jp2box.JPEG2000SignatureBox)) + self.assertEqual(newbox.signature, (13, 10, 135, 10)) + + def test_default_ftyp(self): + """Should be able to instantiate a FileTypeBox""" + ftyp = glymur.jp2box.FileTypeBox() + + # Test the representation instantiation. + newbox = eval(repr(ftyp)) + self.assertTrue(isinstance(newbox, glymur.jp2box.FileTypeBox)) + self.assertEqual(newbox.brand, 'jp2 ') + self.assertEqual(newbox.minor_version, 0) + self.assertEqual(newbox.compatibility_list, ['jp2 ']) + + def test_colourspecification_box(self): + """Verify __repr__ method on colr box.""" + # TODO: add icc_profile + box = ColourSpecificationBox(colorspace=glymur.core.SRGB) + + newbox = eval(repr(box)) + self.assertEqual(newbox.method, glymur.core.ENUMERATED_COLORSPACE) + self.assertEqual(newbox.precedence, 0) + self.assertEqual(newbox.approximation, 0) + self.assertEqual(newbox.colorspace, glymur.core.SRGB) + self.assertIsNone(newbox.icc_profile) + + def test_channeldefinition_box(self): + """Verify __repr__ method on cdef box.""" + channel_type = [COLOR, COLOR, COLOR] + association = [RED, GREEN, BLUE] + cdef = glymur.jp2box.ChannelDefinitionBox(index=[0, 1, 2], + channel_type=channel_type, + association=association) + newbox = eval(repr(cdef)) + self.assertEqual(newbox.index, (0, 1, 2)) + self.assertEqual(newbox.channel_type, (COLOR, COLOR, COLOR)) + self.assertEqual(newbox.association, (RED, GREEN, BLUE)) + + def test_imageheader_box(self): + """Verify __repr__ method on ihdr box.""" + ihdr = ImageHeaderBox(100, 200, num_components=3) + newbox = eval(repr(ihdr)) + self.assertEqual(newbox.height, 100) + self.assertEqual(newbox.width, 200) + self.assertEqual(newbox.num_components, 3) + self.assertFalse(newbox.signed) + self.assertEqual(newbox.bits_per_component, 8) + self.assertEqual(newbox.compression, 7) + self.assertFalse(newbox.colorspace_unknown) + self.assertFalse(newbox.ip_provided) + + class TestJpxBoxes(unittest.TestCase): """Tests for JPX boxes.""" From 18818495d24643806fbd3bd3db3471cc5a1b3ae9 Mon Sep 17 00:00:00 2001 From: John Evans Date: Wed, 23 Oct 2013 06:41:31 -0400 Subject: [PATCH 03/13] Added jp2h __repr__ method. #133 --- glymur/jp2box.py | 8 ++++++-- glymur/test/test_jp2box.py | 13 ++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/glymur/jp2box.py b/glymur/jp2box.py index b7f385f..7f75f60 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -1102,11 +1102,15 @@ class JP2HeaderBox(Jp2kBox): box : list List of boxes contained in this superbox. """ - def __init__(self, length=0, offset=-1): + def __init__(self, box=[], length=0, offset=-1): Jp2kBox.__init__(self, box_id='jp2h', longname='JP2 Header') self.length = length self.offset = offset - self.box = [] + self.box = box + + def __repr__(self): + msg = "glymur.jp2box.JP2HeaderBox(box={0})".format(self.box) + return msg def __str__(self): msg = Jp2kBox.__str__(self) diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index c6de126..fb748f7 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -755,9 +755,20 @@ class TestRepr(unittest.TestCase): self.assertEqual(newbox.channel_type, (COLOR, COLOR, COLOR)) self.assertEqual(newbox.association, (RED, GREEN, BLUE)) - def test_imageheader_box(self): + def test_jp2header_box(self): """Verify __repr__ method on ihdr box.""" ihdr = ImageHeaderBox(100, 200, num_components=3) + colr = ColourSpecificationBox(colorspace=glymur.core.SRGB) + jp2h = JP2HeaderBox(box=[ihdr, colr]) + newbox = eval(repr(jp2h)) + self.assertEqual(newbox.box_id, 'jp2h') + self.assertEqual(newbox.box[0].box_id, 'ihdr') + self.assertEqual(newbox.box[1].box_id, 'colr') + + def test_imageheader_box(self): + """Verify __repr__ method on jhdr box.""" + ihdr = ImageHeaderBox(100, 200, num_components=3) + newbox = eval(repr(ihdr)) self.assertEqual(newbox.height, 100) self.assertEqual(newbox.width, 200) From 3ad31c91b84d28244f7c8aee2fcfafe87f4219f6 Mon Sep 17 00:00:00 2001 From: John Evans Date: Wed, 23 Oct 2013 08:07:15 -0400 Subject: [PATCH 04/13] Added support for jplh, jpch, cmap, res , resd, resc #133 --- glymur/jp2box.py | 48 +++++++++++++++++++++++++++++++------- glymur/test/test_jp2box.py | 43 +++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 9 deletions(-) diff --git a/glymur/jp2box.py b/glymur/jp2box.py index 7f75f60..3c775c0 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -554,11 +554,15 @@ class CodestreamHeaderBox(Jp2kBox): box : list List of boxes contained in this superbox. """ - def __init__(self, length=0, offset=-1): + def __init__(self, box=[], length=0, offset=-1): Jp2kBox.__init__(self, box_id='jpch', longname='Codestream Header') self.length = length self.offset = offset - self.box = [] + self.box = box + + def __repr__(self): + msg = "glymur.jp2box.CodestreamHeaderBox(box={0})".format(self.box) + return msg def __str__(self): msg = Jp2kBox.__str__(self) @@ -612,13 +616,18 @@ class CompositingLayerHeaderBox(Jp2kBox): box : list List of boxes contained in this superbox. """ - def __init__(self, length=0, offset=-1): + def __init__(self, box=[], length=0, offset=-1): Jp2kBox.__init__(self, box_id='jplh', longname='Compositing Layer Header') self.length = length self.offset = offset self.box = [] + def __repr__(self): + msg = "glymur.jp2box.CompositingLayerHeaderBox(box={0})" + msg = msg.format(self.box) + return msg + def __str__(self): msg = Jp2kBox.__str__(self) for box in self.box: @@ -667,11 +676,11 @@ class ComponentMappingBox(Jp2kBox): Offset of the box from the start of the file. longname : str Verbose description of the box. - component_index : int + component_index : tuple Index of component in codestream that is mapped to this channel. - mapping_type : int + mapping_type : tuple mapping type, either direct use (0) or palette (1) - palette_index : int + palette_index : tuple Index component from palette """ def __init__(self, component_index, mapping_type, palette_index, @@ -683,6 +692,14 @@ class ComponentMappingBox(Jp2kBox): self.length = length self.offset = offset + def __repr__(self): + msg = "glymur.jp2box.ComponentMappingBox(" + msg += "component_index={0}, mapping_type={1}, palette_index={2})" + msg = msg.format(self.component_index, + self.mapping_type, + self.palette_index) + return msg + def __str__(self): msg = Jp2kBox.__str__(self) @@ -1610,11 +1627,16 @@ class ResolutionBox(Jp2kBox): box : list List of boxes contained in this superbox. """ - def __init__(self, length=0, offset=-1): + def __init__(self, box=[], length=0, offset=-1): Jp2kBox.__init__(self, box_id='res ', longname='Resolution') self.length = length self.offset = offset - self.box = [] + self.box = box + + def __repr__(self): + msg = "glymur.jp2box.ResolutionBox(box={0})" + msg = msg.format(self.box) + return msg def __str__(self): msg = Jp2kBox.__str__(self) @@ -1676,6 +1698,11 @@ class CaptureResolutionBox(Jp2kBox): self.length = length self.offset = offset + def __repr__(self): + msg = "glymur.jp2box.CaptureResolutionBox({0}, {1})" + msg = msg.format(self.vertical_resolution, self.horizontal_resolution) + return msg + def __str__(self): msg = Jp2kBox.__str__(self) msg += '\n VCR: {0}'.format(self.vertical_resolution) @@ -1733,6 +1760,11 @@ class DisplayResolutionBox(Jp2kBox): self.length = length self.offset = offset + def __repr__(self): + msg = "glymur.jp2box.DisplayResolutionBox({0}, {1})" + msg = msg.format(self.vertical_resolution, self.horizontal_resolution) + return msg + def __str__(self): msg = Jp2kBox.__str__(self) msg += '\n VDR: {0}'.format(self.vertical_resolution) diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index fb748f7..0373b01 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -40,7 +40,7 @@ from glymur.jp2box import JPEG2000SignatureBox from glymur.core import COLOR, OPACITY from glymur.core import RED, GREEN, BLUE, GREY, WHOLE_IMAGE -from .fixtures import OPENJP2_IS_V2_OFFICIAL +from .fixtures import OPENJP2_IS_V2_OFFICIAL, opj_data_file try: FORMAT_CORPUS_DATA_ROOT = os.environ['FORMAT_CORPUS_DATA_ROOT'] @@ -779,6 +779,47 @@ class TestRepr(unittest.TestCase): self.assertFalse(newbox.colorspace_unknown) self.assertFalse(newbox.ip_provided) + def test_codestreamheader_box(self): + """Verify __repr__ method on jpch box.""" + jpch = glymur.jp2box.CodestreamHeaderBox() + newbox = eval(repr(jpch)) + self.assertEqual(newbox.box_id, 'jpch') + self.assertEqual(len(newbox.box), 0) + + def test_compositinglayerheader_box(self): + """Verify __repr__ method on jplh box.""" + jplh = glymur.jp2box.CompositingLayerHeaderBox() + newbox = eval(repr(jplh)) + self.assertEqual(newbox.box_id, 'jplh') + self.assertEqual(len(newbox.box), 0) + + def test_componentmapping_box(self): + """Verify __repr__ method on cmap box.""" + cmap = glymur.jp2box.ComponentMappingBox(component_index=(0, 0, 0), + mapping_type=(1, 1, 1), + palette_index=(0, 1, 2)) + newbox = eval(repr(cmap)) + self.assertEqual(newbox.box_id, 'cmap') + self.assertEqual(newbox.component_index, (0, 0, 0)) + self.assertEqual(newbox.mapping_type, (1, 1, 1)) + self.assertEqual(newbox.palette_index, (0, 1, 2)) + + def test_resolution_boxes(self): + """Verify __repr__ method on resolution boxes.""" + resc = glymur.jp2box.CaptureResolutionBox(0.5, 2.5) + resd = glymur.jp2box.DisplayResolutionBox(2.5, 0.5) + res_super_box = glymur.jp2box.ResolutionBox(box=[resc, resd]) + + newbox = eval(repr(res_super_box)) + + self.assertEqual(newbox.box_id, 'res ') + self.assertEqual(newbox.box[0].box_id, 'resc') + self.assertEqual(newbox.box[0].vertical_resolution, 0.5) + self.assertEqual(newbox.box[0].horizontal_resolution, 2.5) + self.assertEqual(newbox.box[1].box_id, 'resd') + self.assertEqual(newbox.box[1].vertical_resolution, 2.5) + self.assertEqual(newbox.box[1].horizontal_resolution, 0.5) + class TestJpxBoxes(unittest.TestCase): """Tests for JPX boxes.""" From 74420e587ccbb592ec97ee2e8641a83c928feebd Mon Sep 17 00:00:00 2001 From: John Evans Date: Wed, 23 Oct 2013 08:17:17 -0400 Subject: [PATCH 05/13] Added 'url ', 'lbl ' box support. #133 --- glymur/jp2box.py | 9 +++++++++ glymur/test/test_jp2box.py | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/glymur/jp2box.py b/glymur/jp2box.py index 3c775c0..52ad263 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -1826,6 +1826,10 @@ class LabelBox(Jp2kBox): msg += '\n Label: {0}'.format(self.label) return msg + def __repr__(self): + msg = 'glymur.jp2box.LabelBox("{0}")'.format(self.label) + return msg + @staticmethod def parse(fptr, offset, length): """Parse Label box. @@ -2109,6 +2113,11 @@ class DataEntryURLBox(Jp2kBox): self.length = length self.offset = offset + def __repr__(self): + msg = "glymur.jp2box.DataEntryURLBox({0}, {1}, '{2}')" + msg = msg.format(self.version, self.flag, self.url) + return msg + def __str__(self): msg = Jp2kBox.__str__(self) msg += '\n ' diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index 0373b01..55a7b02 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -820,6 +820,25 @@ class TestRepr(unittest.TestCase): self.assertEqual(newbox.box[1].vertical_resolution, 2.5) self.assertEqual(newbox.box[1].horizontal_resolution, 0.5) + def test_label_box(self): + """Verify __repr__ method on label box.""" + lbl = glymur.jp2box.LabelBox("this is a test") + newbox = eval(repr(lbl)) + self.assertEqual(newbox.box_id, 'lbl ') + self.assertEqual(newbox.label, "this is a test") + + def test_data_entry_url_box(self): + """Verify __repr__ method on data entry url box.""" + version = 0 + flag = (0, 0, 0) + url = "http://readthedocs.glymur.org" + box = glymur.jp2box.DataEntryURLBox(version, flag, url) + newbox = eval(repr(box)) + self.assertEqual(newbox.box_id, 'url ') + self.assertEqual(newbox.version, version) + self.assertEqual(newbox.flag, flag) + self.assertEqual(newbox.url, url) + class TestJpxBoxes(unittest.TestCase): """Tests for JPX boxes.""" From 7ac1134872952ef999ad8cde07460807bf7397dd Mon Sep 17 00:00:00 2001 From: John Evans Date: Wed, 23 Oct 2013 18:58:09 -0400 Subject: [PATCH 06/13] Added ulst and uinf repr support. #133 --- CHANGES.txt | 3 +++ glymur/jp2box.py | 12 ++++++++++-- glymur/test/test_jp2box.py | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 40846e4..7e7c53c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,6 @@ +Oct 22, 2013 - v0.5.7 Super boxes constructors now take optional box list + argument. + Oct 13, 2013 - v0.5.6 Fixed handling of non-ascii chars in XML boxes. Fixed some docstring errors in jp2box module. diff --git a/glymur/jp2box.py b/glymur/jp2box.py index 52ad263..8303981 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -1989,6 +1989,10 @@ class UUIDListBox(Jp2kBox): self.length = length self.offset = offset + def __repr__(self): + msg = "glymur.jp2box.UUIDListBox({0})".format(self.ulst) + return msg + def __str__(self): msg = Jp2kBox.__str__(self) for j, uuid_item in enumerate(self.ulst): @@ -2040,11 +2044,15 @@ class UUIDInfoBox(Jp2kBox): box : list List of boxes contained in this superbox. """ - def __init__(self, length=0, offset=-1): + def __init__(self, box=[], length=0, offset=-1): Jp2kBox.__init__(self, box_id='uinf', longname='UUIDInfo') self.length = length self.offset = offset - self.box = [] + self.box = box + + def __repr__(self): + msg = "glymur.jp2box.UUIDInfoBox(box={0})".format(self.box) + return msg def __str__(self): msg = Jp2kBox.__str__(self) diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index 55a7b02..b82af47 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -23,6 +23,7 @@ import struct import sys import tempfile import uuid +from uuid import UUID import xml.etree.cElementTree as ET if sys.hexversion < 0x02070000: @@ -839,6 +840,24 @@ class TestRepr(unittest.TestCase): self.assertEqual(newbox.flag, flag) self.assertEqual(newbox.url, url) + def test_uuidinfo_box(self): + """Verify __repr__ method on uinf box.""" + uinf = glymur.jp2box.UUIDInfoBox() + newbox = eval(repr(uinf)) + self.assertEqual(newbox.box_id, 'uinf') + self.assertEqual(len(newbox.box), 0) + + def test_uuidlist_box(self): + """Verify __repr__ method on ulst box.""" + uuid1 = uuid.UUID('00000000-0000-0000-0000-000000000001') + uuid2 = uuid.UUID('00000000-0000-0000-0000-000000000002') + uuids = [uuid1, uuid2] + ulst = glymur.jp2box.UUIDListBox(ulst=uuids) + newbox = eval(repr(ulst)) + self.assertEqual(newbox.box_id, 'ulst') + self.assertEqual(newbox.ulst[0], uuid1) + self.assertEqual(newbox.ulst[1], uuid2) + class TestJpxBoxes(unittest.TestCase): """Tests for JPX boxes.""" From d3dd654c17aa4924f2ba27f3089594f6949d2b56 Mon Sep 17 00:00:00 2001 From: John Evans Date: Thu, 24 Oct 2013 08:21:14 -0400 Subject: [PATCH 07/13] Proper repr support for SIZ segment. SIZ segment constructor change. The SIZ segment was taking raw buffer arguments, which are difficult for eval(repr()) to deal with, so the constructor was changed to make the arguments more explicit. #133 --- CHANGES.txt | 4 +- glymur/codestream.py | 113 +++++++++++++++++++++------------ glymur/test/test_codestream.py | 44 +++++++++++++ 3 files changed, 119 insertions(+), 42 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7e7c53c..0d23501 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,5 @@ -Oct 22, 2013 - v0.5.7 Super boxes constructors now take optional box list - argument. +Oct 22, 2013 - v0.5.7 Super box constructors now take optional box list + argument. Removed ssiz attribute from SIZsegment class. Oct 13, 2013 - v0.5.6 Fixed handling of non-ascii chars in XML boxes. Fixed some docstring errors in jp2box module. diff --git a/glymur/codestream.py b/glymur/codestream.py index e83c5cc..d01795f 100644 --- a/glymur/codestream.py +++ b/glymur/codestream.py @@ -646,16 +646,50 @@ class Codestream(object): length, = struct.unpack('>H', read_buffer) xy_buffer = fptr.read(36) + data = struct.unpack('>HIIIIIIIIH', xy_buffer) - num_components, = struct.unpack('>H', xy_buffer[-2:]) + rsiz = data[0] + xysiz = (data[1], data[2]) + xyosiz = (data[3], data[4]) + xytsiz = (data[5], data[6]) + xytosiz = (data[7], data[8]) - component_buffer = fptr.read(num_components * 3) + # Csiz is the number of components + Csiz = data[9] - segment = SIZsegment(xy_buffer, component_buffer, length, offset) + component_buffer = fptr.read(Csiz * 3) + data = struct.unpack('>' + 'B' * len(component_buffer), + component_buffer) + + bitdepth = tuple(((x & 0x7f) + 1) for x in data[0::3]) + signed = tuple(((x & 0xb0) > 0) for x in data[0::3]) + + xrsiz = data[1::3] + yrsiz = data[2::3] + + for j, subsampling in enumerate(zip(xrsiz, yrsiz)): + if 0 in subsampling: + msg = "Invalid subsampling value for component {0}: " + msg += "dx={1}, dy={2}." + msg = msg.format(j, subsampling[0], subsampling[1]) + warnings.warn(msg) + + kwargs = {'rsiz': rsiz, + 'xysiz': xysiz, + 'xyosiz': xyosiz, + 'xytsiz': xytsiz, + 'xytosiz': xytosiz, + 'Csiz': Csiz, + 'bitdepth': bitdepth, + 'signed': signed, + 'xyrsiz': (xrsiz, yrsiz), + 'length': length, + 'offset': offset} + segment = SIZsegment(**kwargs) # Need to keep track of the number of components from SIZ for - # other markers - self._csiz = len(segment.ssiz) + # other markers. + self._csiz = Csiz return segment @@ -1441,8 +1475,8 @@ class SIZsegment(Segment): Width and height of reference tile with respect to the reference grid. xtosiz, ytosiz : int Horizontal and vertical offsets of tile from origin of reference grid. - ssiz : iterable bytes - Encoded precision (depth) in bits and sign of each component. + Csiz : int + Number of components in image. bitdepth : iterable bytes Precision (depth) in bits of each component. signed : iterable bool @@ -1457,21 +1491,20 @@ class SIZsegment(Segment): 15444-1:2004 - Information technology -- JPEG 2000 image coding system: Core coding system """ - def __init__(self, xy_buffer, component_buffer, length, offset): - Segment.__init__(self, marker_id='SIZ') + def __init__(self, rsiz=-1, xysiz=None, xyosiz=-1, xytsiz=-1, xytosiz=-1, + Csiz=-1, bitdepth=None, signed=None, xyrsiz=-1, length=-1, + offset=-1): + Segment.__init__(self, marker_id='SIZ', length=length, offset=offset) - data = struct.unpack('>HIIIIIIIIH', xy_buffer) - - self.rsiz = data[0] - self.xsiz = data[1] - self.ysiz = data[2] - self.xosiz = data[3] - self.yosiz = data[4] - self.xtsiz = data[5] - self.ytsiz = data[6] - self.xtosiz = data[7] - self.ytosiz = data[8] - # disregarding the last element in data + self.rsiz = rsiz + self.xsiz, self.ysiz = xysiz + self.xosiz, self.yosiz = xyosiz + self.xtsiz, self.ytsiz = xytsiz + self.xtosiz, self.ytosiz = xytosiz + self.Csiz = Csiz + self.bitdepth = bitdepth + self.signed = signed + self.xrsiz, self.yrsiz = xyrsiz num_tiles_x = (self.xsiz - self.xosiz) / (self.xtsiz - self.xtosiz) num_tiles_y = (self.ysiz - self.yosiz) / (self.ytsiz - self.ytosiz) @@ -1480,26 +1513,23 @@ class SIZsegment(Segment): msg = "Invalid number of tiles ({0}).".format(numtiles) warnings.warn(msg) - data = struct.unpack('>' + 'B' * len(component_buffer), - component_buffer) - - self.ssiz = data[0::3] - - for j, subsampling in enumerate(list(zip(data[1::3], data[2::3]))): - if 0 in subsampling: - msg = "Invalid subsampling value for component {0}: " - msg += "dx={1}, dy={2}." - msg = msg.format(j, subsampling[0], subsampling[1]) - warnings.warn(msg) - self.xrsiz = data[1::3] - self.yrsiz = data[2::3] - - self.bitdepth = tuple(((x & 0x7f) + 1) for x in self.ssiz) - self.signed = tuple(((x & 0xb0) > 0) for x in self.ssiz) - - self.length = length - self.offset = offset + def __repr__(self): + msg = "glymur.codestream.SIZsegment(rsiz={rsiz}, xysiz={xysiz}, " + msg += "xyosiz={xyosiz}, xytsiz={xytsiz}, xytosiz={xytosiz}, " + msg += "Csiz={Csiz}, bitdepth={bitdepth}, signed={signed}, " + msg += "xyrsiz={xyrsiz})" + msg = msg.format(rsiz=self.rsiz, + xysiz=(self.xsiz, self.ysiz), + xyosiz=(self.xosiz, self.yosiz), + xytsiz=(self.xtsiz, self.ytsiz), + xytosiz=(self.xtosiz, self.ytosiz), + Csiz=self.Csiz, + bitdepth=self.bitdepth, + signed=self.signed, + xyrsiz=(self.xrsiz, self.yrsiz)) + return msg + def __str__(self): msg = Segment.__str__(self) msg += '\n ' @@ -1549,6 +1579,9 @@ class SOCsegment(Segment): Segment.__init__(self, marker_id='SOC') self.__dict__.update(**kwargs) + def __repr__(self): + msg = "glymur.codestream.SOCsegment()" + return msg class SODsegment(Segment): """Container for Start of Data (SOD) segment information. diff --git a/glymur/test/test_codestream.py b/glymur/test/test_codestream.py index 8db5039..76042b9 100644 --- a/glymur/test/test_codestream.py +++ b/glymur/test/test_codestream.py @@ -116,5 +116,49 @@ class TestCodestream(unittest.TestCase): # codestream, so the last one is EOC. self.assertEqual(codestream.segment[-1].marker_id, 'EOC') + +class TestCodestreamRepr(unittest.TestCase): + + def setUp(self): + self.jp2file = glymur.data.nemo() + + def tearDown(self): + pass + + def test_soc(self): + """Test SOC segment repr""" + segment = glymur.codestream.SOCsegment() + newseg = eval(repr(segment)) + self.assertEqual(newseg.marker_id, 'SOC') + + def test_siz(self): + """Test SIZ segment repr""" + kwargs = {'rsiz': 0, + 'xysiz': (2592, 1456), + 'xyosiz': (0, 0), + 'xytsiz': (2592, 1456), + 'xytosiz': (0, 0), + 'Csiz': 3, + 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': ((1, 1, 1), (1, 1, 1))} + segment = glymur.codestream.SIZsegment(**kwargs) + newseg = eval(repr(segment)) + self.assertEqual(newseg.marker_id, 'SIZ') + self.assertEqual(newseg.xsiz, 2592) + self.assertEqual(newseg.ysiz, 1456) + self.assertEqual(newseg.xosiz, 0) + self.assertEqual(newseg.yosiz, 0) + self.assertEqual(newseg.xtsiz, 2592) + self.assertEqual(newseg.ytsiz, 1456) + self.assertEqual(newseg.xtosiz, 0) + self.assertEqual(newseg.ytosiz, 0) + + self.assertEqual(newseg.xrsiz, (1, 1, 1)) + self.assertEqual(newseg.yrsiz, (1, 1, 1)) + self.assertEqual(newseg.bitdepth, (8, 8, 8)) + self.assertEqual(newseg.signed, (False, False, False)) + + if __name__ == "__main__": unittest.main() From 9799762fb5ae56a63659ce5a62ed6524cb72c385 Mon Sep 17 00:00:00 2001 From: John Evans Date: Thu, 24 Oct 2013 08:48:14 -0400 Subject: [PATCH 08/13] Added repr support for Jp2k class. #133 --- glymur/jp2k.py | 4 ++++ glymur/test/test_jp2k.py | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 1bd36a4..a9927da 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -72,6 +72,10 @@ class Jp2k(Jp2kBox): if mode == 'rb': self.parse() + def __repr__(self): + msg = "glymur.Jp2k('{0}')".format(self.filename) + return msg + def __str__(self): metadata = ['File: ' + os.path.basename(self.filename)] if len(self.box) > 0: diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index 636ea9a..15da73d 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -67,6 +67,15 @@ class TestJp2k(unittest.TestCase): def tearDown(self): pass + def test_repr(self): + """Verify that results of __repr__ are eval-able.""" + j = Jp2k(self.j2kfile) + newjp2 = eval(repr(j)) + + self.assertEqual(newjp2.filename, self.j2kfile) + self.assertEqual(newjp2.mode, 'rb') + self.assertEqual(len(newjp2.box), 0) + def test_rlevel_max(self): """Verify that rlevel=-1 gets us the lowest resolution image""" j = Jp2k(self.j2kfile) From c8fe9ed7763ed46e90e3feca56a2688a3dc2269f Mon Sep 17 00:00:00 2001 From: John Evans Date: Thu, 24 Oct 2013 09:29:45 -0400 Subject: [PATCH 09/13] Added proper repr support for association box. #133 --- glymur/jp2box.py | 8 ++++++-- glymur/test/test_jp2box.py | 7 +++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/glymur/jp2box.py b/glymur/jp2box.py index 8303981..6466a63 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -1061,11 +1061,15 @@ class AssociationBox(Jp2kBox): box : list List of boxes contained in this superbox. """ - def __init__(self, length=0, offset=-1): + def __init__(self, box=[], length=0, offset=-1): Jp2kBox.__init__(self, box_id='asoc', longname='Association') self.length = length self.offset = offset - self.box = [] + self.box = box + + def __repr__(self): + msg = "glymur.jp2box.AssociationBox(box={0})".format(self.box) + return msg def __str__(self): msg = Jp2kBox.__str__(self) diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index b82af47..bf14789 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -780,6 +780,13 @@ class TestRepr(unittest.TestCase): self.assertFalse(newbox.colorspace_unknown) self.assertFalse(newbox.ip_provided) + def test_association_box(self): + """Verify __repr__ method on asoc box.""" + asoc = glymur.jp2box.AssociationBox() + newbox = eval(repr(asoc)) + self.assertEqual(newbox.box_id, 'asoc') + self.assertEqual(len(newbox.box), 0) + def test_codestreamheader_box(self): """Verify __repr__ method on jpch box.""" jpch = glymur.jp2box.CodestreamHeaderBox() From dc994377e1bf2c425ddee2072e94c01771ccd9f6 Mon Sep 17 00:00:00 2001 From: jevans Date: Sat, 26 Oct 2013 21:43:32 -0400 Subject: [PATCH 10/13] Added palette and xml box repr support. #133 --- glymur/jp2box.py | 16 +++++++++++++++- glymur/test/test_jp2box.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/glymur/jp2box.py b/glymur/jp2box.py index 6466a63..c7d69d5 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -75,6 +75,12 @@ class Jp2kBox(object): self.offset = offset self.longname = longname + def __repr__(self): + msg = "glymur.jp2box.Jp2kBox(box_id='{0}', offset={1}, length={2}, " + msg += "longname='{3}')" + msg = msg.format(self.box_id, self.offset, self.length, self.longname) + return msg + def __str__(self): msg = "{0} Box ({1})".format(self.longname, self.box_id) msg += " @ ({0}, {1})".format(self.offset, self.length) @@ -1273,6 +1279,12 @@ class PaletteBox(Jp2kBox): self.length = length self.offset = offset + def __repr__(self): + msg = "glymur.jp2box.PaletteBox({0}, bits_per_component={1}, " + msg += "signed={2})" + msg = msg.format(self.palette, self.bits_per_component, self.signed) + return msg + def __str__(self): msg = Jp2kBox.__str__(self) msg += '\n Size: ({0} x {1})'.format(len(self.palette[0]), @@ -1895,6 +1907,9 @@ class XMLBox(Jp2kBox): self.length = length self.offset = offset + def __repr__(self): + return "glymur.jp2box.XMLBox(xml={0})".format(self.xml) + def __str__(self): msg = Jp2kBox.__str__(self) xml = self.xml @@ -1953,7 +1968,6 @@ class XMLBox(Jp2kBox): msg = msg.format(offset, ude.reason) warnings.warn(msg, UserWarning) - # Strip out any trailing nulls, as they can foul up XML parsing. text = text.rstrip(chr(0)) diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index bf14789..117d52b 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -18,6 +18,7 @@ Test suite specifically targeting JP2 box layout. import doctest import os +import re import shutil import struct import sys @@ -414,7 +415,7 @@ class TestAppend(unittest.TestCase): jp2 = Jp2k(tfile.name) the_xml = ET.fromstring('0') - xmlbox = glymur.jp2box.XMLBox(xml=the_xml) + xmlbox = glymur.jp2box.XMLBox(xml=ET.ElementTree(the_xml)) jp2.append(xmlbox) # The sequence of box IDs should be the same as before, but with an @@ -865,6 +866,36 @@ class TestRepr(unittest.TestCase): self.assertEqual(newbox.ulst[0], uuid1) self.assertEqual(newbox.ulst[1], uuid2) + def test_jp2k_box(self): + """Verify Superclass repr.""" + box = glymur.jp2box.Jp2kBox(box_id='one', offset=2, length=3, + longname='four') + newbox = eval(repr(box)) + self.assertEqual(newbox.box_id, 'one') + self.assertEqual(newbox.offset, 2) + self.assertEqual(newbox.length, 3) + self.assertEqual(newbox.longname, 'four') + + def test_palette_box(self): + """Verify Palette box repr.""" + palette = np.array([[255, 0, 1000], [0, 255, 0]], dtype=np.int32) + bps = (8, 8, 16) + signed = (True, False, True) + box = glymur.jp2box.PaletteBox(palette=palette, bits_per_component=bps, + signed=(True, False, True)) + # The palette can't be reinstantiated thru eval/repr. + s = repr(box) + self.assertTrue(True) + + def test_xml_box(self): + """Verify xml box repr.""" + elt = ET.fromstring('0') + tree = ET.ElementTree(elt) + box = glymur.jp2box.XMLBox(xml=tree) + + regexp = "glymur.jp2box.XMLBox\(xml= Date: Sat, 26 Oct 2013 22:16:01 -0400 Subject: [PATCH 11/13] Regexp tests can only run on 2.7 and 3.3. #133 --- glymur/test/test_jp2box.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index 117d52b..94c3f73 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -887,15 +887,21 @@ class TestRepr(unittest.TestCase): s = repr(box) self.assertTrue(True) + @unittest.skipIf(sys.hexversion < 0x02070000, "Requires 2.7+") def test_xml_box(self): """Verify xml box repr.""" elt = ET.fromstring('0') tree = ET.ElementTree(elt) box = glymur.jp2box.XMLBox(xml=tree) - regexp = "glymur.jp2box.XMLBox\(xml= Date: Sun, 27 Oct 2013 06:09:39 -0400 Subject: [PATCH 12/13] Added rreg repr support. #133 --- glymur/jp2box.py | 12 ++++++++++++ glymur/test/test_jp2box.py | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/glymur/jp2box.py b/glymur/jp2box.py index c7d69d5..00e2a1e 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -1506,6 +1506,18 @@ class ReaderRequirementsBox(Jp2kBox): self.length = length self.offset = offset + def __repr__(self): + msg = "glymur.jp2box.ReaderRequirementsBox(fuam={fuam}, dcm={dcm}, " + msg += "standard_flag={standard_flag}, standard_mask={standard_mask}, " + msg += "vendor_feature={vendor_feature}, vendor_mask={vendor_mask})" + msg = msg.format(fuam=self.fuam, + dcm=self.dcm, + standard_flag=self.standard_flag, + standard_mask=self.standard_mask, + vendor_feature=self.vendor_feature, + vendor_mask=self.vendor_mask) + return msg + def __str__(self): msg = Jp2kBox.__str__(self) diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index 94c3f73..648e37c 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -903,6 +903,22 @@ class TestRepr(unittest.TestCase): else: self.assertRegex(repr(box), regexp) + def test_readerrequirements_box(self): + """Verify rreq repr method.""" + box = glymur.jp2box.ReaderRequirementsBox(fuam=160, dcm=192, + standard_flag=(5, 61, 43), + standard_mask=(128, 96, 64), + vendor_feature=[], + vendor_mask=[]) + newbox = eval(repr(box)) + self.assertEqual(box.fuam, newbox.fuam) + self.assertEqual(box.dcm, newbox.dcm) + self.assertEqual(box.standard_flag, newbox.standard_flag) + self.assertEqual(box.standard_mask, newbox.standard_mask) + self.assertEqual(box.vendor_feature, newbox.vendor_feature) + self.assertEqual(box.vendor_mask, newbox.vendor_mask) + + class TestJpxBoxes(unittest.TestCase): """Tests for JPX boxes.""" From 5e5bff39c3bd3e22dea650d18b2fb60140e776fa Mon Sep 17 00:00:00 2001 From: John Evans Date: Sun, 27 Oct 2013 14:00:40 -0400 Subject: [PATCH 13/13] Added UUIDBox and ContiguousCodeStreamBox repr support. Closes #133 --- glymur/jp2box.py | 12 +++++++++++- glymur/test/test_jp2box.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/glymur/jp2box.py b/glymur/jp2box.py index 00e2a1e..b363c76 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -773,6 +773,10 @@ class ContiguousCodestreamBox(Jp2kBox): self.length = length self.offset = offset + def __repr__(self): + msg = "glymur.jp2box.ContiguousCodeStreamBox(main_header={0})" + return msg.format(repr(self.main_header)) + def __str__(self): msg = Jp2kBox.__str__(self) msg += '\n Main header:' @@ -978,7 +982,6 @@ class ImageHeaderBox(Jp2kBox): return msg def __str__(self): - msg = Jp2kBox.__str__(self) msg = "{0}" msg += '\n Size: [{1} {2} {3}]' msg += '\n Bitdepth: {4}' @@ -2238,6 +2241,7 @@ class UUIDBox(Jp2kBox): """ Jp2kBox.__init__(self, box_id='uuid', longname='UUID') self.uuid = the_uuid + self.raw_data = raw_data if the_uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'): # XMP data. Parse as XML. Seems to be a difference between @@ -2262,6 +2266,12 @@ class UUIDBox(Jp2kBox): self.length = length self.offset = offset + def __repr__(self): + msg = "glymur.jp2box.UUIDBox(the_uuid={0}, " + msg += "raw_data=)" + return msg.format(repr(self.uuid), len(self.raw_data)) + + def __str__(self): msg = '{0}\n' msg += ' UUID: {1}{2}\n' diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index 648e37c..05c0a83 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -918,6 +918,42 @@ class TestRepr(unittest.TestCase): self.assertEqual(box.vendor_feature, newbox.vendor_feature) self.assertEqual(box.vendor_mask, newbox.vendor_mask) + @unittest.skipIf(sys.hexversion < 0x02070000, "Requires 2.7+") + def test_uuid_box(self): + """Verify uuid repr method.""" + uuid_instance = uuid.UUID('00000000-0000-0000-0000-000000000000') + data = b'0123456789' + box = glymur.jp2box.UUIDBox(the_uuid=uuid_instance, raw_data=data) + + # Since the raw_data parameter is a sequence of bytes which could be + # quite long, don't bother trying to make it conform to eval(repr()). + regexp = "glymur.jp2box.UUIDBox\(" + regexp += "the_uuid=UUID\('00000000-0000-0000-0000-000000000000'\),\s" + regexp += "raw_data=\)" + + if sys.hexversion < 0x03000000: + self.assertRegexpMatches(repr(box), regexp) + else: + self.assertRegex(repr(box), regexp) + + @unittest.skipIf(sys.hexversion < 0x02070000, "Requires 2.7+") + def test_contiguous_codestream_box(self): + """Verify contiguous codestream box repr method.""" + jp2file = glymur.data.nemo() + jp2 = Jp2k(jp2file) + box = jp2.box[-1] + + # Difficult to eval(repr()) this, so just match the general pattern. + regexp = "glymur.jp2box.ContiguousCodeStreamBox" + regexp += "\(main_header=