simpil

Simpil simplifies the loading/creation of images and adding text using PIL/Pillow.

Initialising

There’s one class (SimpilImage) with several ways to instantiate.

Initialise from a URL

from simpil import SimpilImage

image = SimpilImage(source='https://jpeg.org/images/about.jpg')
_images/url.jpg

Initialise from a file

from simpil import SimpilImage

image = SimpilImage(source='../images/orange_and_uke.jpg')
_images/orange_and_uke.jpg

Create a new blank image

The colour is an RGB tuple. There are a few predefined constants:

  • BLACK
  • WHITE
  • GREY
  • RED
  • GREEN
  • BLUE
  • YELLOW
  • ORANGE
from simpil import SimpilImage, RED

image = SimpilImage(width=200,
                    height=100,
                    background_colour=RED)
_images/200x100.jpg

Initialise from data

Use bytes, BytesIO (Py3) or StringIO (Py2). You can also use simpil.BytesIO on Py2.

from simpil import SimpilImage

image_data = b"""\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00C\x00\x08\x06\x06\x07\x06\x05\x08\x07\x07\x07\t\t\x08\n\x0c\x14\r\x0c\x0b\x0b\x0c\x19\x12\x13\x0f\x14\x1d\x1a\x1f\x1e\x1d\x1a\x1c\x1c $.' ",#\x1c\x1c(7),01444\x1f'9=82<.342\xff\xdb\x00C\x01\t\t\t\x0c\x0b\x0c\x18\r\r\x182!\x1c!22222222222222222222222222222222222222222222222222\xff\xc0\x00\x11\x08\x00d\x00\xc8\x03\x01"\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04\x00\x00\x01}\x01\x02\x03\x00\x04\x11\x05\x12!1A\x06\x13Qa\x07"q\x142\x81\x91\xa1\x08#B\xb1\xc1\x15R\xd1\xf0$3br\x82\t\n\x16\x17\x18\x19\x1a%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05\x04\x04\x00\x01\x02w\x00\x01\x02\x03\x11\x04\x05!1\x06\x12AQ\x07aq\x13"2\x81\x08\x14B\x91\xa1\xb1\xc1\t#3R\xf0\x15br\xd1\n\x16$4\xe1%\xf1\x17\x18\x19\x1a&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00?\x00\xe2\xe8\xa2\x8a\xf9\x93\xf7\x10\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x00\xa2\x8a(\x03\xff\xd9"""

image = SimpilImage(source=image_data)
_images/200x100.jpg

Saving

Automatically saving

By default, saving is automatic when changes are made to a SimpilImage if it has a source or destination filename.

from simpil import SimpilImage

image = SimpilImage(source='https://jpeg.org/images/about.jpg',
                    destination='../images/saved_image.png')

For an image loaded from a local file, changes can be saved to another file.

from simpil import SimpilImage, LEFT

image = SimpilImage(source='../images/orange_and_uke.jpg',
                    destination='../images/saved_image.jpg')

image.text(text=("SimpilImage(source='../images/orange_and_uke.jpg',\n"
                 "            destination='../images/saved_image.jpg')"),
           justify=LEFT)

To disable automatic saving, initialise with autosave set to False:

from simpil import SimpilImage

image = SimpilImage(source='https://jpeg.org/images/about.jpg',
                    destination='../images/saved_image.jpg',
                    autosave=False)

Saving manually

To save an image, the SimpilImage needs a destination filename.

from simpil import SimpilImage, RED

image = SimpilImage(width=400,
                    height=200,
                    background_colour=RED)

image.save(filename="../images/red_rect.jpg")

When saving back to the same file, you don’t need to supply the filename

from simpil import SimpilImage

image = SimpilImage(source='../images/orange_and_uke.jpg')

image.save()

Properties

Image data

Get the image data in the desired format. This can be used when there’s no need to save the file to disk, such as for images dynamically created from webserver requests.

from simpil import SimpilImage, PNG

image = SimpilImage(source='https://jpeg.org/images/about.jpg')

jpeg_data = image.image_data()
png_data = image.image_data(fmt=PNG)

Width and height

Width and height properties are available:

from simpil import SimpilImage

image = SimpilImage(source='https://jpeg.org/images/about.jpg')

print(f'width:{image.width}, height:{image.height}')

Scaling

