Glymur 0.13.7__py3-none-any.whl → 0.13.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
glymur/jp2box.py CHANGED
@@ -27,6 +27,7 @@ import warnings
27
27
  # Third party library imports ...
28
28
  try:
29
29
  from osgeo import gdal
30
+
30
31
  _HAVE_GDAL = True
31
32
  except (ImportError, ModuleNotFoundError): # pragma: no cover
32
33
  _HAVE_GDAL = False
@@ -39,10 +40,15 @@ import numpy as np
39
40
  # Local imports ...
40
41
  from .codestream import Codestream
41
42
  from .core import (
42
- _COLORSPACE_MAP_DISPLAY, _COLOR_TYPE_MAP_DISPLAY,
43
- SRGB, GREYSCALE, YCC,
44
- ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE,
45
- ANY_ICC_PROFILE, VENDOR_COLOR_METHOD
43
+ _COLORSPACE_MAP_DISPLAY,
44
+ _COLOR_TYPE_MAP_DISPLAY,
45
+ SRGB,
46
+ GREYSCALE,
47
+ YCC,
48
+ ENUMERATED_COLORSPACE,
49
+ RESTRICTED_ICC_PROFILE,
50
+ ANY_ICC_PROFILE,
51
+ VENDOR_COLOR_METHOD,
46
52
  )
47
53
  from .lib.tiff import tiff_header, BadTiffTagDatatype
48
54
  from . import get_option
@@ -50,37 +56,38 @@ from ._iccprofile import _ICCProfile
50
56
 
51
57
 
52
58
  _COLORSPACE_METHODS = {
53
- ENUMERATED_COLORSPACE: 'enumerated colorspace',
54
- RESTRICTED_ICC_PROFILE: 'restricted ICC profile',
55
- ANY_ICC_PROFILE: 'any ICC profile',
56
- VENDOR_COLOR_METHOD: 'vendor color method',
59
+ ENUMERATED_COLORSPACE: "enumerated colorspace",
60
+ RESTRICTED_ICC_PROFILE: "restricted ICC profile",
61
+ ANY_ICC_PROFILE: "any ICC profile",
62
+ VENDOR_COLOR_METHOD: "vendor color method",
57
63
  }
58
64
 
59
65
 
60
66
  _APPROXIMATION_MEASURES = {
61
- 0: 'JP2 only',
62
- 1: 'accurately represents correct colorspace definition',
63
- 2: 'approximates correct colorspace definition, exceptional quality',
64
- 3: 'approximates correct colorspace definition, reasonable quality',
65
- 4: 'approximates correct colorspace definition, poor quality',
67
+ 0: "JP2 only",
68
+ 1: "accurately represents correct colorspace definition",
69
+ 2: "approximates correct colorspace definition, exceptional quality",
70
+ 3: "approximates correct colorspace definition, reasonable quality",
71
+ 4: "approximates correct colorspace definition, poor quality",
66
72
  }
67
73
 
68
74
  # Three different UUIDs are given special treatment.
69
- _GEOTIFF_UUID = UUID('b14bf8bd-083d-4b43-a5ae-8cd7d5a6ce03')
70
- _EXIF_UUID = UUID(bytes=b'JpgTiffExif->JP2')
71
- _XMP_UUID = UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')
75
+ _GEOTIFF_UUID = UUID("b14bf8bd-083d-4b43-a5ae-8cd7d5a6ce03")
76
+ _EXIF_UUID = UUID(bytes=b"JpgTiffExif->JP2")
77
+ _XMP_UUID = UUID("be7acfcb-97a9-42e8-9c71-999491e3afac")
72
78
 
73
79
 
74
80
  class InvalidJp2kWarning(UserWarning):
75
81
  """Issue this warning in case the file is technically invalid but we can
76
82
  still read the image.
77
83
  """
84
+
78
85
  pass
79
86
 
80
87
 
81
88
  class InvalidJp2kError(RuntimeError):
82
- """Raise this exception in case we cannot parse a valid JP2 file.
83
- """
89
+ """Raise this exception in case we cannot parse a valid JP2 file."""
90
+
84
91
  pass
85
92
 
86
93
 
@@ -123,8 +130,7 @@ class Jp2kBox(object):
123
130
  warnings.warn(msg)
124
131
 
125
132
  def write(self, _):
126
- """Must be implemented in a subclass.
127
- """
133
+ """Must be implemented in a subclass."""
128
134
  msg = f"Writing not supported for {self.longname} box."
129
135
  raise NotImplementedError(msg)
130
136
 
@@ -134,7 +140,7 @@ class Jp2kBox(object):
134
140
  for box in self.box:
135
141
  boxstr = str(box)
136
142
  # Indent the child boxes to make the association clear.
137
- msg += '\n' + textwrap.indent(boxstr, ' ' * 4)
143
+ msg += "\n" + textwrap.indent(boxstr, " " * 4)
138
144
  return msg
139
145
 
140
146
  def _write_superbox(self, fptr, box_id):
@@ -148,7 +154,7 @@ class Jp2kBox(object):
148
154
  4-byte sequence that identifies the superbox.
149
155
  """
150
156
  b = io.BytesIO()
151
- b.write(struct.pack('>I4s', 0, box_id))
157
+ b.write(struct.pack(">I4s", 0, box_id))
152
158
  for box in self.box:
153
159
  box.write(b)
154
160
 
@@ -156,7 +162,7 @@ class Jp2kBox(object):
156
162
 
157
163
  # come back and write the length.
158
164
  b.seek(0)
159
- buffer = struct.pack('>I', box_length)
165
+ buffer = struct.pack(">I", box_length)
160
166
  b.write(buffer)
161
167
 
162
168
  fptr.write(b.getvalue())
@@ -186,12 +192,15 @@ class Jp2kBox(object):
186
192
  # We don't recognize the box ID, so create an UnknownBox and be
187
193
  # done with it.
188
194
  msg = (
189
- f'Unrecognized box ({box_id}) '
190
- f'encountered at byte offset {fptr.tell() - 8}.'
195
+ f"Unrecognized box ({box_id}) "
196
+ f"encountered at byte offset {fptr.tell() - 8}."
191
197
  )
192
198
  warnings.warn(msg, UserWarning)
193
199
  box = UnknownBox(
194
- box_id, offset=start, length=num_bytes, longname='Unknown'
200
+ box_id,
201
+ offset=start,
202
+ length=num_bytes,
203
+ longname="Unknown"
195
204
  )
196
205
 
197
206
  return box
@@ -200,16 +209,16 @@ class Jp2kBox(object):
200
209
  box = parser(fptr, start, num_bytes)
201
210
  except Exception as err:
202
211
  msg = (
203
- f'Encountered an error while parsing a '
204
- f'{_BOX_WITH_ID[box_id]} box at byte offset {start}. '
212
+ f"Encountered an error while parsing a "
213
+ f"{_BOX_WITH_ID[box_id]} box at byte offset {start}. "
205
214
  f'The original error message was "{str(err)}".'
206
215
  )
207
216
  warnings.warn(msg, UserWarning)
208
217
  box = UnknownBox(
209
- box_id.decode('utf-8'),
218
+ box_id.decode("utf-8"),
210
219
  length=num_bytes,
211
220
  offset=start,
212
- longname='Unknown'
221
+ longname="Unknown",
213
222
  )
214
223
 
215
224
  return box
@@ -245,7 +254,7 @@ class Jp2kBox(object):
245
254
  warnings.warn(msg, UserWarning)
246
255
  return superbox
247
256
 
248
- (box_length, box_id) = struct.unpack('>I4s', read_buffer)
257
+ (box_length, box_id) = struct.unpack(">I4s", read_buffer)
249
258
  if box_length == 0:
250
259
  # The length of the box is presumed to last until the end of
251
260
  # the file. Compute the effective length of the box.
@@ -254,7 +263,7 @@ class Jp2kBox(object):
254
263
  elif box_length == 1:
255
264
  # The length of the box is in the XL field, a 64-bit value.
256
265
  read_buffer = fptr.read(8)
257
- num_bytes, = struct.unpack('>Q', read_buffer)
266
+ (num_bytes,) = struct.unpack(">Q", read_buffer)
258
267
 
259
268
  else:
260
269
  # The box_length value really is the length of the box!
@@ -275,9 +284,9 @@ class Jp2kBox(object):
275
284
  # The box must be invalid somehow, as the file pointer is
276
285
  # positioned past the end of the box.
277
286
  msg = (
278
- f'{box_id} box may be invalid, the file pointer is '
279
- f'positioned {fptr.tell() - (start + num_bytes)} bytes '
280
- 'past the end of the box.'
287
+ f"{box_id} box may be invalid, the file pointer is "
288
+ f"positioned {fptr.tell() - (start + num_bytes)} bytes "
289
+ "past the end of the box."
281
290
  )
282
291
  warnings.warn(msg, UserWarning)
283
292
  fptr.seek(start + num_bytes)
@@ -317,13 +326,19 @@ class ColourSpecificationBox(Jp2kBox):
317
326
  ICC profile header according to ICC profile specification. If
318
327
  colorspace is not None, then icc_profile must be empty.
319
328
  """
320
- longname = 'Colour Specification'
321
- box_id = 'colr'
329
+
330
+ longname = "Colour Specification"
331
+ box_id = "colr"
322
332
 
323
333
  def __init__(
324
- self, method=ENUMERATED_COLORSPACE, precedence=0,
325
- approximation=0, colorspace=None, icc_profile=None,
326
- length=0, offset=-1
334
+ self,
335
+ method=ENUMERATED_COLORSPACE,
336
+ precedence=0,
337
+ approximation=0,
338
+ colorspace=None,
339
+ icc_profile=None,
340
+ length=0,
341
+ offset=-1,
327
342
  ):
328
343
  super().__init__()
329
344
 
@@ -345,8 +360,10 @@ class ColourSpecificationBox(Jp2kBox):
345
360
  def _validate(self, writing=False):
346
361
  """Verify that the box obeys the specifications."""
347
362
  if self.colorspace is not None and self.icc_profile is not None:
348
- msg = ("Colorspace and icc_profile cannot both be set when "
349
- "creating a ColourSpecificationBox.")
363
+ msg = (
364
+ "Colorspace and icc_profile cannot both be set when "
365
+ "creating a ColourSpecificationBox."
366
+ )
350
367
  self._dispatch_validation_error(msg, writing=writing)
351
368
 
352
369
  if self.method not in _COLORSPACE_METHODS:
@@ -411,7 +428,7 @@ class ColourSpecificationBox(Jp2kBox):
411
428
 
412
429
  def __str__(self):
413
430
  title = Jp2kBox.__str__(self)
414
- if get_option('print.short') is True:
431
+ if get_option("print.short") is True:
415
432
  return title
416
433
 
417
434
  lst = []
@@ -419,66 +436,65 @@ class ColourSpecificationBox(Jp2kBox):
419
436
  try:
420
437
  item = _COLORSPACE_METHODS[self.method]
421
438
  except KeyError:
422
- item = f'unrecognized value ({self.method})'
423
- text = f'Method: {item}'
439
+ item = f"unrecognized value ({self.method})"
440
+ text = f"Method: {item}"
424
441
 
425
442
  lst.append(text)
426
- text = f'Precedence: {self.precedence}'
443
+ text = f"Precedence: {self.precedence}"
427
444
  lst.append(text)
428
445
 
429
446
  if self.approximation != 0:
430
447
  try:
431
448
  dispvalue = _APPROXIMATION_MEASURES[self.approximation]
432
449
  except KeyError:
433
- dispvalue = f'invalid ({self.approximation})'
434
- text = f'Approximation: {dispvalue}'
450
+ dispvalue = f"invalid ({self.approximation})"
451
+ text = f"Approximation: {dispvalue}"
435
452
  lst.append(text)
436
453
 
437
454
  if self.colorspace is not None:
438
455
  try:
439
456
  dispvalue = _COLORSPACE_MAP_DISPLAY[self.colorspace]
440
457
  except KeyError:
441
- dispvalue = f'{self.colorspace} (unrecognized)'
442
- text = f'Colorspace: {dispvalue}'
458
+ dispvalue = f"{self.colorspace} (unrecognized)"
459
+ text = f"Colorspace: {dispvalue}"
443
460
  else:
444
461
  if self.icc_profile is None:
445
- text = 'ICC Profile: None'
462
+ text = "ICC Profile: None"
446
463
  else:
447
464
  text = pprint.pformat(self.icc_profile_header)
448
- text = textwrap.indent(text, ' ' * 4)
449
- text = '\n'.join(['ICC Profile:', text])
465
+ text = textwrap.indent(text, " " * 4)
466
+ text = "\n".join(["ICC Profile:", text])
450
467
 
451
468
  lst.append(text)
452
469
 
453
- text = '\n'.join(lst)
470
+ text = "\n".join(lst)
454
471
 
455
- text = '\n'.join([title, textwrap.indent(text, ' ' * 4)])
472
+ text = "\n".join([title, textwrap.indent(text, " " * 4)])
456
473
 
457
474
  return text
458
475
 
459
476
  def write(self, fptr):
460
- """Write an Colour Specification box to file.
461
- """
477
+ """Write an Colour Specification box to file."""
462
478
 
463
479
  self._write_validate()
464
480
  length = 15 if self.icc_profile is None else 11 + len(self.icc_profile)
465
- fptr.write(struct.pack('>I4s', length, b'colr'))
481
+ fptr.write(struct.pack(">I4s", length, b"colr"))
466
482
 
467
483
  if self.icc_profile is None:
468
484
 
469
485
  buffer = struct.pack(
470
- '>BBBI',
486
+ ">BBBI",
471
487
  self.method,
472
488
  self.precedence,
473
489
  self.approximation,
474
- self.colorspace
490
+ self.colorspace,
475
491
  )
476
492
  fptr.write(buffer)
477
493
 
478
494
  else:
479
495
 
480
496
  buffer = struct.pack(
481
- '>BBB',
497
+ ">BBB",
482
498
  self.method,
483
499
  self.precedence,
484
500
  self.approximation,
@@ -507,12 +523,12 @@ class ColourSpecificationBox(Jp2kBox):
507
523
  num_bytes = offset + length - fptr.tell()
508
524
  read_buffer = fptr.read(num_bytes)
509
525
 
510
- lst = struct.unpack_from('>BBB', read_buffer, offset=0)
526
+ lst = struct.unpack_from(">BBB", read_buffer, offset=0)
511
527
  method, precedence, approximation = lst
512
528
 
513
529
  if method == 1:
514
530
  # enumerated colour space
515
- colorspace, = struct.unpack_from('>I', read_buffer, offset=3)
531
+ (colorspace,) = struct.unpack_from(">I", read_buffer, offset=3)
516
532
  icc_profile = None
517
533
 
518
534
  else:
@@ -536,7 +552,7 @@ class ColourSpecificationBox(Jp2kBox):
536
552
  colorspace=colorspace,
537
553
  icc_profile=icc_profile,
538
554
  length=length,
539
- offset=offset
555
+ offset=offset,
540
556
  )
541
557
 
542
558
 
@@ -561,8 +577,9 @@ class ChannelDefinitionBox(Jp2kBox):
561
577
  association : list
562
578
  index of the associated color
