Emoji renderer for Pillow with Discord emoji support and multiple emoji sources
—
The Pilmoji class provides the core emoji rendering functionality for Pillow images. It manages emoji placement, text layout, resource lifecycle, and offers comprehensive control over rendering parameters through a context manager interface.
Main emoji rendering interface that handles text rendering with emoji substitution, supporting both Unicode emojis and Discord custom emojis with configurable positioning, scaling, and source providers.
class Pilmoji:
"""
The main emoji rendering interface.
Parameters:
- image: PIL.Image.Image - The Pillow image to render on
- source: Union[BaseSource, Type[BaseSource]] - Emoji source (defaults to Twemoji)
- cache: bool - Whether to cache emojis (default: True)
- draw: Optional[ImageDraw.ImageDraw] - Drawing instance (auto-created if None)
- render_discord_emoji: bool - Enable Discord emoji rendering (default: True)
- emoji_scale_factor: float - Default emoji scaling factor (default: 1.0)
- emoji_position_offset: Tuple[int, int] - Default emoji position offset (default: (0, 0))
"""
def __init__(
self,
image: Image.Image,
*,
source: Union[BaseSource, Type[BaseSource]] = Twemoji,
cache: bool = True,
draw: Optional[ImageDraw.ImageDraw] = None,
render_discord_emoji: bool = True,
emoji_scale_factor: float = 1.0,
emoji_position_offset: Tuple[int, int] = (0, 0)
) -> None: ...from pilmoji import Pilmoji
from pilmoji.source import MicrosoftEmojiSource
from PIL import Image, ImageFont
# Create image and font
image = Image.new('RGB', (400, 100), (255, 255, 255))
font = ImageFont.load_default()
# Use Microsoft emoji source with custom scaling
with Pilmoji(
image,
source=MicrosoftEmojiSource,
emoji_scale_factor=1.2,
emoji_position_offset=(0, -2)
) as pilmoji:
pilmoji.text((10, 10), "Hello! 😊 How are you? 🌟", (0, 0, 0), font)Renders text with emoji support, handling multi-line text, custom positioning, and extensive formatting options compatible with Pillow's ImageDraw.text interface.
def text(
self,
xy: Tuple[int, int],
text: str,
fill: ColorT = None,
font: FontT = None,
anchor: str = None,
spacing: int = 4,
node_spacing: int = 0,
align: str = "left",
direction: str = None,
features: str = None,
language: str = None,
stroke_width: int = 0,
stroke_fill: ColorT = None,
embedded_color: bool = False,
*args,
emoji_scale_factor: float = None,
emoji_position_offset: Tuple[int, int] = None,
**kwargs
) -> None:
"""
Draws text with emoji rendering support, including multi-line text.
Parameters:
- xy: Tuple[int, int] - Position to render text
- text: str - Text to render (supports Unicode and Discord emojis)
- fill: ColorT - Text color
- font: FontT - Font to use
- spacing: int - Line spacing in pixels (default: 4)
- node_spacing: int - Spacing between text/emoji nodes (default: 0)
- emoji_scale_factor: float - Emoji scaling override
- emoji_position_offset: Tuple[int, int] - Emoji position offset override
- Additional parameters inherited from Pillow's ImageDraw.text
"""# Multi-line text with custom emoji positioning
multiline_text = """
Welcome to our app! 👋
Features include:
• Fast processing ⚡
• Beautiful UI 🎨
• Discord support <:custom:123456789>
"""
with Pilmoji(image) as pilmoji:
pilmoji.text(
(20, 20),
multiline_text.strip(),
fill=(50, 50, 50),
font=font,
spacing=6,
node_spacing=2,
emoji_scale_factor=1.1,
emoji_position_offset=(1, -1)
)Calculates text dimensions including emoji substitutions, supporting multi-line text and custom emoji scaling for accurate layout planning.
def getsize(
self,
text: str,
font: FontT = None,
*,
spacing: int = 4,
emoji_scale_factor: float = None
) -> Tuple[int, int]:
"""
Calculate width and height of text when rendered with emojis.
Parameters:
- text: str - Text to measure
- font: FontT - Font to use for measurement
- spacing: int - Line spacing (default: 4)
- emoji_scale_factor: float - Emoji scaling factor
Returns:
- Tuple[int, int] - (width, height) of rendered text
"""text = "Hello! 👋 This is a test 🧪"
with Pilmoji(image) as pilmoji:
width, height = pilmoji.getsize(text, font, emoji_scale_factor=1.2)
print(f"Text dimensions: {width}x{height}")
# Use dimensions for centering
image_width, image_height = image.size
x = (image_width - width) // 2
y = (image_height - height) // 2
pilmoji.text((x, y), text, (0, 0, 0), font)Automatic resource management through context manager protocol, ensuring proper cleanup of HTTP sessions, cached emoji streams, and drawing resources.
def __enter__(self) -> Pilmoji: ...
def __exit__(self, *_) -> None: ...
def open(self) -> None:
"""
Re-opens closed renderer.
Raises:
- ValueError: If renderer is already open
"""
def close(self) -> None:
"""
Safely closes renderer and cleans up resources.
Raises:
- ValueError: If renderer is already closed
"""# Automatic resource management
with Pilmoji(image) as pilmoji:
pilmoji.text((10, 10), "Emoji text 🎉", (0, 0, 0), font)
# Resources automatically cleaned up
# Manual resource management
pilmoji = Pilmoji(image)
try:
pilmoji.text((10, 10), "Emoji text 🎉", (0, 0, 0), font)
finally:
pilmoji.close()Install with Tessl CLI
npx tessl i tessl/pypi-pilmoji