diff options
-rw-r--r-- | PKG-INFO | 2 | ||||
-rw-r--r-- | docs/conf.py | 4 | ||||
-rw-r--r-- | docs/releasenotes.rst | 20 | ||||
-rw-r--r-- | docs/usage.rst | 36 | ||||
-rw-r--r-- | pydenticon.egg-info/PKG-INFO | 2 | ||||
-rw-r--r-- | pydenticon.egg-info/requires.txt | 2 | ||||
-rw-r--r-- | pydenticon/__init__.py | 37 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rw-r--r-- | tests/test_pydenticon.py | 46 |
9 files changed, 111 insertions, 40 deletions
@@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pydenticon -Version: 0.2 +Version: 0.3 Summary: Library for generating identicons. Port of Sigil (https://github.com/cupcake/sigil) with enhancements. Home-page: https://github.com/azaghal/pydenticon Author: Branko Majic diff --git a/docs/conf.py b/docs/conf.py index 54a0eb6..ac49123 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -49,9 +49,9 @@ copyright = u'2013, Branko Majic' # built documents. # # The short X.Y version. -version = '0.2' +version = '0.3' # The full version, including alpha/beta/rc tags. -release = '0.2' +release = '0.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/releasenotes.rst b/docs/releasenotes.rst index ffbe84f..15b5b45 100644 --- a/docs/releasenotes.rst +++ b/docs/releasenotes.rst @@ -1,6 +1,26 @@ Release Notes ============= +0.3 +--- + +Update introducing support for more output formats and ability to use +transparency for PNG identicons. + +New features: + +* `PYD-6: Add support for having transparent backgrounds in identicons + <https://projects.majic.rs/pydenticon/issues/PYD-6>`_ + + Ability to use alpha-channel specification in PNG identicons to obtain + complete or partial transparency. Works for both background and foreground + colour. + +* `PYD-7: Ability to specify image format + <https://projects.majic.rs/pydenticon/issues/PYD-7>`_ + + Ability to specify any output format supported by the Pillow library. + 0.2 --- diff --git a/docs/usage.rst b/docs/usage.rst index c8df87e..b4fa374 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -85,6 +85,12 @@ Finally, the resulting identicons can be in different formats:: identicon_ascii = generator.generate("john.doe@example.com", 200, 200, output_format="ascii") +Supported output formats are dependant on the local Pillow installation. For +exact list of available formats, have a look at `Pillow documentation +<https://pillow.readthedocs.io/>`_. The ``ascii`` format is the only format +explicitly handled by the *Pydenticon* library itself (mainly useful for +debugging purposes). + Using the generated identicons ------------------------------ @@ -93,9 +99,11 @@ either to be stored somewhere on disk, or maybe streamed back to the user via HTTP response. Since the generate function returns raw data, this is quite easy to achieve:: - # Generate same identicon in two different formats. + # Generate same identicon in three different formats. identicon_png = generator.generate("john.doe@example.com", 200, 200, output_format="png") + identicon_gif = generator.generate("john.doe@example.com", 200, 200, + output_format="gif") identicon_ascii = generator.generate("john.doe@example.com", 200, 200, output_format="ascii") @@ -104,9 +112,35 @@ to achieve:: f.write(identicon_png) f.close() + f = open("sample.gif", "wb") + f.write(identicon_gif) + f.close() + # ASCII identicon can be printed-out to console directly. print identicon_ascii + +Working with transparency +------------------------- + +.. note:: + New in version ``0.3``. + +.. warning:: + The only output format that properly supports transparency at the moment is + ``PNG``. If you are using anything else, transparency will not work. + +If you ever find yourself in need of having a transparent background or +foreground, you can easily do this using the syntax +``rgba(224,224,224,0)``. All this does is effectively adding alpha channel to +selected colour. + +The alpha channel value ranges from ``0`` to ``255``, letting you specify how +much transparency/opaqueness you want. For example, to have it at roughly 50% +(more like at ``50.2%`` since you can't use fractions), you would simply specify +value as ``rgba(224,224,224,128)``. + + Full example ------------ diff --git a/pydenticon.egg-info/PKG-INFO b/pydenticon.egg-info/PKG-INFO index 6bfb983..8e48c91 100644 --- a/pydenticon.egg-info/PKG-INFO +++ b/pydenticon.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pydenticon -Version: 0.2 +Version: 0.3 Summary: Library for generating identicons. Port of Sigil (https://github.com/cupcake/sigil) with enhancements. Home-page: https://github.com/azaghal/pydenticon Author: Branko Majic diff --git a/pydenticon.egg-info/requires.txt b/pydenticon.egg-info/requires.txt index 5873a22..7e2fba5 100644 --- a/pydenticon.egg-info/requires.txt +++ b/pydenticon.egg-info/requires.txt @@ -1 +1 @@ -Pillow
\ No newline at end of file +Pillow diff --git a/pydenticon/__init__.py b/pydenticon/__init__.py index 9d484b2..0edfdb0 100644 --- a/pydenticon/__init__.py +++ b/pydenticon/__init__.py @@ -184,11 +184,11 @@ class Generator(object): return [int(digest[i * 2:i * 2 + 2], 16) for i in range(16)] - def _generate_png(self, matrix, width, height, padding, foreground, background): + def _generate_image(self, matrix, width, height, padding, foreground, background, image_format): """ - Generates an identicon image in the PNG format out of the passed block - matrix, with the requested width, height, padding, foreground colour, - and background colour. + Generates an identicon image in requested image format out of the passed + block matrix, with the requested width, height, padding, foreground + colour, background colour, and image format. Arguments: @@ -212,13 +212,16 @@ class Generator(object): represented as a string of format supported by the PIL.ImageColor module. + image_format - Format to use for the image. Format needs to be + supported by the Pillow library. + Returns: - Identicon image in PNG format, returned as a byte list. + Identicon image in requested format, returned as a byte list. """ # Set-up a new image object, setting the background to provided value. - image = Image.new("RGB", (width + padding[2] + padding[3], height + padding[0] + padding[1]), background) + image = Image.new("RGBA", (width + padding[2] + padding[3], height + padding[0] + padding[1]), background) # Set-up a draw image (for drawing the blocks). draw = ImageDraw.Draw(image) @@ -244,11 +247,14 @@ class Generator(object): stream = BytesIO() # Save the image to stream. - image.save(stream, format="png", optimize=True) + try: + image.save(stream, format=image_format, optimize=True) + except KeyError: + raise ValueError("Pillow does not support requested image format: %s" % image_format) image_raw = stream.getvalue() stream.close() - # Return the resulting PNG. + # Return the resulting image. return image_raw def _generate_ascii(self, matrix, foreground, background): @@ -296,7 +302,8 @@ class Generator(object): bottom, left, right. output_format - Output format of resulting identicon image. Supported - formats are: "png", "ascii". Default is "png". + formats are anything that is supported by Pillow, plus a special + "ascii" mode. inverted - Specifies whether the block colours should be inverted or not. Default is False. @@ -313,21 +320,19 @@ class Generator(object): matrix = self._generate_matrix(digest_byte_list) # Determine the background and foreground colours. - if output_format == "png": - background = self.background - foreground = self.foreground[digest_byte_list[0] % len(self.foreground)] - elif output_format == "ascii": + if output_format == "ascii": foreground = "+" background = "-" + else: + background = self.background + foreground = self.foreground[digest_byte_list[0] % len(self.foreground)] # Swtich the colours if inverted image was requested. if inverted: foreground, background = background, foreground # Generate the identicon in requested format. - if output_format == "png": - return self._generate_png(matrix, width, height, padding, foreground, background) if output_format == "ascii": return self._generate_ascii(matrix, foreground, background) else: - raise ValueError("Unsupported format requested: %s" % output_format) + return self._generate_image(matrix, width, height, padding, foreground, background, output_format) @@ -10,7 +10,7 @@ os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) setup( name='pydenticon', - version='0.2', + version='0.3', packages=['pydenticon'], include_package_data=True, license='BSD', # example license diff --git a/tests/test_pydenticon.py b/tests/test_pydenticon.py index 515b271..cc6b13c 100644 --- a/tests/test_pydenticon.py +++ b/tests/test_pydenticon.py @@ -152,7 +152,7 @@ class GeneratorTest(unittest.TestCase): # Verify the expected and actual result are identical. self.assertEqual(expected_digest_byte_list, digest_byte_list) - def test_generate_png_basics(self): + def test_generate_image_basics(self): """ Tests some basics about generated PNG identicon image. This includes: @@ -179,7 +179,7 @@ class GeneratorTest(unittest.TestCase): generator = Generator(5, 5) # Generate the raw image. - raw_image = generator._generate_png(matrix, width, height, padding, foreground, background) + raw_image = generator._generate_image(matrix, width, height, padding, foreground, background, "png") # Try to load the raw image. image_stream = BytesIO(raw_image) @@ -189,7 +189,7 @@ class GeneratorTest(unittest.TestCase): self.assertEqual(image.size[0], 240) self.assertEqual(image.size[1], 240) self.assertEqual(image.format, "PNG") - self.assertEqual(image.mode, "RGB") + self.assertEqual(image.mode, "RGBA") def test_generate_ascii(self): """ @@ -238,6 +238,18 @@ class GeneratorTest(unittest.TestCase): image = PIL.Image.open(image_stream) self.assertEqual(image.format, "PNG") + # Verify that JPEG image is returned when requested. + raw_image = generator.generate(data, 200, 200, output_format="jpeg") + image_stream = BytesIO(raw_image) + image = PIL.Image.open(image_stream) + self.assertEqual(image.format, "JPEG") + + # Verify that GIF image is returned when requested. + raw_image = generator.generate(data, 200, 200, output_format="gif") + image_stream = BytesIO(raw_image) + image = PIL.Image.open(image_stream) + self.assertEqual(image.format, "GIF") + # Verify that ASCII "image" is returned when requested. raw_image = generator.generate(data, 200, 200, output_format="ascii") self.assertIsInstance(raw_image, str) @@ -257,8 +269,8 @@ class GeneratorTest(unittest.TestCase): # Verify that an exception is raised in case of unsupported format. self.assertRaises(ValueError, generator.generate, data, 200, 200, output_format="invalid") - @mock.patch.object(Generator, '_generate_png') - def test_generate_inverted_png(self, generate_png_mock): + @mock.patch.object(Generator, '_generate_image') + def test_generate_inverted_png(self, generate_image_mock): """ Tests if the foreground and background are properly inverted when generating PNG images. @@ -276,11 +288,11 @@ class GeneratorTest(unittest.TestCase): # Verify that colours are picked correctly when no inverstion is requsted. generator.generate(data, 200, 200, inverted=False, output_format="png") - generate_png_mock.assert_called_with(mock.ANY, mock.ANY, mock.ANY, mock.ANY, foreground, background) + generate_image_mock.assert_called_with(mock.ANY, mock.ANY, mock.ANY, mock.ANY, foreground, background, "png") # Verify that colours are picked correctly when inversion is requsted. generator.generate(data, 200, 200, inverted=True, output_format="png") - generate_png_mock.assert_called_with(mock.ANY, mock.ANY, mock.ANY, mock.ANY, background, foreground) + generate_image_mock.assert_called_with(mock.ANY, mock.ANY, mock.ANY, mock.ANY, background, foreground, "png") @mock.patch.object(Generator, '_generate_ascii') def test_generate_inverted_ascii(self, generate_ascii_mock): @@ -310,8 +322,8 @@ class GeneratorTest(unittest.TestCase): generator.generate(data, 200, 200, inverted=True, output_format="ascii") generate_ascii_mock.assert_called_with(mock.ANY, "-", "+") - @mock.patch.object(Generator, '_generate_png') - def test_generate_foreground(self, generate_png_mock): + @mock.patch.object(Generator, '_generate_image') + def test_generate_foreground(self, generate_image_mock): """ Tests if the foreground colour is picked correctly. """ @@ -327,15 +339,15 @@ class GeneratorTest(unittest.TestCase): # result in foreground colour of index '1'. data = "some test data" generator.generate(data, 200, 200) - generate_png_mock.assert_called_with(mock.ANY, mock.ANY, mock.ANY, mock.ANY, foreground[1], background) + generate_image_mock.assert_called_with(mock.ANY, mock.ANY, mock.ANY, mock.ANY, foreground[1], background, "png") # The first byte of hex digest should be 149 for this data, which should # result in foreground colour of index '5'. data = "some other test data" generator.generate(data, 200, 200) - generate_png_mock.assert_called_with(mock.ANY, mock.ANY, mock.ANY, mock.ANY, foreground[5], background) + generate_image_mock.assert_called_with(mock.ANY, mock.ANY, mock.ANY, mock.ANY, foreground[5], background, "png") - def test_generate_png_compare(self): + def test_generate_image_compare(self): """ Tests generated PNG identicon against a set of pre-generated samples. """ @@ -359,10 +371,10 @@ class GeneratorTest(unittest.TestCase): height = 200 padding = (20, 20, 20, 20) - # Load the reference images, making sure they're in RGB mode. - test1_ref = PIL.Image.open("tests/samples/test1.png").convert(mode="RGB") - test2_ref = PIL.Image.open("tests/samples/test2.png").convert(mode="RGB") - test3_ref = PIL.Image.open("tests/samples/test3.png").convert(mode="RGB") + # Load the reference images, making sure they're in RGBA mode. + test1_ref = PIL.Image.open("tests/samples/test1.png").convert(mode="RGBA") + test2_ref = PIL.Image.open("tests/samples/test2.png").convert(mode="RGBA") + test3_ref = PIL.Image.open("tests/samples/test3.png").convert(mode="RGBA") # Set-up the Generator. generator = Generator(5, 5, foreground=foreground, background=background) @@ -389,7 +401,7 @@ class GeneratorTest(unittest.TestCase): # Verify that all the diffs are essentially black (i.e. no differences # between generated identicons and reference samples). - expected_extrema = ((0, 0), (0, 0), (0, 0)) + expected_extrema = ((0, 0), (0, 0), (0, 0), (0, 0)) self.assertEqual(diff1.getextrema(), expected_extrema) self.assertEqual(diff2.getextrema(), expected_extrema) |