Create a super aesthetic, handwritten-style revision note page for the topic: Tissues • Make it look like neat exam notes written on pages(top view, clean layout, colorful headings, underlines, boxes). • Keep it crisp, high-yield, and exam-oriented. • Use clear headings and organize content like this (modify if needed for topic): ○ Definition ○ Etiology ○ Pathogenesis (flow/arrow style) ○ Clinical features ○ Radiographic features (if applicable) ○ Histopathology (if applicable) ○ Key features ○ Differential diagnosis ○ Management ○ Complications ○ Key points / last-minute revision box • Add simple diagrams or labeled sketches wherever helpful. • Highlight important keywords. • Include one memory trick / one-line summary • Keep handwriting-style fonts, pastel colors, and clean spacing
/home/daytona/workspace
mkdir -p /home/daytona/workspace/tissues-notes && ls /home/daytona/workspace/tissues-notes
~/tissues-notes/generate_notes.py
#!/usr/bin/env python3
"""
Generate aesthetic handwritten-style revision notes for TISSUES topic
Using PIL/Pillow to create a multi-page image
"""
from PIL import Image, ImageDraw, ImageFont
import os
import math
# ── Output path ──────────────────────────────────────────────────────────────
OUT_DIR = "/home/daytona/workspace/tissues-notes/output"
os.makedirs(OUT_DIR, exist_ok=True)
# ── Fonts ─────────────────────────────────────────────────────────────────────
FONT_PATHS = {
"title": "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
"heading": "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
"sub": "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
"body": "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
"italic": "/usr/share/fonts/truetype/dejavu/DejaVuSans-Oblique.ttf",
}
def load_font(key, size):
try:
return ImageFont.truetype(FONT_PATHS[key], size)
except:
return ImageFont.load_default()
# ── Palette ──────────────────────────────────────────────────────────────────
BG = (255, 252, 245) # warm cream paper
RULE_LINE = (210, 200, 185) # faint ruled lines
MARGIN_LINE = (255, 180, 160) # pink margin line
BLACK = (30, 25, 20)
DARK_BLUE = (30, 60, 120)
# Section heading colours (pastel + dark text on light bg)
COLORS = {
"pink": {"bg": (255, 213, 220), "fg": (180, 30, 60), "line": (240, 100, 130)},
"blue": {"bg": (210, 230, 255), "fg": (20, 60, 160), "line": (80, 130, 220)},
"green": {"bg": (200, 240, 210), "fg": (20, 110, 50), "line": (60, 180, 90)},
"purple": {"bg": (230, 215, 255), "fg": (90, 30, 170), "line": (160, 80, 230)},
"orange": {"bg": (255, 230, 200), "fg": (180, 80, 10), "line": (240, 150, 50)},
"teal": {"bg": (200, 240, 240), "fg": (10, 120, 130), "line": (30, 180, 190)},
"yellow": {"bg": (255, 245, 180), "fg": (140, 100, 10), "line": (220, 185, 30)},
"red": {"bg": (255, 210, 205), "fg": (160, 20, 20), "line": (220, 80, 70)},
}
# Section order → colour mapping
SECTION_COLORS = ["pink", "blue", "green", "purple", "orange", "teal", "yellow", "red"]
# ── Canvas ────────────────────────────────────────────────────────────────────
W, H = 1600, 2200 # portrait A4-ish @ 200 dpi
MARGIN_L = 100
MARGIN_R = 80
RULE_STEP = 38
TOP_OFFSET = 180 # below title header
# ── Helpers ───────────────────────────────────────────────────────────────────
def draw_ruled_lines(draw, top, bottom):
for y in range(top, bottom, RULE_STEP):
draw.line([(MARGIN_L + 60, y), (W - MARGIN_R, y)], fill=RULE_LINE, width=1)
def draw_margin(draw):
draw.line([(MARGIN_L + 55, 0), (MARGIN_L + 55, H)], fill=MARGIN_LINE, width=2)
def draw_page_header(draw, page_num, total):
# pink top strip
draw.rectangle([0, 0, W, 60], fill=(255, 182, 193))
f = load_font("title", 32)
draw.text((W//2, 30), "✦ TISSUES – Revision Notes ✦",
font=f, fill=(120, 20, 60), anchor="mm")
# page number
fp = load_font("body", 20)
draw.text((W - MARGIN_R, 45), f"pg {page_num}/{total}",
font=fp, fill=(160, 80, 100), anchor="rm")
def wrap_text(draw, text, font, max_width):
"""Return list of lines that fit within max_width."""
words = text.split()
lines, line = [], ""
for w in words:
test = (line + " " + w).strip()
bb = draw.textbbox((0, 0), test, font=font)
if bb[2] - bb[0] > max_width and line:
lines.append(line)
line = w
else:
line = test
if line:
lines.append(line)
return lines
def draw_section_heading(draw, y, text, color_key, icon=""):
c = COLORS[color_key]
f = load_font("heading", 26)
bb = draw.textbbox((0, 0), f"{icon} {text}", font=f)
tw = bb[2] - bb[0]
pad = 14
rx0, ry0 = MARGIN_L + 60, y - 4
rx1, ry1 = rx0 + tw + pad * 2, y + 36
draw.rounded_rectangle([rx0, ry0, rx1, ry1], radius=8, fill=c["bg"])
draw.line([rx0, ry1 + 2, rx1, ry1 + 2], fill=c["line"], width=3)
draw.text((rx0 + pad, y + 2), f"{icon} {text}", font=f, fill=c["fg"])
return ry1 + 10
def draw_bullet(draw, y, text, font, color=(50, 50, 50), indent=0, bullet="•"):
x = MARGIN_L + 70 + indent
max_w = W - x - MARGIN_R - 10
lines = wrap_text(draw, text, font, max_w)
draw.text((x - 20, y + 2), bullet, font=font, fill=color)
for i, ln in enumerate(lines):
draw.text((x, y + i * RULE_STEP), ln, font=font, fill=color)
return y + len(lines) * RULE_STEP
def draw_highlight(draw, y, text, font, bg=(255, 245, 150), fg=(100, 60, 0)):
x = MARGIN_L + 70
bb = draw.textbbox((0, 0), text, font=font)
tw = bb[2] - bb[0]
draw.rounded_rectangle([x - 4, y - 2, x + tw + 8, y + 28], radius=5, fill=bg)
draw.text((x, y), text, font=font, fill=fg)
return y + RULE_STEP
def draw_box(draw, lines_text, y, color_key, title=""):
c = COLORS[color_key]
fb = load_font("body", 21)
fh = load_font("sub", 22)
x0 = MARGIN_L + 60
x1 = W - MARGIN_R
# measure height
all_lines = []
for ln in lines_text:
wrapped = wrap_text(draw, ln, fb, x1 - x0 - 24)
all_lines.extend(wrapped)
box_h = len(all_lines) * 30 + (40 if title else 16) + 16
draw.rounded_rectangle([x0, y, x1, y + box_h], radius=10,
fill=c["bg"], outline=c["line"], width=2)
if title:
draw.text((x0 + 12, y + 8), title, font=fh, fill=c["fg"])
ty = y + 40
else:
ty = y + 12
for ln in all_lines:
draw.text((x0 + 20, ty), ln, font=fb, fill=BLACK)
ty += 30
return y + box_h + 12
def draw_arrow_flow(draw, items, y, color_key):
"""Draw a horizontal flow: item → item → item"""
c = COLORS[color_key]
fb = load_font("body", 20)
x = MARGIN_L + 70
box_w = 190
box_h = 44
gap = 30
for i, item in enumerate(items):
draw.rounded_rectangle([x, y, x + box_w, y + box_h],
radius=8, fill=c["bg"], outline=c["line"], width=2)
# center text
bb = draw.textbbox((0, 0), item, font=fb)
tw = bb[2] - bb[0]
tx = x + (box_w - tw) // 2
draw.text((tx, y + 10), item, font=fb, fill=c["fg"])
x += box_w
if i < len(items) - 1:
# draw arrow
ay = y + box_h // 2
draw.line([(x, ay), (x + gap - 4, ay)], fill=c["line"], width=2)
draw.polygon([(x + gap, ay), (x + gap - 8, ay - 6), (x + gap - 8, ay + 6)],
fill=c["line"])
x += gap
return y + box_h + 16
def draw_table(draw, headers, rows, y, color_key):
c = COLORS[color_key]
fh = load_font("sub", 20)
fb = load_font("body", 19)
x0 = MARGIN_L + 60
col_w = (W - MARGIN_R - x0) // len(headers)
row_h = 36
# header row
draw.rectangle([x0, y, W - MARGIN_R, y + row_h], fill=c["bg"])
for i, h in enumerate(headers):
draw.text((x0 + i * col_w + 8, y + 8), h, font=fh, fill=c["fg"])
y += row_h
for ridx, row in enumerate(rows):
bg2 = (248, 248, 248) if ridx % 2 == 0 else (238, 238, 248)
draw.rectangle([x0, y, W - MARGIN_R, y + row_h], fill=bg2)
for i, cell in enumerate(row):
draw.text((x0 + i * col_w + 8, y + 8), cell, font=fb, fill=BLACK)
y += row_h
# border
draw.rectangle([x0, y - row_h * (len(rows) + 1), W - MARGIN_R, y],
outline=c["line"], width=2)
return y + 14
def draw_memory_trick(draw, y, trick, summary):
c = COLORS["yellow"]
fb = load_font("italic", 22)
fh = load_font("sub", 22)
x0 = MARGIN_L + 60
x1 = W - MARGIN_R
# star decoration
draw.text((x0 - 10, y - 4), "★", font=load_font("heading", 28), fill=(220, 160, 10))
draw.text((x1 - 20, y - 4), "★", font=load_font("heading", 28), fill=(220, 160, 10))
lines_t = wrap_text(draw, trick, fb, x1 - x0 - 40)
lines_s = wrap_text(draw, summary, fb, x1 - x0 - 40)
total_h = 50 + (len(lines_t) + len(lines_s) + 1) * 30 + 20
draw.rounded_rectangle([x0, y, x1, y + total_h], radius=12,
fill=c["bg"], outline=c["line"], width=3)
draw.text((x0 + 16, y + 8), "🧠 Memory Trick", font=fh, fill=c["fg"])
ty = y + 44
for ln in lines_t:
draw.text((x0 + 20, ty), ln, font=fb, fill=(100, 60, 0))
ty += 30
ty += 6
draw.line([x0 + 16, ty, x1 - 16, ty], fill=c["line"], width=1)
ty += 10
draw.text((x0 + 16, ty), "📌 One-line summary:", font=fh, fill=c["fg"])
ty += 30
for ln in lines_s:
draw.text((x0 + 20, ty), ln, font=fb, fill=BLACK)
ty += 30
return y + total_h + 16
# ══════════════════════════════════════════════════════════════════════════════
# PAGE CONTENT DEFINITIONS
# ══════════════════════════════════════════════════════════════════════════════
def build_page1():
img = Image.new("RGB", (W, H), BG)
draw = ImageDraw.Draw(img)
draw_ruled_lines(draw, TOP_OFFSET, H)
draw_margin(draw)
draw_page_header(draw, 1, 3)
fb = load_font("body", 22)
fbb = load_font("body", 20)
fi = load_font("italic", 21)
fh = load_font("sub", 23)
y = TOP_OFFSET + 10
# ── TITLE ─────────────────────────────────────────────────────────────────
ft = load_font("title", 42)
draw.text((W // 2, y + 16), "TISSUES", font=ft, fill=DARK_BLUE, anchor="mm")
y += 52
draw.line([MARGIN_L + 60, y, W - MARGIN_R, y], fill=DARK_BLUE, width=3)
y += 14
# ── DEFINITION ────────────────────────────────────────────────────────────
y = draw_section_heading(draw, y, "DEFINITION", "pink", "📖")
y = draw_bullet(draw, y, "A tissue is a group of cells with similar structure & function, working together with ECM to perform a specific role.", fb, color=BLACK)
y = draw_bullet(draw, y, "4 Basic tissue types exist in the body:", fb, color=BLACK)
y += 6
# flow diagram
y = draw_arrow_flow(draw, ["Epithelial", "Connective", "Muscle", "Nervous"], y, "pink")
y += 4
# ── OVERVIEW TABLE ────────────────────────────────────────────────────────
y = draw_section_heading(draw, y, "OVERVIEW TABLE", "blue", "📊")
headers = ["Tissue", "Cells", "ECM", "Main Function"]
rows = [
["Epithelial", "Polyhedral, tightly packed", "Small", "Lining, secretion, absorption"],
["Connective", "Fixed + wandering cells", "Abundant", "Support, protection"],
["Muscle", "Elongated, contractile", "Moderate", "Movement, contraction"],
["Nervous", "Neurons + glia", "Very small", "Impulse transmission"],
]
y = draw_table(draw, headers, rows, y, "blue")
# ── EMBRYOLOGICAL ORIGIN ──────────────────────────────────────────────────
y = draw_section_heading(draw, y, "EMBRYOLOGICAL ORIGIN (Histogenesis)", "green", "🧬")
y = draw_arrow_flow(draw, ["Ectoderm", "Mesoderm", "Endoderm"], y, "green")
germ_bullets = [
("Ectoderm →", "Epidermis, CNS (neuroectoderm), cornea, enamel, adenohypophysis, sensory epithelia"),
("Mesoderm →", "Connective tissue, muscle, heart, blood vessels, kidneys, gonads, adrenal cortex"),
("Endoderm →", "GI epithelium, liver, pancreas, gallbladder, respiratory epithelium, thyroid, bladder"),
]
fi2 = load_font("italic", 20)
for label, desc in germ_bullets:
draw.text((MARGIN_L + 70, y + 2), label, font=load_font("sub", 20), fill=COLORS["green"]["fg"])
bb = draw.textbbox((0, 0), label + " ", font=load_font("sub", 20))
xoff = bb[2] - bb[0]
lines = wrap_text(draw, desc, fbb, W - MARGIN_R - MARGIN_L - 80 - xoff)
draw.text((MARGIN_L + 70 + xoff, y + 2), lines[0], font=fbb, fill=BLACK)
for ln in lines[1:]:
y += RULE_STEP
draw.text((MARGIN_L + 90 + xoff, y + 2), ln, font=fbb, fill=BLACK)
y += RULE_STEP
# ── EPITHELIAL TISSUE ─────────────────────────────────────────────────────
y = draw_section_heading(draw, y, "EPITHELIAL TISSUE", "purple", "🔬")
y = draw_bullet(draw, y, "Closely packed cells with minimal ECM; always rests on a basement membrane", fb)
y = draw_bullet(draw, y, "Avascular — nutrients supplied by diffusion from underlying connective tissue", fb)
y = draw_bullet(draw, y, "Shows polarity: apical pole (free surface) ≠ basal pole (attached)", fb)
y = draw_bullet(draw, y, "Functions: covering/lining • absorption • secretion • sensory reception", fb)
y += 6
# Classification sub-box
y = draw_section_heading(draw, y, "Classification of Epithelium", "purple", "📐")
headers2 = ["Layers", "Shape", "Example"]
rows2 = [
["Simple (1 layer)", "Squamous", "Alveoli, Bowman's capsule"],
["Simple", "Cuboidal", "Thyroid follicle, kidney tubules"],
["Simple", "Columnar", "Small intestine (with villi)"],
["Pseudostratified", "Columnar", "Respiratory tract (ciliated)"],
["Stratified (≥2 layers)","Squamous", "Skin (keratinised), oral mucosa"],
["Transitional", "Variable", "Urinary bladder (urothelium)"],
]
y = draw_table(draw, headers2, rows2, y, "purple")
return img
def build_page2():
img = Image.new("RGB", (W, H), BG)
draw = ImageDraw.Draw(img)
draw_ruled_lines(draw, TOP_OFFSET, H)
draw_margin(draw)
draw_page_header(draw, 2, 3)
fb = load_font("body", 22)
fbb = load_font("body", 20)
fi = load_font("italic", 21)
y = TOP_OFFSET + 10
# ── JUNCTIONS ─────────────────────────────────────────────────────────────
y = draw_section_heading(draw, y, "INTERCELLULAR JUNCTIONS (Epithelium)", "orange", "🔗")
junctions = [
("Tight (Occluding)", "Claudin & occludin → prevents paracellular leakage → BARRIER function"),
("Adherens junctions", "Cadherins → Zonula adherens (actin) + Desmosomes / Maculae adherens (keratin IF)"),
("Hemidesmosomes", "Integrins → attach cell to basal lamina"),
("Gap junctions", "Connexins (connexons) → intercellular channels → cell communication & coupling"),
]
for jname, jdesc in junctions:
draw.text((MARGIN_L + 70, y + 2), f"◆ {jname}:", font=load_font("sub", 20), fill=COLORS["orange"]["fg"])
y += RULE_STEP
lines = wrap_text(draw, jdesc, fbb, W - MARGIN_R - MARGIN_L - 100)
for ln in lines:
draw.text((MARGIN_L + 90, y + 2), ln, font=fbb, fill=BLACK)
y += RULE_STEP
# ── APICAL SPECIALISATIONS ────────────────────────────────────────────────
y = draw_section_heading(draw, y, "APICAL SURFACE SPECIALISATIONS", "teal", "⬆️")
apical = [
("Microvilli", "Short membrane protrusions; actin core; ↑ absorption surface area (brush border in gut)"),
("Stereocilia", "Long microvilli (NOT true cilia); mechanosensory (inner ear); absorption (male repro tract)"),
("Cilia", "9+2 microtubule axoneme; dynein-powered; propel mucus (respiratory tract) + oocytes (fallopian tube)"),
]
for aname, adesc in apical:
y = draw_bullet(draw, y, f"{aname}: {adesc}", fb, color=COLORS["teal"]["fg"], bullet="▸")
# ── GLANDS ────────────────────────────────────────────────────────────────
y = draw_section_heading(draw, y, "GLANDS (Secretory Epithelium)", "yellow", "🧪")
y = draw_bullet(draw, y, "Exocrine glands — have ducts; deliver secretion to surface", fb)
y = draw_bullet(draw, y, "Endocrine glands — no ducts; secrete hormones directly into blood", fb)
y += 4
secretion_data = [
("Merocrine", "Exocytosis (no cell loss)", "Pancreatic acini, salivary glands"),
("Apocrine", "Apical cytoplasm pinched off", "Mammary glands, axillary sweat glands"),
("Holocrine", "Whole cell disintegrates", "Sebaceous glands"),
]
y = draw_table(draw, ["Type", "Mechanism", "Example"], secretion_data, y, "yellow")
y = draw_bullet(draw, y, "Mucous glands → mucin (stains with PAS); Serous glands → enzymes/proteins (stains dark H&E)", fbb)
# ── CONNECTIVE TISSUE ─────────────────────────────────────────────────────
y = draw_section_heading(draw, y, "CONNECTIVE TISSUE", "blue", "🕸️")
y = draw_bullet(draw, y, "Characterised by its EXTRACELLULAR MATRIX (ECM) — most abundant component", fb)
y = draw_bullet(draw, y, "Underlies & supports all other three tissue types", fb)
y += 4
ct_types = [
("Embryonic CT", "Mesenchyme (most), Mucous CT (Wharton's jelly in umbilical cord)"),
("CT Proper – Loose", "Areolar, Adipose, Reticular tissue"),
("CT Proper – Dense", "Dense regular (tendons, ligaments) / Dense irregular (dermis)"),
("Specialised CT", "Bone, Cartilage (hyaline/elastic/fibrocartilage), Blood, Lymph"),
]
for tname, tdesc in ct_types:
draw.text((MARGIN_L + 70, y + 2), f"• {tname}:", font=load_font("sub", 20), fill=COLORS["blue"]["fg"])
y += RULE_STEP
lines = wrap_text(draw, tdesc, fbb, W - MARGIN_R - MARGIN_L - 100)
for ln in lines:
draw.text((MARGIN_L + 90, y + 2), ln, font=fbb, fill=BLACK)
y += RULE_STEP
# ECM sub-section
y = draw_section_heading(draw, y, "ECM Components", "blue", "🔩")
y = draw_bullet(draw, y, "Fibres: Collagen (types I–IV+), Elastic fibres, Reticular fibres (type III collagen)", fb)
y = draw_bullet(draw, y, "Ground substance: GAGs (hyaluronic acid, heparan sulphate), Proteoglycans, Adhesion proteins (fibronectin, laminin)", fb)
y = draw_bullet(draw, y, "Cells: Fibroblasts, Macrophages, Mast cells, Plasma cells, Adipocytes, Leukocytes", fb)
# ── MUSCLE TISSUE ─────────────────────────────────────────────────────────
y = draw_section_heading(draw, y, "MUSCLE TISSUE", "green", "💪")
headers3 = ["Feature", "Skeletal", "Cardiac", "Smooth"]
rows3 = [
["Striations", "Yes", "Yes", "No"],
["Nuclei", "Multiple, peripheral", "1–2, central", "1, central"],
["Control", "Voluntary", "Involuntary", "Involuntary"],
["T-tubules", "Wide (triad)", "Narrow (dyad)", "Absent"],
["Intercalated", "Absent", "Present", "Absent"],
["Regeneration", "Satellite cells", "Very limited (fibrosis)", "Yes (mitosis)"],
]
y = draw_table(draw, headers3, rows3, y, "green")
y = draw_highlight(draw, y, " KEY: All muscle cells contain ACTIN + MYOSIN (contractile proteins) ",
load_font("sub", 21), bg=(200, 255, 200), fg=(10, 100, 30))
return img
def build_page3():
img = Image.new("RGB", (W, H), BG)
draw = ImageDraw.Draw(img)
draw_ruled_lines(draw, TOP_OFFSET, H)
draw_margin(draw)
draw_page_header(draw, 3, 3)
fb = load_font("body", 22)
fbb = load_font("body", 20)
fi = load_font("italic", 21)
y = TOP_OFFSET + 10
# ── NERVOUS TISSUE ────────────────────────────────────────────────────────
y = draw_section_heading(draw, y, "NERVOUS TISSUE", "purple", "⚡")
y = draw_bullet(draw, y, "Receives, integrates & transmits information — electrical impulses (action potentials)", fb)
y = draw_bullet(draw, y, "Components: Neurons + Supporting (glial) cells", fb)
y += 4
neuron_rows = [
["Cell body (Soma)", "Contains nucleus, Nissl bodies (RER), organelles"],
["Axon", "Carries impulses AWAY from cell body; 1 per neuron; may be myelinated"],
["Dendrites", "Carry impulses TOWARD cell body; multiple per neuron; no myelin"],
]
y = draw_table(draw, ["Part", "Description"], neuron_rows, y, "purple")
y = draw_section_heading(draw, y, "Glial Cells", "purple", "🧠")
glial = [
("Astrocytes", "Blood-brain barrier; metabolic support; K+ buffering"),
("Oligodendrocytes", "Myelin sheath in CNS (one cell → many axons)"),
("Schwann cells", "Myelin sheath in PNS (one cell → one axon)"),
("Microglia", "CNS macrophages; immune surveillance"),
("Ependymal cells", "Line ventricles; produce CSF"),
("Satellite cells", "Support neurons in PNS ganglia"),
]
for gname, gdesc in glial:
y = draw_bullet(draw, y, f"{gname}: {gdesc}", fbb, color=COLORS["purple"]["fg"], bullet="▸")
# ── BASEMENT MEMBRANE ─────────────────────────────────────────────────────
y = draw_section_heading(draw, y, "BASEMENT MEMBRANE (Key Exam Point)", "red", "🧱")
bm_items = [
"Basal lamina (made by epithelial cells): Type IV collagen + Laminin",
"Reticular lamina (made by CT cells): Type III collagen (reticulin) + Type VII anchoring fibrils",
"Functions: Attachment • Filtration • Scaffold for regeneration • Compartmentalisation",
"Stains: PAS-positive (basement membrane), Silver stain (reticular fibres)",
]
for item in bm_items:
y = draw_bullet(draw, y, item, fbb, bullet="✔")
# ── KEY FACTS BOX ─────────────────────────────────────────────────────────
y = draw_section_heading(draw, y, "HIGH-YIELD KEY FACTS", "orange", "⭐")
kf = [
"Only tissue type that is AVASCULAR → Epithelium",
"Only epithelium found in urinary tract → Transitional (Urothelium)",
"Respiratory epithelium → Pseudostratified ciliated columnar with goblet cells",
"ECM most abundant in → Connective tissue",
"Cardiac muscle unique feature → Intercalated discs (desmosomes + gap junctions)",
"Repair: Skeletal muscle = satellite cells | Cardiac = fibrosis | Smooth = mitosis",
"Myelination: CNS = Oligodendrocytes | PNS = Schwann cells",
"Wharton's jelly (umbilical cord) = mucous connective tissue (embryonic)",
]
for kf_item in kf:
y = draw_bullet(draw, y, kf_item, fbb, color=COLORS["orange"]["fg"], bullet="★")
y += 4
# ── LAST-MINUTE REVISION BOX ──────────────────────────────────────────────
y = draw_box(draw, [
"4 Tissue types: Epithelial · Connective · Muscle · Nervous",
"Epithelium: avascular, on BM, polar, no ECM",
"CT: most ECM, supports all others",
"Muscle: actin+myosin; Skeletal=voluntary; Cardiac=intercalated discs",
"Nervous: neurons (axon away, dendrites toward) + glia",
"Germ layers → Ecto (epidermis/CNS) | Meso (CT/muscle) | Endo (GI/respiratory)",
], y, "teal", title="📋 LAST-MINUTE REVISION")
# ── MEMORY TRICK ─────────────────────────────────────────────────────────
y = draw_memory_trick(
draw, y,
trick='Mnemonic: "Every Cat Meows Nicely" → Epithelial · Connective · Muscle · Nervous',
summary="Tissues are organised assemblies of specialised cells + ECM performing a shared function; "
"the 4 basic types arise from 3 embryonic germ layers and collectively form every organ in the body."
)
return img
# ── Build & save ──────────────────────────────────────────────────────────────
pages = [build_page1(), build_page2(), build_page3()]
# Save individual pages
for i, page in enumerate(pages):
path = f"{OUT_DIR}/tissues_notes_page{i+1}.png"
page.save(path, "PNG", dpi=(200, 200))
print(f"Saved: {path}")
# Also save a combined vertical strip
total_h = sum(p.height for p in pages)
combined = Image.new("RGB", (W, total_h), BG)
cy = 0
for page in pages:
combined.paste(page, (0, cy))
cy += page.height
combined_path = f"{OUT_DIR}/tissues_notes_ALL_PAGES.png"
combined.save(combined_path, "PNG")
print(f"Combined saved: {combined_path}")
print("Done!")