Skip to content

image

Image processing and manipulation.

get_img_ext

get_img_ext(img: bytes) -> str

Get the extension of an image from a byte string.

Parameters:

Name Type Description Default
img bytes

A byte string representing an image.

required

Returns:

Type Description
str

The extension of the image file.

Source code in src/images_upload_cli/image.py
11
12
13
14
15
16
17
18
19
20
21
22
def get_img_ext(img: bytes) -> str:
    """Get the extension of an image from a byte string.

    Args:
        img: A byte string representing an image.

    Returns:
        The extension of the image file.
    """
    with BytesIO(img) as f:
        ext = Image.open(f).format
        return "" if ext is None else ext.lower()

get_font

get_font(size: int = 14) -> FreeTypeFont

Get font for thumbnail captions.

Parameters:

Name Type Description Default
size int

The size of the font. Defaults to 14.

14

Returns:

Type Description
FreeTypeFont

ImageFont.FreeTypeFont: Represents the font.

Source code in src/images_upload_cli/image.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def get_font(size: int = 14) -> ImageFont.FreeTypeFont:
    """Get font for thumbnail captions.

    Args:
        size: The size of the font. Defaults to 14.

    Returns:
        ImageFont.FreeTypeFont: Represents the font.
    """
    if font_name := getenv("CAPTION_FONT"):
        return ImageFont.truetype(font_name, size=size)

    default_fonts = [
        "Helvetica",
        "NotoSerif-Regular",
        "Menlo",
        "DejaVuSerif",
        "arial",
    ]
    return search_font(fonts=default_fonts, size=size)

search_font

search_font(fonts: list[str], size: int = 14) -> FreeTypeFont

Attempt to retrieve a TTF font from the system.

Parameters:

Name Type Description Default
fonts list[str]

A list of font names to search for.

required
size int

The font size. Defaults to 14.

14

Returns:

Type Description
FreeTypeFont

ImageFont.FreeTypeFont: Represents the font.

Raises:

Type Description
GetEnvError

If none of the default fonts are found.

Source code in src/images_upload_cli/image.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
def search_font(fonts: list[str], size: int = 14) -> ImageFont.FreeTypeFont:
    """Attempt to retrieve a TTF font from the system.

    Args:
        fonts: A list of font names to search for.
        size: The font size. Defaults to 14.

    Returns:
        ImageFont.FreeTypeFont: Represents the font.

    Raises:
        GetEnvError: If none of the default fonts are found.
    """
    for font_name in fonts:
        try:
            return ImageFont.truetype(font_name, size=size)
        except OSError:  # noqa: PERF203
            continue

    msg = (
        f"None of the fonts were found: {fonts}.\n"
        f"Please setup CAPTION_FONT in environment variables or in '{get_config_path()}'.",
    )
    raise GetEnvError(msg)

make_thumbnail

make_thumbnail(img: bytes, font: FreeTypeFont, size: tuple[int, int] = (300, 300)) -> bytes

Generate thumbnail for the image.

Parameters:

Name Type Description Default
img bytes

The input image in bytes format.

required
font FreeTypeFont

The font to be used for the text caption.

required
size tuple[int, int]

The desired size of the thumbnail image. Defaults to (300, 300).

(300, 300)

Returns:

Type Description
bytes

The modified image in bytes format.

Source code in src/images_upload_cli/image.py
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def make_thumbnail(
    img: bytes,
    font: ImageFont.FreeTypeFont,
    size: tuple[int, int] = (300, 300),
) -> bytes:
    """Generate thumbnail for the image.

    Args:
        img: The input image in bytes format.
        font: The font to be used for the text caption.
        size: The desired size of the thumbnail image. Defaults to (300, 300).

    Returns:
        The modified image in bytes format.
    """
    # Open the input image and create a copy in RGB format.
    im = Image.open(BytesIO(img))
    pw = im.copy()

    if pw.mode != "RGB":
        pw = pw.convert("RGB")

    # Resize the image to the desired size using Lanczos resampling.
    pw.thumbnail(size=size, resample=Image.Resampling.LANCZOS)

    # Create a blank image for the text
    pw_with_line = Image.new(
        mode="RGB",
        size=(pw.width, pw.height + 16),
        color=(255, 255, 255),
    )
    pw_with_line.paste(pw, box=(0, 0))

    # Get the file size of the input image.
    fsize = human_size(len(img))

    # Draw the text caption
    d = ImageDraw.Draw(pw_with_line)
    d.text(
        xy=(pw.width / 5, pw.height),
        text=f"{im.width}x{im.height} ({im.format}) [{fsize}]",
        font=font,
        fill=(0, 0, 0),
    )

    # Save the modified image as a JPEG file in bytes format.
    buffer = BytesIO()
    pw_with_line.save(
        buffer,
        format="JPEG",
        quality=95,
        optimize=True,
        progressive=True,
    )

    return buffer.getvalue()