DEV Community

Cover image for Python Tutorial: Adding Shadows and Outlines to Text on Images
Franco Zanardi
Franco Zanardi

Posted on

Python Tutorial: Adding Shadows and Outlines to Text on Images

Creating visually striking images often means adding effects like drop shadows or crisp outlines to your text. These details can make text pop, improve readability, and give your designs a professional finish.

However, if you've ever tried to do this programmatically in Python using libraries like Pillow (PIL), you know it can be a real headache. The process often involves rendering text multiple times with slight offsets, manually blurring layers, or complex pixel manipulation with NumPy. It’s cumbersome and rarely looks perfect.

In this tutorial, we'll show you how to achieve these professional-looking effects in just a few lines of code using a modern Python library called pictex.

What is PicTex?

pictex is a Python library for creating beautifully styled visual compositions. It provides a high-level, declarative API inspired by CSS, allowing you to build complex images by composing simple builders. Forget about calculating coordinates; think in terms of rows, columns, padding, and styles.

Setup

First, let's get pictex installed. All you need is pip:

pip install pictex
Enter fullscreen mode Exit fullscreen mode

That's it! No other complex dependencies are needed. Let's dive in.

The Basics: Adding Text to an Image

Before we add effects, let's start with a simple foundation. We'll create a Canvas, define some basic styles, and render a Text element. This will be our starting point.

from pictex import Canvas, Text

# Create a canvas with a dark background to make our effects stand out
canvas = (
    Canvas()
    .font_size(150)
    .font_family("Impact") # A bold font works great for effects
    .padding(40)
    .background_color("#404040") # Gray background
)

# Render the text and save it
image = canvas.render(Text("Hello World").color("white"))
image.save("hello_world.png")
Enter fullscreen mode Exit fullscreen mode

This gives us a clean and simple base image:

Base 'Hello World' text in white on a dark background

Part 1: Adding an Outline (Text Stroke)

An outline, or "stroke," is perfect for creating sticker-like text or making it stand out against a busy background. With pictex, this is a single method call: .text_stroke().

The method takes two main arguments: width for the outline's thickness and color.

from pictex import Canvas, Text

canvas = (
    Canvas()
    .font_size(150)
    .font_family("Impact")
    .padding(40)
    .background_color("#404040")
)

# Add a yellow text color and a black outline
image = canvas.render(
    Text("COMIC")
    .color("yellow")
    .text_stroke(width=14, color="black")
)
image.save("comic_style.png")
Enter fullscreen mode Exit fullscreen mode

The result is crisp, high-impact text, achieved with zero hassle:

Comic-style text with a thick black outline

Part 2: Adding a Drop Shadow

A classic drop shadow gives text depth and makes it feel like it's floating above the background. To do this, we use the .text_shadows() method, which takes one or more Shadow objects.

First, make sure to import Shadow from the library. A Shadow object has three key parameters:

  • offset: A tuple (dx, dy) indicating the horizontal and vertical shift.
  • blur_radius: The amount of blur. A higher value creates a softer shadow.
  • color: The color of the shadow.
from pictex import Canvas, Text, Shadow

canvas = (
    Canvas()
    .font_size(150)
    .font_family("Impact")
    .padding(40)
    .background_color("#404040")
)

# Add a simple, soft drop shadow
image = canvas.render(
    Text("DEPTH")
    .color("white")
    .text_shadows(
        Shadow(offset=(8, 8), blur_radius=5, color="#00000080") # Black with 50% opacity
    )
)
image.save("drop_shadow.png")
Enter fullscreen mode Exit fullscreen mode

With just one extra line of code, we've added convincing depth to our text:

Text with a soft drop shadow

Part 3: Advanced Effect - Creating a "Neon Glow"

This is where pictex really shows its power. The .text_shadows() method isn't limited to a single shadow. You can pass it multiple Shadow objects to create complex effects like a neon glow.

The trick to a neon effect is to stack multiple shadows of the same color (or similar colors) with an offset of (0, 0) and different blur_radius values.

from pictex import Canvas, Text, Shadow

canvas = (
    Canvas()
    .font_size(150)
    .font_family("Impact")
    .padding(40)
    .background_color("#404040")
)

# Stack multiple shadows to create a glowing effect
image = canvas.render(
    Text("NEON")
    .color("#00FFAA") # Bright cyan color for the text
    .text_shadows(
        Shadow(offset=(0, 0), blur_radius=3, color="#00FFAA80"),
        Shadow(offset=(0, 0), blur_radius=5, color="#00FFAA80"),
        Shadow(offset=(0, 0), blur_radius=10, color="#FFFFFF80") # A faint white outer glow
    )
)
image.save("neon_glow.png")
Enter fullscreen mode Exit fullscreen mode

The result is a stunning glow effect that would be extremely complex to achieve with traditional tools:

Bright neon text created with multiple shadows

Conclusion

As you've seen, adding advanced text effects that would normally require significant effort becomes trivial with the declarative API of pictex. Whether it's a sharp outline, a subtle shadow, or a complex glow, you can achieve it in a readable way with very few lines of code.

This is just one of the many features pictex offers to simplify image generation in Python. If you found this tutorial helpful, I encourage you to explore the GitHub Repository.

Happy coding

Top comments (0)