563
579
  """
564
- box_id = 'cdef'
565
- longname = 'Channel Definition'
580
+
581
+ box_id = "cdef"
582
+ longname = "Channel Definition"
566
583
 
567
584
  def __init__(self, channel_type, association, index=None, **kwargs):
568
585
  super().__init__()
@@ -580,8 +597,10 @@ class ChannelDefinitionBox(Jp2kBox):
580
597
  def _validate(self, writing=False):
581
598
  """Verify that the box obeys the specifications."""
582
599
  # channel type and association must be specified.
583
- if not ((len(self.index) == len(self.channel_type))
584
- and (len(self.channel_type) == len(self.association))):
600
+ if not (
601
+ (len(self.index) == len(self.channel_type))
602
+ and (len(self.channel_type) == len(self.association))
603
+ ):
585
604
  msg = (
586
605
  f"The length of the index ({len(self.index)}), "
587
606
  f"channel_type ({len(self.channel_type)}), "
@@ -604,7 +623,7 @@ class ChannelDefinitionBox(Jp2kBox):
604
623
 
605
624
  def __str__(self):
606
625
  title = Jp2kBox.__str__(self)
607
- if get_option('print.short') is True:
626
+ if get_option("print.short") is True:
608
627
  return title
609
628
 
610
629
  lst = []
@@ -616,13 +635,13 @@ class ChannelDefinitionBox(Jp2kBox):
616
635
  except KeyError:
617
636
  color_type_string = f"invalid ({channel_type})"
618
637
 
619
- association = str(association) if association else 'whole image'
620
- text = f'Channel {index} ({color_type_string}) ==> ({association})'
638
+ association = str(association) if association else "whole image"
639
+ text = f"Channel {index} ({color_type_string}) ==> ({association})"
621
640
  lst.append(text)
622
641
 
623
- text = '\n'.join(lst)
624
- text = textwrap.indent(text, ' ' * 4)
625
- text = '\n'.join([title, text])
642
+ text = "\n".join(lst)
643
+ text = textwrap.indent(text, " " * 4)
644
+ text = "\n".join([title, text])
626
645
  return text
627
646
 
628
647
  def __repr__(self):
@@ -637,14 +656,16 @@ class ChannelDefinitionBox(Jp2kBox):
637
656
  """Write a channel definition box to file."""
638
657
  self._validate(writing=True)
639
658
  num_components = len(self.association)
640
- fptr.write(struct.pack('>I4s', 8 + 2 + num_components * 6, b'cdef'))
641
- fptr.write(struct.pack('>H', num_components))
659
+ fptr.write(struct.pack(">I4s", 8 + 2 + num_components * 6, b"cdef"))
660
+ fptr.write(struct.pack(">H", num_components))
642
661
  for j in range(num_components):
643
- fptr.write(struct.pack(
644
- '>' + 'H' * 3,
645
- self.index[j],
646
- self.channel_type[j],
647
- self.association[j])
662
+ fptr.write(
663
+ struct.pack(
664
+ ">" + "H" * 3,
665
+ self.index[j],
666
+ self.channel_type[j],
667
+ self.association[j],
668
+ )
648
669
  )
649
670
 
650
671
  @classmethod
@@ -669,11 +690,11 @@ class ChannelDefinitionBox(Jp2kBox):
669
690
  read_buffer = fptr.read(num_bytes)
670
691
 
671
692
  # Read the number of components.
672
- num_components, = struct.unpack_from('>H', read_buffer)
693
+ (num_components,) = struct.unpack_from(">H", read_buffer)
694
+
695
+ fmt = ">" + "HHH" * num_components
696
+ data = struct.unpack_from(fmt, read_buffer, offset=2)
673
697
 
674
- data = struct.unpack_from(
675
- '>' + 'HHH' * num_components, read_buffer, offset=2
676
- )
677
698
  index = data[0:num_components * 6:3]
678
699
  channel_type = data[1:num_components * 6:3]
679
700
  association = data[2:num_components * 6:3]
@@ -682,7 +703,8 @@ class ChannelDefinitionBox(Jp2kBox):
682
703
  index=tuple(index),
683
704
  channel_type=tuple(channel_type),
684
705
  association=tuple(association),
685
- length=length, offset=offset
706
+ length=length,
707
+ offset=offset,
686
708
  )
687
709
 
688
710
 
@@ -702,8 +724,9 @@ class CodestreamHeaderBox(Jp2kBox):
702
724
  box : list
703
725
  List of boxes contained in this superbox.
704
726
  """
705
- box_id = 'jpch'
706
- longname = 'Codestream Header'
727
+
728
+ box_id = "jpch"
729
+ longname = "Codestream Header"
707
730
 
708
731
  def __init__(self, box=None, length=0, offset=-1):
709
732
  super().__init__()
@@ -721,7 +744,7 @@ class CodestreamHeaderBox(Jp2kBox):
721
744
 
722
745
  def write(self, fptr):
723
746
  """Write a codestream header box to file."""
724
- self._write_superbox(fptr, b'jpch')
747
+ self._write_superbox(fptr, b"jpch")
725
748
 
726
749
  @classmethod
727
750
  def parse(cls, fptr, offset, length):
@@ -766,8 +789,9 @@ class ColourGroupBox(Jp2kBox):
766
789
  box : list
767
790
  List of boxes contained in this superbox.
768
791
  """
769
- box_id = 'cgrp'
770
- longname = 'Colour Group'
792
+
793
+ box_id = "cgrp"
794
+ longname = "Colour Group"
771
795
 
772
796
  def __init__(self, box=None, length=0, offset=-1):
773
797
  super().__init__()
@@ -785,15 +809,17 @@ class ColourGroupBox(Jp2kBox):
785
809
 
786
810
  def _validate(self, writing=True):
787
811
  """Verify that the box obeys the specifications."""
788
- if any([box.box_id != 'colr' for box in self.box]):
789
- msg = ("Colour group boxes can only contain colour specification "
790
- "boxes.")
812
+ if any([box.box_id != "colr" for box in self.box]):
813
+ msg = (
814
+ "Colour group boxes can only contain colour specification "
815
+ "boxes."
816
+ )
791
817
  self._dispatch_validation_error(msg, writing=writing)
792
818
 
793
819
  def write(self, fptr):
794
820
  """Write a colour group box to file."""
795
821
  self._validate(writing=True)
796
- self._write_superbox(fptr, b'cgrp')
822
+ self._write_superbox(fptr, b"cgrp")
797
823
 
798
824
  @classmethod
799
825
  def parse(cls, fptr, offset, length):
@@ -838,8 +864,9 @@ class CompositingLayerHeaderBox(Jp2kBox):
838
864
  box : list
839
865
  List of boxes contained in this superbox.
840
866
  """
841
- box_id = 'jplh'
842
- longname = 'Compositing Layer Header'
867
+
868
+ box_id = "jplh"
869
+ longname = "Compositing Layer Header"
843
870
 
844
871
  def __init__(self, box=None, length=0, offset=-1):
845
872
  super().__init__()
@@ -857,7 +884,7 @@ class CompositingLayerHeaderBox(Jp2kBox):
857
884
 
858
885
  def write(self, fptr):
859
886
  """Write a compositing layer header box to file."""
860
- self._write_superbox(fptr, b'jplh')
887
+ self._write_superbox(fptr, b"jplh")
861
888
 
862
889
  @classmethod
863
890
  def parse(cls, fptr, offset, length):
@@ -905,11 +932,13 @@ class ComponentMappingBox(Jp2kBox):
905
932
  palette_index : tuple
906
933
  Index component from palette
907
934
  """
908
- box_id = 'cmap'
909
- longname = 'Component Mapping'
910
935
 
911
- def __init__(self, component_index, mapping_type, palette_index,
912
- length=0, offset=-1):
936
+ box_id = "cmap"
937
+ longname = "Component Mapping"
938
+
939
+ def __init__(
940
+ self, component_index, mapping_type, palette_index, length=0, offset=-1
941
+ ):
913
942
  super().__init__()
914
943
  self.component_index = component_index
915
944
  self.mapping_type = mapping_type
@@ -928,7 +957,7 @@ class ComponentMappingBox(Jp2kBox):
928
957
 
929
958
  def __str__(self):
930
959
  title = Jp2kBox.__str__(self)
931
- if get_option('print.short') is True:
960
+ if get_option("print.short") is True:
932
961
  return title
933
962
 
934
963
  lst = []
@@ -938,32 +967,32 @@ class ComponentMappingBox(Jp2kBox):
938
967
  if mapping_type == 1:
939
968
  # palette mapping
940
969
  text = (
941
- f'Component {component_idx} ==> '
942
- f'palette column {palette_idx}'
970
+ f"Component {component_idx} ==> "
971
+ f"palette column {palette_idx}"
943
972
  )
944
973
  else:
945
974
  # Direct use
946
- text = f'Component {component_idx} ==> {k}'
975
+ text = f"Component {component_idx} ==> {k}"
947
976
  lst.append(text)
948
977
 
949
- text = '\n'.join(lst)
950
- text = textwrap.indent(text, ' ' * 4)
951
- text = '\n'.join([title, text])
978
+ text = "\n".join(lst)
979
+ text = textwrap.indent(text, " " * 4)
980
+ text = "\n".join([title, text])
952
981
 
953
982
  return text
954
983
 
955
984
  def write(self, fptr):
956
985
  """Write a Component Mapping box to file."""
957
986
  length = 8 + 4 * len(self.component_index)
958
- write_buffer = struct.pack('>I4s', length, b'cmap')
987
+ write_buffer = struct.pack(">I4s", length, b"cmap")
959
988
  fptr.write(write_buffer)
960
989
 
961
990
  for j in range(len(self.component_index)):
962
991
  write_buffer = struct.pack(
963
- '>HBB',
992
+ ">HBB",
964
993
  self.component_index[j],
965
994
  self.mapping_type[j],
966
- self.palette_index[j]
995
+ self.palette_index[j],
967
996
  )
968
997
  fptr.write(write_buffer)
969
998
 
@@ -989,14 +1018,19 @@ class ComponentMappingBox(Jp2kBox):
989
1018
  num_components = int(num_bytes / 4)
990
1019
 
991
1020
  read_buffer = fptr.read(num_bytes)
992
- data = struct.unpack('>' + 'HBB' * num_components, read_buffer)
1021
+ data = struct.unpack(">" + "HBB" * num_components, read_buffer)
993
1022
 
994
1023
  component_index = data[0:num_bytes:3]
995
1024
  mapping_type = data[1:num_bytes:3]
996
1025
  palette_index = data[2:num_bytes:3]
997
1026
 
998
- return cls(component_index, mapping_type, palette_index,
999
- length=length, offset=offset)
1027
+ return cls(
1028
+ component_index,
1029
+ mapping_type,
1030
+ palette_index,
1031
+ length=length,
1032
+ offset=offset
1033
+ )
1000
1034
 
1001
1035
 
1002
1036
  class ContiguousCodestreamBox(Jp2kBox):
@@ -1018,11 +1052,17 @@ class ContiguousCodestreamBox(Jp2kBox):
1018
1052
  main_header_offset : int
1019
1053
  offset of main header from start of file
1020
1054
  """
1021
- box_id = 'jp2c'
1022
- longname = 'Contiguous Codestream'
1023
1055
 
1024
- def __init__(self, codestream=None, main_header_offset=None, length=0,
1025
- offset=-1):
1056
+ box_id = "jp2c"
1057
+ longname = "Contiguous Codestream"
1058
+
1059
+ def __init__(
1060
+ self,
1061
+ codestream=None,
1062
+ main_header_offset=None,
1063
+ length=0,
1064
+ offset=-1
1065
+ ):
1026
1066
  super().__init__()
1027
1067
  self._codestream = codestream
1028
1068
  self.length = length
@@ -1034,18 +1074,20 @@ class ContiguousCodestreamBox(Jp2kBox):
1034
1074
 
1035
1075
  @property
1036
1076
  def codestream(self):
1037
- if get_option('parse.full_codestream') is True:
1077
+ if get_option("parse.full_codestream") is True:
1038
1078
  header_only = False
1039
1079
  else:
1040
1080
  header_only = True
1041
1081
  if self._codestream is None:
1042
1082
  if self._filename is not None:
1043
- with open(self._filename, 'rb') as fptr:
1083
+ with open(self._filename, "rb") as fptr:
1044
1084
  fptr.seek(self.main_header_offset)
