diff --git a/.travis.yml b/.travis.yml
index 2f45864..c055dce 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,8 +11,8 @@ before_install:
# command to install dependencies
install:
- - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --use-mirrors contextlib2 mock; fi
- - if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install --use-mirrors numpy; fi
+ - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --use-mirrors lxml contextlib2 mock; fi
+ - if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install --use-mirrors lxml numpy; fi
# command to run tests
script:
diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst
index ad4e40b..bbba98e 100644
--- a/docs/source/changelog.rst
+++ b/docs/source/changelog.rst
@@ -5,6 +5,7 @@ ChangeLog
0.6.0 (pending)
===============
+ * Added lxml requirement.
* added set_printoptions, get_printoptions function
* dropped support for Python 2.6, added support for Python 3.4
* dropped windows support
diff --git a/docs/source/detailed_installation.rst b/docs/source/detailed_installation.rst
index d1b3084..cfbb168 100644
--- a/docs/source/detailed_installation.rst
+++ b/docs/source/detailed_installation.rst
@@ -68,8 +68,8 @@ For python 3.3, you should install the following set of ports:
* python33
* py33-numpy
+ * py33-lxml
* py33-distribute
- * py33-matplotlib (optional, for running certain tests)
* py33-Pillow (optional, for running certain tests)
MacPorts supplies both OpenJPEG 1.5.0 and OpenJPEG 2.0.0.
@@ -85,6 +85,7 @@ packages may also need to be installed. Consult your package manager
documentation or use pip.
* setuptools
+ * python-lxml
* matplotlib
* pillow
* contextlib2 (2.7 only)
diff --git a/docs/source/how_do_i.rst b/docs/source/how_do_i.rst
index fa8f098..4bde854 100644
--- a/docs/source/how_do_i.rst
+++ b/docs/source/how_do_i.rst
@@ -131,7 +131,6 @@ From within Python, it is as simple as printing the Jp2k object, i.e. ::
-
Contiguous Codestream Box (jp2c) @ (3223, 1132296)
Main header:
SOC marker segment @ (3231, 0)
@@ -228,7 +227,6 @@ The :py:meth:`append` method can add an XML box as shown below::
>>> import shutil
>>> import glymur
>>> shutil.copyfile(glymur.data.nemo(), 'myfile.jp2')
- >>> from xml.etree import cElementTree as ET
>>> jp2 = glymur.Jp2k('myfile.jp2')
>>> xmlbox = glymur.jp2box.XMLBox(filename='data.xml')
>>> jp2.append(xmlbox)
@@ -408,12 +406,12 @@ The example JP2 file shipped with glymur has an XMP UUID. ::
.
-Since the UUID data in this case is returned as an ElementTree instance,
-one can use ElementTree from the standard library to access the data.
-For example, to extract the **CreatorTool** attribute value, one could do the
+Since the UUID data in this case is returned as an lxml ElementTree
+instance, one can use lxml to access the data. For example, to
+extract the **CreatorTool** attribute value, one could do the
following
- >>> xmp = j.box[3].data.packet
+ >>> xmp = j.box[3].data
>>> rdf = '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}'
>>> ns2 = '{http://ns.adobe.com/xap/1.0/}'
>>> name = '{0}RDF/{0}Description/{1}CreatorTool'.format(rdf, ns2)
diff --git a/glymur/_uuid_io.py b/glymur/_uuid_io.py
index ed610e5..399fa99 100644
--- a/glymur/_uuid_io.py
+++ b/glymur/_uuid_io.py
@@ -7,7 +7,8 @@ import re
import struct
import sys
import warnings
-import xml.etree.cElementTree as ET
+
+import lxml.etree as ET
if sys.hexversion < 0x02070000:
# pylint: disable=F0401,E0611
diff --git a/glymur/core.py b/glymur/core.py
index 620d0e3..4e8f950 100644
--- a/glymur/core.py
+++ b/glymur/core.py
@@ -1,7 +1,7 @@
"""Core definitions to be shared amongst the modules.
"""
import copy
-import xml.etree.cElementTree as ET
+import lxml.etree as ET
# Progression order
LRCP = 0
@@ -76,45 +76,3 @@ _CAPABILITIES_DISPLAY = {
1: '0',
2: '1',
3: '3'}
-
-
-def _pretty_print_xml(xml, level=0):
- """Pretty print XML data.
- """
- xml = copy.deepcopy(xml)
- _indent(xml.getroot(), level=level)
- xmltext = ET.tostring(xml.getroot(), encoding='utf-8').decode('utf-8')
-
- # Indent it a bit.
- lst = [(' ' + x) for x in xmltext.split('\n')]
- try:
- xml = '\n'.join(lst)
- return '\n{0}'.format(xml)
- except UnicodeEncodeError:
- # This can happen on python 2.x if the character set contains certain
- # non-ascii characters. Just print out the corresponding xml char
- # entities instead.
- xml = u'\n'.join(lst)
- text = u'\n{0}'.format(xml)
- text = text.encode('ascii', 'xmlcharrefreplace')
- return text
-
-
-def _indent(elem, level=0):
- """Recipe for pretty printing XML. Please see
-
- http://effbot.org/zone/element-lib.htm#prettyprint
- """
- i = "\n" + level * " "
- if len(elem):
- if not elem.text or not elem.text.strip():
- elem.text = i + " "
- if not elem.tail or not elem.tail.strip():
- elem.tail = i
- for elem in elem:
- _indent(elem, level + 1)
- if not elem.tail or not elem.tail.strip():
- elem.tail = i
- else:
- if level and (not elem.tail or not elem.tail.strip()):
- elem.tail = i
diff --git a/glymur/jp2box.py b/glymur/jp2box.py
index 3cf637d..5a0febf 100644
--- a/glymur/jp2box.py
+++ b/glymur/jp2box.py
@@ -20,10 +20,11 @@ import os
import pprint
import struct
import sys
+import textwrap
import uuid
import warnings
-import xml.etree.cElementTree as ET
+import lxml.etree as ET
import numpy as np
from .codestream import Codestream
@@ -32,7 +33,6 @@ from .core import _COLOR_TYPE_MAP_DISPLAY
from .core import SRGB, GREYSCALE, YCC
from .core import ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE
from .core import ANY_ICC_PROFILE, VENDOR_COLOR_METHOD
-from .core import _pretty_print_xml
from . import _uuid_io
@@ -97,10 +97,8 @@ class Jp2kBox(object):
msg = Jp2kBox.__str__(self)
for box in self.box:
boxstr = str(box)
-
- # Add indentation.
- strs = [('\n ' + x) for x in boxstr.split('\n')]
- msg += ''.join(strs)
+ # Indent the child boxes to make the association clear.
+ msg += '\n' + textwrap.indent(boxstr, ' ')
return msg
@@ -890,10 +888,7 @@ class ContiguousCodestreamBox(Jp2kBox):
msg += '\n Main header:'
for segment in self.main_header.segment:
- segstr = str(segment)
- # Add indentation.
- strs = [('\n ' + x) for x in segstr.split('\n')]
- msg += ''.join(strs)
+ msg += '\n' + textwrap.indent(str(segment), ' ')
return msg
@@ -2563,11 +2558,14 @@ class XMLBox(Jp2kBox):
if _printoptions['xml'] == False:
return msg
- xml = self.xml
+ msg += '\n'
if self.xml is not None:
- msg += _pretty_print_xml(self.xml)
+ xmlstring = ET.tostring(self.xml,
+ encoding='utf-8',
+ pretty_print=True).decode('utf-8')
else:
- msg += '\n {0}'.format(xml)
+ xmlstring = 'None'
+ msg += textwrap.indent(xmlstring, ' ')
return msg
def write(self, fptr):
@@ -2971,9 +2969,12 @@ class UUIDBox(Jp2kBox):
return msg
if self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'):
- line = '\n UUID Data: {0}'
- xmlstring = _pretty_print_xml(self.data)
- xmlstring = xmlstring.rstrip()
+ line = '\n UUID Data:\n{0}'
+ xmlstring = ET.tostring(self.data,
+ encoding='utf-8',
+ pretty_print=True).decode('utf-8')
+ # indent it a bit
+ xmlstring = textwrap.indent(xmlstring.rstrip(), ' ')
msg += line.format(xmlstring)
elif self.uuid.bytes == b'JpgTiffExif->JP2':
msg += '\n UUID Data: {0}'.format(str(self.data))
diff --git a/glymur/test/fixtures.py b/glymur/test/fixtures.py
index a7906c5..fe0777e 100644
--- a/glymur/test/fixtures.py
+++ b/glymur/test/fixtures.py
@@ -4,6 +4,7 @@ Test fixtures common to more than one test point.
import os
import re
import sys
+import textwrap
import warnings
import numpy as np
@@ -181,92 +182,100 @@ def read_pgx_header(pgx_file):
header = header.rstrip()
return header, pos
+nemo_xmp = """
+
+
+
+ Google
+ 2013-02-09T14:47:53
+
+
+ 1
+ 72/1
+ 72/1
+ 2
+ HTC
+ HTC Glacier
+ 2592
+ 1456
+
+
+ 8
+ 8
+ 8
+
+
+ 2
+ 3
+
+
+ 1343036288/4294967295
+ 1413044224/4294967295
+
+
+
+
+ 2748779008/4294967295
+ 1417339264/4294967295
+ 1288490240/4294967295
+ 2576980480/4294967295
+ 644245120/4294967295
+ 257698032/4294967295
+
+
+
+
+ 1
+ 2528
+ 1424
+ 353/100
+ 0
+ 0/1
+ WGS-84
+ 2013-02-09T14:47:53
+
+
+ 76
+
+
+ 0220
+ 0100
+
+
+ 1
+ 2
+ 3
+ 0
+
+
+ 42,20.56N
+ 71,5.29W
+ 2013-02-09T19:47:53Z
+ NETWORK
+
+
+ 2013-02-09T14:47:53
+
+
+
+
+ Glymur
+ Python XMP Toolkit
+
+
+
+
+
+"""
nemo_xmp_box = """UUID Box (uuid) @ (77, 3146)
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
- UUID Data:
-
-
-
- Google
- 2013-02-09T14:47:53
-
-
- 1
- 72/1
- 72/1
- 2
- HTC
- HTC Glacier
- 2592
- 1456
-
-
- 8
- 8
- 8
-
-
- 2
- 3
-
-
- 1343036288/4294967295
- 1413044224/4294967295
-
-
-
-
- 2748779008/4294967295
- 1417339264/4294967295
- 1288490240/4294967295
- 2576980480/4294967295
- 644245120/4294967295
- 257698032/4294967295
-
-
-
-
- 1
- 2528
- 1424
- 353/100
- 0
- 0/1
- WGS-84
- 2013-02-09T14:47:53
-
-
- 76
-
-
- 0220
- 0100
-
-
- 1
- 2
- 3
- 0
-
-
- 42,20.56N
- 71,5.29W
- 2013-02-09T19:47:53Z
- NETWORK
-
-
- 2013-02-09T14:47:53
-
-
-
-
- Glymur
- Python XMP Toolkit
-
-
-
-
- """
+ UUID Data:
+{0}""".format(textwrap.indent(nemo_xmp, ' '))
+
+nemo_xmp_box = """UUID Box (uuid) @ (77, 3146)
+ UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
+ UUID Data:
+{0}""".format(textwrap.indent(nemo_xmp, ' '))
SimpleRDF = """
-
-
- Google
- 2013-02-09T14:47:53
-
-
- 1
- 72/1
- 72/1
- 2
- HTC
- HTC Glacier
- 2592
- 1456
-
-
- 8
- 8
- 8
-
-
- 2
- 3
-
-
- 1343036288/4294967295
- 1413044224/4294967295
-
-
-
-
- 2748779008/4294967295
- 1417339264/4294967295
- 1288490240/4294967295
- 2576980480/4294967295
- 644245120/4294967295
- 257698032/4294967295
-
-
-
-
- 1
- 2528
- 1424
- 353/100
- 0
- 0/1
- WGS-84
- 2013-02-09T14:47:53
-
-
- 76
-
-
- 0220
- 0100
-
-
- 1
- 2
- 3
- 0
-
-
- 42,20.56N
- 71,5.29W
- 2013-02-09T19:47:53Z
- NETWORK
-
-
- 2013-02-09T14:47:53
-
-
-
-
- Glymur
- Python XMP Toolkit
-
-
-
-
-
+ UUID Data:
+{0}
Contiguous Codestream Box (jp2c) @ (3223, 1132296)
Main header:
SOC marker segment @ (3231, 0)
@@ -492,6 +419,7 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296)
Step size: [(0, 8), (0, 9), (0, 9), (0, 10)]
CME marker segment @ (3305, 37)
"Created by OpenJPEG version 2.0.0"'''
+nemo_dump_full = dump.format(textwrap.indent(nemo_xmp, ' '))
nemo_dump_short = r"""JPEG 2000 Signature Box (jP ) @ (0, 12)
File Type Box (ftyp) @ (12, 20)
@@ -557,7 +485,7 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296)
CME marker segment @ (3305, 37)
"Created by OpenJPEG version 2.0.0"'''
-nemo_dump_no_codestream = r"""JPEG 2000 Signature Box (jP ) @ (0, 12)
+dump = r"""JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a
File Type Box (ftyp) @ (12, 20)
Brand: jp2
@@ -575,91 +503,10 @@ JP2 Header Box (jp2h) @ (32, 45)
Colorspace: sRGB
UUID Box (uuid) @ (77, 3146)
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
- UUID Data:
-
-
-
- Google
- 2013-02-09T14:47:53
-
-
- 1
- 72/1
- 72/1
- 2
- HTC
- HTC Glacier
- 2592
- 1456
-
-
- 8
- 8
- 8
-
-
- 2
- 3
-
-
- 1343036288/4294967295
- 1413044224/4294967295
-
-
-
-
- 2748779008/4294967295
- 1417339264/4294967295
- 1288490240/4294967295
- 2576980480/4294967295
- 644245120/4294967295
- 257698032/4294967295
-
-
-
-
- 1
- 2528
- 1424
- 353/100
- 0
- 0/1
- WGS-84
- 2013-02-09T14:47:53
-
-
- 76
-
-
- 0220
- 0100
-
-
- 1
- 2
- 3
- 0
-
-
- 42,20.56N
- 71,5.29W
- 2013-02-09T19:47:53Z
- NETWORK
-
-
- 2013-02-09T14:47:53
-
-
-
-
- Glymur
- Python XMP Toolkit
-
-
-
-
-
+ UUID Data:
+{0}
Contiguous Codestream Box (jp2c) @ (3223, 1132296)"""
+nemo_dump_no_codestream = dump.format(textwrap.indent(nemo_xmp, ' '))
nemo_dump_no_codestream_no_xml = r"""JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a
@@ -691,10 +538,10 @@ file7_rreq = r"""Reader Requirements Box (rreq) @ (44, 24)
Feature 043: 0x40 Deprecated - compositing layer uses restricted ICC profile
Vendor Features:"""
-file1_xml = r"""XML Box (xml ) @ (36, 439)
-
-
- 2001-11-01T13:45:00.000-06:00
- Professional 120 Image
-
- """
+file1_xml = """XML Box (xml ) @ (36, 439)
+
+ \t
+ \t\t2001-11-01T13:45:00.000-06:00
+ \t\tProfessional 120 Image
+ \t
+ """
diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py
index 5861971..5dedbcc 100644
--- a/glymur/test/test_jp2box.py
+++ b/glymur/test/test_jp2box.py
@@ -22,10 +22,10 @@ import sys
import tempfile
import uuid
from uuid import UUID
-import xml.etree.cElementTree as ET
import unittest
import warnings
+import lxml.etree as ET
import numpy as np
import glymur
@@ -1056,7 +1056,7 @@ class TestRepr(unittest.TestCase):
box = glymur.jp2box.XMLBox(xml=tree)
regexp = r"""glymur.jp2box.XMLBox"""
- regexp += r"""\(xml=<(xml.etree.ElementTree.){0,1}ElementTree object """
+ regexp += r"""\(xml=\)"""
if sys.hexversion < 0x03000000:
diff --git a/glymur/test/test_jp2box_jpx.py b/glymur/test/test_jp2box_jpx.py
index 7493dea..f8e9db8 100644
--- a/glymur/test/test_jp2box_jpx.py
+++ b/glymur/test/test_jp2box_jpx.py
@@ -8,7 +8,7 @@ import struct
import sys
import tempfile
import unittest
-import xml.etree.cElementTree as ET
+import lxml.etree as ET
import glymur
from glymur import Jp2k
diff --git a/glymur/test/test_jp2box_uuid.py b/glymur/test/test_jp2box_uuid.py
index 82dc253..a904174 100644
--- a/glymur/test/test_jp2box_uuid.py
+++ b/glymur/test/test_jp2box_uuid.py
@@ -18,7 +18,6 @@ import sys
import tempfile
import uuid
import warnings
-import xml.etree
if sys.hexversion < 0x02070000:
import unittest2 as unittest
@@ -35,6 +34,8 @@ if sys.hexversion <= 0x03030000:
else:
from unittest.mock import patch
+import lxml.etree
+
from .fixtures import HAS_PYTHON_XMP_TOOLKIT, OPJ_DATA_ROOT
if HAS_PYTHON_XMP_TOOLKIT:
from libxmp import XMPMeta
@@ -71,7 +72,7 @@ class TestUUIDXMP(unittest.TestCase):
# The data should be an XMP packet, which gets interpreted as
# an ElementTree.
self.assertTrue(isinstance(jp2.box[-1].data,
- xml.etree.ElementTree.ElementTree))
+ lxml.etree._ElementTree))
class TestUUIDExif(unittest.TestCase):
"""Tests for UUIDs of Exif type."""
diff --git a/glymur/test/test_jp2box_xml.py b/glymur/test/test_jp2box_xml.py
index 4950da4..facc4df 100644
--- a/glymur/test/test_jp2box_xml.py
+++ b/glymur/test/test_jp2box_xml.py
@@ -20,7 +20,6 @@ import sys
import tempfile
import unittest
import warnings
-import xml.etree.cElementTree as ET
if sys.hexversion < 0x03000000:
from StringIO import StringIO
@@ -32,6 +31,8 @@ if sys.hexversion <= 0x03030000:
else:
from unittest.mock import patch
+import lxml.etree as ET
+
import glymur
from glymur import Jp2k
from glymur.jp2box import ColourSpecificationBox, ContiguousCodestreamBox
diff --git a/glymur/test/test_printing.py b/glymur/test/test_printing.py
index 132d1ec..882f57c 100644
--- a/glymur/test/test_printing.py
+++ b/glymur/test/test_printing.py
@@ -15,7 +15,6 @@ import struct
import sys
import tempfile
import warnings
-from xml.etree import cElementTree as ET
import unittest
if sys.hexversion < 0x03000000:
@@ -28,10 +27,12 @@ if sys.hexversion <= 0x03030000:
else:
from unittest.mock import patch
+import lxml.etree as ET
+
import glymur
from glymur import Jp2k
from . import fixtures
-from .fixtures import OPJ_DATA_ROOT, opj_data_file, nemo_xmp_box
+from .fixtures import OPJ_DATA_ROOT, opj_data_file
from .fixtures import text_gbr_27, text_gbr_33, text_gbr_34
@@ -567,7 +568,7 @@ class TestPrinting(unittest.TestCase):
print(j.box[3])
actual = fake_out.getvalue().strip()
- expected = nemo_xmp_box
+ expected = fixtures.nemo_xmp_box
self.assertEqual(actual, expected)
def test_codestream(self):
@@ -639,8 +640,7 @@ class TestPrinting(unittest.TestCase):
#
# 2.7.5 (fedora 19) prints xml entities.
# 2.7.3 seems to want to print hex escapes.
- text = u"""
- Strömung"""
+ text = u"""Strömung"""
if sys.hexversion < 0x03000000:
xml = ET.parse(StringIO(text.encode('utf-8')))
else:
@@ -668,8 +668,7 @@ class TestPrinting(unittest.TestCase):
#
# 2.7.5 (fedora 19) prints xml entities.
# 2.7.3 seems to want to print hex escapes.
- text = u"""
- Россия"""
+ text = u"""Россия"""
if sys.hexversion < 0x03000000:
xml = ET.parse(StringIO(text.encode('utf-8')))
else:
diff --git a/setup.py b/setup.py
index b1332d8..5b91590 100644
--- a/setup.py
+++ b/setup.py
@@ -16,7 +16,7 @@ kwargs = {'name': 'Glymur',
'license': 'MIT',
'test_suite': 'glymur.test'}
-instllrqrs = ['numpy>=1.4.1']
+instllrqrs = ['numpy>=1.4.1', 'lxml>=2.3.2']
if sys.hexversion < 0x03030000:
instllrqrs.append('contextlib2>=0.4')
instllrqrs.append('mock>=1.0.1')