🔍 Code Extractor

class SignatureGenerator

Maturity: 46

A class that generates signature-like images from text names using italic fonts and decorative flourishes.

File:
/tf/active/vicechatdev/document_auditor/src/utils/signature_generator.py
Lines:
10 - 134
Complexity:
moderate

Purpose

The SignatureGenerator class creates realistic signature images from text input. It searches for available italic fonts on the system, renders the provided name in a signature-style font, and adds a flowing Bezier curve line beneath the text to simulate a handwritten signature. The generated images have transparent backgrounds and are saved as temporary PNG files. This is useful for creating signature placeholders, document signing interfaces, or personalized graphics.

Source Code

class SignatureGenerator:
    """Generate signature-like text images from names"""
    
    def __init__(self):
        # Try to find signature-style fonts
        self.signature_fonts = []
        self.font_paths = [
            "/usr/share/fonts/truetype/dejavu/DejaVuSerif-Italic.ttf",
            "/usr/share/fonts/truetype/liberation/LiberationSerif-Italic.ttf",
            "/usr/share/fonts/truetype/liberation/LiberationSerif-BoldItalic.ttf",
            "/usr/share/fonts/truetype/freefont/FreeSerifItalic.ttf",
            "/usr/share/fonts/truetype/freefont/FreeSerifBoldItalic.ttf"
        ]
        
        # Load available fonts
        for font_path in self.font_paths:
            if os.path.exists(font_path):
                self.signature_fonts.append(font_path)
        
        # Set default signature parameters
        self.base_font_size = 32
        self.line_variations = [(0, 0, 0, 5), (0, 5, 0, 0), (0, -2, 0, 2)]  # Slight y-variations for characters
    
    def generate_signature_image(self, name, width=300, height=120):
        """
        Generate a signature-like image from a name
        
        Args:
            name (str): Name to convert to signature
            width (int): Width in pixels
            height (int): Height in pixels
            
        Returns:
            str: Path to temporary image file with signature
        """
        try:
            # Create a temporary file for the signature image
            temp_file = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
            temp_path = temp_file.name
            temp_file.close()
            
            # Create transparent background image
            img = Image.new('RGBA', (width, height), (255, 255, 255, 0))
            draw = ImageDraw.Draw(img)
            
            # Select a font - use random from available ones for variety
            if self.signature_fonts:
                font_path = random.choice(self.signature_fonts)
                font_size = self.base_font_size
                font = ImageFont.truetype(font_path, font_size)
            else:
                # Fall back to default font
                font = None
                font_size = 24
            
            # Calculate text dimensions and position
            if font:
                try:
                    # For newer versions of PIL with textbbox
                    left, top, right, bottom = draw.textbbox((0, 0), name, font=font)
                    text_width = right - left
                    text_height = bottom - top
                except AttributeError:
                    # Fallback for older PIL versions
                    text_width, text_height = draw.textsize(name, font=font)
            else:
                # Rough estimate for default font
                text_width = len(name) * (font_size * 0.6)
                text_height = font_size * 1.2
            
            # Center horizontally, but place slightly above center vertically
            x = (width - text_width) / 2
            y = (height - text_height) / 2 - 10  # Slight upward adjustment
            
            # Draw the signature text
            if font:
                draw.text((x, y), name, fill=(0, 0, 0, 255), font=font)
            else:
                draw.text((x, y), name, fill=(0, 0, 0, 255))
            
            # Add a fluid signature line below the text
            self._add_signature_line(draw, x, y, text_width, text_height)
            
            # Save the image
            img.save(temp_path, 'PNG')
            return temp_path
            
        except Exception as e:
            logger.error(f"Error generating signature image: {e}")
            return None
    
    def _add_signature_line(self, draw, x, y, text_width, text_height):
        """Add a realistic flowing line below the signature text"""
        # Start point is slightly below and to the right of text start
        start_x = x + (text_width * 0.1)
        start_y = y + text_height + 5
        
        # End point is below and slightly beyond text end
        end_x = x + text_width + (text_width * 0.1)
        end_y = start_y + random.randint(-5, 5)
        
        # Create control points for the bezier curve
        control1_x = x + (text_width * 0.4)
        control1_y = start_y + random.randint(5, 15)
        
        control2_x = x + (text_width * 0.7)
        control2_y = start_y + random.randint(-5, 5)
        
        # Draw the line with slight thickness variation
        line_width = random.uniform(1.5, 2.5)
        
        # Create points for the bezier curve
        points = []
        steps = 50
        for i in range(steps + 1):
            t = i / steps
            # Cubic Bezier formula
            px = (1-t)**3 * start_x + 3*(1-t)**2*t * control1_x + 3*(1-t)*t**2 * control2_x + t**3 * end_x
            py = (1-t)**3 * start_y + 3*(1-t)**2*t * control1_y + 3*(1-t)*t**2 * control2_y + t**3 * end_y
            points.append((px, py))
        
        # Draw the line
        if len(points) > 1:
            for i in range(len(points) - 1):
                draw.line([points[i], points[i+1]], fill=(0, 0, 0, 255), width=int(line_width))

Parameters

Name Type Default Kind
bases - -

Parameter Details

__init__: No parameters required. The constructor automatically searches for available signature-style fonts on the system and initializes default signature parameters.

generate_signature_image.name: The text string to render as a signature. Typically a person's name or initials.

generate_signature_image.width: The width of the output image in pixels. Default is 300. The signature will be centered within this width.

generate_signature_image.height: The height of the output image in pixels. Default is 120. The signature will be vertically centered (slightly above center) within this height.

