pygments code#

[1]:
from typing import List, Tuple, Union

import numpy as np
from PIL import Image, ImageDraw
from pygments import lex
from pygments.formatters import ImageFormatter
from pygments.lexers import get_lexer_by_name

from enjoyn import GifAnimator, Preprocessor


def _custom_format(
    formatter: ImageFormatter,
    tokensource: List[Tuple],
    max_line_length: int = None,
    max_line_number: int = None,
) -> Image:
    formatter._create_drawables(tokensource)
    formatter._draw_line_numbers()
    max_line_length = max_line_length or formatter.maxlinelength
    max_line_number = max_line_number or formatter.maxlineno

    image = Image.new(
        "RGB",
        formatter._get_image_size(max_line_length, max_line_number),
        formatter.background_color,
    )
    formatter._paint_line_number_bg(image)
    draw = ImageDraw.Draw(image)
    # Highlight
    if formatter.hl_lines:
        x = (
            formatter.image_pad
            + formatter.line_number_width
            - formatter.line_number_pad
            + 1
        )
        recth = formatter._get_line_height()
        rectw = image.size[0] - x
        for linenumber in formatter.hl_lines:
            y = formatter._get_line_y(linenumber - 1)
            draw.rectangle([(x, y), (x + rectw, y + recth)], fill=formatter.hl_color)
    for pos, value, font, text_fg, text_bg in formatter.drawables:
        if text_bg:
            text_size = draw.textsize(text=value, font=font)
            draw.rectangle(
                [pos[0], pos[1], pos[0] + text_size[0], pos[1] + text_size[1]],
                fill=text_bg,
            )
        draw.text(pos, value, font=font, fill=text_fg)
    return np.asarray(image)


def render_frame(
    code: str,
    formatter: ImageFormatter,
    max_line_length: int = None,
    max_line_number: int = None,
) -> Image:
    lexer = get_lexer_by_name("python")
    return _custom_format(
        formatter,
        lex(code, lexer),
        max_line_length=max_line_length,
        max_line_number=max_line_number,
    )


code = """
from io import BytesIO

import matplotlib.pyplot as plt
import numpy as np

from enjoyn import GifAnimator, Preprocessor

def plot(i):
    x = np.linspace(0, 2, 1000)
    y = np.sin(2 * np.pi * (x - 0.01 * i))
    plt.plot(x, y)

    buf = BytesIO()
    plt.savefig(buf)
    plt.close()
    return buf

output = GifAnimator(
    preprocessor=plot,
    items=np.arange(0, 200),
    output_path="sine_wave.gif",
).compute()
""".strip()

formatter = ImageFormatter(
    image_format="gif",
    line_pad=8,
    font_name=None,
    line_number_bg=None,
    line_number_fg=None,
    encoding="utf-8",
)
longest_line = max(code.splitlines(), key=len) + " " * 12
max_line_length, _ = formatter.fonts.get_text_size(longest_line)
max_line_number = code.count("\n") + 1
items = [code[:i] for i in range(0, len(code) + 3, 3)]
items += [items[-1]] * 20  # pause at the end

preprocessor = Preprocessor(
    func=render_frame,
    args=(formatter,),
    kwds=dict(max_line_length=max_line_length, max_line_number=max_line_number),
)

output = GifAnimator(
    preprocessor=preprocessor, items=items, output_path="assets/pygments_code.gif"
).compute()
[########################################] | 100% Completed |  4.2s

pygments-code