Я напишу тебе программу на языке python, на входе которой даётся словарь с датами заездов, а на выходе рисуется картинка, содержащая таблицу из 8 строк и 5 столбцов.

Программа:
from PIL import Image, ImageDraw, ImageFont

def draw_date_header(text, font, cell_width):
    cell_text = text
    
    # Находим размер текста с помощью метода font.getbbox
    text_bbox = font.getbbox(cell_text)
    text_width = text_bbox[2] - text_bbox[0]
    text_height = text_bbox[3] - text_bbox[1]
    
    text_x = x + (cell_width - text_width) // 2
    text_y = y + (row_height - text_height) // 2
    draw.text((text_x, text_y), cell_text, fill=(0, 0, 0), font=font)


def find_table_width(column_widths, column_spacing):
    cell_width = 0
    for i in range(0, len(column_widths)):    
        cell_width = cell_width + column_widths[i] + column_spacing
    return cell_width


def draw_table_with_dates(data):
    # Создаем изображение размером 1080x1350 пикселей
    image = Image.new("RGB", (1080, 1350), (255, 255, 255))
    draw = ImageDraw.Draw(image)

    # Задаем параметры таблицы
    row_height = 75
    column_widths = [75, 305, 125, 125, 150]
    column_spacing = 10
    row_spacing = 10

    font = ImageFont.truetype("arial.ttf", size=30)
    date_header_font = ImageFont.truetype("arial.ttf", size=50)
    
    # Вычисляем положение таблицы
    table_width = sum(column_widths) + column_spacing * (len(column_widths) - 1)
    table_height = row_height * 5
    x = (image.width - table_width) // 2
    y = (image.height - table_height) // 2

    # Выводим даты над строками таблицы
    #dates = [data.get("first_day_date"), data.get("second_day_date"), data.get("third_day_date")]
    #date_y_positions = [y - 50, y + row_height * 2 - 50, y + row_height * 4 - 50]

    #for i in range(3):
        #date_left, date_top, date_width, date_height = font.getbbox(dates[i])
        #date_x_position = (image.width - date_width) // 2
        #draw.text((date_x_position, date_y_positions[i]), dates[i], fill=(0, 0, 0), font=font)

    current_table_row = 0
    current_run = 0
    # Рисуем таблицу
    for row in range(8):
        for col in range(5):
            if current_table_row == 0:
                cell_width = find_table_width(column_widths, column_spacing)
                draw_date_header(data[current_run], date_header_font, cell_width)
                continue

            else:
                if current_table_row >= 2:
                    if data[current_run] != data[current_run - 1]:
                        cell_width = find_table_width(column_widths, column_spacing)
                        draw_date_header(data[current_run], date_header_font)
                        continue

                    else:
                        cell_width = column_widths[col]
            
                        if col == 1:
                            cell_text = data.get("first_run_date")
                        elif col == 2:
                            cell_text = data.get("second_run_date")
                        elif col == 3:
                            cell_text = data.get("third_run_date")
                        else:
                            cell_text = ""
            
                        # Находим размер текста с помощью метода font.getbbox
                        text_bbox = font.getbbox(cell_text)
                        text_width = text_bbox[2] - text_bbox[0]
                        text_height = text_bbox[3] - text_bbox[1]
                        
                        text_x = x + (cell_width - text_width) // 2
                        text_y = y + (row_height - text_height) // 2
                        draw.text((text_x, text_y), cell_text, fill=(0, 0, 0), font=font)
            
                        # Рисуем ячейку
                        draw.rectangle([(x, y), (x + cell_width, y + row_height)], outline=(0, 0, 0))
            
                        x += cell_width + column_spacing  # переходим к следующей ячейке
      
                        if (col == 4):
                            current_run += 1

        # Переходим на новую строку
        x = (image.width - table_width) // 2
        y += row_height + row_spacing
        current_table_row += 1

    # Сохраняем изображение в файл
    image.save("table_with_dates.png")


# Пример использования функции с примером словаря данных
data = {
    "first_run_date": "29.11",
    "second_run_date": "29.11",
    "third_run_date": "30.11",
    "fourth_run_date": "30.11",
    "fifth_run_date": "31.11"
}

draw_table_with_dates(data)

Сделай так, чтобы в первом столбце каждой строки вставлялась картинка heat.png, с автоматической подгонкой размера под размер ячейки и автоматическим выравниваем картинки в центр ячейки.