mkdir -p /tmp/workspace/fibroid-notes && python3 << 'PYEOF'
from PIL import Image, ImageDraw, ImageFont
import textwrap
import os
# Canvas: A3 portrait at 150 DPI = 1240 x 1754
W, H = 1240, 3600 # tall enough to fit all content
img = Image.new("RGB", (W, H), color="#FAFAFA")
draw = ImageDraw.Draw(img)
# ββ Fonts ββ
def load_font(size, bold=False):
paths = [
f"/usr/share/fonts/truetype/dejavu/DejaVuSans{'-Bold' if bold else ''}.ttf",
f"/usr/share/fonts/truetype/liberation/LiberationSans{'-Bold' if bold else ''}.ttf",
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
]
for p in paths:
if os.path.exists(p):
return ImageFont.truetype(p, size)
return ImageFont.load_default()
f_title = load_font(36, bold=True)
f_section = load_font(20, bold=True)
f_body = load_font(15)
f_small = load_font(13)
f_bold = load_font(15, bold=True)
# ββ Palette ββ
C_BG = "#FAFAFA"
C_HEADER = "#1A3C5E" # dark navy
C_SEC = "#1A6B8A" # teal
C_SEC2 = "#2E7D32" # green
C_SEC3 = "#7B1FA2" # purple
C_SEC4 = "#C62828" # red
C_SEC5 = "#E65100" # orange
C_SEC6 = "#1565C0" # blue
C_SECBG = "#E3F2FD"
C_ROWALT = "#EFF8FF"
C_ROWALT2 = "#F3E5F5"
C_WHITE = "#FFFFFF"
C_TEXT = "#1A1A1A"
C_MUTED = "#555555"
C_LINE = "#BBDEFB"
PAD = 28
COL_GAP = 18
y = 0
def draw_rect(x, y, w, h, fill, radius=8):
draw.rounded_rectangle([x, y, x+w, y+h], radius=radius, fill=fill)
def draw_header_banner():
draw_rect(0, 0, W, 88, C_HEADER, radius=0)
draw.text((W//2, 26), "π¬ UTERINE FIBROID (LEIOMYOMA / MYOMA)", font=f_title, fill="#FFFFFF", anchor="mt")
draw.text((W//2, 64), "Gynaecology One-Sheet Notes Β· Source: Berek & Novak's Gynecology", font=f_small, fill="#90CAF9", anchor="mt")
return 96
def section_bar(y, label, color, icon=""):
draw_rect(PAD, y, W - 2*PAD, 28, color, radius=5)
draw.text((PAD+10, y+4), f"{icon} {label}" if icon else label, font=f_section, fill=C_WHITE)
return y + 32
def wrap_text(text, font, max_width):
words = text.split()
lines, line = [], ""
for w in words:
test = (line + " " + w).strip()
if draw.textlength(test, font=font) <= max_width:
line = test
else:
if line: lines.append(line)
line = w
if line: lines.append(line)
return lines
def draw_text_block(x, y, text, font, color=C_TEXT, max_w=None):
if max_w:
for ln in wrap_text(text, font, max_w):
draw.text((x, y), ln, font=font, fill=color)
y += font.size + 3
else:
draw.text((x, y), text, font=font, fill=color)
y += font.size + 4
return y
def bullet(x, y, text, font=None, color=C_TEXT, bullet_char="β’", max_w=600):
if font is None: font = f_body
bx = x + 14
draw.text((x, y), bullet_char, font=font, fill=C_SEC)
lines = wrap_text(text, font, max_w - 20)
for i, ln in enumerate(lines):
draw.text((bx, y + i*(font.size+3)), ln, font=font, fill=color)
return y + len(lines)*(font.size+3) + 2
def table_row(cols, widths, x, y, bg, font=None, text_colors=None, pad_x=8, pad_y=5):
if font is None: font = f_body
h = font.size + 2*pad_y + 4
cx = x
for i, (col, w) in enumerate(zip(cols, widths)):
draw_rect(cx, y, w, h, bg, radius=0)
tc = text_colors[i] if text_colors else C_TEXT
draw.text((cx+pad_x, y+pad_y), col, font=font, fill=tc)
cx += w
return y + h
def table_header(cols, widths, x, y, bg=C_HEADER):
return table_row(cols, widths, x, y, bg, font=f_bold, text_colors=[C_WHITE]*len(cols))
# ββββββββββββββββββββββββββββββββββββββββββββββββββββ
y = draw_header_banner()
y += 6
# ββ COLUMNS layout ββ
col1_x = PAD
col2_x = PAD + (W - 2*PAD)//2 + COL_GAP//2
col_w = (W - 2*PAD - COL_GAP) // 2
# βββββββββββββββ LEFT COLUMN βββββββββββββββ
lx = col1_x
ly = y
# DEFINITION
ly = section_bar(ly, "DEFINITION & PATHOGENESIS", C_SEC, "π")
ly += 4
ly = draw_text_block(lx, ly, "Benign, monoclonal smooth muscle tumors of myometrium (collagen, elastin, fibronectin, proteoglycan extracellular matrix).", f_body, max_w=col_w)
ly = draw_text_block(lx, ly, "Do NOT undergo sarcomatous transformation (LMS are genetically distinct).", f_body, C_SEC4, max_w=col_w)
ly += 4
ly = bullet(lx, ly, "Estrogen + Progesterone both promote growth", max_w=col_w)
ly = bullet(lx, ly, "Aromatase in fibroids β de novo estradiol production", max_w=col_w)
ly = bullet(lx, ly, "Increased PR-A and PR-B vs. normal myometrium", max_w=col_w)
ly = bullet(lx, ly, "GnRH agonists shrink fibroids; concurrent progestin blocks this", max_w=col_w)
ly = bullet(lx, ly, "Growth factors: TGF-Ξ², bFGF, EGF, PDGF, VEGF, IGF, Prolactin", max_w=col_w)
ly += 8
# EPIDEMIOLOGY
ly = section_bar(ly, "EPIDEMIOLOGY", C_SEC2, "π")
ly += 4
tw = col_w
ws = [tw//4, tw//4, tw//2]
ly = table_header(["Population", "By Age 35", "By Age 50"], ws, lx, ly)
for row, bg in [
(["African American", "60%", ">80%"], C_WHITE),
(["White women", "40%", "~70%"], C_ROWALT),
(["Hispanic (preg.)", "18%", "-"], C_WHITE),
]:
ly = table_row(row, ws, lx, ly, bg)
ly += 4
ly = bullet(lx, ly, "77% of hysterectomy specimens contain fibroids (serial sectioning)", max_w=col_w)
ly = bullet(lx, ly, "#1 indication for hysterectomy (~240,000/yr in USA)", max_w=col_w)
ly = bullet(lx, ly, "Inpatient surgery costs $2.1 billion/year (USA)", max_w=col_w)
ly += 8
# RISK FACTORS
ly = section_bar(ly, "RISK FACTORS", C_SEC3, "β οΈ")
ly += 4
rw = col_w // 2
ly = table_header(["Increases Risk β", "Decreases Risk β"], [rw, rw], lx, ly)
risks = [
("African American race", "Smoking"),
("Increasing age (peak 40-44)", "Regular exercise"),
("Early menarche (<10 yrs)", "Increased parity"),
("Obesity / Low parity", "Late menarche"),
("Family history", "Oral contraceptives"),
]
for i, (r, p) in enumerate(risks):
bg = C_WHITE if i%2==0 else C_ROWALT2
ly = table_row([r, p], [rw, rw], lx, ly, bg, font=f_small)
ly += 8
# SYMPTOMS
ly = section_bar(ly, "SYMPTOMS", C_SEC4, "π©Ί")
ly += 4
syms = [
("Abnormal Uterine Bleeding", "Types 0-1 (submucosal) most associated; anemia common"),
("Pain", "Degeneration pain - analgesics; Torsion - surgery needed"),
("Bulk symptoms", "Urinary frequency, urgency, constipation, pelvic pressure"),
("Infertility", "Submucosal (cavity-distorting) clearly associated"),
("Urinary", "Frequency, urgency; ureteral obstruction if very large"),
]
sw = [col_w//3, col_w*2//3]
ly = table_header(["Symptom", "Notes"], sw, lx, ly)
for i, (s, n) in enumerate(syms):
bg = C_WHITE if i%2==0 else C_ROWALT
ly = table_row([s, n], sw, lx, ly, bg, font=f_small)
ly += 8
# DIAGNOSIS
ly = section_bar(ly, "DIAGNOSIS", C_SEC5, "π")
ly += 4
dw = [col_w//3, col_w*2//3]
ly = table_header(["Modality", "Use"], dw, lx, ly)
diags = [
("Bimanual exam", "Enlarged, firm, irregular, nontender uterus"),
("TVS (USS)", "First-line; most cost-effective"),
("SIS", "Best for submucosal fibroids Types 0-2"),
("Hysteroscopy", "Direct visualization + treatment of submucosal"),
("MRI", "Gold standard mapping; differentiates adenomyosis"),
("Abdominal USS", "Large / multiple fibroids"),
]
for i, (m, u) in enumerate(diags):
bg = C_WHITE if i%2==0 else C_ROWALT
ly = table_row([m, u], dw, lx, ly, bg, font=f_small)
ly += 8
# DEGENERATION TYPES
ly = section_bar(ly, "FIBROID DEGENERATION", C_SEC6, "π¬")
ly += 4
degt = [
("Hyaline", "Most common type"),
("Calcification", "Postmenopausal; 'womb stones'"),
("Cystic", "Liquefaction after hyaline degeneration"),
("Hemorrhagic/Red", "Classic in PREGNANCY; acute pain"),
("Myxoid", "Rare"),
("Sarcomatous", "Very rare; NOT from benign fibroid"),
]
dw2 = [col_w//3, col_w*2//3]
ly = table_header(["Type", "Notes"], dw2, lx, ly)
for i, (t, n) in enumerate(degt):
bg = C_WHITE if i%2==0 else C_ROWALT
ly = table_row([t, n], dw2, lx, ly, bg, font=f_small)
left_bottom = ly
# βββββββββββββββ RIGHT COLUMN βββββββββββββββ
rx = col2_x
ry = y
# FIGO CLASSIFICATION
ry = section_bar(ry, "FIGO CLASSIFICATION (by location)", C_SEC3, "π")
ry += 4
fw = [col_w//6, col_w//3, col_w//2]
ry = table_header(["Type", "Location", "Description"], fw, rx, ry)
figo = [
("0", "Submucosal", "Pedunculated - entirely intracavitary"),
("1", "Submucosal", "<50% within myometrium"),
("2", "Submucosal", "β₯50% within myometrium"),
("3", "Intramural", "Abuts endometrium, no cavity component"),
("4", "Intramural", "Entirely within myometrium"),
("5", "Subserosal", "β₯50% intramural"),
("6", "Subserosal", "<50% intramural"),
("7", "Subserosal", "Pedunculated, stalk to serosa"),
("8", "Other", "Cervical, broad/round lig., parasitic"),
]
for i, (t, l, d) in enumerate(figo):
bg = C_WHITE if i%2==0 else "#E8F5E9"
bold_types = ["0","1","2"]
tc = [C_SEC4 if t in bold_types else C_TEXT, C_TEXT, C_TEXT]
ry = table_row([t, l, d], fw, rx, ry, bg, font=f_small, text_colors=tc)
ry += 4
draw.text((rx, ry), " β
Types 0-2 = most symptomatic (bleeding, infertility)", font=f_small, fill=C_SEC4)
ry += 18
ry += 8
# FIBROIDS IN PREGNANCY
ry = section_bar(ry, "FIBROIDS IN PREGNANCY", C_SEC5, "π€°")
ry += 4
ry = bullet(rx, ry, "Prevalence: 18% Black | 10% Hispanic | 8% White (1st trimester USS)", max_w=col_w)
ry = bullet(rx, ry, "69% of fibroids do NOT grow during pregnancy", max_w=col_w)
ry = bullet(rx, ry, "If growth occurs - mostly before 10th week of gestation", max_w=col_w)
ry = bullet(rx, ry, "Fibroid degeneration in ~5-9% β acute pain; managed with analgesics (ibuprofen)", max_w=col_w)
ry = bullet(rx, ry, "Uterine rupture risk after myomectomy: 0.47% (trial of labor)", max_w=col_w)
ry += 8
# TREATMENT - MEDICAL
ry = section_bar(ry, "MEDICAL TREATMENT", C_SEC2, "π")
ry += 4
mw = [col_w*2//5, col_w*3//5]
ry = table_header(["Drug", "Notes"], mw, rx, ry)
meds = [
("Tranexamic acid", "1.3 g TDS x3-5 days during menses - antifibrinolytic"),
("NSAIDs", "Reduce dysmenorrhea + blood loss"),
("LNG-IUS (Mirena)", "Reduces HMB; does not shrink fibroid"),
("Combined OCP", "Reduces bleeding; no size reduction"),
("GnRH agonists", "β30% fibroid vol; 6mo preop use; SE: hot flushes, bone loss"),
("GnRH antagonist", "29% reduction in 3 weeks (ganirelix)"),
("Ulipristal acetate", "Progesterone receptor modulator - reduces size + bleeding"),
("Mifepristone", "Antiprogestogen - reduces fibroid size"),
]
for i, (d, n) in enumerate(meds):
bg = C_WHITE if i%2==0 else C_ROWALT
ry = table_row([d, n], mw, rx, ry, bg, font=f_small)
ry += 8
# TREATMENT - SURGICAL
ry = section_bar(ry, "SURGICAL TREATMENT", C_SEC, "π₯")
ry += 4
sw2 = [col_w*2//5, col_w*3//5]
ry = table_header(["Procedure", "Notes"], sw2, rx, ry)
surgs = [
("Hysteroscopic myo.", "Types 0-2; fertility-sparing; day procedure"),
("Laparoscopic myo.", "Subserosal/intramural; multilayer closure essential"),
("Abdominal myo.", "Large/multiple; safe alternative to hysterectomy"),
("UAE", "Effective; fertility outcomes uncertain; uterus-preserving"),
("MRgFUS", "Non-invasive thermal ablation; selected cases"),
("Endometrial ablation", "Heavy bleeding; no fertility wish"),
("Hysterectomy", "Definitive cure; most common fibroid surgery overall"),
]
for i, (p, n) in enumerate(surgs):
bg = C_WHITE if i%2==0 else C_ROWALT2
ry = table_row([p, n], sw2, rx, ry, bg, font=f_small)
ry += 8
# PREOP ANEMIA
ry = section_bar(ry, "PREOPERATIVE ANEMIA MANAGEMENT", C_SEC6, "π©Έ")
ry += 4
ry = bullet(rx, ry, "IV iron infusion: Hb β 3.0 g/dL (vs. 0.8 g/dL oral iron)", max_w=col_w)
ry = bullet(rx, ry, "Epoetin 250 IU/kg/week x3 weeks: Hb β 1.6 g/dL; β transfusion rate", max_w=col_w)
ry = bullet(rx, ry, "GnRH-a + iron x12 weeks: 74% achieve Hb >12 g (vs 46% iron alone)", max_w=col_w)
ry += 8
# INDICATIONS FOR SURGERY
ry = section_bar(ry, "INDICATIONS FOR SURGERY", C_SEC4, "β
")
ry += 4
ind = [
"Heavy menstrual bleeding impairing quality of life",
"Pelvic pain/pressure unresponsive to medical treatment",
"Severe anemia / ureteral obstruction / hydronephrosis",
"Infertility related to submucosal / cavity-distorting fibroid",
"Torsion of pedunculated fibroid (acute emergency - surgery)",
]
not_ind = [
"Inability to palpate ovaries on examination",
"Asymptomatic rapid growth alone (sarcoma risk extremely low)",
]
for i in ind:
ry = bullet(rx, ry, i, bullet_char="β", color="#1B5E20", max_w=col_w)
draw.text((rx, ry), " β NOT indications for surgery:", font=f_bold, fill=C_SEC4)
ry += 20
for n in not_ind:
ry = bullet(rx, ry, n, bullet_char="β", color=C_SEC4, max_w=col_w)
ry += 8
# KEY EXAM POINTS
ry = section_bar(ry, "KEY EXAM POINTS β
", C_HEADER, "π―")
ry += 4
keys = [
"#1 indication for hysterectomy in reproductive age women",
"Benign monoclonal tumor - does NOT become sarcoma",
"Estrogen AND progesterone dependent (both required for growth)",
"Type 0-2 = submucosal (most symptomatic: bleeding + infertility)",
"Red/carneous degeneration = classic in pregnancy",
"MRI = gold standard fibroid mapping & excludes adenomyosis",
"GnRH agonists preoperatively max 3-6 months only (bone loss risk)",
"UAE effective but fertility outcomes remain uncertain",
"Watchful waiting safe for asymptomatic women",
"Fibroid does NOT obstruct ureter unless massively enlarged",
]
for i, k in enumerate(keys):
ry = bullet(rx, ry, k, bullet_char=f"{i+1}.", color=C_TEXT, max_w=col_w)
right_bottom = ry
# ββ Footer ββ
total_h = max(left_bottom, right_bottom) + 40
draw_rect(0, total_h, W, 36, C_HEADER, radius=0)
draw.text((W//2, total_h+8), "Berek & Novak's Gynecology Β· Chapter 11: Uterine Fibroids Β· For Educational Use", font=f_small, fill="#90CAF9", anchor="mt")
total_h += 36
# Crop to content
img_crop = img.crop((0, 0, W, total_h))
out = "/tmp/workspace/fibroid-notes/uterine_fibroid_onesheet.png"
img_crop.save(out, "PNG", dpi=(150,150))
print(f"Saved: {out} | Size: {img_crop.size} | File: {os.path.getsize(out)//1024}KB")
PYEOF
show command