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