Scale by using x and y. Use a value of 2 to double the size. If only one of x or y is used, the aspect ratio is retained. If you want to scale on one axis only, set the other to ``1`.

from simpil import SimpilImage, CONSOLAS, RED, TOP, BOTTOM

image = SimpilImage(width=200,
                    height=50,
                    background_colour=RED,
                    destination='../images/scaled_image.jpg',
                    font=CONSOLAS[12])

image.text(text=f'This image was created\n'
                f'as {image.width}x{image.height}.',
           y=TOP)

image.scale(x=2,
            y=4)

# TODO: Figure out why text can't be added after scaling an image
image.text(text=f'This image has been\n'
                f'scaled to {image.width}x{image.height}.',
           y=BOTTOM)
_images/scaled_image.jpg

Note from this example that there is an issue with adding text after an image is scaled.

Adding Text

There are a few simple ways to add text to an image…

Single line

Use SimpilImage.text() to add text to an image.

from simpil import SimpilImage

image = SimpilImage(width=400,
                    height=200,
                    destination='../images/add_text.jpg')

image.text(text='Text')

This example shows the default font. The default colour is WHITE.

_images/add_text.jpg

Declaring fonts

You can you use instances of PIL.ImageFont, but an easier way is to use the fonts object. This caches font instances in one place, accessed by name and size (in pixels). The name must match the filename, but you can often omit the file extension (.ttf and .otf).

from simpil import fonts, ImageFont

font_24 = fonts['consola'][24]
font_36 = fonts['consola'][36]

Using different fonts

Fonts can be supplied as a default for the SimpilImage or discretely to each text function:

from simpil import SimpilImage, fonts, RED, BLUE, GREEN, TOP

image = SimpilImage(width=400,
                    height=200,
                    background_colour=RED,
                    destination='../images/using_different_fonts.jpg',
                    font=fonts['consola'][24])

image.text(text='Default Font',
           y=TOP,
           colour=BLUE)

image.text(text='Times 36',
           colour=GREEN,
           font=fonts['times.ttf'][36])
_images/using_different_fonts.jpg

Note how different colours are used in this example.

Multiline Text

Newlines in strings will be respected. You can also use a list of strings, but if those strings should not contain newlines.

from simpil import SimpilImage, CONSOLAS, RED, WHITE

POEM = """There are holes in the sky
where the rain gets in
but they're ever so small,
That's why rain is thin!"""

image = SimpilImage(width=400,
                    height=200,
                    background_colour=RED,
                    destination='../images/multi_line_text.jpg')

image.text(text=POEM,
           font=CONSOLAS[24],
           colour=WHITE)
_images/multi_line_text.jpg

Positioning Text on the Image

You can specify positions x which can use constants LEFT, CENTRE or RIGHT and y which can use the constants TOP, CENTRE or BOTTOM. x and y can also take absolute values for the top left of the text.

from simpil import SimpilImage, CONSOLAS, RED, LEFT, CENTRE, RIGHT, TOP, BOTTOM

image = SimpilImage(width=600,
                    height=300,
                    background_colour=RED,
                    destination='../images/positioning.jpg',
                    font=CONSOLAS[24])

for x in (LEFT, CENTRE, RIGHT):
    for y in (TOP, CENTRE, BOTTOM):
        image.text(text=f'x:{x}\ny:{y}',
                   x=x,
                   y=y)

for x in (150, 400):
    for y in (75, 175):
        image.text(text=f'x:{x}\ny:{y}',
                   x=x,
                   y=y)
_images/positioning.jpg

Justifying a block of text

You can control how the text is justified by passing value to justify. This should be LEFT, CENTRE or RIGHT. The default is CENTRE.

from simpil import SimpilImage, fonts, CONSOLAS, YELLOW, BLUE, LEFT, RIGHT, CENTRE

image = SimpilImage(width=400,
                    height=200,
                    background_colour=BLUE,
                    destination='../images/justified_text_block.jpg',
                    font=CONSOLAS[20])

for justification in (LEFT, CENTRE, RIGHT):
    image.text(text=f'this\ntext\nis\n{justification}\njustified',
               justify=justification,
               x=justification,  # can use LEFT, CENTRE, RIGHT for x
               colour=YELLOW)
_images/justified_text_block.jpg

Linespace in a block of text

By default, there’s no gap between lines, so descenders might meet ascenders. If you want a bit more spce, use a value for spacing. The default is 1. Using``1.5`` adds a spacing of half a line height between each line.

from simpil import SimpilImage, CONSOLAS, YELLOW, BLUE, LEFT, RIGHT, CENTRE

image = SimpilImage(width=400,
                    height=200,
                    background_colour=BLUE,
                    destination='../images/line_spacing.jpg',
                    font=CONSOLAS[16])

for spacing, x in zip((1, 1.5, 2), (LEFT, CENTRE, RIGHT)):
    image.text(text=f'The spacing\nfor this text\nis {spacing}',
               spacing=spacing,
               x=x,
               colour=YELLOW)
_images/line_spacing.jpg

Text with a Drop Shadow

Use SimpilImage.shadowed_text() to add text to an image with a bottom-right shadow. Useful for making text stand out again a background that is similar to the text colour

from simpil import SimpilImage, CONSOLAS, BLACK, WHITE, BLUE, LEFT, RIGHT

image = SimpilImage(width=400,
                    height=200,
                    background_colour=BLUE,
                    destination='../images/shadowed_text.jpg',
                    font=CONSOLAS[48])

image.shadowed_text(text=['Shadowed', 'Text'],
                    justify=LEFT,
                    x=RIGHT,
                    colour=WHITE,
                    shadow_colour=BLACK,
                    shadow_size=3)
_images/shadowed_text.jpg

Note how the colour of the shadow can be set with shadow_colour and the shadow depth with shadow_size.

Text with an Outline

Use SimpilImage.outlined_text() to add plain text to an image with an outline. Useful for making text stand out against a background that is similar to the text colour

from simpil import SimpilImage, CONSOLAS, BLACK, WHITE, BLUE

image = SimpilImage(width=400,
                    height=200,
                    background_colour=BLUE,
                    destination='../images/outlined_text.jpg',
                    font=CONSOLAS[48])

image.outlined_text(text='Outlined\nText',
                    colour=BLACK,
                    outline_colour=WHITE,
                    outline_size=3)
_images/outlined_text.jpg

Note how the colour of the outline can be set with outline_colour and the outline size with outline_size.