import os
from PIL import Image, ImageDraw, ImageFont
import docx
from docx import Document
from docx.shared import Inches, Pt, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_ALIGN_VERTICAL
from docx.oxml import OxmlElement, parse_xml
from docx.oxml.ns import nsdecls, qn
os.makedirs("assets", exist_ok=True)
# Website color palette extracted from user's screenshot:
# Deep dark navy background: #0D1326
# Card/table secondary dark navy: #151F38
# Table alt row navy: #111A30
# Border navy: #263354
# Accent bright lime green: #5CE116
# Primary royal blue button color: #1D52F3
# Pure white text: #FFFFFF
# Muted cool grey-blue text: #94A3B8
BG_HEX = "0D1326"
CARD_HEX = "151F38"
ALT_ROW_HEX = "111A30"
BORDER_HEX = "263354"
GREEN_HEX = "5CE116"
BLUE_HEX = "1D52F3"
WHITE_HEX = "FFFFFF"
MUTED_HEX = "94A3B8"
def get_font(size, bold=False):
font_paths = [
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" if bold else "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
"/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf" if bold else "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
]
for p in font_paths:
if os.path.exists(p):
return ImageFont.truetype(p, size)
return ImageFont.load_default()
# 1. Create Top Website-Style Pill Badges Banner
def create_pills_banner(filename):
w, h = 1200, 130
img = Image.new("RGB", (w, h), (13, 19, 38))
draw = ImageDraw.Draw(img)
font = get_font(22, bold=False)
pills_row1 = ["ВКонтакте", "Яндекс Директ", "Senler / Боты", "Авито", "Яндекс Карты"]
x = 10
y = 15
for text in pills_row1:
bbox = draw.textbbox((0, 0), text, font=font)
tw = bbox[2] - bbox[0]
pw = tw + 58
ph = 46
draw.rounded_rectangle([x, y, x + pw, y + ph], radius=23, fill=(21, 31, 56), outline=(45, 59, 92), width=2)
# Green dot
draw.ellipse([x + 18, y + 17, x + 30, y + 29], fill=(92, 225, 22))
draw.text((x + 38, y + 10), text, font=font, fill=(255, 255, 255))
x += pw + 16
pills_row2 = ["Практический гайд 2026", "Исследования Ehrenberg-Bass", "Для всех ниш"]
x = 10
y = 72
for text in pills_row2:
bbox = draw.textbbox((0, 0), text, font=font)
tw = bbox[2] - bbox[0]
pw = tw + 58
ph = 46
draw.rounded_rectangle([x, y, x + pw, y + ph], radius=23, fill=(21, 31, 56), outline=(45, 59, 92), width=2)
draw.ellipse([x + 18, y + 17, x + 30, y + 29], fill=(92, 225, 22))
draw.text((x + 38, y + 10), text, font=font, fill=(203, 213, 225))
x += pw + 16
img.save(filename)
# 2. Create VK Ads Banner Icon
def create_vk_banner(filename):
w, h = 1200, 130
img = Image.new("RGB", (w, h), (13, 19, 38))
draw = ImageDraw.Draw(img)
# Outer card
draw.rounded_rectangle([4, 4, w - 4, h - 4], radius=22, fill=(21, 31, 56), outline=(29, 82, 243), width=3)
# VK icon square
draw.rounded_rectangle([24, 23, 108, 107], radius=20, fill=(0, 119, 255))
font_icon = get_font(38, bold=True)
draw.text((38, 42), "VK", font=font_icon, fill=(255, 255, 255))
# Green indicator dot on icon
draw.ellipse([92, 17, 114, 39], fill=(92, 225, 22), outline=(21, 31, 56), width=3)
font_title = get_font(30, bold=True)
font_sub = get_font(21, bold=False)
draw.text((134, 26), "VK РЕКЛАМА (ТАРГЕТ)", font=font_title, fill=(255, 255, 255))
draw.text((134, 68), "Заход на 95% холодной аудитории: создаём повод и вытаскиваем из спячки", font=font_sub, fill=(92, 225, 22))
img.save(filename)
# 3. Create Yandex Direct Banner Icon
def create_yd_banner(filename):
w, h = 1200, 130
img = Image.new("RGB", (w, h), (13, 19, 38))
draw = ImageDraw.Draw(img)
# Outer card
draw.rounded_rectangle([4, 4, w - 4, h - 4], radius=22, fill=(21, 31, 56), outline=(92, 225, 22), width=3)
# Yandex icon square (Red/Yellow brand nod)
draw.rounded_rectangle([24, 23, 108, 107], radius=20, fill=(252, 63, 29))
font_icon = get_font(44, bold=True)
draw.text((48, 38), "Я", font=font_icon, fill=(255, 255, 255))
draw.ellipse([92, 17, 114, 39], fill=(92, 225, 22), outline=(21, 31, 56), width=3)
font_title = get_font(30, bold=True)
font_sub = get_font(21, bold=False)
draw.text((134, 26), "ЯНДЕКС ДИРЕКТ (ПОИСК + РСЯ)", font=font_title, fill=(255, 255, 255))
draw.text((134, 68), "Заход на 5% горячего спроса и дожим: даём расчёт, конкретику и забираем заявку", font=font_sub, fill=(92, 225, 22))
img.save(filename)
# 4. Create 95/5 Rule Minimalist Diagram
def create_95_5_diagram(filename):
w, h = 1200, 220
img = Image.new("RGB", (w, h), (13, 19, 38))
draw = ImageDraw.Draw(img)
draw.rounded_rectangle([4, 4, w - 4, h - 4], radius=22, fill=(21, 31, 56), outline=(38, 51, 84), width=2)
font_h = get_font(24, bold=True)
font_b = get_font(20, bold=False)
font_big = get_font(34, bold=True)
draw.text((32, 22), "ПРАВИЛО 95/5 В РЕКЛАМЕ (EHRENBERG-BASS INSTITUTE)", font=font_h, fill=(255, 255, 255))
# Bar chart: 95% vs 5%
bar_x, bar_y, bar_w, bar_h = 32, 70, 1136, 64
w_95 = int(bar_w * 0.88) # visual proportion so 5% is clearly readable
# 95% segment (Royal blue)
draw.rounded_rectangle([bar_x, bar_y, bar_x + w_95, bar_y + bar_h], radius=14, fill=(29, 82, 243))
# 5% segment (Bright Lime Green)
draw.rounded_rectangle([bar_x + w_95 + 8, bar_y, bar_x + bar_w, bar_y + bar_h], radius=14, fill=(92, 225, 22))
draw.text((bar_x + 24, bar_y + 13), "95% АУДИТОРИИ — «МНЕ ПОКА НЕ НАДО» (НУЖЕН ПОВОД)", font=font_h, fill=(255, 255, 255))
draw.text((bar_x + w_95 + 22, bar_y + 13), "5% ИЩУТ", font=font_h, fill=(13, 19, 38))
draw.text((32, 152), "• 95% людей не ищут ваш продукт сегодня — скидки они пролистывают. Их цепляет только триггерная ситуация.", font=font_b, fill=(203, 213, 225))
draw.text((32, 182), "• За 5% готовых купить дерутся все конкуренты. Здесь решает не «скидка 30%», а расчёт и простая посадка.", font=font_b, fill=(92, 225, 22))
img.save(filename)
create_pills_banner("assets/pills_banner.png")
create_vk_banner("assets/vk_banner.png")
create_yd_banner("assets/yd_banner.png")
create_95_5_diagram("assets/rule_95_5.png")
print("Images generated successfully.")