Skip to content

Commit

Permalink
Merge pull request #40 from mpenkov/dev
Browse files Browse the repository at this point in the history
Thanks.
  • Loading branch information
hMatoba committed Sep 10, 2017
2 parents 96c3f9c + af8fc2a commit b124dc9
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 11 deletions.
69 changes: 63 additions & 6 deletions piexif/helper.py
@@ -1,8 +1,65 @@
#
# Names of encodings that we publicly support.
#
ASCII = 'ascii'
JIS = 'jis'
UNICODE = 'unicode'
ENCODINGS = (ASCII, JIS, UNICODE)

# return "str"
def dump_user_comment(string):
return None
#
# The actual encodings accepted by the standard library differ slightly from
# the above.
#
_JIS = 'shift_jis'
_UNICODE = 'utf_16_be'

# return "bytes"
def load_user_comment(binary):
return None
_PREFIX_SIZE = 8
#
# From Table 9: Character Codes and their Designation
#
_ASCII_PREFIX = b'\x41\x53\x43\x49\x49\x00\x00\x00'
_JIS_PREFIX = b'\x4a\x49\x53\x00\x00\x00\x00\x00'
_UNICODE_PREFIX = b'\x55\x4e\x49\x43\x4f\x44\x45\x00'
_UNDEFINED_PREFIX = b'\x00\x00\x00\x00\x00\x00\x00\x00'


def load_user_comment(data):
"""
Convert "UserComment" value in exif format to str.
:param bytes data: "UserComment" value from exif
:return: u"foobar"
:rtype: str(Unicode)
:raises: ValueError if the data does not conform to the EXIF specification,
or the encoding is unsupported.
"""
if len(data) < _PREFIX_SIZE:
raise ValueError('not enough data to decode UserComment')
prefix = data[:_PREFIX_SIZE]
body = data[_PREFIX_SIZE:]
if prefix == _UNDEFINED_PREFIX:
raise ValueError('prefix is UNDEFINED, unable to decode UserComment')
try:
encoding = {
_ASCII_PREFIX: ASCII, _JIS_PREFIX: _JIS, _UNICODE_PREFIX: _UNICODE,
}[prefix]
except KeyError:
raise ValueError('unable to determine appropriate encoding')
return body.decode(encoding, errors='replace')


def dump_user_comment(data, encoding="ascii"):
"""
Convert str to appropriate format for "UserComment".
:param data: Like u"foobar"
:param str encoding: "ascii", "jis", or "unicode"
:return: b"ASCII\x00\x00\x00foobar"
:rtype: bytes
:raises: ValueError if the encoding is unsupported.
"""
if encoding not in ENCODINGS:
raise ValueError('encoding %r must be one of %r' % (encoding, ENCODINGS))
prefix = {ASCII: _ASCII_PREFIX, JIS: _JIS_PREFIX, UNICODE: _UNICODE_PREFIX}[encoding]
internal_encoding = {UNICODE: _UNICODE, JIS: _JIS}.get(encoding, encoding)
return prefix + data.encode(internal_encoding, errors='replace')
20 changes: 20 additions & 0 deletions requirements.txt
@@ -0,0 +1,20 @@
alabaster==0.7.10
Babel==2.5.0
certifi==2017.7.27.1
chardet==3.0.4
docutils==0.14
idna==2.6
imagesize==0.7.1
Jinja2==2.9.6
MarkupSafe==1.0
olefile==0.44
Pillow==4.2.1
Pygments==2.2.0
pytz==2017.2
requests==2.18.4
six==1.10.0
snowballstemmer==1.2.1
Sphinx==1.6.3
sphinxcontrib-websupport==1.0.1
typing==3.6.2
urllib3==1.22
74 changes: 69 additions & 5 deletions tests/s_test.py
Expand Up @@ -745,7 +745,7 @@ def test_dump_user_comment(self):
# jis
header = b"\x4a\x49\x53\x00\x00\x00\x00\x00"
string = u"abcd"
binary = header + string.encode("jis")
binary = header + string.encode("shift_jis")
result = helper.dump_user_comment(string, "jis")
self.assertEqual(binary, result)

Expand All @@ -760,7 +760,7 @@ def test_dump_user_comment(self):
header = b"\x00\x00\x00\x00\x00\x00\x00\x00"
string = u"abcd"
binary = header + string.encode("latin")
self.assertRaises(ValueError, helper.dump_user_comment(string, "undefined"))
self.assertRaises(ValueError, helper.dump_user_comment, string, "undefined")


def test_load_user_comment(self):
Expand All @@ -774,7 +774,7 @@ def test_load_user_comment(self):
# jis
header = b"\x4a\x49\x53\x00\x00\x00\x00\x00"
string = u"abcd"
binary = header + string.encode("jis")
binary = header + string.encode("shift_jis")
result = helper.load_user_comment(binary)
self.assertEqual(string, result)

Expand All @@ -789,13 +789,77 @@ def test_load_user_comment(self):
header = b"\x00\x00\x00\x00\x00\x00\x00\x00"
string = u"abcd"
binary = header + string.encode("ascii")
self.assertRaises(ValueError, load_user_comment(binary))
self.assertRaises(ValueError, helper.load_user_comment, binary)


class HelperTests(unittest.TestCase):
def test_headers(self):
"""Are our headers the correct length?"""
self.assertEqual(len(helper._ASCII_PREFIX), helper._PREFIX_SIZE)
self.assertEqual(len(helper._JIS_PREFIX), helper._PREFIX_SIZE)
self.assertEqual(len(helper._UNICODE_PREFIX), helper._PREFIX_SIZE)
self.assertEqual(len(helper._UNDEFINED_PREFIX), helper._PREFIX_SIZE)

def test_encode_ascii(self):
"""Do we encode ASCII correctly?"""
text = 'hello world'
expected = b'\x41\x53\x43\x49\x49\x00\x00\x00hello world'
actual = helper.dump_user_comment(text, encoding='ascii')
self.assertEqual(expected, actual)

def test_decode_ascii(self):
"""Do we decode ASCII correctly?"""
binary = b'\x41\x53\x43\x49\x49\x00\x00\x00hello world'
expected = 'hello world'
actual = helper.load_user_comment(binary)
self.assertEqual(expected, actual)

def test_encode_jis(self):
"""Do we encode JIS correctly?"""
text = '\u3053\u3093\u306b\u3061\u306f\u4e16\u754c'
expected = b'\x4a\x49\x53\x00\x00\x00\x00\x00' + text.encode('shift_jis')
actual = helper.dump_user_comment(text, encoding='jis')
self.assertEqual(expected, actual)

def test_decode_jis(self):
"""Do we decode JIS correctly?"""
expected = '\u3053\u3093\u306b\u3061\u306f\u4e16\u754c'
binary = b'\x4a\x49\x53\x00\x00\x00\x00\x00' + expected.encode('shift_jis')
actual = helper.load_user_comment(binary)
self.assertEqual(expected, actual)

def test_encode_unicode(self):
"""Do we encode Unicode correctly?"""
text = '\u3053\u3093\u306b\u3061\u306f\u4e16\u754c'
expected = b'\x55\x4e\x49\x43\x4f\x44\x45\x00' + text.encode('utf_16_be')
actual = helper.dump_user_comment(text, encoding='unicode')
self.assertEqual(expected, actual)

def test_decode_unicode(self):
"""Do we decode Unicode correctly?"""
expected = '\u3053\u3093\u306b\u3061\u306f\u4e16\u754c'
binary = b'\x55\x4e\x49\x43\x4f\x44\x45\x00' + expected.encode('utf_16_be')
actual = helper.load_user_comment(binary)
self.assertEqual(expected, actual)

def test_encode_bad_encoding(self):
"""De we gracefully handle bad input when encoding?"""
self.assertRaises(ValueError, helper.dump_user_comment, 'hello world', 'koi-8r')

def test_decode_bad_encoding(self):
"""De we gracefully handle bad input when decoding?"""
self.assertRaises(ValueError, helper.load_user_comment,
b'\x00\x00\x00\x00\x00\x00\x00\x00hello')
self.assertRaises(ValueError, helper.load_user_comment,
b'\x12\x34\x56\x78\x9a\xbc\xde\xffhello')
self.assertRaises(ValueError, helper.load_user_comment, b'hello world')


def suite():
suite = unittest.TestSuite()
suite.addTests([unittest.makeSuite(UTests),
unittest.makeSuite(ExifTests)])
unittest.makeSuite(ExifTests),
unittest.makeSuite(HelperTests)])
return suite


Expand Down

0 comments on commit b124dc9

Please sign in to comment.