How to add customizable drop shadow to text in Python using Pillow? - Stack Overflow

admin2025-04-21  2

Image of photoshop shadow customization

I would like to be able to customize all of the properties of the shadow like shown from this Photoshop screenshot.

Please double check if my assumptions are right:

  • Opacity is the A value of the shadow text
  • Angle is just the angle
  • Distance is distance
  • Is spread gaussian blur radius? How would that transfer from a percent value?
  • Size is size of text? Wonder how I would make it pixel value like shown in image
from PIL import Image, ImageDraw, ImageFont

from typing import Optional, Dict, Tuple, Union

class Text:

    def __init__(self, image: Image):
        self._draw = ImageDraw.Draw(image)
        self._image = image


    def text(self, position: Tuple[int, int], text: str, font: ImageFont, color: Tuple[int, int, int, int], drop_shadow: Optional[Dict[str, Union[int, Tuple[int, int, int]]]] = None):
        if drop_shadow is not None:
            self._add_drop_shadow(position, text, font, drop_shadow)

        self._draw.text(position, text, font=font, fill=color)


    def _add_drop_shadow(self, original_position: Tuple[int, int], text: str, font: ImageFont, drop_shadow: Dict[str, Union[int, Tuple[int, int, int]]]):
        # Not sure. How do I gaussian blur just some text and then put it onto the image? This class represents an entire image, where I will put multiple drop shadowed text or normal text
        
        # Also, how do I make everything match the Photoshop image I linked? Like the percent and px and stuff.
        pass





if __name__ == "__main__":
    args = {
        "opacity": 65,
        "angle" : 30,
        "distance": 15,
        "spread": 29,
        "size": 15,
        "color": (0, 0, 0)
    }

    img = Image.new("RGB", (1920, 1080), (255, 255, 255))

    text = Text(image=img)
    font = ImageFont.truetype("./assets/Hiragino.ttf", 200)
    text.text((200, 400), "ダミーテキスト", font, (0, 153, 255, 255), args)

    img.show()

Also, I'm trying out Python typing but it seems very tedious. If there's anything unidiomatic about my code please tell me also.

Image of photoshop shadow customization

I would like to be able to customize all of the properties of the shadow like shown from this Photoshop screenshot.

Please double check if my assumptions are right:

  • Opacity is the A value of the shadow text
  • Angle is just the angle
  • Distance is distance
  • Is spread gaussian blur radius? How would that transfer from a percent value?
  • Size is size of text? Wonder how I would make it pixel value like shown in image
from PIL import Image, ImageDraw, ImageFont

from typing import Optional, Dict, Tuple, Union

class Text:

    def __init__(self, image: Image):
        self._draw = ImageDraw.Draw(image)
        self._image = image


    def text(self, position: Tuple[int, int], text: str, font: ImageFont, color: Tuple[int, int, int, int], drop_shadow: Optional[Dict[str, Union[int, Tuple[int, int, int]]]] = None):
        if drop_shadow is not None:
            self._add_drop_shadow(position, text, font, drop_shadow)

        self._draw.text(position, text, font=font, fill=color)


    def _add_drop_shadow(self, original_position: Tuple[int, int], text: str, font: ImageFont, drop_shadow: Dict[str, Union[int, Tuple[int, int, int]]]):
        # Not sure. How do I gaussian blur just some text and then put it onto the image? This class represents an entire image, where I will put multiple drop shadowed text or normal text
        
        # Also, how do I make everything match the Photoshop image I linked? Like the percent and px and stuff.
        pass





if __name__ == "__main__":
    args = {
        "opacity": 65,
        "angle" : 30,
        "distance": 15,
        "spread": 29,
        "size": 15,
        "color": (0, 0, 0)
    }

    img = Image.new("RGB", (1920, 1080), (255, 255, 255))

    text = Text(image=img)
    font = ImageFont.truetype("./assets/Hiragino.ttf", 200)
    text.text((200, 400), "ダミーテキスト", font, (0, 153, 255, 255), args)

    img.show()

Also, I'm trying out Python typing but it seems very tedious. If there's anything unidiomatic about my code please tell me also.

Share Improve this question edited Jan 22 at 22:07 woowaaahehahah asked Jan 22 at 22:04 woowaaahehahahwoowaaahehahah 216 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

Done, final result that scales properly:

from PIL import Image, ImageDraw, ImageFont, ImageFilter

from typing import Optional, Dict, Tuple, Union
import math

class Text:

    def __init__(self, image: Image):
        self._draw = ImageDraw.Draw(image)
        self._image = image


    def add_text(self, position: Tuple[int, int], text: str, font_path: str, font_size: int, color: Tuple[int, int, int, int], drop_shadow: Optional[Dict[str, Union[int, Tuple[int, int, int], Tuple[int, int]]]] = None):
    
        if drop_shadow is not None:
            self._add_drop_shadow(position, text, font_path, font_size, drop_shadow)

        font = ImageFont.truetype(font_path, font_size)
        self._draw.text(position, text, font=font, fill=color)


    def _add_drop_shadow(self, original_position: Tuple[int, int], text: str, font_path: str, original_font_size: int, drop_shadow: Dict[str, Union[int, Tuple[int, int, int], Tuple[int, int]]]):
        shadow = Image.new("RGBA", self._image.size)
        draw = ImageDraw.Draw(shadow)

        r, g, b = drop_shadow["color"]
        a = drop_shadow["opacity"]

        offset_scale_x, offset_scale_y = drop_shadow["offset_scale_font"]
        offset_x = offset_scale_x * original_font_size
        offset_y = offset_scale_y * original_font_size

        blur_scale = drop_shadow["blur_scale_font"]
        blur = blur_scale * original_font_size

        shadow_font = ImageFont.truetype(font_path, original_font_size * drop_shadow["size_scale"])
        draw.text((original_position[0] + offset_x, original_position[1] + offset_y), text, font=shadow_font, fill=(r, g, b, a))
        shadow = shadow.filter(ImageFilter.GaussianBlur(radius=blur))
        self._image.paste(shadow, mask=shadow.getchannel("A"))





if __name__ == "__main__":
    # args = {
    #     "opacity": 255,
    #     "angle" : 30,
    #     "distance": 15,
    #     "spread": 29,
    #     "size": 15,
    #     "color": (128, 128, 128)
    # }
    args = {
        "opacity": 255,
        "offset_scale_font": (0.015, 0.01),
        "blur_scale_font": 0.02,
        "size_scale": 1.01,
        "color": (128, 128, 128)
    }

    img = Image.new("RGB", (1920, 1080), (255, 255, 255))

    text = Text(image=img)
    text.add_text((200, 400), "ダミーテキスト", "assets/Hiragino.ttf", 200, (0, 153, 255, 255), args)

    text.add_text((200, 600), "ダミーテキスト", "assets/Hiragino.ttf", 100, (255, 0, 0, 255), args)


    img.show()

(However, I'm still not sure if the "size" works the same as in Photoshop. I might be confused about size and spread. In Photoshop, size seems to make it bigger AND blurrier, while spread changes blurriness. Mine just makes it bigger).

转载请注明原文地址:http://www.anycun.com/QandA/1745226627a90473.html