_add_signature_line.draw: The PIL ImageDraw object used to draw on the image.

_add_signature_line.x: The x-coordinate of the text's left edge.

_add_signature_line.y: The y-coordinate of the text's top edge.

_add_signature_line.text_width: The width of the rendered text in pixels.

_add_signature_line.text_height: The height of the rendered text in pixels.

Return Value

The generate_signature_image method returns a string containing the file path to a temporary PNG file with the generated signature image. The image has a transparent background (RGBA mode) with black signature text and line. Returns None if an error occurs during generation. The _add_signature_line method is a private helper that returns None and modifies the draw object in place.

Class Interface

Methods

__init__(self) -> None

Purpose: Initializes the SignatureGenerator by searching for available signature-style fonts on the system and setting default parameters

Returns: None. Initializes instance attributes: signature_fonts (list of available font paths), font_paths (list of paths to search), base_font_size (default 32), and line_variations (list of y-coordinate variations for character positioning)

generate_signature_image(self, name: str, width: int = 300, height: int = 120) -> str | None

Purpose: Generates a signature-like image from the provided name text and saves it to a temporary PNG file

Parameters:

  • name: The text string to render as a signature
  • width: Width of the output image in pixels (default: 300)
  • height: Height of the output image in pixels (default: 120)

Returns: String path to the temporary PNG file containing the signature image, or None if an error occurs. The image has a transparent background with black signature text and decorative line

_add_signature_line(self, draw: ImageDraw.Draw, x: float, y: float, text_width: float, text_height: float) -> None

Purpose: Private helper method that adds a flowing Bezier curve line below the signature text to simulate a handwritten flourish

Parameters:

  • draw: PIL ImageDraw object to draw on
  • x: X-coordinate of the text's left edge
  • y: Y-coordinate of the text's top edge
  • text_width: Width of the rendered text in pixels
  • text_height: Height of the rendered text in pixels

Returns: None. Modifies the draw object in place by adding a cubic Bezier curve line with randomized control points and thickness

Attributes

Name Type Description Scope
signature_fonts list[str] List of file paths to available signature-style italic fonts found on the system. Populated during __init__ by checking font_paths instance
font_paths list[str] List of potential font file paths to search for signature-style fonts. Contains paths to DejaVu, Liberation, and FreeFont italic fonts in standard Linux locations instance
base_font_size int Default font size in points for rendering signature text. Set to 32 by default instance
line_variations list[tuple[int, int, int, int]] List of tuples representing slight y-coordinate variations for character positioning to create a more natural handwritten appearance. Currently defined but not actively used in the implementation instance

Dependencies

  • os
  • io
  • tempfile
  • logging
  • PIL
  • random

Required Imports

import os
import io
import tempfile
import logging
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
import random

Usage Example

import os
import tempfile
import logging
from PIL import Image, ImageDraw, ImageFont
import random

# Setup logger
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

# Instantiate the signature generator
sig_gen = SignatureGenerator()

# Generate a signature image
signature_path = sig_gen.generate_signature_image(
    name="John Doe",
    width=400,
    height=150
)

if signature_path:
    print(f"Signature saved to: {signature_path}")
    # Use the signature image
    img = Image.open(signature_path)
    img.show()
    # Clean up when done
    os.unlink(signature_path)
else:
    print("Failed to generate signature")

Best Practices

  • Always clean up temporary files: The generate_signature_image method creates temporary PNG files that are not automatically deleted. Use os.unlink() to remove them when done.
  • Font availability: The class searches for fonts in Linux-specific paths. On other operating systems, modify the font_paths list in __init__ to include appropriate font directories.
  • Logger configuration: Ensure a logger named 'logger' is configured before using this class, as error messages are logged via logger.error().
  • Error handling: Always check if generate_signature_image returns None before using the result, as it returns None on errors.
  • Thread safety: The class uses random.choice() and random.randint() which are thread-safe, but if you need deterministic output, seed the random number generator before use.
  • Font fallback: If no signature fonts are found, the class falls back to PIL's default font, which may not look signature-like.
  • Image dimensions: Ensure width and height parameters are large enough to accommodate the text. Very small dimensions may result in clipped signatures.
  • Stateless usage: Each call to generate_signature_image is independent. The class maintains no state between calls except for the loaded font paths.

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function create_signature_image 67.3% similar

    Generates a synthetic signature image for a given name, either as stylized text or as a random hand-drawn curve, and saves it as a PNG file with transparent background.

    From: /tf/active/vicechatdev/document_auditor/generate_sample_signatures.py
  • function main_v32 63.7% similar

    Generates sample signature images (PNG files) for a predefined list of names and saves them to a 'signatures' directory.

    From: /tf/active/vicechatdev/document_auditor/generate_sample_signatures.py
  • class SignatureImage_v1 60.9% similar

    A custom ReportLab Flowable class that renders signature images in PDF documents with automatic fallback to placeholder text when images are unavailable or cannot be loaded.

    From: /tf/active/vicechatdev/document_auditor/src/audit_page_generator.py
  • class SignatureImage 59.8% similar

    A ReportLab Flowable subclass for embedding signature images in PDFs with automatic fallback to placeholder text when images are unavailable or cannot be loaded.

    From: /tf/active/vicechatdev/CDocs/utils/pdf_utils.py
  • class SignatureManager 57.7% similar

    A class that manages digital signature images for documents, providing functionality to store, retrieve, and list signature files in a designated directory.

    From: /tf/active/vicechatdev/document_auditor/src/security/signature_manager.py
← Back to Browse