1045
- codestream = Codestream(
1046
- fptr, self.length, header_only=header_only
1085
+ self._codestream = Codestream(
1086
+ fptr,
1087
+ self.length,
1088
+ header_only=header_only
1047
1089
  )
1048
- self._codestream = codestream
1090
+
1049
1091
  return self._codestream
1050
1092
 
1051
1093
  def __repr__(self):
@@ -1055,18 +1097,18 @@ class ContiguousCodestreamBox(Jp2kBox):
1055
1097
 
1056
1098
  def __str__(self):
1057
1099
  title = Jp2kBox.__str__(self)
1058
- if get_option('print.short') is True:
1100
+ if get_option("print.short") is True:
1059
1101
  return title
1060
- if get_option('print.codestream') is False:
1102
+ if get_option("print.codestream") is False:
1061
1103
  return title
1062
1104
 
1063
1105
  lst = []
1064
1106
  for segment in self.codestream.segment:
1065
1107
  lst.append(str(segment))
1066
1108
 
1067
- text = '\n'.join(lst)
1068
- text = textwrap.indent(text, ' ' * 4)
1069
- text = '\n'.join([title, text])
1109
+ text = "\n".join(lst)
1110
+ text = textwrap.indent(text, " " * 4)
1111
+ text = "\n".join([title, text])
1070
1112
  return text
1071
1113
 
1072
1114
  @classmethod
@@ -1088,7 +1130,7 @@ class ContiguousCodestreamBox(Jp2kBox):
1088
1130
  Instance of the current contiguous codestream box.
1089
1131
  """
1090
1132
  main_header_offset = fptr.tell()
1091
- if get_option('parse.full_codestream'):
1133
+ if get_option("parse.full_codestream"):
1092
1134
  codestream = Codestream(fptr, length, header_only=False)
1093
1135
  else:
1094
1136
  codestream = None
@@ -1096,7 +1138,7 @@ class ContiguousCodestreamBox(Jp2kBox):
1096
1138
  codestream,
1097
1139
  main_header_offset=main_header_offset,
1098
1140
  length=length,
1099
- offset=offset
1141
+ offset=offset,
1100
1142
  )
1101
1143
  box._filename = fptr.name
1102
1144
  return box
@@ -1118,8 +1160,9 @@ class DataReferenceBox(Jp2kBox):
1118
1160
  DR : list
1119
1161
  Data Entry URL boxes.
1120
1162
  """
1121
- box_id = 'dtbl'
1122
- longname = 'Data Reference'
1163
+
1164
+ box_id = "dtbl"
1165
+ longname = "Data Reference"
1123
1166
 
1124
1167
  def __init__(self, data_entry_url_boxes=None, length=0, offset=-1):
1125
1168
  super().__init__()
@@ -1134,9 +1177,11 @@ class DataReferenceBox(Jp2kBox):
1134
1177
  def _validate(self, writing=False):
1135
1178
  """Verify that the box obeys the specifications."""
1136
1179
  for box in self.DR:
1137
- if box.box_id != 'url ':
1138
- msg = ('Child boxes of a data reference box can only be data '
1139
- 'entry URL boxes.')
1180
+ if box.box_id != "url ":
1181
+ msg = (
1182
+ "Child boxes of a data reference box can only be data "
1183
+ "entry URL boxes."
1184
+ )
1140
1185
  self._dispatch_validation_error(msg, writing=writing)
1141
1186
 
1142
1187
  def _write_validate(self):
@@ -1153,10 +1198,10 @@ class DataReferenceBox(Jp2kBox):
1153
1198
 
1154
1199
  # Very similar to the way a superbox is written.
1155
1200
  orig_pos = fptr.tell()
1156
- fptr.write(struct.pack('>I4s', 0, b'dtbl'))
1201
+ fptr.write(struct.pack(">I4s", 0, b"dtbl"))
1157
1202
 
1158
1203
  # Write the number of data entry url boxes.
1159
- write_buffer = struct.pack('>H', len(self.DR))
1204
+ write_buffer = struct.pack(">H", len(self.DR))
1160
1205
  fptr.write(write_buffer)
1161
1206
 
1162
1207
  for box in self.DR:
@@ -1164,12 +1209,12 @@ class DataReferenceBox(Jp2kBox):
1164
1209
 
1165
1210
  end_pos = fptr.tell()
1166
1211
  fptr.seek(orig_pos)
1167
- fptr.write(struct.pack('>I', end_pos - orig_pos))
1212
+ fptr.write(struct.pack(">I", end_pos - orig_pos))
1168
1213
  fptr.seek(end_pos)
1169
1214
 
1170
1215
  def __str__(self):
1171
1216
  title = Jp2kBox.__str__(self)
1172
- if get_option('print.short') is True:
1217
+ if get_option("print.short") is True:
1173
1218
  return title
1174
1219
 
1175
1220
  if len(self.DR) == 0:
@@ -1178,14 +1223,14 @@ class DataReferenceBox(Jp2kBox):
1178
1223
  lst = []
1179
1224
  for box in self.DR:
1180
1225
  lst.append(str(box))
1181
- text = '\n'.join(lst)
1182
- text = textwrap.indent(text, ' ' * 4)
1226
+ text = "\n".join(lst)
1227
+ text = textwrap.indent(text, " " * 4)
1183
1228
 
1184
- text = '\n'.join([title, text])
1229
+ text = "\n".join([title, text])
1185
1230
  return text
1186
1231
 
1187
1232
  def __repr__(self):
1188
- msg = 'glymur.jp2box.DataReferenceBox()'
1233
+ msg = "glymur.jp2box.DataReferenceBox()"
1189
1234
  return msg
1190
1235
 
1191
1236
  @classmethod
@@ -1210,7 +1255,7 @@ class DataReferenceBox(Jp2kBox):
1210
1255
  read_buffer = fptr.read(num_bytes)
1211
1256
 
1212
1257
  # Read the number of data references
1213
- ndr, = struct.unpack_from('>H', read_buffer, offset=0)
1258
+ (ndr,) = struct.unpack_from(">H", read_buffer, offset=0)
1214
1259
 
1215
1260
  # Need to keep track of where the next url box starts.
1216
1261
  box_offset = 2
@@ -1220,10 +1265,8 @@ class DataReferenceBox(Jp2kBox):
1220
1265
 
1221
1266
  # Create an in-memory binary stream for each URL box.
1222
1267
  box_fptr = io.BytesIO(read_buffer[box_offset:])
1223
- box_buffer = box_fptr.read(8)
1224
- (box_length, box_id) = struct.unpack_from(
1225
- '>I4s', box_buffer, offset=0
1226
- )
1268
+ buffer = box_fptr.read(8)
1269
+ box_length, box_id = struct.unpack_from(">I4s", buffer, offset=0)
1227
1270
  box = DataEntryURLBox.parse(box_fptr, 0, box_length)
1228
1271
 
1229
1272
  # Need to adjust the box start to that of the "real" file.
@@ -1257,19 +1300,24 @@ class FileTypeBox(Jp2kBox):
1257
1300
  compatibility_list: list
1258
1301
  List of file conformance profiles.
1259
1302
  """
1260
- box_id = 'ftyp'
1261
- longname = 'File Type'
1262
- _valid_cls = ['jp2 ', 'jph ', 'jpx ', 'jpxb']
1303
+
1304
+ box_id = "ftyp"
1305
+ longname = "File Type"
1306
+ _valid_cls = ["jp2 ", "jph ", "jpx ", "jpxb"]
1263
1307
 
1264
1308
  def __init__(
1265
- self, brand='jp2 ', minor_version=0, compatibility_list=None,
1266
- length=0, offset=-1
1309
+ self,
1310
+ brand="jp2 ",
1311
+ minor_version=0,
1312
+ compatibility_list=None,
1313
+ length=0,
1314
+ offset=-1,
1267
1315
  ):
1268
1316
  super().__init__()
1269
1317
  self.brand = brand
1270
1318
  self.minor_version = minor_version
1271
1319
  if compatibility_list is None:
1272
- self.compatibility_list = ['jp2 ']
1320
+ self.compatibility_list = ["jp2 "]
1273
1321
  else:
1274
1322
  self.compatibility_list = compatibility_list
1275
1323
  self.length = length
@@ -1287,19 +1335,19 @@ class FileTypeBox(Jp2kBox):
1287
1335
 
1288
1336
  def __str__(self):
1289
1337
  title = Jp2kBox.__str__(self)
1290
- if get_option('print.short') is True:
1338
+ if get_option("print.short") is True:
1291
1339
  return title
1292
1340
 
1293
1341
  lst = []
1294
- text = f'Brand: {self.brand}'
1342
+ text = f"Brand: {self.brand}"
1295
1343
  lst.append(text)
1296
- text = f'Compatibility: {self.compatibility_list}'
1344
+ text = f"Compatibility: {self.compatibility_list}"
1297
1345
  lst.append(text)
1298
1346
 
1299
- text = '\n'.join(lst)
1300
- text = textwrap.indent(text, ' ' * 4)
1347
+ text = "\n".join(lst)
1348
+ text = textwrap.indent(text, " " * 4)
1301
1349
 
1302
- text = '\n'.join([title, text])
1350
+ text = "\n".join([title, text])
1303
1351
 
1304
1352
  return text
1305
1353
 
@@ -1307,7 +1355,7 @@ class FileTypeBox(Jp2kBox):
1307
1355
  """
1308
1356
  Validate the box before writing to file.
1309
1357
  """
1310
- if self.brand not in ['jp2 ', 'jpx ', 'jph ']:
1358
+ if self.brand not in ["jp2 ", "jpx ", "jph "]:
1311
1359
  msg = (
1312
1360
  f"The file type brand was '{self.brand}'. "
1313
1361
  f"It should be either 'jp2 ', 'jpx ', or 'jph '."
@@ -1333,9 +1381,9 @@ class FileTypeBox(Jp2kBox):
1333
1381
  """Write a File Type box to file."""
1334
1382
  self._validate(writing=True)
1335
1383
  length = 16 + 4 * len(self.compatibility_list)
1336
- fptr.write(struct.pack('>I4s', length, b'ftyp'))
1384
+ fptr.write(struct.pack(">I4s", length, b"ftyp"))
1337
1385
  fptr.write(self.brand.encode())
1338
- fptr.write(struct.pack('>I', self.minor_version))
1386
+ fptr.write(struct.pack(">I", self.minor_version))
1339
1387
 
1340
1388
  for item in self.compatibility_list:
1341
1389
  fptr.write(item.encode())
@@ -1361,16 +1409,16 @@ class FileTypeBox(Jp2kBox):
1361
1409
  num_bytes = offset + length - fptr.tell()
1362
1410
  read_buffer = fptr.read(num_bytes)
1363
1411
  # Extract the brand, minor version.
1364
- (brand, minor_version) = struct.unpack_from('>4sI', read_buffer, 0)
1365
- brand = brand.decode('utf-8')
1412
+ (brand, minor_version) = struct.unpack_from(">4sI", read_buffer, 0)
1413
+ brand = brand.decode("utf-8")
1366
1414
 
1367
1415
  # Extract the compatibility list. Each entry has 4 bytes.
1368
1416
  num_entries = int((length - 16) / 4)
1369
1417
  compatibility_list = []
1370
1418
  for j in range(int(num_entries)):
1371
- entry, = struct.unpack_from('>4s', read_buffer, 8 + j * 4)
1419
+ (entry,) = struct.unpack_from(">4s", read_buffer, 8 + j * 4)
1372
1420
  try:
1373
- entry = entry.decode('utf-8')
1421
+ entry = entry.decode("utf-8")
1374
1422
  except UnicodeDecodeError:
1375
1423
  # The entry is invalid, but we've got code to catch this
1376
1424
  # later on.
@@ -1383,7 +1431,7 @@ class FileTypeBox(Jp2kBox):
1383
1431
  minor_version=minor_version,
1384
1432
  compatibility_list=compatibility_list,
1385
1433
  length=length,
1386
- offset=offset
1434
+ offset=offset,
1387
1435
  )
1388
1436
 
1389
1437
 
@@ -1401,11 +1449,16 @@ class FragmentListBox(Jp2kBox):
1401
1449
  longname : str
1402
1450
  more verbose description of the box.
1403
1451
  """
1404
- box_id = 'flst'
1405
- longname = 'Fragment List'
1452
+
1453
+ box_id = "flst"
1454
+ longname = "Fragment List"
1406
1455
 
1407
1456
  def __init__(
1408
- self, fragment_offset, fragment_length, data_reference, length=0,
1457
+ self,
1458
+ fragment_offset,
1459
+ fragment_length,
1460
+ data_reference,
1461
+ length=0,
1409
1462
  offset=-1
1410
1463
  ):
1411
1464
  super().__init__()
@@ -1418,8 +1471,9 @@ class FragmentListBox(Jp2kBox):
1418
1471
 
1419
1472
  def _validate(self, writing=False):
1420
1473
  """Validate internal correctness."""
1421
- if ((((len(self.fragment_offset) != len(self.fragment_length))
1422
- or (len(self.fragment_length) != len(self.data_reference))))):
1474
+ if (len(self.fragment_offset) != len(self.fragment_length)) or (
1475
+ len(self.fragment_length) != len(self.data_reference)
1476
+ ):
1423
1477
  msg = (
1424
1478
  f"A FragmentListBox at byte offset {self.offset} has invalid "
1425
1479
  f"parameters. The lengths of the fragment offsets, fragment "
@@ -1440,15 +1494,13 @@ class FragmentListBox(Jp2kBox):
1440
1494
  def __repr__(self):
1441
1495
  msg = "glymur.jp2box.FragmentListBox({0}, {1}, {2})"
1442
1496
  msg = msg.format(
1443
- self.fragment_offset,
1444
- self.fragment_length,
1445
- self.data_reference
1497
+ self.fragment_offset, self.fragment_length, self.data_reference
1446
1498
  )
1447
1499
  return msg
1448
1500
 
1449
1501
  def __str__(self):
1450
1502
  title = Jp2kBox.__str__(self)
1451
- if get_option('print.short') is True:
1503
+ if get_option("print.short") is True:
1452
1504
  return title
1453
1505
 
1454
1506
  lst = []
@@ -1460,9 +1512,9 @@ class FragmentListBox(Jp2kBox):
1460
1512
  text = f"Data Reference {j}: {self.data_reference[j]}"
1461
1513
  lst.append(text)
1462
1514
 
1463
- text = '\n'.join(lst)
1464
- text = textwrap.indent(text, ' ' * 4)
1465
- text = '\n'.join([title, text])
1515
+ text = "\n".join(lst)
1516
+ text = textwrap.indent(text, " " * 4)
1517
+ text = "\n".join([title, text])
1466
1518
  return text
1467
1519
 
1468
1520
  def write(self, fptr):
@@ -1470,14 +1522,14 @@ class FragmentListBox(Jp2kBox):
1470
1522
  self._validate(writing=True)
1471
1523
  num_items = len(self.fragment_offset)
1472
1524
  length = 8 + 2 + num_items * 14
1473
- fptr.write(struct.pack('>I4s', length, b'flst'))
1474
- fptr.write(struct.pack('>H', num_items))
1525
+ fptr.write(struct.pack(">I4s", length, b"flst"))
1526
+ fptr.write(struct.pack(">H", num_items))
1475
1527
  for j in range(num_items):
1476
1528
  write_buffer = struct.pack(
1477
- '>QIH',
1529
+ ">QIH",
1478
1530
  self.fragment_offset[j],
1479
1531
  self.fragment_length[j],
1480
- self.data_reference[j]
1532
+ self.data_reference[j],
1481
1533
  )
1482
1534
  fptr.write(write_buffer)
1483
1535
 
@@ -1501,16 +1553,22 @@ class FragmentListBox(Jp2kBox):
1501
1553
  """
1502
1554
  num_bytes = offset + length - fptr.tell()
1503
1555
  read_buffer = fptr.read(num_bytes)
1504
- num_fragments, = struct.unpack_from('>H', read_buffer, offset=0)
1556
+ (num_fragments,) = struct.unpack_from(">H", read_buffer, offset=0)
1557
+
1558
+ fmt = ">" + "QIH" * num_fragments
1559
+ lst = struct.unpack_from(fmt, read_buffer, offset=2)
1505
1560
 
1506
- lst = struct.unpack_from('>' + 'QIH' * num_fragments,
1507
- read_buffer,
1508
- offset=2)
1509
1561
  frag_offset = lst[0::3]
1510
1562
  frag_len = lst[1::3]
1511
1563
  data_reference = lst[2::3]
1512
- return cls(frag_offset, frag_len, data_reference,
1513
- length=length, offset=offset)
1564
+
1565
+ return cls(
1566
+ frag_offset,
1567
+ frag_len,
1568
+ data_reference,
1569
+ length=length,
1570
+ offset=offset
1571
+ )
1514
1572
 
1515
1573
 
1516
1574
  class FragmentTableBox(Jp2kBox):
@@ -1529,8 +1587,9 @@ class FragmentTableBox(Jp2kBox):
1529
1587
  box : list
1530
1588
  List containing exactly one FragmentListBox
1531
1589
  """
1532
- box_id = 'ftbl'
1533
- longname = 'Fragment Table'
1590
+
1591
+ box_id = "ftbl"
1592
+ longname = "Fragment Table"
1534
1593
 
1535
1594
  def __init__(self, box=None, length=0, offset=-1):
1536
1595
  super().__init__()
@@ -1575,15 +1634,17 @@ class FragmentTableBox(Jp2kBox):
1575
1634
  def _validate(self, writing=False):
1576
1635
  """Self-validate the box before writing."""
1577
1636
  box_ids = [box.box_id for box in self.box]
1578
- if len(box_ids) != 1 or box_ids[0] != 'flst':
1579
- msg = ("Fragment table boxes must have a single fragment list "
1580
- "box as a child box.")
1637
+ if len(box_ids) != 1 or box_ids[0] != "flst":
1638
+ msg = (
1639
+ "Fragment table boxes must have a single fragment list "
1640
+ "box as a child box."
1641
+ )
1581
1642
  self._dispatch_validation_error(msg, writing=writing)
1582
1643
 
1583
1644
  def write(self, fptr):
1584
1645
  """Write a fragment table box to file."""
1585
1646
  self._validate(writing=True)
1586
- self._write_superbox(fptr, b'ftbl')
1647
+ self._write_superbox(fptr, b"ftbl")
1587
1648
 
1588
1649
 
1589
1650
  class FreeBox(Jp2kBox):
@@ -1600,8 +1661,9 @@ class FreeBox(Jp2kBox):
1600
1661
  longname : str
1601
1662
  more verbose description of the box.
1602
1663
  """
1603
- box_id = 'free'
1604
- longname = 'Free'
1664
+
1665
+ box_id = "free"
1666
+ longname = "Free"
1605
1667
 
1606
1668
  def __init__(self, length=0, offset=-1):
1607
1669
  super().__init__()
@@ -1668,13 +1730,22 @@ class ImageHeaderBox(Jp2kBox):
1668
1730
  False if the file does not contain intellectual propery rights
1669
1731
  information.
1670
1732
  """
1671
- box_id = 'ihdr'
1672
- longname = 'Image Header'
1733
+
1734
+ box_id = "ihdr"
1735
+ longname = "Image Header"
1673
1736
 
1674
1737
  def __init__(
1675
- self, height, width, num_components=1, signed=False,
1676
- bits_per_component=8, compression=7, colorspace_unknown=False,
1677
- ip_provided=False, length=0, offset=-1
1738
+ self,
1739
+ height,
1740
+ width,
1741
+ num_components=1,
1742
+ signed=False,
1743
+ bits_per_component=8,
1744
+ compression=7,
1745
+ colorspace_unknown=False,
1746
+ ip_provided=False,
1747
+ length=0,
1748
+ offset=-1,
1678
1749
  ):
1679
1750
  """Examples
1680
1751
  --------
@@ -1707,18 +1778,18 @@ class ImageHeaderBox(Jp2kBox):
1707
1778
 
1708
1779
  def __str__(self):
1709
1780
  title = Jp2kBox.__str__(self)
1710
- if get_option('print.short') is True:
1781
+ if get_option("print.short") is True:
1711
1782
  return title
1712
1783
 
1713
1784
  lst = []
1714
1785
 
1715
- text = f'Size: [{self.height} {self.width} {self.num_components}]'
1786
+ text = f"Size: [{self.height} {self.width} {self.num_components}]"
1716
1787
  lst.append(text)
1717
1788
 
1718
- text = f'Bitdepth: {self.bits_per_component}'
1789
+ text = f"Bitdepth: {self.bits_per_component}"
1719
1790
  lst.append(text)
1720
1791
 
1721
- text = f'Signed: {self.signed}'
1792
+ text = f"Signed: {self.signed}"
1722
1793
  lst.append(text)
1723
1794
 
1724
1795
  text = (
@@ -1727,30 +1798,31 @@ class ImageHeaderBox(Jp2kBox):
1727
1798
  )
1728
1799
  lst.append(text)
1729
1800
 
1730
- text = f'Colorspace Unknown: {self.colorspace_unknown}'
1801
+ text = f"Colorspace Unknown: {self.colorspace_unknown}"
1731
1802
  lst.append(text)
1732
1803
 
1733
- text = '\n'.join(lst)
1734
- text = textwrap.indent(text, ' ' * 4)
1735
- text = '\n'.join([title, text])
1804
+ text = "\n".join(lst)
1805
+ text = textwrap.indent(text, " " * 4)
1806
+ text = "\n".join([title, text])
1736
1807
 
1737
1808
  return text
1738
1809
 
1739
1810
  def write(self, fptr):
1740
1811
  """Write an Image Header box to file."""
1741
- fptr.write(struct.pack('>I4s', 22, b'ihdr'))
1812
+ fptr.write(struct.pack(">I4s", 22, b"ihdr"))
1742
1813
 
1743
1814
  # signedness and bps are stored together in a single byte
1744
1815
  bit_depth_signedness = 0x80 if self.signed else 0x00
1745
1816
  bit_depth_signedness |= self.bits_per_component - 1
1746
1817
  read_buffer = struct.pack(
1747
- '>IIHBBBB',
1748
- self.height, self.width,
1818
+ ">IIHBBBB",
1819
+ self.height,
1820
+ self.width,
1749
1821
  self.num_components,
1750
1822
  bit_depth_signedness,
1751
1823
  self.compression,
1752
1824
  1 if self.colorspace_unknown else 0,
1753
- 1 if self.ip_provided else 0
1825
+ 1 if self.ip_provided else 0,
1754
1826
  )
1755
1827
  fptr.write(read_buffer)
1756
1828
 
@@ -1774,23 +1846,28 @@ class ImageHeaderBox(Jp2kBox):
1774
1846
  """
1775
1847
  # Read the box information
1776
1848
  read_buffer = fptr.read(14)
1777
- params = struct.unpack('>IIHBBBB', read_buffer)
1849
+ params = struct.unpack(">IIHBBBB", read_buffer)
1778
1850
  height = params[0]
1779
1851
  width = params[1]
1780
1852
  num_components = params[2]
1781
- bits_per_component = (params[3] & 0x7f) + 1
1853
+ bits_per_component = (params[3] & 0x7F) + 1
1782
1854
  signed = (params[3] & 0x80) > 1
1783
1855
  compression = params[4]
1784
1856
  colorspace_unknown = True if params[5] else False
1785
1857
  ip_provided = True if params[6] else False
1786
1858
 
1787
- return cls(height, width, num_components=num_components,
1788
- bits_per_component=bits_per_component,
1789
- signed=signed,
1790
- compression=compression,
1791
- colorspace_unknown=colorspace_unknown,
1792
- ip_provided=ip_provided,
1793
- length=length, offset=offset)
1859
+ return cls(
1860
+ height,
1861
+ width,
1862
+ num_components=num_components,
1863
+ bits_per_component=bits_per_component,
1864
+ signed=signed,
1865
+ compression=compression,
1866
+ colorspace_unknown=colorspace_unknown,
1867
+ ip_provided=ip_provided,
1868
+ length=length,
1869
+ offset=offset,
1870
+ )
1794
1871
 
1795
1872
 
1796
1873
  class AssociationBox(Jp2kBox):
@@ -1809,8 +1886,9 @@ class AssociationBox(Jp2kBox):
1809
1886
  box : list
1810
1887
  List of boxes contained in this superbox.
1811
1888
  """
1812
- box_id = 'asoc'
1813
- longname = 'Association'
1889
+
1890
+ box_id = "asoc"
1891
+ longname = "Association"
1814
1892
 
1815
1893
  def __init__(self, box=None, length=0, offset=-1):
1816
1894
  super().__init__()
@@ -1828,17 +1906,17 @@ class AssociationBox(Jp2kBox):
1828
1906
  # Is it a GML JP2 box? If so, add the gdal info.
1829
1907
  if (
1830
1908
  len(self.box) == 2
1831
- and self.box[0].box_id == 'lbl '
1832
- and self.box[0].label == 'gml.data'
1833
- and self.box[1].box_id == 'asoc'
1909
+ and self.box[0].box_id == "lbl "
1910
+ and self.box[0].label == "gml.data"
1911
+ and self.box[1].box_id == "asoc"
1834
1912
  and len(self.box[1].box) == 2
1835
- and self.box[1].box[0].box_id == 'lbl '
1836
- and self.box[1].box[0].label == 'gml.root-instance'
1837
- and self.box[1].box[1].box_id == 'xml '
1913
+ and self.box[1].box[0].box_id == "lbl "
1914
+ and self.box[1].box[0].label == "gml.root-instance"
1915
+ and self.box[1].box[1].box_id == "xml "
1838
1916
  ):
1839
1917
  options = gdal.InfoOptions(showColorTable=False)
1840
1918
  txt = gdal.Info(self._filename, options=options)
1841
- txt = textwrap.indent(txt, ' ' * 4)
1919
+ txt = textwrap.indent(txt, " " * 4)
1842
1920
  msg = f"{msg}\n{txt}"
1843
1921
 
1844
1922
  return msg
@@ -1871,7 +1949,7 @@ class AssociationBox(Jp2kBox):
1871
1949
 
1872
1950
  def write(self, fptr):
1873
1951
  """Write an association box to file."""
1874
- self._write_superbox(fptr, b'asoc')
1952
+ self._write_superbox(fptr, b"asoc")
1875
1953
 
1876
1954
 
1877
1955
  class BitsPerComponentBox(Jp2kBox):
@@ -1892,8 +1970,9 @@ class BitsPerComponentBox(Jp2kBox):
1892
1970
  signed : list
1893
1971
  True if signed, false if not, for each component
1894
1972
  """
1895
- box_id = 'bpcc'
1896
- longname = 'Bits Per Component'
1973
+
1974
+ box_id = "bpcc"
1975
+ longname = "Bits Per Component"
1897
1976
 
1898
1977
  def __init__(self, bpc, signed, length=0, offset=-1):
1899
1978
  super().__init__()
@@ -1908,13 +1987,13 @@ class BitsPerComponentBox(Jp2kBox):
1908
1987
 
1909
1988
  def __str__(self):
1910
1989
  title = Jp2kBox.__str__(self)
1911
- if get_option('print.short') is True:
1990
+ if get_option("print.short") is True:
1912
1991
  return title
1913
1992
 
1914
- body = f'Bits per component: {self.bpc}\nSigned: {self.signed}'
1915
- body = textwrap.indent(body, ' ' * 4)
1993
+ body = f"Bits per component: {self.bpc}\nSigned: {self.signed}"
1994
+ body = textwrap.indent(body, " " * 4)
1916
1995
 
1917
- text = '\n'.join([title, body])
1996
+ text = "\n".join([title, body])
1918
1997
  return text
1919
1998
 
1920
1999
  @classmethod
@@ -1937,7 +2016,7 @@ class BitsPerComponentBox(Jp2kBox):
1937
2016
  """
1938
2017
  nbytes = length - 8
1939
2018
  data = fptr.read(nbytes)
1940
- bpc = tuple(((x & 0x7f) + 1) for x in bytearray(data))
2019
+ bpc = tuple(((x & 0x7F) + 1) for x in bytearray(data))
1941
2020
  signed = tuple(((x & 0x80) > 0) for x in bytearray(data))
1942
2021
 
1943
2022
  return cls(bpc, signed, length=length, offset=offset)
@@ -1959,8 +2038,9 @@ class JP2HeaderBox(Jp2kBox):
1959
2038
  box : list
1960
2039
  List of boxes contained in this superbox.
1961
2040
  """
1962
- box_id = 'jp2h'
1963
- longname = 'JP2 Header'
2041
+
2042
+ box_id = "jp2h"
2043
+ longname = "JP2 Header"
1964
2044
 
1965
2045
  def __init__(self, box=None, length=0, offset=-1):
1966
2046
  super().__init__()
@@ -1978,7 +2058,7 @@ class JP2HeaderBox(Jp2kBox):
1978
2058
 
1979
2059
  def write(self, fptr):
1980
2060
  """Write a JP2 Header box to file."""
1981
- self._write_superbox(fptr, b'jp2h')
2061
+ self._write_superbox(fptr, b"jp2h")
1982
2062
 
1983
2063
  @classmethod
1984
2064
  def parse(cls, fptr, offset, length):
@@ -2023,8 +2103,9 @@ class JPEG2000SignatureBox(Jp2kBox):
2023
2103
  signature : tuple
2024
2104
  Four-byte tuple identifying the file as JPEG 2000.
2025
2105
  """
2026
- box_id = 'jP '
2027
- longname = 'JPEG 2000 Signature'
2106
+
2107
+ box_id = "jP "
2108
+ longname = "JPEG 2000 Signature"
2028
2109
 
2029
2110
  def __init__(self, signature=(13, 10, 135, 10), length=0, offset=-1):
2030
2111
  super().__init__()
@@ -2033,23 +2114,23 @@ class JPEG2000SignatureBox(Jp2kBox):
2033
2114
  self.offset = offset
2034
2115
 
2035
2116
  def __repr__(self):
2036
- return 'glymur.jp2box.JPEG2000SignatureBox()'
2117
+ return "glymur.jp2box.JPEG2000SignatureBox()"
2037
2118
 
2038
2119
  def __str__(self):
2039
2120
  title = Jp2kBox.__str__(self)
2040
- if get_option('print.short') is True:
2121
+ if get_option("print.short") is True:
2041
2122
  return title
2042
2123
 
2043
- body = 'Signature: {0:02x}{1:02x}{2:02x}{3:02x}'
2124
+ body = "Signature: {0:02x}{1:02x}{2:02x}{3:02x}"
2044
2125
  body = body.format(*self.signature)
2045
- body = textwrap.indent(body, ' ' * 4)
2046
- text = '\n'.join([title, body])
2126
+ body = textwrap.indent(body, " " * 4)
2127
+ text = "\n".join([title, body])
2047
2128
  return text
2048
2129
 
2049
2130
  def write(self, fptr):
2050
2131
  """Write a JPEG 2000 Signature box to file."""
2051
- fptr.write(struct.pack('>I4s', 12, b'jP '))
2052
- fptr.write(struct.pack('>BBBB', *self.signature))
2132
+ fptr.write(struct.pack(">I4s", 12, b"jP "))
2133
+ fptr.write(struct.pack(">BBBB", *self.signature))
2053
2134
 
2054
2135
  @classmethod
2055
2136
  def parse(cls, fptr, offset, length):
@@ -2070,7 +2151,7 @@ class JPEG2000SignatureBox(Jp2kBox):
2070
2151
  Instance of the current JPEG2000 signature box.
2071
2152
  """
2072
2153
  read_buffer = fptr.read(4)
2073
- signature = struct.unpack('>BBBB', read_buffer)
2154
+ signature = struct.unpack(">BBBB", read_buffer)
2074
2155
 
2075
2156
  return cls(signature=signature, length=length, offset=offset)
2076
2157
 
@@ -2091,11 +2172,18 @@ class PaletteBox(Jp2kBox):
2091
2172
  palette : ndarray
2092
2173
  Colormap array.
2093
2174
  """
2094
- longname = 'Palette'
2095
- box_id = 'pclr'
2096
2175
 
2097
- def __init__(self, palette, bits_per_component, signed, length=0,
2098
- offset=-1):
2176
+ longname = "Palette"
2177
+ box_id = "pclr"
2178
+
2179
+ def __init__(
2180
+ self,
2181
+ palette,
2182
+ bits_per_component,
2183
+ signed,
2184
+ length=0,
2185
+ offset=-1
2186
+ ):
2099
2187
  super().__init__()
2100
2188
  self.palette = palette
2101
2189
  self.bits_per_component = bits_per_component
@@ -2106,8 +2194,9 @@ class PaletteBox(Jp2kBox):
2106
2194
 
2107
2195
  def _validate(self, writing=False):
2108
2196
  """Verify that the box obeys the specifications."""
2109
- if (((len(self.bits_per_component) != len(self.signed))
2110
- or (len(self.signed) != self.palette.shape[1]))):
2197
+ if (len(self.bits_per_component) != len(self.signed)) or (
2198
+ len(self.signed) != self.palette.shape[1]
2199
+ ):
2111
2200
  msg = (
2112
2201
  "The length of the 'bits_per_component' and the 'signed' "
2113
2202
  "members must equal the number of columns of the palette."
@@ -2131,13 +2220,13 @@ class PaletteBox(Jp2kBox):
2131
2220
 
2132
2221
  def __str__(self):
2133
2222
  title = Jp2kBox.__str__(self)
2134
- if get_option('print.short') is True:
2223
+ if get_option("print.short") is True:
2135
2224
  return title
2136
2225
 
2137
- body = f'Size: ({self.palette.shape[0]} x {self.palette.shape[1]})'
2138
- body = textwrap.indent(body, ' ' * 4)
2226
+ body = f"Size: ({self.palette.shape[0]} x {self.palette.shape[1]})"
2227
+ body = textwrap.indent(body, " " * 4)
2139
2228
 
2140
- text = '\n'.join([title, body])
2229
+ text = "\n".join([title, body])
2141
2230
  return text
2142
2231
 
2143
2232
  def write(self, fptr):
@@ -2153,20 +2242,22 @@ class PaletteBox(Jp2kBox):
2153
2242
  box_length = 8 + 3 + self.palette.shape[1] + bytes_per_palette
2154
2243
 
2155
2244
  # Write the usual (L, T) header.
2156
- write_buffer = struct.pack('>I4s', int(box_length), b'pclr')
2245
+ write_buffer = struct.pack(">I4s", int(box_length), b"pclr")
2157
2246
  fptr.write(write_buffer)
2158
2247
 
2159
2248
  # NE, NPC
2160
2249
  write_buffer = struct.pack(
2161
- '>HB', self.palette.shape[0], self.palette.shape[1]
2250
+ ">HB",
2251
+ self.palette.shape[0],
2252
+ self.palette.shape[1]
2162
2253
  )
2163
2254
  fptr.write(write_buffer)
2164
2255
 
2165
2256
  # Bits Per Sample. Signed components aren't supported.
2166
2257
  bps_signed = [x - 1 for x in self.bits_per_component]
2167
- write_buffer = struct.pack(
2168
- '>' + 'B' * self.palette.shape[1], *bps_signed
2169
- )
2258
+
2259
+ fmt = ">" + "B" * self.palette.shape[1]
2260
+ write_buffer = struct.pack(fmt, *bps_signed)
2170
2261
  fptr.write(write_buffer)
2171
2262
 
2172
2263
  # C(i,j)
@@ -2192,11 +2283,12 @@ class PaletteBox(Jp2kBox):
2192
2283
  """
2193
2284
  num_bytes = offset + length - fptr.tell()
2194
2285
  read_buffer = fptr.read(num_bytes)
2195
- nrows, ncols = struct.unpack_from('>HB', read_buffer, offset=0)
2286
+ nrows, ncols = struct.unpack_from(">HB", read_buffer, offset=0)
2287
+
2288
+ fmt = ">" + "B" * ncols
2289
+ bps_signed = struct.unpack_from(fmt, read_buffer, offset=3)
2196
2290
 
2197
- bps_signed = struct.unpack_from('>' + 'B' * ncols, read_buffer,
2198
- offset=3)
2199
- bps = [((x & 0x7f) + 1) for x in bps_signed]
2291
+ bps = [((x & 0x7F) + 1) for x in bps_signed]
2200
2292
  signed = [((x & 0x80) > 1) for x in bps_signed]
2201
2293
 
2202
2294
  # Are any components signed or differently sized? We don't handle
@@ -2221,93 +2313,116 @@ class PaletteBox(Jp2kBox):
2221
2313
 
2222
2314
  # Map rreq codes to display text.
2223
2315
  _READER_REQUIREMENTS_DISPLAY = {
2224
- 0: 'File not completely understood',
2225
- 1: 'Deprecated - contains no extensions',
2226
- 2: 'Contains multiple composition layers',
2227
- 3: ('Deprecated - codestream is compressed using JPEG 2000 and requires '
2228
- 'at least a Profile 0 decoder as defind in ITU-T Rec. T.800 '
2229
- '| ISO/IEC 15444-1, A.10 Table A.45'),
2230
- 4: 'JPEG 2000 Part 1 Profile 1 codestream',
2231
- 5: ('Unrestricted JPEG 2000 Part 1 codestream, ITU-T Rec. T.800 '
2232
- '| ISO/IEC 15444-1'),
2233
- 6: 'Unrestricted JPEG 2000 Part 2 codestream',
2234
- 7: 'JPEG codestream as defined in ISO/IEC 10918-1',
2235
- 8: 'Deprecated - does not contain opacity',
2236
- 9: 'Non-premultiplied opacity channel',
2237
- 10: 'Premultiplied opacity channel',
2238
- 11: 'Chroma-key based opacity',
2239
- 12: 'Deprecated - codestream is contiguous',
2240
- 13: 'Fragmented codestream where all fragments are in file and in order',
2241
- 14: ('Fragmented codestream where all fragments are in file '
2242
- 'but are out of order'),
2243
- 15: ('Fragmented codestream where not all fragments are within the file '
2244
- 'but are all in locally accessible files'),
2245
- 16: ('Fragmented codestream where some fragments may be accessible '
2246
- 'only through a URL specified network connection'),
2247
- 17: ('Compositing required to produce rendered result from multiple '
2248
- 'compositing layers'),
2249
- 18: 'Deprecated - support for compositing is not required',
2250
- 19: ('Deprecated - contains multiple, discrete layers that should not '
2251
- 'be combined through either animation or compositing'),
2252
- 20: ('Deprecated - compositing layers each contain only a single '
2253
- 'codestream'),
2254
- 21: 'At least one compositing layer consists of multiple codestreams',
2255
- 22: 'Deprecated - all compositing layers are in the same colourspace',
2256
- 23: ('Colourspace transformations are required to combine compositing '
2257
- 'layers; not all compositing layers are in the same colourspace'),
2258
- 24: 'Deprecated - rendered result created without using animation',
2259
- 25: ('Deprecated - animated, but first layer covers entire area and is '
2260
- 'opaque'),
2261
- 26: 'First animation layer does not cover entire rendered result',
2262
- 27: 'Deprecated - animated, and no layer is reused',
2263
- 28: 'Reuse of animation layers',
2264
- 29: 'Deprecated - animated, but layers are reused',
2265
- 30: 'Some animated frames are non-persistent',
2266
- 31: 'Deprecated - rendered result created without using scaling',
2267
- 32: 'Rendered result involves scaling within a layer',
2268
- 33: 'Rendered result involves scaling between layers',
2269
- 34: 'ROI metadata',
2270
- 35: 'IPR metadata',
2271
- 36: 'Content metadata',
2272
- 37: 'History metadata',
2273
- 38: 'Creation metadata',
2274
- 39: 'JPX digital signatures',
2275
- 40: 'JPX checksums',
2276
- 41: 'Desires Graphics Arts Reproduction specified',
2277
- 42: 'Deprecated - compositing layer uses palettized colour',
2278
- 43: 'Deprecated - compositing layer uses restricted ICC profile',
2279
- 44: 'Compositing layer uses Any ICC profile',
2280
- 45: 'Deprecated - compositing layer uses sRGB enumerated colourspace',
2281
- 46: 'Deprecated - compositing layer uses sRGB-grey enumerated colourspace',
2282
- 47: 'BiLevel 1 enumerated colourspace',
2283
- 48: 'BiLevel 2 enumerated colourspace',
2284
- 49: 'YCbCr 1 enumerated colourspace',
2285
- 50: 'YCbCr 2 enumerated colourspace',
2286
- 51: 'YCbCr 3 enumerated colourspace',
2287
- 52: 'PhotoYCC enumerated colourspace',
2288
- 53: 'YCCK enumerated colourspace',
2289
- 54: 'CMY enumerated colourspace',
2290
- 55: 'CMYK enumerated colorspace',
2291
- 56: 'CIELab enumerated colourspace with default parameters',
2292
- 57: 'CIELab enumerated colourspace with non-default parameters',
2293
- 58: 'CIEJab enumerated colourspace with default parameters',
2294
- 59: 'CIEJab enumerated colourspace with non-default parameters',
2295
- 60: 'e-sRGB enumerated colorspace',
2296
- 61: 'ROMM_RGB enumerated colorspace',
2297
- 62: 'Non-square samples',
2298
- 63: 'Deprecated - compositing layers have labels',
2299
- 64: 'Deprecated - codestreams have labels',
2300
- 65: 'Deprecated - compositing layers have different colour spaces',
2301
- 66: 'Deprecated - compositing layers have different metadata',
2302
- 67: 'GIS metadata XML box',
2303
- 68: 'JPSEC extensions in codestream as specified by ISO/IEC 15444-8',
2304
- 69: 'JP3D extensions in codestream as specified by ISO/IEC 15444-10',
2305
- 70: 'Deprecated - compositing layer uses sYCC enumerated colour space',
2306
- 71: 'e-sYCC enumerated colourspace',
2307
- 72: ('JPEG 2000 Part 2 codestream as restricted by baseline conformance '
2308
- 'requirements in M.9.2.3'),
2309
- 73: 'YPbPr(1125/60) enumerated colourspace',
2310
- 74: 'YPbPr(1250/50) enumerated colourspace'}
2316
+ 0: "File not completely understood",
2317
+ 1: "Deprecated - contains no extensions",
2318
+ 2: "Contains multiple composition layers",
2319
+ 3: (
2320
+ "Deprecated - codestream is compressed using JPEG 2000 and requires "
2321
+ "at least a Profile 0 decoder as defind in ITU-T Rec. T.800 "
2322
+ "| ISO/IEC 15444-1, A.10 Table A.45"
2323
+ ),
2324
+ 4: "JPEG 2000 Part 1 Profile 1 codestream",
2325
+ 5: (
2326
+ "Unrestricted JPEG 2000 Part 1 codestream, ITU-T Rec. T.800 "
2327
+ "| ISO/IEC 15444-1"
2328
+ ),
2329
+ 6: "Unrestricted JPEG 2000 Part 2 codestream",
2330
+ 7: "JPEG codestream as defined in ISO/IEC 10918-1",
2331
+ 8: "Deprecated - does not contain opacity",
2332
+ 9: "Non-premultiplied opacity channel",
2333
+ 10: "Premultiplied opacity channel",
2334
+ 11: "Chroma-key based opacity",
2335
+ 12: "Deprecated - codestream is contiguous",
2336
+ 13: "Fragmented codestream where all fragments are in file and in order",
2337
+ 14: (
2338
+ "Fragmented codestream where all fragments are in file "
2339
+ "but are out of order"
2340
+ ),
2341
+ 15: (
2342
+ "Fragmented codestream where not all fragments are within the file "
2343
+ "but are all in locally accessible files"
2344
+ ),
2345
+ 16: (
2346
+ "Fragmented codestream where some fragments may be accessible "
2347
+ "only through a URL specified network connection"
2348
+ ),
2349
+ 17: (
2350
+ "Compositing required to produce rendered result from multiple "
2351
+ "compositing layers"
2352
+ ),
2353
+ 18: "Deprecated - support for compositing is not required",
2354
+ 19: (
2355
+ "Deprecated - contains multiple, discrete layers that should not "
2356
+ "be combined through either animation or compositing"
2357
+ ),
2358
+ 20: (
2359
+ "Deprecated - compositing layers each contain only a single "
2360
+ "codestream"
2361
+ ),
2362
+ 21: "At least one compositing layer consists of multiple codestreams",
2363
+ 22: "Deprecated - all compositing layers are in the same colourspace",
2364
+ 23: (
2365
+ "Colourspace transformations are required to combine compositing "
2366
+ "layers; not all compositing layers are in the same colourspace"
2367
+ ),
2368
+ 24: "Deprecated - rendered result created without using animation",
2369
+ 25: (
2370
+ "Deprecated - animated, but first layer covers entire area and is "
2371
+ "opaque"
2372
+ ),
2373
+ 26: "First animation layer does not cover entire rendered result",
2374
+ 27: "Deprecated - animated, and no layer is reused",
2375
+ 28: "Reuse of animation layers",
2376
+ 29: "Deprecated - animated, but layers are reused",
2377
+ 30: "Some animated frames are non-persistent",
2378
+ 31: "Deprecated - rendered result created without using scaling",
2379
+ 32: "Rendered result involves scaling within a layer",
2380
+ 33: "Rendered result involves scaling between layers",
2381
+ 34: "ROI metadata",
2382
+ 35: "IPR metadata",
2383
+ 36: "Content metadata",
2384
+ 37: "History metadata",
2385
+ 38: "Creation metadata",
2386
+ 39: "JPX digital signatures",
2387
+ 40: "JPX checksums",
2388
+ 41: "Desires Graphics Arts Reproduction specified",
2389
+ 42: "Deprecated - compositing layer uses palettized colour",
2390
+ 43: "Deprecated - compositing layer uses restricted ICC profile",
2391
+ 44: "Compositing layer uses Any ICC profile",
2392
+ 45: "Deprecated - compositing layer uses sRGB enumerated colourspace",
2393
+ 46: "Deprecated - compositing layer uses sRGB-grey enumerated colourspace",
2394
+ 47: "BiLevel 1 enumerated colourspace",
2395
+ 48: "BiLevel 2 enumerated colourspace",
2396
+ 49: "YCbCr 1 enumerated colourspace",
2397
+ 50: "YCbCr 2 enumerated colourspace",
2398
+ 51: "YCbCr 3 enumerated colourspace",
2399
+ 52: "PhotoYCC enumerated colourspace",
2400
+ 53: "YCCK enumerated colourspace",
2401
+ 54: "CMY enumerated colourspace",
2402
+ 55: "CMYK enumerated colorspace",
2403
+ 56: "CIELab enumerated colourspace with default parameters",
2404
+ 57: "CIELab enumerated colourspace with non-default parameters",
2405
+ 58: "CIEJab enumerated colourspace with default parameters",
2406
+ 59: "CIEJab enumerated colourspace with non-default parameters",
2407
+ 60: "e-sRGB enumerated colorspace",
2408
+ 61: "ROMM_RGB enumerated colorspace",
2409
+ 62: "Non-square samples",
2410
+ 63: "Deprecated - compositing layers have labels",
2411
+ 64: "Deprecated - codestreams have labels",
2412
+ 65: "Deprecated - compositing layers have different colour spaces",
2413
+ 66: "Deprecated - compositing layers have different metadata",
2414
+ 67: "GIS metadata XML box",
2415
+ 68: "JPSEC extensions in codestream as specified by ISO/IEC 15444-8",
2416
+ 69: "JP3D extensions in codestream as specified by ISO/IEC 15444-10",
2417
+ 70: "Deprecated - compositing layer uses sYCC enumerated colour space",
2418
+ 71: "e-sYCC enumerated colourspace",
2419
+ 72: (
2420
+ "JPEG 2000 Part 2 codestream as restricted by baseline conformance "
2421
+ "requirements in M.9.2.3"
2422
+ ),
2423
+ 73: "YPbPr(1125/60) enumerated colourspace",
2424
+ 74: "YPbPr(1250/50) enumerated colourspace",
2425
+ }
2311
2426
 
2312
2427
 
2313
2428
  class ReaderRequirementsBox(Jp2kBox):
@@ -2338,12 +2453,20 @@ class ReaderRequirementsBox(Jp2kBox):
2338
2453
  Specifies the compatibility mask for each corresponding vendor
2339
2454
  feature.
2340
2455
  """
2341
- box_id = 'rreq'
2342
- longname = 'Reader Requirements'
2456
+
2457
+ box_id = "rreq"
2458
+ longname = "Reader Requirements"
2343
2459
 
2344
2460
  def __init__(
2345
- self, fuam, dcm, standard_flag, standard_mask, vendor_feature,
2346
- vendor_mask, length=0, offset=-1
2461
+ self,
2462
+ fuam,
2463
+ dcm,
2464
+ standard_flag,
2465
+ standard_mask,
2466
+ vendor_feature,
2467
+ vendor_mask,
2468
+ length=0,
2469
+ offset=-1,
2347
2470
  ):
2348
2471
  super().__init__()
2349
2472
  self.fuam = fuam
@@ -2367,53 +2490,53 @@ class ReaderRequirementsBox(Jp2kBox):
2367
2490
  standard_flag=self.standard_flag,
2368
2491
  standard_mask=self.standard_mask,
2369
2492
  vendor_feature=self.vendor_feature,
2370
- vendor_mask=self.vendor_mask
2493
+ vendor_mask=self.vendor_mask,
2371
2494
  )
2372
2495
  return msg
2373
2496
 
2374
2497
  def __str__(self):
2375
2498
  title = Jp2kBox.__str__(self)
2376
- if get_option('print.short') is True:
2499
+ if get_option("print.short") is True:
2377
2500
  return title
2378
2501
 
2379
2502
  lst = []
2380
2503
 
2381
- text = f'Fully Understands Aspect Mask: 0x{self.fuam:x}'
2504
+ text = f"Fully Understands Aspect Mask: 0x{self.fuam:x}"
2382
2505
  lst.append(text)
2383
2506
 
2384
- text = f'Display Completely Mask: 0x{self.dcm:x}'
2507
+ text = f"Display Completely Mask: 0x{self.dcm:x}"
2385
2508
  lst.append(text)
2386
2509
 
2387
- text = 'Standard Features and Masks:'
2510
+ text = "Standard Features and Masks:"
2388
2511
  lst.append(text)
2389
2512
 
2390
2513
  lst2 = []
2391
- text = 'Feature {flag:03d}: 0x{mask:x} {decoded}'
2514
+ text = "Feature {flag:03d}: 0x{mask:x} {decoded}"
2392
2515
  for j in range(len(self.standard_flag)):
2393
2516
  kwargs = {
2394
- 'flag': self.standard_flag[j],
2395
- 'mask': self.standard_mask[j],
2396
- 'decoded': _READER_REQUIREMENTS_DISPLAY[self.standard_flag[j]],
2517
+ "flag": self.standard_flag[j],
2518
+ "mask": self.standard_mask[j],
2519
+ "decoded": _READER_REQUIREMENTS_DISPLAY[self.standard_flag[j]],
2397
2520
  }
2398
2521
  lst2.append(text.format(**kwargs))
2399
- text = '\n'.join(lst2)
2400
- text = textwrap.indent(text, ' ' * 4)
2522
+ text = "\n".join(lst2)
2523
+ text = textwrap.indent(text, " " * 4)
2401
2524
  lst.append(text)
2402
2525
 
2403
- text = 'Vendor Features:'
2526
+ text = "Vendor Features:"
2404
2527
  lst.append(text)
2405
2528
 
2406
2529
  lst2 = []
2407
2530
  for j in range(len(self.vendor_feature)):
2408
- text = f'UUID {self.vendor_feature[j]}'
2531
+ text = f"UUID {self.vendor_feature[j]}"
2409
2532
  lst2.append(text)
2410
- text = '\n'.join(lst2)
2411
- text = textwrap.indent(text, ' ' * 4)
2533
+ text = "\n".join(lst2)
2534
+ text = textwrap.indent(text, " " * 4)
2412
2535
  lst.append(text)
2413
2536
 
2414
- text = '\n'.join(lst)
2415
- text = textwrap.indent(text, ' ' * 4)
2416
- text = '\n'.join([title, text])
2537
+ text = "\n".join(lst)
2538
+ text = textwrap.indent(text, " " * 4)
2539
+ text = "\n".join([title, text])
2417
2540
 
2418
2541
  return text
2419
2542
 
@@ -2437,7 +2560,7 @@ class ReaderRequirementsBox(Jp2kBox):
2437
2560
  """
2438
2561
  num_bytes = offset + length - fptr.tell()
2439
2562
  read_buffer = fptr.read(num_bytes)
2440
- mask_length, = struct.unpack_from('>B', read_buffer, offset=0)
2563
+ (mask_length,) = struct.unpack_from(">B", read_buffer, offset=0)
2441
2564
 
2442
2565
  # Fully Understands Aspect Mask
2443
2566
  # Decodes Completely Mask
@@ -2447,31 +2570,45 @@ class ReaderRequirementsBox(Jp2kBox):
2447
2570
  # The mask length tells us the format string to use when unpacking
2448
2571
  # from the buffer read from file.
2449
2572
  try:
2450
- mask_format = {1: 'B', 2: 'H', 4: 'I', 8: 'Q'}[mask_length]
2451
- fuam, dcm = struct.unpack_from('>' + mask_format * 2, read_buffer,
2452
- offset=1)
2573
+ mask_format = {1: "B", 2: "H", 4: "I", 8: "Q"}[mask_length]
2574
+ fuam, dcm = struct.unpack_from(
2575
+ ">" + mask_format * 2,
2576
+ read_buffer,
2577
+ offset=1
2578
+ )
2453
2579
  std_flg_offset = 1 + 2 * mask_length
2454
- data = _parse_standard_flag(read_buffer[std_flg_offset:],
2455
- mask_length)
2580
+ data = _parse_standard_flag(
2581
+ read_buffer[std_flg_offset:],
2582
+ mask_length
2583
+ )
2456
2584
  standard_flag, standard_mask = data
2457
2585
 
2458
2586
  nflags = len(standard_flag)
2459
2587
  vndr_offset = 1 + 2 * mask_length + 2 + (2 + mask_length) * nflags
2460
- data = _parse_vendor_features(read_buffer[vndr_offset:],
2461
- mask_length)
2588
+ data = _parse_vendor_features(
2589
+ read_buffer[vndr_offset:],
2590
+ mask_length
2591
+ )
2462
2592
  vendor_feature, vendor_mask = data
2463
2593
 
2464
2594
  except KeyError:
2465
2595
  msg = (
2466
- f'The ReaderRequirements box (rreq) has a mask length of '
2467
- f'{mask_length} bytes, but only values of 1, 2, 4, or 8 are '
2468
- f'supported. The box contents will not be interpreted.'
2596
+ f"The ReaderRequirements box (rreq) has a mask length of "
2597
+ f"{mask_length} bytes, but only values of 1, 2, 4, or 8 are "
2598
+ f"supported. The box contents will not be interpreted."
2469
2599
  )
2470
2600
  warnings.warn(msg, UserWarning)
2471
2601
 
2472
- return cls(fuam, dcm, standard_flag, standard_mask,
2473
- vendor_feature, vendor_mask,
2474
- length=length, offset=offset)
2602
+ return cls(
2603
+ fuam,
2604
+ dcm,
2605
+ standard_flag,
2606
+ standard_mask,
2607
+ vendor_feature,
2608
+ vendor_mask,
2609
+ length=length,
2610
+ offset=offset,
2611
+ )
2475
2612
 
2476
2613
 
2477
2614
  def _parse_standard_flag(read_buffer, mask_length):
@@ -2488,14 +2625,14 @@ def _parse_standard_flag(read_buffer, mask_length):
2488
2625
  """
2489
2626
  # The mask length tells us the format string to use when unpacking
2490
2627
  # from the buffer read from file.
2491
- mask_format = {1: 'B', 2: 'H', 4: 'I'}[mask_length]
2628
+ mask_format = {1: "B", 2: "H", 4: "I"}[mask_length]
2492
2629
 
2493
- num_standard_flags, = struct.unpack_from('>H', read_buffer, offset=0)
2630
+ (num_standard_flags,) = struct.unpack_from(">H", read_buffer, offset=0)
2494
2631
 
2495
2632
  # Read in standard flags and standard masks. Each standard flag should
2496
2633
  # be two bytes, but the standard mask flag is as long as specified by
2497
2634
  # the mask length.
2498
- fmt = '>' + ('H' + mask_format) * num_standard_flags
2635
+ fmt = ">" + ("H" + mask_format) * num_standard_flags
2499
2636
  data = struct.unpack_from(fmt, read_buffer, offset=2)
2500
2637
 
2501
2638
  standard_flag = data[0:num_standard_flags * 2:2]
@@ -2518,9 +2655,9 @@ def _parse_vendor_features(read_buffer, mask_length):
2518
2655
  """
2519
2656
  # The mask length tells us the format string to use when unpacking
2520
2657
  # from the buffer read from file.
2521
- mask_format = {1: 'B', 2: 'H', 4: 'I'}[mask_length]
2658
+ mask_format = {1: "B", 2: "H", 4: "I"}[mask_length]
2522
2659
 
2523
- num_vendor_features, = struct.unpack_from('>H', read_buffer)
2660
+ (num_vendor_features,) = struct.unpack_from(">H", read_buffer)
2524
2661
 
2525
2662
  # Each vendor feature consists of a 16-byte UUID plus a mask whose
2526
2663
  # length is specified by, you guessed it, "mask_length".
@@ -2532,7 +2669,7 @@ def _parse_vendor_features(read_buffer, mask_length):
2532
2669
  ubuffer = read_buffer[uslice]
2533
2670
  vendor_feature.append(UUID(bytes=ubuffer[0:16]))
2534
2671
 
2535
- vmask = struct.unpack('>' + mask_format, ubuffer[16:])
2672
+ vmask = struct.unpack(">" + mask_format, ubuffer[16:])
2536
2673
  vendor_mask.append(vmask)
2537
2674
 
2538
2675
  return vendor_feature, vendor_mask
@@ -2554,8 +2691,9 @@ class ResolutionBox(Jp2kBox):
2554
2691
  box : list
2555
2692
  List of boxes contained in this superbox.
2556
2693
  """
2557
- box_id = 'res '
2558
- longname = 'Resolution'
2694
+
2695
+ box_id = "res "
2696
+ longname = "Resolution"
2559
2697
 
2560
2698
  def __init__(self, box=None, length=0, offset=-1):
2561
2699
  super().__init__()
@@ -2573,7 +2711,7 @@ class ResolutionBox(Jp2kBox):
2573
2711
 
2574
2712
  def write(self, fptr):
2575
2713
  """Write a Resolution super box to file."""
2576
- self._write_superbox(fptr, b'res ')
2714
+ self._write_superbox(fptr, b"res ")
2577
2715
 
2578
2716
  @classmethod
2579
2717
  def parse(cls, fptr, offset, length):
@@ -2618,11 +2756,16 @@ class CaptureResolutionBox(Jp2kBox):
2618
2756
  vertical_resolution, horizontal_resolution : float
2619
2757
  Vertical, horizontal resolution.
2620
2758
  """
2621
- box_id = 'resc'
2622
- longname = 'Capture Resolution'
2759
+
2760
+ box_id = "resc"
2761
+ longname = "Capture Resolution"
2623
2762
 
2624
2763
  def __init__(
2625
- self, vertical_resolution, horizontal_resolution, length=0, offset=-1
2764
+ self,
2765
+ vertical_resolution,
2766
+ horizontal_resolution,
2767
+ length=0,
2768
+ offset=-1
2626
2769
  ):
2627
2770
  super().__init__()
2628
2771
  self.vertical_resolution = vertical_resolution
@@ -2639,19 +2782,19 @@ class CaptureResolutionBox(Jp2kBox):
2639
2782
 
2640
2783
  def __str__(self):
2641
2784
  title = Jp2kBox.__str__(self)
2642
- if get_option('print.short') is True:
2785
+ if get_option("print.short") is True:
2643
2786
  return title
2644
2787
 
2645
2788
  lst = []
2646
- text = f'VCR: {self.vertical_resolution}'
2789
+ text = f"VCR: {self.vertical_resolution}"
2647
2790
  lst.append(text)
2648
- text = f'HCR: {self.horizontal_resolution}'
2791
+ text = f"HCR: {self.horizontal_resolution}"
2649
2792
  lst.append(text)
2650
2793
 
2651
- text = '\n'.join(lst)
2652
- text = textwrap.indent(text, ' ' * 4)
2794
+ text = "\n".join(lst)
2795
+ text = textwrap.indent(text, " " * 4)
2653
2796
 
2654
- text = '\n'.join([title, text])
2797
+ text = "\n".join([title, text])
2655
2798
  return text
2656
2799
 
2657
2800
  @classmethod
@@ -2673,9 +2816,9 @@ class CaptureResolutionBox(Jp2kBox):
2673
2816
  Instance of the current capture resolution box.
2674
2817
  """
2675
2818
  read_buffer = fptr.read(10)
2676
- (rn1, rd1, rn2, rd2, re1, re2) = struct.unpack('>HHHHbb', read_buffer)
2677
- vres = rn1 / rd1 * 10 ** re1
2678
- hres = rn2 / rd2 * 10 ** re2
2819
+ (rn1, rd1, rn2, rd2, re1, re2) = struct.unpack(">HHHHbb", read_buffer)
2820
+ vres = rn1 / rd1 * 10**re1
2821
+ hres = rn2 / rd2 * 10**re2
2679
2822
 
2680
2823
  return cls(vres, hres, length=length, offset=offset)
2681
2824
 
@@ -2685,12 +2828,12 @@ class CaptureResolutionBox(Jp2kBox):
2685
2828
  # 4 bytes for length, 4 for the ID, always 10 bytes for the payload
2686
2829
  length = 18
2687
2830
 
2688
- fptr.write(struct.pack('>I4s', length, b'resc'))
2831
+ fptr.write(struct.pack(">I4s", length, b"resc"))
2689
2832
 
2690
2833
  re1, rn1, rd1 = decompose_resolution(self.vertical_resolution)
2691
2834
  re2, rn2, rd2 = decompose_resolution(self.horizontal_resolution)
2692
2835
 
2693
- buffer = struct.pack('>HHHHbb', rn1, rd1, rn2, rd2, re1, re2)
2836
+ buffer = struct.pack(">HHHHbb", rn1, rd1, rn2, rd2, re1, re2)
2694
2837
  fptr.write(buffer)
2695
2838
 
2696
2839
 
@@ -2710,11 +2853,16 @@ class DisplayResolutionBox(Jp2kBox):
2710
2853
  vertical_resolution, horizontal_resolution : float
2711
2854
  Vertical, horizontal resolution.
2712
2855
  """
2713
- box_id = 'resd'
2714
- longname = 'Display Resolution'
2856
+
2857
+ box_id = "resd"
2858
+ longname = "Display Resolution"
2715
2859
 
2716
2860
  def __init__(
2717
- self, vertical_resolution, horizontal_resolution, length=0, offset=-1
2861
+ self,
2862
+ vertical_resolution,
2863
+ horizontal_resolution,
2864
+ length=0,
2865
+ offset=-1
2718
2866
  ):
2719
2867
  super().__init__()
2720
2868
  self.vertical_resolution = vertical_resolution
@@ -2729,19 +2877,19 @@ class DisplayResolutionBox(Jp2kBox):
2729
2877
 
2730
2878
  def __str__(self):
2731
2879
  title = Jp2kBox.__str__(self)
2732
- if get_option('print.short') is True:
2880
+ if get_option("print.short") is True:
2733
2881
  return title
2734
2882
 
2735
2883
  lst = []
2736
- text = f'VDR: {self.vertical_resolution}'
2884
+ text = f"VDR: {self.vertical_resolution}"
2737
2885
  lst.append(text)
2738
- text = f'HDR: {self.horizontal_resolution}'
2886
+ text = f"HDR: {self.horizontal_resolution}"
2739
2887
  lst.append(text)
2740
2888
 
2741
- text = '\n'.join(lst)
2742
- text = textwrap.indent(text, ' ' * 4)
2889
+ text = "\n".join(lst)
2890
+ text = textwrap.indent(text, " " * 4)
2743
2891
 
2744
- text = '\n'.join([title, text])
2892
+ text = "\n".join([title, text])
2745
2893
  return text
2746
2894
 
2747
2895
  @classmethod
@@ -2764,9 +2912,9 @@ class DisplayResolutionBox(Jp2kBox):
2764
2912
  """
2765
2913
 
2766
2914
  read_buffer = fptr.read(10)
2767
- (rn1, rd1, rn2, rd2, re1, re2) = struct.unpack('>HHHHbb', read_buffer)
2768
- vres = rn1 / rd1 * 10 ** re1
2769
- hres = rn2 / rd2 * 10 ** re2
2915
+ (rn1, rd1, rn2, rd2, re1, re2) = struct.unpack(">HHHHbb", read_buffer)
2916
+ vres = rn1 / rd1 * 10**re1
2917
+ hres = rn2 / rd2 * 10**re2
2770
2918
 
2771
2919
  return cls(vres, hres, length=length, offset=offset)
2772
2920
 
@@ -2776,12 +2924,12 @@ class DisplayResolutionBox(Jp2kBox):
2776
2924
  # 4 bytes for length, 4 for the ID, always 10 bytes for the payload
2777
2925
  length = 18
2778
2926
 
2779
- fptr.write(struct.pack('>I4s', length, b'resd'))
2927
+ fptr.write(struct.pack(">I4s", length, b"resd"))
2780
2928
 
2781
2929
  re1, rn1, rd1 = decompose_resolution(self.vertical_resolution)
2782
2930
  re2, rn2, rd2 = decompose_resolution(self.horizontal_resolution)
2783
2931
 
2784
- buffer = struct.pack('>HHHHbb', rn1, rd1, rn2, rd2, re1, re2)
2932
+ buffer = struct.pack(">HHHHbb", rn1, rd1, rn2, rd2, re1, re2)
2785
2933
  fptr.write(buffer)
2786
2934
 
2787
2935
 
@@ -2801,8 +2949,9 @@ class LabelBox(Jp2kBox):
2801
2949
  label : str
2802
2950
  Textual label.
2803
2951
  """
2804
- box_id = 'lbl '
2805
- longname = 'Label'
2952
+
2953
+ box_id = "lbl "
2954
+ longname = "Label"
2806
2955
 
2807
2956
  def __init__(self, label, length=0, offset=-1):
2808
2957
  super().__init__()
@@ -2812,13 +2961,13 @@ class LabelBox(Jp2kBox):
2812
2961
 
2813
2962
  def __str__(self):
2814
2963
  title = Jp2kBox.__str__(self)
2815
- if get_option('print.short') is True:
2964
+ if get_option("print.short") is True:
2816
2965
  return title
2817
2966
 
2818
- text = f'Label: {self.label}'
2819
- text = textwrap.indent(text, ' ' * 4)
2967
+ text = f"Label: {self.label}"
2968
+ text = textwrap.indent(text, " " * 4)
2820
2969
 
2821
- text = '\n'.join([title, text])
2970
+ text = "\n".join([title, text])
2822
2971
  return text
2823
2972
 
2824
2973
  def __repr__(self):
@@ -2828,7 +2977,7 @@ class LabelBox(Jp2kBox):
2828
2977
  def write(self, fptr):
2829
2978
  """Write a Label box to file."""
2830
2979
  length = 8 + len(self.label.encode())
2831
- fptr.write(struct.pack('>I4s', length, b'lbl '))
2980
+ fptr.write(struct.pack(">I4s", length, b"lbl "))
2832
2981
  fptr.write(self.label.encode())
2833
2982
 
2834
2983
  @classmethod
@@ -2851,7 +3000,7 @@ class LabelBox(Jp2kBox):
2851
3000
  """
2852
3001
  num_bytes = offset + length - fptr.tell()
2853
3002
  read_buffer = fptr.read(num_bytes)
2854
- label = read_buffer.decode('utf-8')
3003
+ label = read_buffer.decode("utf-8")
2855
3004
  return cls(label, length=length, offset=offset)
2856
3005
 
2857
3006
 
@@ -2872,8 +3021,9 @@ class NumberListBox(Jp2kBox):
2872
3021
  Descriptors of an entity with which the data contained within the same
2873
3022
  Association box is associated.
2874
3023
  """
2875
- box_id = 'nlst'
2876
- longname = 'Number List'
3024
+
3025
+ box_id = "nlst"
3026
+ longname = "Number List"
2877
3027
 
2878
3028
  def __init__(self, associations, length=0, offset=-1):
2879
3029
  super().__init__()
@@ -2883,32 +3033,32 @@ class NumberListBox(Jp2kBox):
2883
3033
 
2884
3034
  def __str__(self):
2885
3035
  title = Jp2kBox.__str__(self)
2886
- if get_option('print.short') is True:
3036
+ if get_option("print.short") is True:
2887
3037
  return title
2888
3038
 
2889
3039
  lst = []
2890
3040
  for j, association in enumerate(self.associations):
2891
- text = f'Association[{j}]: '
3041
+ text = f"Association[{j}]: "
2892
3042
  if association == 0:
2893
- text += 'the rendered result'
3043
+ text += "the rendered result"
2894
3044
  elif (association >> 24) == 1:
2895
3045
  idx = association & 0x00FFFFFF
2896
- text += f'codestream {idx}'
3046
+ text += f"codestream {idx}"
2897
3047
  elif (association >> 24) == 2:
2898
3048
  idx = association & 0x00FFFFFF
2899
- text += f'compositing layer {idx}'
3049
+ text += f"compositing layer {idx}"
2900
3050
  else:
2901
- text += 'unrecognized'
3051
+ text += "unrecognized"
2902
3052
  lst.append(text)
2903
3053
 
2904
- body = '\n'.join(lst)
2905
- body = textwrap.indent(body, ' ' * 4)
3054
+ body = "\n".join(lst)
3055
+ body = textwrap.indent(body, " " * 4)
2906
3056
 
2907
- text = '\n'.join([title, body])
3057
+ text = "\n".join([title, body])
2908
3058
  return text
2909
3059
 
2910
3060
  def __repr__(self):
2911
- msg = f'glymur.jp2box.NumberListBox(associations={self.associations})'
3061
+ msg = f"glymur.jp2box.NumberListBox(associations={self.associations})"
2912
3062
  return msg
2913
3063
 
2914
3064
  @classmethod
@@ -2932,15 +3082,14 @@ class NumberListBox(Jp2kBox):
2932
3082
  num_bytes = offset + length - fptr.tell()
2933
3083
  raw_data = fptr.read(num_bytes)
2934
3084
  num_associations = int(len(raw_data) / 4)
2935
- lst = struct.unpack('>' + 'I' * num_associations, raw_data)
3085
+ lst = struct.unpack(">" + "I" * num_associations, raw_data)
2936
3086
  return cls(lst, length=length, offset=offset)
2937
3087
 
2938
3088
  def write(self, fptr):
2939
3089
  """Write a NumberList box to file."""
2940
- fptr.write(struct.pack('>I4s',
2941
- len(self.associations) * 4 + 8, b'nlst'))
3090
+ fptr.write(struct.pack(">I4s", len(self.associations) * 4 + 8, b"nlst"))
2942
3091
 
2943
- fmt = '>' + 'I' * len(self.associations)
3092
+ fmt = ">" + "I" * len(self.associations)
2944
3093
  write_buffer = struct.pack(fmt, *self.associations)
2945
3094
  fptr.write(write_buffer)
2946
3095
 
@@ -2961,8 +3110,9 @@ class XMLBox(Jp2kBox):
2961
3110
  xml : ElementTree object
2962
3111
  XML section.
2963
3112
  """
2964
- box_id = 'xml '
2965
- longname = 'XML'
3113
+
3114
+ box_id = "xml "
3115
+ longname = "XML"
2966
3116
 
2967
3117
  def __init__(self, xml=None, filename=None, length=0, offset=-1):
2968
3118
  """Parameters
@@ -2992,26 +3142,28 @@ class XMLBox(Jp2kBox):
2992
3142
 
2993
3143
  def __str__(self):
2994
3144
  title = Jp2kBox.__str__(self)
2995
- if get_option('print.short') is True:
3145
+ if get_option("print.short") is True:
2996
3146
  return title
2997
- if get_option('print.xml') is False:
3147
+ if get_option("print.xml") is False:
2998
3148
  return title
2999
3149
 
3000
3150
  if self.xml is not None:
3001
- body = ET.tostring(self.xml,
3002
- encoding='utf-8',
3003
- pretty_print=True).decode('utf-8').rstrip()
3151
+ body = (
3152
+ ET.tostring(self.xml, encoding="utf-8", pretty_print=True)
3153
+ .decode("utf-8")
3154
+ .rstrip()
3155
+ )
3004
3156
  else:
3005
- body = 'None'
3006
- body = textwrap.indent(body, ' ' * 4)
3157
+ body = "None"
3158
+ body = textwrap.indent(body, " " * 4)
3007
3159
 
3008
- text = '\n'.join([title, body])
3160
+ text = "\n".join([title, body])
3009
3161
  return text
3010
3162
 
3011
3163
  def write(self, fptr):
3012
3164
  """Write an XML box to file."""
3013
- read_buffer = ET.tostring(self.xml.getroot(), encoding='utf-8')
3014
- fptr.write(struct.pack('>I4s', len(read_buffer) + 8, b'xml '))
3165
+ read_buffer = ET.tostring(self.xml.getroot(), encoding="utf-8")
3166
+ fptr.write(struct.pack(">I4s", len(read_buffer) + 8, b"xml "))
3015
3167
  fptr.write(read_buffer)
3016
3168
 
3017
3169
  @classmethod
@@ -3036,48 +3188,48 @@ class XMLBox(Jp2kBox):
3036
3188
  read_buffer = fptr.read(num_bytes)
3037
3189
 
3038
3190
  try:
3039
- text = read_buffer.decode('utf-8')
3191
+ text = read_buffer.decode("utf-8")
3040
3192
  except UnicodeDecodeError as err:
3041
3193
  # Possibly bad string of bytes to begin with.
3042
3194
  # Try to search for <?xml and go from there.
3043
- decl_start = read_buffer.find(b'<?xml')
3195
+ decl_start = read_buffer.find(b"<?xml")
3044
3196
  if decl_start <= -1:
3045
3197
  # Nope, that's not it. All is lost.
3046
3198
  msg = (
3047
- f'A problem was encountered while parsing an XML box:'
3048
- f'\n\n\t'
3199
+ f"A problem was encountered while parsing an XML box:"
3200
+ f"\n\n\t"
3049
3201
  f'"{str(err)}"'
3050
- f'\n\n'
3051
- f'No XML was retrieved.'
3202
+ f"\n\n"
3203
+ f"No XML was retrieved."
3052
3204
  )
3053
3205
  warnings.warn(msg, UserWarning)
3054
3206
  return XMLBox(xml=None, length=length, offset=offset)
3055
3207
 
3056
- text = read_buffer[decl_start:].decode('utf-8')
3208
+ text = read_buffer[decl_start:].decode("utf-8")
3057
3209
 
3058
3210
  # Let the user know that the XML box was problematic.
3059
3211
  msg = (
3060
- f'A UnicodeDecodeError was encountered parsing an XML box '
3061
- f'at byte position {offset:d} ({err.reason}), but the XML was '
3062
- f'still recovered.'
3212
+ f"A UnicodeDecodeError was encountered parsing an XML box "
3213
+ f"at byte position {offset:d} ({err.reason}), but the XML was "
3214
+ f"still recovered."
3063
3215
  )
3064
3216
  warnings.warn(msg, UserWarning)
3065
3217
 
3066
3218
  # Strip out any trailing nulls, as they can foul up XML parsing.
3067
3219
  text = text.rstrip(chr(0))
3068
- f = io.BytesIO(text.encode('utf-8'))
3220
+ f = io.BytesIO(text.encode("utf-8"))
3069
3221
 
3070
3222
  try:
3071
3223
  xml = ET.parse(f)
3072
3224
  except ET.ParseError as err:
3073
3225
  exc_type, _, _ = sys.exc_info()
3074
3226
  msg = (
3075
- f'{exc_type.__name__} encountered while parsing an XML '
3076
- f'box at byte offset {offset:d}:'
3077
- f'\n\n\t'
3227
+ f"{exc_type.__name__} encountered while parsing an XML "
3228
+ f"box at byte offset {offset:d}:"
3229
+ f"\n\n\t"
3078
3230
  f'"{err}"'
3079
- f'\n\n'
3080
- f'No XML was retrieved.'
3231
+ f"\n\n"
3232
+ f"No XML was retrieved."
3081
3233
  )
3082
3234
  warnings.warn(msg, UserWarning)
3083
3235
  xml = None
@@ -3101,8 +3253,9 @@ class UUIDListBox(Jp2kBox):
3101
3253
  ulst : list
3102
3254
  List of UUIDs.
3103
3255
  """
3104
- box_id = 'ulst'
3105
- longname = 'UUID List'
3256
+
3257
+ box_id = "ulst"
3258
+ longname = "UUID List"
3106
3259
 
3107
3260
  def __init__(self, ulst, length=0, offset=-1):
3108
3261
  super().__init__()
@@ -3116,24 +3269,24 @@ class UUIDListBox(Jp2kBox):
3116
3269
 
3117
3270
  def __str__(self):
3118
3271
  title = Jp2kBox.__str__(self)
3119
- if get_option('print.short') is True:
3272
+ if get_option("print.short") is True:
3120
3273
  return title
3121
3274
 
3122
3275
  lst = []
3123
3276
  for j, uuid_item in enumerate(self.ulst):
3124
- text = f'UUID[{j}]: {uuid_item}'
3277
+ text = f"UUID[{j}]: {uuid_item}"
3125
3278
  lst.append(text)
3126
- body = '\n'.join(lst)
3127
- body = textwrap.indent(body, ' ' * 4)
3279
+ body = "\n".join(lst)
3280
+ body = textwrap.indent(body, " " * 4)
3128
3281
 
3129
- text = '\n'.join([title, body])
3282
+ text = "\n".join([title, body])
3130
3283
  return text
3131
3284
 
3132
3285
  def write(self, fptr):
3133
3286
  """Write a UUID list box to file."""
3134
3287
  num_uuids = len(self.ulst)
3135
3288
  length = 4 + 4 + 2 + num_uuids * 16
3136
- write_buffer = struct.pack('>I4sH', length, b'ulst', num_uuids)
3289
+ write_buffer = struct.pack(">I4sH", length, b"ulst", num_uuids)
3137
3290
  fptr.write(write_buffer)
3138
3291
 
3139
3292
  for j in range(num_uuids):
@@ -3160,7 +3313,7 @@ class UUIDListBox(Jp2kBox):
3160
3313
  num_bytes = offset + length - fptr.tell()
3161
3314
  read_buffer = fptr.read(num_bytes)
3162
3315
 
3163
- num_uuids, = struct.unpack_from('>H', read_buffer)
3316
+ (num_uuids,) = struct.unpack_from(">H", read_buffer)
3164
3317
 
3165
3318
  ulst = []
3166
3319
  for j in range(num_uuids):
@@ -3186,8 +3339,9 @@ class UUIDInfoBox(Jp2kBox):
3186
3339
  box : list
3187
3340
  List of boxes contained in this superbox.
3188
3341
  """
3189
- box_id = 'uinf'
3190
- longname = 'UUIDInfo'
3342
+
3343
+ box_id = "uinf"
3344
+ longname = "UUIDInfo"
3191
3345
 
3192
3346
  def __init__(self, box=None, length=0, offset=-1):
3193
3347
  super().__init__()
@@ -3205,7 +3359,7 @@ class UUIDInfoBox(Jp2kBox):
3205
3359
 
3206
3360
  def write(self, fptr):
3207
3361
  """Write a UUIDInfo box to file."""
3208
- self._write_superbox(fptr, b'uinf')
3362
+ self._write_superbox(fptr, b"uinf")
3209
3363
 
3210
3364
  @classmethod
3211
3365
  def parse(cls, fptr, offset, length):
@@ -3255,8 +3409,9 @@ class DataEntryURLBox(Jp2kBox):
3255
3409
  URL : str
3256
3410
  Associated URL.
3257
3411
  """
3258
- box_id = 'url '
3259
- longname = 'Data Entry URL'
3412
+
3413
+ box_id = "url "
3414
+ longname = "Data Entry URL"
3260
3415
 
3261
3416
  def __init__(self, version, flag, url, length=0, offset=-1):
3262
3417
  super().__init__()
@@ -3276,10 +3431,13 @@ class DataEntryURLBox(Jp2kBox):
3276
3431
 
3277
3432
  length = 8 + 1 + 3 + len(url)
3278
3433
  write_buffer = struct.pack(
3279
- '>I4sBBBB',
3280
- length, b'url ',
3434
+ ">I4sBBBB",
3435
+ length,
3436
+ b"url ",
3281
3437
  self.version,
3282
- self.flag[0], self.flag[1], self.flag[2]
3438
+ self.flag[0],
3439
+ self.flag[1],
3440
+ self.flag[2],
3283
3441
  )
3284
3442
  fptr.write(write_buffer)
3285
3443
  fptr.write(url)
@@ -3291,17 +3449,17 @@ class DataEntryURLBox(Jp2kBox):
3291
3449
 
3292
3450
  def __str__(self):
3293
3451
  title = Jp2kBox.__str__(self)
3294
- if get_option('print.short') is True:
3452
+ if get_option("print.short") is True:
3295
3453
  return title
3296
3454
 
3297
3455
  body = (
3298
- f'Version: {self.version}\n'
3299
- f'Flag: {self.flag[0]} {self.flag[1]} {self.flag[2]}\n'
3456
+ f"Version: {self.version}\n"
3457
+ f"Flag: {self.flag[0]} {self.flag[1]} {self.flag[2]}\n"
3300
3458
  f'URL: "{self.url}"'
3301
3459
  )
3302
- body = textwrap.indent(body, ' ' * 4)
3460
+ body = textwrap.indent(body, " " * 4)
3303
3461
 
3304
- text = '\n'.join([title, body])
3462
+ text = "\n".join([title, body])
3305
3463
  return text
3306
3464
 
3307
3465
  @classmethod
@@ -3324,11 +3482,11 @@ class DataEntryURLBox(Jp2kBox):
3324
3482
  """
3325
3483
  num_bytes = offset + length - fptr.tell()
3326
3484
  read_buffer = fptr.read(num_bytes)
3327
- data = struct.unpack_from('>BBBB', read_buffer)
3485
+ data = struct.unpack_from(">BBBB", read_buffer)
3328
3486
  version = data[0]
3329
3487
  flag = data[1:4]
3330
3488
 
3331
- url = read_buffer[4:].decode('utf-8').rstrip(chr(0))
3489
+ url = read_buffer[4:].decode("utf-8").rstrip(chr(0))
3332
3490
  return cls(version, flag, url, length=length, offset=offset)
3333
3491
 
3334
3492
 
@@ -3350,10 +3508,11 @@ class UnknownBox(Jp2kBox):
3350
3508
  longname : str
3351
3509
  more verbose description of the box.
3352
3510
  """
3353
- def __init__(self, claimed_box_id, length=0, offset=-1, longname=''):
3511
+
3512
+ def __init__(self, claimed_box_id, length=0, offset=-1, longname=""):
3354
3513
  super().__init__()
3355
3514
  self.longname = longname
3356
- self.box_id = 'xxxx'
3515
+ self.box_id = "xxxx"
3357
3516
  self.claimed_box_id = claimed_box_id
3358
3517
  self.length = length
3359
3518
  self.offset = offset
@@ -3364,8 +3523,8 @@ class UnknownBox(Jp2kBox):
3364
3523
 
3365
3524
  def __str__(self):
3366
3525
  title = Jp2kBox.__str__(self)
3367
- body = f' Claimed ID: {self.claimed_box_id}'
3368
- text = '\n'.join([title, body])
3526
+ body = f" Claimed ID: {self.claimed_box_id}"
3527
+ text = "\n".join([title, body])
3369
3528
  return text
3370
3529
 
3371
3530
 
@@ -3397,8 +3556,9 @@ class UUIDBox(Jp2kBox):
3397
3556
  16684-1:2012 - Graphic technology -- Extensible metadata platform (XMP)
3398
3557
  specification -- Part 1: Data model, serialization and core properties
3399
3558
  """
3400
- box_id = 'uuid'
3401
- longname = 'UUID'
3559
+
3560
+ box_id = "uuid"
3561
+ longname = "UUID"
3402
3562
 
3403
3563
  def __init__(self, the_uuid, raw_data, length=0, offset=-1):
3404
3564
  """Parameters
@@ -3436,29 +3596,29 @@ class UUIDBox(Jp2kBox):
3436
3596
  """Private function for parsing UUID payloads if possible."""
3437
3597
  if self.uuid == _XMP_UUID:
3438
3598
 
3439
- txt = self.raw_data.decode('utf-8')
3599
+ txt = self.raw_data.decode("utf-8")
3440
3600
 
3441
3601
  # If XMP comes from a TIFF tag, then it should be terminated
3442
3602
  # by a null byte. libxml2 now requires that null byte to be
3443
3603
  # stripped off before being fed into lxml.
3444
- elt = ET.fromstring(txt.strip('\x00'))
3604
+ elt = ET.fromstring(txt.strip("\x00"))
3445
3605
  self.data = ET.ElementTree(elt)
3446
3606
 
3447
3607
  elif self.uuid == _GEOTIFF_UUID:
3448
3608
  self.data = tiff_header(self.raw_data)
3449
3609
  elif self.uuid == _EXIF_UUID:
3450
- if self.raw_data[0:4].decode('utf-8').lower() == 'exif':
3610
+ if self.raw_data[0:4].decode("utf-8").lower() == "exif":
3451
3611
  # Cut off 'EXIF\0\0' part.
3452
3612
  payload = self.raw_data[6:]
3453
3613
  self.data = tiff_header(payload)
3454
- elif self.raw_data[:2].decode('utf-8').lower() in ['ii', 'mm']:
3614
+ elif self.raw_data[:2].decode("utf-8").lower() in ["ii", "mm"]:
3455
3615
  # Missing the exif lead-in, take the data as-is.
3456
3616
  payload = self.raw_data
3457
3617
  self.data = tiff_header(payload)
3458
3618
  else:
3459
3619
  msg = (
3460
- 'A UUID that identified itself as an EXIF UUID could not '
3461
- 'be parsed.'
3620
+ "A UUID that identified itself as an EXIF UUID could not "
3621
+ "be parsed."
3462
3622
  )
3463
3623
  warnings.warn(msg)
3464
3624
  self.data = None
@@ -3466,36 +3626,34 @@ class UUIDBox(Jp2kBox):
3466
3626
  self.data = self.raw_data
3467
3627
 
3468
3628
  def __repr__(self):
3469
- msg = (
3470
- "glymur.jp2box.UUIDBox({0}, raw_data=<byte array {1} elements>)"
3471
- )
3629
+ msg = "glymur.jp2box.UUIDBox({0}, raw_data=<byte array {1} elements>)"
3472
3630
  return msg.format(repr(self.uuid), len(self.raw_data))
3473
3631
 
3474
3632
  def __str__(self):
3475
3633
  title = Jp2kBox.__str__(self)
3476
- if get_option('print.short') is True:
3634
+ if get_option("print.short") is True:
3477
3635
  return title
3478
3636
 
3479
- text = f'UUID: {self.uuid}'
3637
+ text = f"UUID: {self.uuid}"
3480
3638
  if self.uuid == _XMP_UUID:
3481
- text += ' (XMP)'
3639
+ text += " (XMP)"
3482
3640
  elif self.uuid == _GEOTIFF_UUID:
3483
- text += ' (GeoTIFF)'
3641
+ text += " (GeoTIFF)"
3484
3642
  elif self.uuid == _EXIF_UUID:
3485
- text += ' (EXIF)'
3643
+ text += " (EXIF)"
3486
3644
  else:
3487
- text += ' (unknown)'
3645
+ text += " (unknown)"
3488
3646
 
3489
3647
  lst = [text]
3490
3648
 
3491
- if not get_option('print.xml') and self.uuid == _XMP_UUID:
3649
+ if not get_option("print.xml") and self.uuid == _XMP_UUID:
3492
3650
  # If it's an XMP UUID, don't print the XML contents.
3493
3651
  pass
3494
3652
 
3495
3653
  elif self.uuid == _XMP_UUID:
3496
- b = ET.tostring(self.data, encoding='utf-8', pretty_print=True)
3497
- s = b.decode('utf-8').strip()
3498
- text = f'UUID Data:\n{s}'
3654
+ b = ET.tostring(self.data, encoding="utf-8", pretty_print=True)
3655
+ s = b.decode("utf-8").strip()
3656
+ text = f"UUID Data:\n{s}"
3499
3657
  lst.append(text)
3500
3658
  elif self.uuid == _EXIF_UUID:
3501
3659
  s = io.StringIO()
@@ -3503,35 +3661,35 @@ class UUIDBox(Jp2kBox):
3503
3661
  if self.data is None:
3504
3662
  # If the UUID was malformed, just say so and go on. This
3505
3663
  # should not be a showstopper.
3506
- text = 'UUID Data: Invalid Exif UUID'
3664
+ text = "UUID Data: Invalid Exif UUID"
3507
3665
  lst.append(text)
3508
3666
  else:
3509
3667
  with np.printoptions(threshold=4):
3510
3668
  pprint.pprint(self.data, stream=s, indent=4)
3511
- text = f'UUID Data: {s.getvalue().rstrip()}'
3669
+ text = f"UUID Data: {s.getvalue().rstrip()}"
3512
3670
  lst.append(text)
3513
3671
  elif self.uuid == _GEOTIFF_UUID:
3514
3672
 
3515
3673
  options = gdal.InfoOptions(showColorTable=False)
3516
3674
  txt = gdal.Info(self._fptr.name, options=options)
3517
- txt = textwrap.indent(txt, ' ' * 4).rstrip()
3675
+ txt = textwrap.indent(txt, " " * 4).rstrip()
3518
3676
 
3519
- txt = f'UUID Data:\n{txt}'
3677
+ txt = f"UUID Data:\n{txt}"
3520
3678
  lst.append(txt)
3521
3679
  else:
3522
- text = f'UUID Data: {len(self.raw_data)} bytes'
3680
+ text = f"UUID Data: {len(self.raw_data)} bytes"
3523
3681
  lst.append(text)
3524
3682
 
3525
- body = '\n'.join(lst)
3526
- body = textwrap.indent(body, ' ' * 4)
3683
+ body = "\n".join(lst)
3684
+ body = textwrap.indent(body, " " * 4)
3527
3685
 
3528
- text = '\n'.join([title, body])
3686
+ text = "\n".join([title, body])
3529
3687
  return text
3530
3688
 
3531
3689
  def write(self, fptr):
3532
3690
  """Write a UUID box to file."""
3533
3691
  length = 4 + 4 + 16 + len(self.raw_data)
3534
- write_buffer = struct.pack('>I4s', length, b'uuid')
3692
+ write_buffer = struct.pack(">I4s", length, b"uuid")
3535
3693
  fptr.write(write_buffer)
3536
3694
  fptr.write(self.uuid.bytes)
3537
3695
  fptr.write(self.raw_data)
@@ -3565,35 +3723,36 @@ class UUIDBox(Jp2kBox):
3565
3723
 
3566
3724
  # Map each box ID to the corresponding class.
3567
3725
  _BOX_WITH_ID = {
3568
- b'asoc': AssociationBox,
3569
- b'bpcc': BitsPerComponentBox,
3570
- b'cdef': ChannelDefinitionBox,
3571
- b'cgrp': ColourGroupBox,
3572
- b'cmap': ComponentMappingBox,
3573
- b'colr': ColourSpecificationBox,
3574
- b'dtbl': DataReferenceBox,
3575
- b'ftyp': FileTypeBox,
3576
- b'ihdr': ImageHeaderBox,
3577
- b'jP ': JPEG2000SignatureBox,
3578
- b'jpch': CodestreamHeaderBox,
3579
- b'jplh': CompositingLayerHeaderBox,
3580
- b'jp2c': ContiguousCodestreamBox,
3581
- b'free': FreeBox,
3582
- b'flst': FragmentListBox,
3583
- b'ftbl': FragmentTableBox,
3584
- b'jp2h': JP2HeaderBox,
3585
- b'lbl ': LabelBox,
3586
- b'nlst': NumberListBox,
3587
- b'pclr': PaletteBox,
3588
- b'res ': ResolutionBox,
3589
- b'resc': CaptureResolutionBox,
3590
- b'resd': DisplayResolutionBox,
3591
- b'rreq': ReaderRequirementsBox,
3592
- b'uinf': UUIDInfoBox,
3593
- b'ulst': UUIDListBox,
3594
- b'url ': DataEntryURLBox,
3595
- b'uuid': UUIDBox,
3596
- b'xml ': XMLBox}
3726
+ b"asoc": AssociationBox,
3727
+ b"bpcc": BitsPerComponentBox,
3728
+ b"cdef": ChannelDefinitionBox,
3729
+ b"cgrp": ColourGroupBox,
3730
+ b"cmap": ComponentMappingBox,
3731
+ b"colr": ColourSpecificationBox,
3732
+ b"dtbl": DataReferenceBox,
3733
+ b"ftyp": FileTypeBox,
3734
+ b"ihdr": ImageHeaderBox,
3735
+ b"jP ": JPEG2000SignatureBox,
3736
+ b"jpch": CodestreamHeaderBox,
3737
+ b"jplh": CompositingLayerHeaderBox,
3738
+ b"jp2c": ContiguousCodestreamBox,
3739
+ b"free": FreeBox,
3740
+ b"flst": FragmentListBox,
3741
+ b"ftbl": FragmentTableBox,
3742
+ b"jp2h": JP2HeaderBox,
3743
+ b"lbl ": LabelBox,
3744
+ b"nlst": NumberListBox,
3745
+ b"pclr": PaletteBox,
3746
+ b"res ": ResolutionBox,
3747
+ b"resc": CaptureResolutionBox,
3748
+ b"resd": DisplayResolutionBox,
3749
+ b"rreq": ReaderRequirementsBox,
3750
+ b"uinf": UUIDInfoBox,
3751
+ b"ulst": UUIDListBox,
3752
+ b"url ": DataEntryURLBox,
3753
+ b"uuid": UUIDBox,
3754
+ b"xml ": XMLBox,
3755
+ }
3597
3756
 
3598
3757
 
3599
3758
  def decompose_resolution(value: Number) -> Tuple[int, int, int]: