diff --git a/doc/appendices.rst b/doc/appendices.rst
index dec742d..371598d 100644
--- a/doc/appendices.rst
+++ b/doc/appendices.rst
@@ -13,10 +13,14 @@ http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf
+---------------+----------------------+
| BYTE | int |
+---------------+----------------------+
+| SIGNED BYTE | int |
++---------------+----------------------+
| ASCII | str |
+---------------+----------------------+
| SHORT | int |
+---------------+----------------------+
+| SIGNED SHORT | int |
++---------------+----------------------+
| LONG | int |
+---------------+----------------------+
| RATIONAL | (int, int) |
@@ -25,6 +29,10 @@ http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf
+---------------+----------------------+
| SRATIONAL | (int, int) |
+---------------+----------------------+
+| FLOAT | float |
++---------------+----------------------+
+| DOUBLE | float |
++---------------+----------------------+
If value type is number(BYTE, SHORT, LONG, RATIONAL, or SRATIONAL) and value count is two or more number, it is expressed with tuple.
diff --git a/doc/changes.rst b/doc/changes.rst
index 0491af6..184358f 100644
--- a/doc/changes.rst
+++ b/doc/changes.rst
@@ -1,6 +1,12 @@
Changelog
=========
+1.0.13
+------
+
+- Added helper function to read and write "UserComment".
+- Added to support for SignedByte, SigendShort, Float, and Double.
+
1.0.12
------
diff --git a/doc/helper.rst b/doc/helper.rst
new file mode 100644
index 0000000..86531ff
--- /dev/null
+++ b/doc/helper.rst
@@ -0,0 +1,38 @@
+================
+Helper Functions
+================
+
+UserComment
+-----------
+.. py:function:: piexif.helper.UserComment.load(data)
+
+ Convert "UserComment" value in exif format to str.
+
+ :param bytes data: "UserComment" value from exif
+ :return: u"foobar"
+ :rtype: str(Unicode)
+
+::
+
+ import piexif
+ import piexif.helper
+ exif_dict = piexif.load("foo.jpg")
+ user_comment = piexif.helper.UserComment.load(exif_dict["Exif"][piexif.ExifIFD.UserComment])
+
+.. py:function:: piexif.helper.UserComment.dump(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
+
+::
+
+ import piexif
+ import piexif.helper
+ user_comment = piexif.helper.UserComment.dump(u"Edit now.")
+ exif_dict = piexif.load("foo.jpg")
+ exif_dict["Exif"][piexif.ExifIFD.UserComment] = user_comment
+ exif_bytes = piexif.dump(exif_dict)
diff --git a/doc/index.rst b/doc/index.rst
index 060c18d..1811e82 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -19,6 +19,7 @@ To simplify exif manipulations with python. Writing, reading, and more... Piexif
about
installation
functions
+ helper
appendices
sample
changes
diff --git a/piexif.pyproj b/piexif.pyproj
index 9141455..20c6cde 100644
--- a/piexif.pyproj
+++ b/piexif.pyproj
@@ -35,7 +35,11 @@
Code
+
+
+ Code
+
Code
diff --git a/piexif/__init__.py b/piexif/__init__.py
index 46133d2..600f499 100644
--- a/piexif/__init__.py
+++ b/piexif/__init__.py
@@ -4,7 +4,7 @@
from ._transplant import transplant
from ._insert import insert
from ._exif import *
-from ._exeptions import *
+from ._exceptions import *
-VERSION = '1.0.12'
+VERSION = '1.0.13'
diff --git a/piexif/_common.py b/piexif/_common.py
index f8ca2fd..28dc9f9 100644
--- a/piexif/_common.py
+++ b/piexif/_common.py
@@ -1,6 +1,6 @@
import struct
-from ._exeptions import InvalidImageDataError
+from ._exceptions import InvalidImageDataError
def split_into_segments(data):
diff --git a/piexif/_dump.py b/piexif/_dump.py
index 5867fdc..4617a69 100644
--- a/piexif/_dump.py
+++ b/piexif/_dump.py
@@ -67,8 +67,6 @@ def dump(exif_dict_original):
if exif_is:
exif_set = _dict_to_bytes(exif_ifd, "Exif", zeroth_length)
exif_length = len(exif_set[0]) + interop_is * 12 + len(exif_set[1])
- #exif_bytes = b"".join(exif_set)
- #exif_length = len(exif_bytes)
else:
exif_bytes = b""
exif_length = 0
@@ -163,18 +161,27 @@ def _get_thumbnail(jpeg):
def _pack_byte(*args):
return struct.pack("B" * len(args), *args)
+def _pack_signed_byte(*args):
+ return struct.pack("b" * len(args), *args)
def _pack_short(*args):
return struct.pack(">" + "H" * len(args), *args)
+def _pack_signed_short(*args):
+ return struct.pack(">" + "h" * len(args), *args)
def _pack_long(*args):
return struct.pack(">" + "L" * len(args), *args)
-
def _pack_slong(*args):
return struct.pack(">" + "l" * len(args), *args)
+def _pack_float(*args):
+ return struct.pack(">" + "f" * len(args), *args)
+
+def _pack_double(*args):
+ return struct.pack(">" + "d" * len(args), *args)
+
def _value_to_bytes(raw_value, value_type, offset):
four_bytes_over = b""
@@ -265,7 +272,33 @@ def _value_to_bytes(raw_value, value_type, offset):
value_str = raw_value + b"\x00" * (4 - length)
except TypeError:
raise ValueError("Got invalid type to convert.")
-
+ elif value_type == TYPES.SByte: # Signed Byte
+ length = len(raw_value)
+ if length <= 4:
+ value_str = (_pack_signed_byte(*raw_value) +
+ b"\x00" * (4 - length))
+ else:
+ value_str = struct.pack(">I", offset)
+ four_bytes_over = _pack_signed_byte(*raw_value)
+ elif value_type == TYPES.SShort: # Signed Short
+ length = len(raw_value)
+ if length <= 2:
+ value_str = (_pack_signed_short(*raw_value) +
+ b"\x00\x00" * (2 - length))
+ else:
+ value_str = struct.pack(">I", offset)
+ four_bytes_over = _pack_signed_short(*raw_value)
+ elif value_type == TYPES.Float:
+ length = len(raw_value)
+ if length <= 1:
+ value_str = _pack_float(*raw_value)
+ else:
+ value_str = struct.pack(">I", offset)
+ four_bytes_over = _pack_float(*raw_value)
+ elif value_type == TYPES.DFloat: # Double
+ length = len(raw_value)
+ value_str = struct.pack(">I", offset)
+ four_bytes_over = _pack_double(*raw_value)
length_str = struct.pack(">I", length)
return length_str, value_str, four_bytes_over
@@ -294,7 +327,7 @@ def _dict_to_bytes(ifd_dict, ifd, ifd_offset):
type_str = struct.pack(">H", value_type)
four_bytes_over = b""
- if isinstance(raw_value, numbers.Integral):
+ if isinstance(raw_value, numbers.Integral) or isinstance(raw_value, float):
raw_value = (raw_value,)
offset = TIFF_HEADER_LENGTH + entries_length + ifd_offset + len(values)
diff --git a/piexif/_exeptions.py b/piexif/_exceptions.py
similarity index 100%
rename from piexif/_exeptions.py
rename to piexif/_exceptions.py
diff --git a/piexif/_exif.py b/piexif/_exif.py
index 1f91a8e..abdf0bc 100644
--- a/piexif/_exif.py
+++ b/piexif/_exif.py
@@ -4,10 +4,13 @@ class TYPES:
Short = 3
Long = 4
Rational = 5
+ SByte = 6
Undefined = 7
+ SShort = 8
SLong = 9
SRational = 10
Float = 11
+ DFloat = 12
TAGS = {
@@ -196,7 +199,10 @@ class TYPES:
51009: {'name': 'OpcodeList2', 'type': TYPES.Undefined},
51022: {'name': 'OpcodeList3', 'type': TYPES.Undefined},
60606: {'name': 'ZZZTestSlong1', 'type': TYPES.SLong},
- 60607: {'name': 'ZZZTestSlong2', 'type': TYPES.SLong}},
+ 60607: {'name': 'ZZZTestSlong2', 'type': TYPES.SLong},
+ 60608: {'name': 'ZZZTestSByte', 'type': TYPES.SByte},
+ 60609: {'name': 'ZZZTestSShort', 'type': TYPES.SShort},
+ 60610: {'name': 'ZZZTestDFloat', 'type': TYPES.DFloat},},
'Exif': {33434: {'name': 'ExposureTime', 'type': TYPES.Rational},
33437: {'name': 'FNumber', 'type': TYPES.Rational},
34850: {'name': 'ExposureProgram', 'type': TYPES.Short},
@@ -229,7 +235,7 @@ class TYPES:
37386: {'name': 'FocalLength', 'type': TYPES.Rational},
37396: {'name': 'SubjectArea', 'type': TYPES.Short},
37500: {'name': 'MakerNote', 'type': TYPES.Undefined},
- 37510: {'name': 'UserComment', 'type': TYPES.Ascii},
+ 37510: {'name': 'UserComment', 'type': TYPES.Undefined},
37520: {'name': 'SubSecTime', 'type': TYPES.Ascii},
37521: {'name': 'SubSecTimeOriginal', 'type': TYPES.Ascii},
37522: {'name': 'SubSecTimeDigitized', 'type': TYPES.Ascii},
@@ -503,6 +509,9 @@ class ImageIFD:
NoiseProfile = 51041
ZZZTestSlong1 = 60606
ZZZTestSlong2 = 60607
+ ZZZTestSByte = 60608
+ ZZZTestSShort = 60609
+ ZZZTestDFloat = 60610
class ExifIFD:
diff --git a/piexif/_insert.py b/piexif/_insert.py
index 6df0eac..6e390c7 100644
--- a/piexif/_insert.py
+++ b/piexif/_insert.py
@@ -2,7 +2,7 @@
import struct
from ._common import *
-from ._exeptions import InvalidImageDataError
+from ._exceptions import InvalidImageDataError
def insert(exif, image, new_file=None):
diff --git a/piexif/_load.py b/piexif/_load.py
index 784faab..7d3b088 100644
--- a/piexif/_load.py
+++ b/piexif/_load.py
@@ -1,7 +1,7 @@
import struct
from ._common import *
-from ._exeptions import InvalidImageDataError
+from ._exceptions import InvalidImageDataError
from ._exif import *
@@ -133,20 +133,20 @@ def convert_value(self, val):
length = val[1]
value = val[2]
- if t == 1: # BYTE
+ if t == TYPES.Byte: # BYTE
if length > 4:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = struct.unpack("B" * length,
self.tiftag[pointer: pointer + length])
else:
data = struct.unpack("B" * length, value[0:length])
- elif t == 2: # ASCII
+ elif t == TYPES.Ascii: # ASCII
if length > 4:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = self.tiftag[pointer: pointer+length - 1]
else:
data = value[0: length - 1]
- elif t == 3: # SHORT
+ elif t == TYPES.Short: # SHORT
if length > 2:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = struct.unpack(self.endian_mark + "H" * length,
@@ -154,7 +154,7 @@ def convert_value(self, val):
else:
data = struct.unpack(self.endian_mark + "H" * length,
value[0:length * 2])
- elif t == 4: # LONG
+ elif t == TYPES.Long: # LONG
if length > 1:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = struct.unpack(self.endian_mark + "L" * length,
@@ -162,7 +162,7 @@ def convert_value(self, val):
else:
data = struct.unpack(self.endian_mark + "L" * length,
value)
- elif t == 5: # RATIONAL
+ elif t == TYPES.Rational: # RATIONAL
pointer = struct.unpack(self.endian_mark + "L", value)[0]
if length > 1:
data = tuple(
@@ -180,13 +180,28 @@ def convert_value(self, val):
struct.unpack(self.endian_mark + "L",
self.tiftag[pointer + 4: pointer + 8]
)[0])
- elif t == 7: # UNDEFINED BYTES
+ elif t == TYPES.SByte: # SIGNED BYTES
+ if length > 4:
+ pointer = struct.unpack(self.endian_mark + "L", value)[0]
+ data = struct.unpack("b" * length,
+ self.tiftag[pointer: pointer + length])
+ else:
+ data = struct.unpack("b" * length, value[0:length])
+ elif t == TYPES.Undefined: # UNDEFINED BYTES
if length > 4:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = self.tiftag[pointer: pointer+length]
else:
data = value[0:length]
- elif t == 9: # SLONG
+ elif t == TYPES.SShort: # SIGNED SHORT
+ if length > 2:
+ pointer = struct.unpack(self.endian_mark + "L", value)[0]
+ data = struct.unpack(self.endian_mark + "h" * length,
+ self.tiftag[pointer: pointer+length*2])
+ else:
+ data = struct.unpack(self.endian_mark + "h" * length,
+ value[0:length * 2])
+ elif t == TYPES.SLong: # SLONG
if length > 1:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = struct.unpack(self.endian_mark + "l" * length,
@@ -194,7 +209,7 @@ def convert_value(self, val):
else:
data = struct.unpack(self.endian_mark + "l" * length,
value)
- elif t == 10: # SRATIONAL
+ elif t == TYPES.SRational: # SRATIONAL
pointer = struct.unpack(self.endian_mark + "L", value)[0]
if length > 1:
data = tuple(
@@ -210,6 +225,18 @@ def convert_value(self, val):
struct.unpack(self.endian_mark + "l",
self.tiftag[pointer + 4: pointer + 8]
)[0])
+ elif t == TYPES.Float: # FLOAT
+ if length > 1:
+ pointer = struct.unpack(self.endian_mark + "L", value)[0]
+ data = struct.unpack(self.endian_mark + "f" * length,
+ self.tiftag[pointer: pointer+length*4])
+ else:
+ data = struct.unpack(self.endian_mark + "f" * length,
+ value)
+ elif t == TYPES.DFloat: # DOUBLE
+ pointer = struct.unpack(self.endian_mark + "L", value)[0]
+ data = struct.unpack(self.endian_mark + "d" * length,
+ self.tiftag[pointer: pointer+length*8])
else:
raise ValueError("Exif might be wrong. Got incorrect value " +
"type to decode.\n" +
diff --git a/piexif/helper.py b/piexif/helper.py
new file mode 100644
index 0000000..0e4d10c
--- /dev/null
+++ b/piexif/helper.py
@@ -0,0 +1,66 @@
+class UserComment:
+ #
+ # Names of encodings that we publicly support.
+ #
+ ASCII = 'ascii'
+ JIS = 'jis'
+ UNICODE = 'unicode'
+ ENCODINGS = (ASCII, JIS, UNICODE)
+
+ #
+ # The actual encodings accepted by the standard library differ slightly from
+ # the above.
+ #
+ _JIS = 'shift_jis'
+ _UNICODE = 'utf_16_be'
+
+ _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'
+
+ @classmethod
+ def load(cls, 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) < cls._PREFIX_SIZE:
+ raise ValueError('not enough data to decode UserComment')
+ prefix = data[:cls._PREFIX_SIZE]
+ body = data[cls._PREFIX_SIZE:]
+ if prefix == cls._UNDEFINED_PREFIX:
+ raise ValueError('prefix is UNDEFINED, unable to decode UserComment')
+ try:
+ encoding = {
+ cls._ASCII_PREFIX: cls.ASCII, cls._JIS_PREFIX: cls._JIS, cls._UNICODE_PREFIX: cls._UNICODE,
+ }[prefix]
+ except KeyError:
+ raise ValueError('unable to determine appropriate encoding')
+ return body.decode(encoding, errors='replace')
+
+ @classmethod
+ def dump(cls, 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 cls.ENCODINGS:
+ raise ValueError('encoding %r must be one of %r' % (encoding, cls.ENCODINGS))
+ prefix = {cls.ASCII: cls._ASCII_PREFIX, cls.JIS: cls._JIS_PREFIX, cls.UNICODE: cls._UNICODE_PREFIX}[encoding]
+ internal_encoding = {cls.UNICODE: cls._UNICODE, cls.JIS: cls._JIS}.get(encoding, encoding)
+ return prefix + data.encode(internal_encoding, errors='replace')
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..ad3cbfd
--- /dev/null
+++ b/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
diff --git a/tests/s_test.py b/tests/s_test.py
index a76609f..8baf3e2 100644
--- a/tests/s_test.py
+++ b/tests/s_test.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+
import copy
import glob
import io
@@ -10,6 +12,7 @@
from PIL import Image
import piexif
from piexif import _common, ImageIFD, ExifIFD, GPSIFD, TAGS, InvalidImageDataError
+from piexif import helper
print("piexif version: {0}".format(piexif.VERSION))
@@ -345,6 +348,57 @@ def test_dump_and_load3(self):
self.assertEqual(e["Exif"][ExifIFD.ISOSpeed], long_v[x])
self.assertEqual(e["GPS"][GPSIFD.GPSVersionID], byte_v[x])
+ def test_dump_and_load_specials(self):
+ """test dump and load special types(SingedByte, SiginedShort, DoubleFloat)"""
+ zeroth_ifd_original = {
+ ImageIFD.ZZZTestSByte:-128,
+ ImageIFD.ZZZTestSShort:-32768,
+ ImageIFD.ZZZTestDFloat:1.0e-100,
+ }
+ exif_dict = {"0th":zeroth_ifd_original}
+ exif_bytes = piexif.dump(exif_dict)
+
+ exif = piexif.load(exif_bytes)
+ zeroth_ifd = exif["0th"]
+ self.assertEqual(
+ zeroth_ifd_original[ImageIFD.ZZZTestSByte],
+ zeroth_ifd[ImageIFD.ZZZTestSByte]
+ )
+ self.assertEqual(
+ zeroth_ifd_original[ImageIFD.ZZZTestSShort],
+ zeroth_ifd[ImageIFD.ZZZTestSShort]
+ )
+ self.assertEqual(
+ zeroth_ifd_original[ImageIFD.ZZZTestDFloat],
+ zeroth_ifd[ImageIFD.ZZZTestDFloat]
+ )
+
+ def test_dump_and_load_specials2(self):
+ """test dump and load special types(SingedByte, SiginedShort, DoubleFloat)"""
+ zeroth_ifd_original = {
+ ImageIFD.ZZZTestSByte:(-128, -128),
+ ImageIFD.ZZZTestSShort:(-32768, -32768),
+ ImageIFD.ZZZTestDFloat:(1.0e-100, 1.0e-100),
+ }
+ exif_dict = {"0th":zeroth_ifd_original}
+ exif_bytes = piexif.dump(exif_dict)
+
+ exif = piexif.load(exif_bytes)
+ zeroth_ifd = exif["0th"]
+ self.assertEqual(
+ zeroth_ifd_original[ImageIFD.ZZZTestSByte],
+ zeroth_ifd[ImageIFD.ZZZTestSByte]
+ )
+ self.assertEqual(
+ zeroth_ifd_original[ImageIFD.ZZZTestSShort],
+ zeroth_ifd[ImageIFD.ZZZTestSShort]
+ )
+ self.assertEqual(
+ zeroth_ifd_original[ImageIFD.ZZZTestDFloat],
+ zeroth_ifd[ImageIFD.ZZZTestDFloat]
+ )
+
+
def test_roundtrip_files(self):
files = glob.glob(os.path.join("tests", "images", "r_*.jpg"))
for input_file in files:
@@ -680,11 +734,132 @@ def test_merge_segments(self):
o = io.BytesIO(new_data)
Image.open(o).close()
+ def test_dump_user_comment(self):
+ # ascii
+ header = b"\x41\x53\x43\x49\x49\x00\x00\x00"
+ string = u"abcd"
+ binary = header + string.encode("ascii")
+ result = helper.UserComment.dump(string, "ascii")
+ self.assertEqual(binary, result)
+
+ # jis
+ header = b"\x4a\x49\x53\x00\x00\x00\x00\x00"
+ string = u"abcd"
+ binary = header + string.encode("shift_jis")
+ result = helper.UserComment.dump(string, "jis")
+ self.assertEqual(binary, result)
+
+ # unicode
+ header = b"\x55\x4e\x49\x43\x4f\x44\x45\x00"
+ string = u"abcd"
+ binary = header + string.encode("utf-16-be")
+ result = helper.UserComment.dump(string, "unicode")
+ self.assertEqual(binary, result)
+
+ # undefined
+ header = b"\x00\x00\x00\x00\x00\x00\x00\x00"
+ string = u"abcd"
+ binary = header + string.encode("latin")
+ self.assertRaises(ValueError, helper.UserComment.dump, string, "undefined")
+
+
+ def test_load_user_comment(self):
+ # ascii
+ header = b"\x41\x53\x43\x49\x49\x00\x00\x00"
+ string = u"abcd"
+ binary = header + string.encode("ascii")
+ result = helper.UserComment.load(binary)
+ self.assertEqual(string, result)
+
+ # jis
+ header = b"\x4a\x49\x53\x00\x00\x00\x00\x00"
+ string = u"abcd"
+ binary = header + string.encode("shift_jis")
+ result = helper.UserComment.load(binary)
+ self.assertEqual(string, result)
+
+ # unicode
+ header = b"\x55\x4e\x49\x43\x4f\x44\x45\x00"
+ string = u"abcd"
+ binary = header + string.encode("utf-16-be")
+ result = helper.UserComment.load(binary)
+ self.assertEqual(string, result)
+
+ # undefined
+ header = b"\x00\x00\x00\x00\x00\x00\x00\x00"
+ string = u"abcd"
+ binary = header + string.encode("ascii")
+ self.assertRaises(ValueError, helper.UserComment.load, binary)
+
+
+class HelperTests(unittest.TestCase):
+ def test_headers(self):
+ """Are our headers the correct length?"""
+ self.assertEqual(len(helper.UserComment._ASCII_PREFIX), helper.UserComment._PREFIX_SIZE)
+ self.assertEqual(len(helper.UserComment._JIS_PREFIX), helper.UserComment._PREFIX_SIZE)
+ self.assertEqual(len(helper.UserComment._UNICODE_PREFIX), helper.UserComment._PREFIX_SIZE)
+ self.assertEqual(len(helper.UserComment._UNDEFINED_PREFIX), helper.UserComment._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.UserComment.dump(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.UserComment.load(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.UserComment.dump(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.UserComment.load(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.UserComment.dump(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.UserComment.load(binary)
+ self.assertEqual(expected, actual)
+
+ def test_encode_bad_encoding(self):
+ """De we gracefully handle bad input when encoding?"""
+ self.assertRaises(ValueError, helper.UserComment.dump, 'hello world', 'koi-8r')
+
+ def test_decode_bad_encoding(self):
+ """De we gracefully handle bad input when decoding?"""
+ self.assertRaises(ValueError, helper.UserComment.load,
+ b'\x00\x00\x00\x00\x00\x00\x00\x00hello')
+ self.assertRaises(ValueError, helper.UserComment.load,
+ b'\x12\x34\x56\x78\x9a\xbc\xde\xffhello')
+ self.assertRaises(ValueError, helper.UserComment.load, b'hello world')
+
def suite():
suite = unittest.TestSuite()
suite.addTests([unittest.makeSuite(UTests),
- unittest.makeSuite(ExifTests)])
+ unittest.makeSuite(ExifTests),
+ unittest.makeSuite(HelperTests)])
return suite