Can you make a detailed notes on cutaneous hyperandrogenism , etiology, pathogenesis, approach, clinical features, management for university theory md dermatology examination
| Condition | Notes |
|---|---|
| Polycystic Ovarian Syndrome (PCOS) | Most common cause; biochemical or clinical hyperandrogenism + chronic anovulation (<9 periods/year) without adrenal or pituitary disease |
| Functional ovarian hyperandrogenism | Ovarian steroidogenic dysregulation |
| Ovarian neoplasms | Sertoli-Leydig cell tumors, granulosa-theca cell tumors, hilus cell tumors - low metastatic potential, surgical treatment |
| Hyperthecosis | Hyperplasia of theca interna cells |
| Syndromes of extreme insulin resistance | Severe acanthosis nigricans |
| Condition | Notes |
|---|---|
| Non-classic (late-onset) Congenital Adrenal Hyperplasia (NCAH) | Most commonly 21-hydroxylase deficiency (CYP21A2 mutation); elevated 17-OHP is hallmark |
| Cushing syndrome | ACTH-driven adrenal androgen excess |
| Adrenal adenoma | Secretes testosterone |
| Adrenal carcinoma | Secretes testosterone + DHEA-S + cortisol |
| Premature adrenarche | |
| Functional adrenal hyperandrogenism |
| Condition | Notes |
|---|---|
| Idiopathic hyperandrogenism | Elevated androgens without identifiable cause |
| Idiopathic hirsutism | 5-15% of cases; normal androgens, regular cycles - increased end-organ sensitivity |
| Hyperprolactinaemia | Prolactinoma, drugs, hypothyroidism; hirsutism + galactorrhoea |
| Acromegaly | Elevated GH and IGF-1 |
| Hypothyroidism | Reduces SHBG; increases free androgens |
| HAIR-AN syndrome | Hyperandrogenism, Insulin Resistance, Acanthosis Nigricans |
| Pregnancy | Luteoma, hyperreactio luteinalis, aromatase deficiency |
| Test | Purpose |
|---|---|
| Free testosterone (or total testosterone) | Best screening marker; most sensitive biochemical marker of hyperandrogenism |
| DHEA-S | Screens for adrenal androgen source; elevated in adrenal tumours and CAH |
| LH:FSH ratio | Ratio >2-3 suggests PCOS |
| Prolactin | Screens for hyperprolactinaemia |
| TSH | Rules out hypothyroidism |
| Fasting glucose/GTT | Insulin resistance assessment |
| 17-OHP (early morning) | Elevated in 21-hydroxylase deficiency NCAH (>10 ng/mL after ACTH stimulation) |
| Test | Indication |
|---|---|
| ACTH stimulation test | Confirm NCAH (17-OHP >10 ng/mL post-ACTH) |
| Dexamethasone suppression test | Distinguish adrenal vs. ovarian vs. Cushing origin |
| 24-hour urinary cortisol / late-night salivary cortisol | Rule out Cushing syndrome |
| Pelvic ultrasound | PCOS (polycystic ovaries), ovarian tumour |
| CT abdomen | Adrenal mass (if DHEA-S 2-3× upper limit) |
| Pregnancy test (β-hCG) | Rule out pregnancy if amenorrhoeic |
| IGF-1, GH | Acromegaly |

| Drug | Mechanism | Main Uses | Key Side Effects | Contraindication |
|---|---|---|---|---|
| Spironolactone | AR antagonist + 5α-RI + ↓ androgen synthesis | Hirsutism, acne, AGA | Hyperkalaemia, menstrual irregularity, breast tenderness | Pregnancy |
| Cyproterone acetate | AR antagonist + progestin | Hirsutism, acne, AGA | Hepatotoxicity (high dose), depression | Pregnancy |
| Finasteride | 5α-RI (type 1+2) | AGA (men), hirsutism | Decreased libido, ED, depression, post-finasteride syndrome | Pregnancy (teratogenic) |
| Dutasteride | 5α-RI dual (type 1+2) | AGA, hirsutism | Same as finasteride | Pregnancy |
| Flutamide | AR antagonist | Hirsutism | Fatal hepatitis | Pregnancy, liver disease |
| Bicalutamide | AR antagonist (pure non-steroidal) | Hirsutism (PCOS, idiopathic) | Hepatotoxicity at ≥50 mg | Pregnancy |
| OCP | ↑ SHBG, ↓ LH, antiandrogenic | Hirsutism, acne, AGA | Thromboembolism, hypertension | Thrombophilia, age >35 + smoking |
| Metformin | Insulin sensitiser (indirect) | PCOS | GI side effects, lactic acidosis | Renal failure |
| Clascoterone | Topical AR antagonist | Acne | Local irritation | - |
| Eflornithine | Ornithine decarboxylase inhibitor | Facial hirsutism | Local irritation | - |
Can you make this notes into download able colourful pdf include all information
mkdir -p /tmp/workspace/cutaneous-hyperandrogenism && ls /tmp/workspace/
~/cutaneous-hyperandrogenism/generate_pdf.py
#!/usr/bin/env python3
"""
Cutaneous Hyperandrogenism - MD Dermatology Notes PDF Generator
Colourful, well-structured, exam-ready PDF using ReportLab
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import mm, cm
from reportlab.lib import colors
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether
)
from reportlab.platypus.flowables import BalancedColumns
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.pdfgen import canvas
from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate
# ── Colour Palette ──────────────────────────────────────────────────────────
DARK_NAVY = colors.HexColor("#1A237E") # Title / major headings
ROYAL_BLUE = colors.HexColor("#1565C0") # Section headers
TEAL = colors.HexColor("#00695C") # Sub-headings
ORANGE = colors.HexColor("#E65100") # Highlight / accent
AMBER = colors.HexColor("#F57F17") # Warning / key-point boxes
GREEN_DARK = colors.HexColor("#2E7D32") # Management headers
PURPLE = colors.HexColor("#6A1B9A") # Special boxes
RED_DARK = colors.HexColor("#B71C1C") # Red-flag boxes
# Table header backgrounds
TBL_HDR_NAVY = colors.HexColor("#283593")
TBL_HDR_TEAL = colors.HexColor("#00796B")
TBL_HDR_GREEN = colors.HexColor("#388E3C")
TBL_HDR_PURP = colors.HexColor("#7B1FA2")
# Soft backgrounds for boxes
BOX_YELLOW = colors.HexColor("#FFFDE7")
BOX_BLUE = colors.HexColor("#E3F2FD")
BOX_GREEN = colors.HexColor("#E8F5E9")
BOX_RED = colors.HexColor("#FFEBEE")
BOX_PURPLE = colors.HexColor("#F3E5F5")
BOX_TEAL = colors.HexColor("#E0F2F1")
BOX_ORANGE = colors.HexColor("#FFF3E0")
PAGE_W, PAGE_H = A4
MARGIN = 18*mm
# ── Page numbering canvas ────────────────────────────────────────────────────
def add_page_decorations(canvas_obj, doc):
canvas_obj.saveState()
w, h = A4
# Header bar
canvas_obj.setFillColor(DARK_NAVY)
canvas_obj.rect(0, h - 14*mm, w, 14*mm, fill=1, stroke=0)
canvas_obj.setFillColor(colors.white)
canvas_obj.setFont("Helvetica-Bold", 9)
canvas_obj.drawString(MARGIN, h - 9*mm, "CUTANEOUS HYPERANDROGENISM")
canvas_obj.setFont("Helvetica", 8)
canvas_obj.drawRightString(w - MARGIN, h - 9*mm, "MD Dermatology Examination Notes")
# Footer bar
canvas_obj.setFillColor(ROYAL_BLUE)
canvas_obj.rect(0, 0, w, 10*mm, fill=1, stroke=0)
canvas_obj.setFillColor(colors.white)
canvas_obj.setFont("Helvetica", 8)
canvas_obj.drawCentredString(w/2, 3.5*mm, f"Page {doc.page}")
canvas_obj.drawString(MARGIN, 3.5*mm, "Sources: Fitzpatrick's Dermatology 9e | Andrews' 13e | Dermatology 2-Vol Set 5e")
canvas_obj.restoreState()
# ── Style factory ────────────────────────────────────────────────────────────
def make_styles():
base = getSampleStyleSheet()
s = {}
s['doc_title'] = ParagraphStyle(
'doc_title', parent=base['Normal'],
fontName='Helvetica-Bold', fontSize=24,
textColor=colors.white, alignment=TA_CENTER,
spaceAfter=4, spaceBefore=0, leading=30)
s['doc_sub'] = ParagraphStyle(
'doc_sub', parent=base['Normal'],
fontName='Helvetica', fontSize=12,
textColor=colors.HexColor("#BBDEFB"), alignment=TA_CENTER,
spaceAfter=2, leading=16)
s['section'] = ParagraphStyle(
'section', parent=base['Normal'],
fontName='Helvetica-Bold', fontSize=13,
textColor=colors.white, alignment=TA_LEFT,
spaceBefore=10, spaceAfter=4, leading=17,
leftIndent=0, borderPadding=(4, 8, 4, 8))
s['subsection'] = ParagraphStyle(
'subsection', parent=base['Normal'],
fontName='Helvetica-Bold', fontSize=11,
textColor=TEAL, alignment=TA_LEFT,
spaceBefore=7, spaceAfter=3, leading=14)
s['subsubsection'] = ParagraphStyle(
'subsubsection', parent=base['Normal'],
fontName='Helvetica-Bold', fontSize=10,
textColor=ORANGE, alignment=TA_LEFT,
spaceBefore=5, spaceAfter=2, leading=13)
s['body'] = ParagraphStyle(
'body', parent=base['Normal'],
fontName='Helvetica', fontSize=9.5,
textColor=colors.HexColor("#212121"),
alignment=TA_JUSTIFY, spaceBefore=2, spaceAfter=2, leading=14,
leftIndent=0)
s['bullet'] = ParagraphStyle(
'bullet', parent=base['Normal'],
fontName='Helvetica', fontSize=9.5,
textColor=colors.HexColor("#212121"),
alignment=TA_LEFT, spaceBefore=1, spaceAfter=1, leading=13,
leftIndent=12, bulletIndent=4)
s['bullet2'] = ParagraphStyle(
'bullet2', parent=base['Normal'],
fontName='Helvetica', fontSize=9,
textColor=colors.HexColor("#424242"),
alignment=TA_LEFT, spaceBefore=1, spaceAfter=1, leading=12,
leftIndent=24, bulletIndent=14)
s['keypoint'] = ParagraphStyle(
'keypoint', parent=base['Normal'],
fontName='Helvetica-Bold', fontSize=9.5,
textColor=AMBER, alignment=TA_LEFT,
spaceBefore=2, spaceAfter=2, leading=13, leftIndent=4)
s['redbox'] = ParagraphStyle(
'redbox', parent=base['Normal'],
fontName='Helvetica-Bold', fontSize=9.5,
textColor=RED_DARK, alignment=TA_LEFT,
spaceBefore=2, spaceAfter=2, leading=13, leftIndent=4)
s['tbl_hdr'] = ParagraphStyle(
'tbl_hdr', parent=base['Normal'],
fontName='Helvetica-Bold', fontSize=9,
textColor=colors.white, alignment=TA_CENTER, leading=12)
s['tbl_cell'] = ParagraphStyle(
'tbl_cell', parent=base['Normal'],
fontName='Helvetica', fontSize=8.5,
textColor=colors.HexColor("#212121"), alignment=TA_LEFT, leading=11)
s['tbl_cell_bold'] = ParagraphStyle(
'tbl_cell_bold', parent=base['Normal'],
fontName='Helvetica-Bold', fontSize=8.5,
textColor=colors.HexColor("#1A237E"), alignment=TA_LEFT, leading=11)
s['caption'] = ParagraphStyle(
'caption', parent=base['Normal'],
fontName='Helvetica-Oblique', fontSize=8,
textColor=colors.HexColor("#616161"), alignment=TA_CENTER,
spaceBefore=2, spaceAfter=4, leading=11)
s['box_title'] = ParagraphStyle(
'box_title', parent=base['Normal'],
fontName='Helvetica-Bold', fontSize=10,
textColor=PURPLE, alignment=TA_LEFT,
spaceBefore=2, spaceAfter=2, leading=14)
s['exam_point'] = ParagraphStyle(
'exam_point', parent=base['Normal'],
fontName='Helvetica', fontSize=9.5,
textColor=colors.HexColor("#212121"),
alignment=TA_LEFT, spaceBefore=1, spaceAfter=1, leading=13,
leftIndent=8)
return s
# ── Helper: section header ───────────────────────────────────────────────────
def section_header(text, style, bg_color=ROYAL_BLUE):
data = [[Paragraph(f" {text}", style)]]
t = Table(data, colWidths=[PAGE_W - 2*MARGIN])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), bg_color),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 8),
('RIGHTPADDING', (0,0), (-1,-1), 8),
('ROUNDEDCORNERS', [4, 4, 4, 4]),
]))
return t
def coloured_box(content_rows, bg_color=BOX_BLUE, border_color=ROYAL_BLUE):
"""Wraps content in a coloured background box."""
data = [[c] for c in content_rows]
flat = [[item for sublist in data for item in sublist]]
inner = []
for row in content_rows:
inner.append([row])
t = Table(inner, colWidths=[PAGE_W - 2*MARGIN - 4])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), bg_color),
('BOX', (0,0), (-1,-1), 1, border_color),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 8),
('RIGHTPADDING', (0,0), (-1,-1), 8),
]))
return t
# ── Main PDF builder ─────────────────────────────────────────────────────────
def build_pdf(output_path):
doc = SimpleDocTemplate(
output_path,
pagesize=A4,
leftMargin=MARGIN, rightMargin=MARGIN,
topMargin=20*mm, bottomMargin=16*mm,
title="Cutaneous Hyperandrogenism - MD Dermatology Notes",
author="Orris Medical AI",
subject="MD Dermatology University Examination Notes"
)
s = make_styles()
story = []
col_w = PAGE_W - 2*MARGIN
# ── TITLE PAGE ────────────────────────────────────────────────────────────
title_bg = Table(
[[Paragraph("CUTANEOUS HYPERANDROGENISM", s['doc_title'])],
[Paragraph("Comprehensive Notes for MD Dermatology University Theory Examination", s['doc_sub'])],
[Spacer(1, 4)],
[Paragraph("Etiology · Pathogenesis · Clinical Features · Approach · Management", s['doc_sub'])],
[Spacer(1, 6)],
[Paragraph("Sources: Fitzpatrick's Dermatology 9e | Andrews' Diseases of the Skin 13e | Dermatology 2-Vol Set 5e",
ParagraphStyle('src', fontName='Helvetica', fontSize=9, textColor=colors.HexColor("#90CAF9"), alignment=TA_CENTER, leading=12))]
],
colWidths=[col_w]
)
title_bg.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), DARK_NAVY),
('TOPPADDING', (0,0), (-1,-1), 14),
('BOTTOMPADDING', (0,0), (-1,-1), 14),
('LEFTPADDING', (0,0), (-1,-1), 14),
('RIGHTPADDING', (0,0), (-1,-1), 14),
('ROUNDEDCORNERS', [8, 8, 8, 8]),
]))
story.append(title_bg)
story.append(Spacer(1, 10))
# ── DEFINITION ────────────────────────────────────────────────────────────
story.append(section_header("1. DEFINITION", s['section'], ROYAL_BLUE))
story.append(Spacer(1, 4))
story.append(Paragraph(
"<b>Cutaneous hyperandrogenism</b> refers to clinical manifestations in the skin, hair, and "
"sebaceous glands resulting from <b>excessive androgen activity</b> — either due to elevated circulating "
"androgens <i>or</i> increased end-organ sensitivity to normal androgen levels.",
s['body']))
story.append(Spacer(1, 4))
# SAHA box
saha_data = [
[Paragraph("★ SAHA SYNDROME", s['box_title'])],
[Paragraph("<b>S</b>eborrhoea + <b>A</b>cne + <b>H</b>irsutism + <b>A</b>ndrogenetic Alopecia", s['keypoint'])],
[Paragraph("Four subtypes: Idiopathic · Ovarian · Adrenal · Hyperprolactinaemic | Prevalence: 17.7% of PCOS patients", s['body'])],
]
story.append(coloured_box([row[0] for row in saha_data], BOX_PURPLE, PURPLE))
story.append(Spacer(1, 3))
hair_data = [
[Paragraph("★ HAIR-AN SYNDROME", ParagraphStyle('ht', fontName='Helvetica-Bold', fontSize=10, textColor=RED_DARK))],
[Paragraph("<b>H</b>yperandrogenism + <b>I</b>nsulin <b>R</b>esistance + <b>A</b>canthosis <b>N</b>igricans", s['keypoint'])],
]
story.append(coloured_box([row[0] for row in hair_data], BOX_RED, RED_DARK))
story.append(Spacer(1, 6))
# ── ETIOLOGY ─────────────────────────────────────────────────────────────
story.append(section_header("2. ETIOLOGY", s['section'], ROYAL_BLUE))
story.append(Spacer(1, 5))
# Etiology table
etio_headers = [
Paragraph("SOURCE", s['tbl_hdr']),
Paragraph("CONDITION", s['tbl_hdr']),
Paragraph("KEY FEATURES", s['tbl_hdr'])
]
def ec(txt, bold=False):
st = s['tbl_cell_bold'] if bold else s['tbl_cell']
return Paragraph(txt, st)
etio_data = [
etio_headers,
[ec("OVARIAN", bold=True), ec("PCOS ★ Most common", bold=True),
ec("Biochemical/clinical hyperandrogenism + anovulation; LH:FSH >2-3; polycystic ovaries on USG")],
[ec(""), ec("Functional ovarian hyperandrogenism"), ec("Ovarian steroidogenic dysregulation")],
[ec(""), ec("Ovarian neoplasms"), ec("Sertoli-Leydig cell tumour, granulosa-theca cell tumour, hilus cell tumour — low metastatic potential; surgical Rx")],
[ec(""), ec("Hyperthecosis"), ec("Hyperplasia of theca interna cells; more severe hyperandrogenism than PCOS")],
[ec(""), ec("HAIR-AN syndrome"), ec("Hyperandrogenism + Insulin Resistance + Acanthosis Nigricans")],
[ec("ADRENAL", bold=True), ec("NCAH (21-OH deficiency) ★", bold=True),
ec("CYP21A2 mutation; elevated 17-OHP; late-onset; peripubertal hirsutism, acne, amenorrhoea")],
[ec(""), ec("Cushing syndrome"), ec("Cortisol excess + ACTH-driven adrenal androgen excess; purple striae, moon facies")],
[ec(""), ec("Adrenal adenoma"), ec("Secretes testosterone; usually unilateral")],
[ec(""), ec("Adrenal carcinoma"), ec("Secretes testosterone + DHEA-S + cortisol; rapid-onset virilisation")],
[ec(""), ec("Premature adrenarche"), ec("Early adrenal androgen secretion before age 8 in girls")],
[ec("PITUITARY /\nOTHER", bold=True), ec("Hyperprolactinaemia"), ec("Prolactinoma, drugs, hypothyroidism; hirsutism + galactorrhoea")],
[ec(""), ec("Acromegaly"), ec("Elevated GH + IGF-1; coarsened skin, macroglossia, enlarged hands")],
[ec(""), ec("Hypothyroidism"), ec("Reduces SHBG → increased free androgens")],
[ec("IDIOPATHIC", bold=True), ec("Idiopathic hirsutism"), ec("Normal androgens, regular cycles; increased 5α-reductase activity locally (5–15% of cases)")],
[ec(""), ec("Idiopathic hyperandrogenism"), ec("Elevated androgens, no identifiable source")],
[ec("DRUGS", bold=True), ec("Androgenic medications"), ec("Exogenous androgens, anabolic steroids, danazol, norgestrel, norethindrone, valproate, minoxidil")],
[ec("PREGNANCY", bold=True), ec("Pregnancy-related"), ec("Luteoma of pregnancy, hyperreactio luteinalis, fetal aromatase deficiency")],
]
etio_colw = [col_w*0.13, col_w*0.32, col_w*0.55]
etio_table = Table(etio_data, colWidths=etio_colw, repeatRows=1)
etio_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), TBL_HDR_NAVY),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 9),
('ALIGN', (0,0), (-1,0), 'CENTER'),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor("#EEF2FF"), colors.white]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#BDBDBD")),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('VALIGN', (0,0), (-1,-1), 'TOP'),
# Colour rows by source
('BACKGROUND', (0,1), (0,5), colors.HexColor("#E3F2FD")),
('BACKGROUND', (0,6), (0,10), colors.HexColor("#E8F5E9")),
('BACKGROUND', (0,11), (0,13), colors.HexColor("#F3E5F5")),
('BACKGROUND', (0,14), (0,15), colors.HexColor("#FFF3E0")),
('BACKGROUND', (0,16), (0,16), colors.HexColor("#FFEBEE")),
('BACKGROUND', (0,17), (0,17), colors.HexColor("#FCE4EC")),
]))
story.append(etio_table)
story.append(Spacer(1, 8))
# ── PATHOGENESIS ──────────────────────────────────────────────────────────
story.append(section_header("3. PATHOGENESIS", s['section'], TEAL))
story.append(Spacer(1, 5))
story.append(Paragraph("A. Androgen Physiology in Skin", s['subsection']))
story.append(Paragraph(
"<b>Sources of androgens in women:</b> Ovaries (testosterone, androstenedione) + "
"Adrenal cortex (DHEA, DHEA-S, androstenedione) + Peripheral conversion (skin, fat, liver).",
s['body']))
story.append(Spacer(1, 3))
# Key hormones mini table
horm_headers = [Paragraph("HORMONE", s['tbl_hdr']), Paragraph("POTENCY / ROLE", s['tbl_hdr']), Paragraph("NOTES", s['tbl_hdr'])]
horm_data = [
horm_headers,
[ec("Testosterone"), ec("Principal circulating androgen"), ec("Converted to DHT in target tissues by 5α-reductase")],
[ec("DHT (5α-dihydrotestosterone)", bold=True), ec("★ Most potent intracellular androgen"), ec("Higher receptor binding affinity than testosterone; acts on sebaceous glands, hair follicles")],
[ec("DHEA / DHEA-S"), ec("Weak androgens"), ec("Adrenal origin; large quantities; substrate for peripheral conversion")],
[ec("Androstenedione"), ec("Intermediate"), ec("Converted to testosterone peripherally; ovarian + adrenal source")],
[ec("Progesterone"), ec("Progestational"), ec("Binds androgen receptor weakly; can have androgenic effects")],
]
horm_colw = [col_w*0.22, col_w*0.30, col_w*0.48]
horm_table = Table(horm_data, colWidths=horm_colw, repeatRows=1)
horm_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), TBL_HDR_TEAL),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor("#E0F2F1"), colors.white]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#BDBDBD")),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('BACKGROUND', (0,2), (-1,2), colors.HexColor("#B2DFDB")),
]))
story.append(horm_table)
story.append(Spacer(1, 5))
# 5a reductase and SHBG
story.append(Paragraph("B. Key Regulatory Mechanisms", s['subsection']))
enzyme_box = [
Paragraph("<b>5α-Reductase</b>", s['subsubsection']),
Paragraph("Converts Testosterone → DHT in peripheral target tissues (sebaceous glands, hair follicles, genital skin)", s['body']),
Paragraph("• <b>Type 1:</b> Sebaceous glands, liver, skin", s['bullet']),
Paragraph("• <b>Type 2:</b> Hair follicles, genital skin, prostate", s['bullet']),
Paragraph("Drugs that inhibit this enzyme: Finasteride (Type 1+2) | Dutasteride (dual Type 1+2)", s['body']),
]
story.append(coloured_box(enzyme_box, BOX_TEAL, TEAL))
story.append(Spacer(1, 3))
shbg_box = [
Paragraph("<b>Sex Hormone-Binding Globulin (SHBG)</b>", s['subsubsection']),
Paragraph("Binds testosterone and estrogen; only <b>free (unbound) fraction</b> is biologically active.", s['body']),
Paragraph("SHBG is <b>decreased</b> by (→ more free androgens):", s['body']),
Paragraph("• Synthetic progestins (norgestrel, norethindrone, norgestimate, desogestrel)", s['bullet']),
Paragraph("• Obesity · Insulin resistance · Hypothyroidism · Acromegaly", s['bullet']),
Paragraph("SHBG is <b>increased</b> by: Oestrogens · OCPs (oestrogen component) → less free androgen", s['bullet']),
]
story.append(coloured_box(shbg_box, BOX_BLUE, ROYAL_BLUE))
story.append(Spacer(1, 5))
story.append(Paragraph("C. Mechanisms of Individual Cutaneous Manifestations", s['subsection']))
mech_headers = [Paragraph("MANIFESTATION", s['tbl_hdr']), Paragraph("MECHANISM", s['tbl_hdr'])]
mech_data = [
mech_headers,
[ec("Seborrhoea", bold=True),
ec("DHT stimulates sebaceous gland hypertrophy + sebum production. Type 1 5α-reductase predominates in sebaceous glands. Androgen receptors densely expressed here.")],
[ec("Acne vulgaris", bold=True),
ec("Androgens → ↑ sebum + ↓ linoleic acid → follicular hyperkeratosis → microcomedone → Cutibacterium acnes colonisation → inflammation → papule/pustule/nodule/cyst")],
[ec("Hirsutism", bold=True),
ec("DHT activates androgen receptors in hair follicle papilla → conversion of vellus hair (short, fine, unpigmented) to terminal hair (long, pigmented, coarse) in androgen-sensitive areas")],
[ec("Androgenetic Alopecia", bold=True),
ec("Paradoxical: same DHT causes miniaturisation on scalp follicles. Terminal scalp hair → vellus hair over successive cycles. Primarily autosomal dominant genetic predisposition; exacerbated by androgen excess")],
[ec("Acanthosis nigricans", bold=True),
ec("NOT direct androgen effect. Marker of insulin resistance (PCOS/HAIR-AN). Insulin + IGF-1 stimulate keratinocyte and fibroblast proliferation → velvety hyperpigmented plaques")],
[ec("Idiopathic hirsutism", bold=True),
ec("Normal circulating androgens + regular cycles. Caused by increased LOCAL 5α-reductase activity in hair follicle → increased end-organ sensitivity. More common in Mediterranean/South Asian/Middle Eastern populations")],
]
mech_colw = [col_w*0.22, col_w*0.78]
mech_table = Table(mech_data, colWidths=mech_colw, repeatRows=1)
mech_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), TEAL),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor("#E0F2F1"), colors.white]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#BDBDBD")),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('VALIGN', (0,0), (-1,-1), 'TOP'),
]))
story.append(mech_table)
story.append(Spacer(1, 8))
# ── CLINICAL FEATURES ────────────────────────────────────────────────────
story.append(section_header("4. CLINICAL FEATURES", s['section'], ORANGE))
story.append(Spacer(1, 5))
# 1. Hirsutism
story.append(Paragraph("A. Hirsutism", s['subsection']))
story.append(Paragraph(
"<b>Definition:</b> Excessive hair growth in women in a <b>male-pattern (androgenic) distribution</b>. "
"Distinct from hypertrichosis (generalised, androgen-independent).",
s['body']))
hir_items = [
"<b>Distribution:</b> Upper lip > Thighs > Lower abdomen > Upper back; also chin, cheeks, chest, suprapubic, perianal, intergluteal",
"<b>Abrupt/rapid onset</b> → suspect malignancy (androgen-secreting tumour)",
"Severity of hirsutism does not always correlate with degree of androgen excess",
"<b>Ethnicity:</b> Mediterranean > South Asian > Middle Eastern > Asian (lowest prevalence)",
]
for item in hir_items:
story.append(Paragraph(f"• {item}", s['bullet']))
story.append(Spacer(1, 4))
# mFG scoring
mfg_box = [
Paragraph("<b>Modified Ferriman-Gallwey (mFG) Score</b>", s['subsubsection']),
Paragraph("Scores 9 body areas (upper lip, chin/cheeks, chest, upper abdomen, lower abdomen, upper back, lower back, arms, legs)", s['body']),
Paragraph("Score 0–4 per area (0 = no hair; 4 = extensive growth) | Maximum score: 36", s['body']),
]
mfg_score_data = [
[Paragraph("SCORE", s['tbl_hdr']), Paragraph("INTERPRETATION", s['tbl_hdr'])],
[ec("≤ 8"), ec("Normal")],
[ec("8 – 14"), ec("Mild hirsutism")],
[ec("≥ 15", bold=True), ec("Moderate to Severe hirsutism", bold=True)],
]
mfg_table = Table(mfg_score_data, colWidths=[col_w*0.25, col_w*0.75])
mfg_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), TBL_HDR_PURP),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor("#F3E5F5"), colors.white]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#BDBDBD")),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('ALIGN', (0,0), (-1,-1), 'CENTER'),
('BACKGROUND', (0,3), (-1,3), colors.HexColor("#EF9A9A")),
]))
story.append(coloured_box(mfg_box + [mfg_table], BOX_PURPLE, PURPLE))
story.append(Spacer(1, 5))
story.append(Paragraph("B. Acne (Hormonal)", s['subsection']))
acne_box = [
Paragraph("<b>Features suggesting hormonal/hyperandrogenic acne:</b>", s['body']),
Paragraph("• Sudden onset or severe acne refractory to conventional treatment", s['bullet']),
Paragraph("• Predominantly lower face / jawline / neck distribution", s['bullet']),
Paragraph("• Premenstrual flares", s['bullet']),
Paragraph("• Irregular menstrual periods", s['bullet']),
Paragraph("• Associated hirsutism, seborrhoea, or alopecia", s['bullet']),
Paragraph("• Acne onset ages 1–7 with virilisation → refer to paediatric endocrinologist", s['bullet']),
Paragraph("• Comedonal acne ages 9–12 without other features → normal puberty, no workup needed", s['bullet']),
]
story.append(coloured_box(acne_box, BOX_ORANGE, ORANGE))
story.append(Spacer(1, 4))
story.append(Paragraph("C. Seborrhoea", s['subsection']))
story.append(Paragraph(
"Oily, shiny skin on face and scalp. Scaling and erythema (seborrhoeic dermatitis). "
"Present in <b>100% of SAHA syndrome</b> cases.",
s['body']))
story.append(Spacer(1, 4))
story.append(Paragraph("D. Androgenetic Alopecia (AGA)", s['subsection']))
aga_data = [
[Paragraph("PATTERN", s['tbl_hdr']), Paragraph("MALES", s['tbl_hdr']), Paragraph("FEMALES", s['tbl_hdr'])],
[ec("Hair loss pattern", bold=True), ec("Bitemporal recession + vertex loss (Hamilton-Norwood pattern)"), ec("Diffuse crown thinning; frontal hairline PRESERVED (Ludwig pattern)")],
[ec("Mechanism", bold=True), ec("DHT-driven miniaturisation of scalp follicles"), ec("Same mechanism; genetic predisposition + androgen excess")],
[ec("Genetics", bold=True), ec("Autosomal dominant (variable penetrance)"), ec("More commonly acquired in androgen excess states")],
]
aga_table = Table(aga_data, colWidths=[col_w*0.18, col_w*0.41, col_w*0.41], repeatRows=1)
aga_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), GREEN_DARK),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor("#E8F5E9"), colors.white]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#BDBDBD")),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('VALIGN', (0,0), (-1,-1), 'TOP'),
]))
story.append(aga_table)
story.append(Spacer(1, 5))
story.append(Paragraph("E. Signs of Virilisation (Severe Hyperandrogenism)", s['subsection']))
vir_box = [
Paragraph("⚠ Signs indicating severe hyperandrogenism — investigate urgently for androgen-secreting tumour:", s['redbox']),
Paragraph("• Clitoromegaly • Male-pattern frontal balding in women • Deepening of voice", s['bullet']),
Paragraph("• Decreased breast size / breast tissue atrophy • Increased muscle mass", s['bullet']),
Paragraph("• Galactorrhoea (if associated hyperprolactinaemia)", s['bullet']),
]
story.append(coloured_box(vir_box, BOX_RED, RED_DARK))
story.append(Spacer(1, 5))
story.append(Paragraph("F. PCOS-Specific Cutaneous Findings", s['subsection']))
story.append(Paragraph("• Severe truncal hirsutism • Axillary acanthosis nigricans • Skin tags • Seborrhoea • Hormonal acne", s['bullet']))
story.append(Paragraph("Associated systemic features: Obesity, oligomenorrhoea/amenorrhoea, infertility, insulin resistance, dyslipidaemia, sleep apnoea, risk of endometrial cancer", s['body']))
story.append(Spacer(1, 8))
# ── APPROACH TO DIAGNOSIS ─────────────────────────────────────────────────
story.append(section_header("5. APPROACH TO DIAGNOSIS", s['section'], PURPLE))
story.append(Spacer(1, 5))
story.append(Paragraph("A. History", s['subsection']))
hist_items = [
"Age of onset + rate of progression — <b>slow/gradual</b> → functional cause; <b>rapid/abrupt</b> → tumour",
"Menstrual history: oligomenorrhoea, amenorrhoea, intermenstrual bleeding",
"Family history of hirsutism, PCOS, or CAH",
"Medications: anabolic steroids, danazol, valproate, norgestrel, minoxidil, CsA",
"Obstetric/reproductive history; desire for fertility",
"Symptoms of Cushing: central weight gain, easy bruising, purple striae, proximal myopathy",
"Symptoms of hypothyroidism: cold intolerance, constipation, dry skin, weight gain",
"Galactorrhoea (suggests hyperprolactinaemia)",
]
for item in hist_items:
story.append(Paragraph(f"• {item}", s['bullet']))
story.append(Spacer(1, 4))
story.append(Paragraph("B. Physical Examination", s['subsection']))
exam_items = [
"mFG scoring for hirsutism (score 9 body areas; score ≥8 = hirsute)",
"Signs of virilisation: clitoromegaly, frontal balding, voice change, muscle bulk",
"Signs of insulin resistance: acanthosis nigricans (axillae, neck, groin), skin tags, central obesity",
"Signs of Cushing: purple striae, moon facies, buffalo hump, easy bruising",
"Thyroid palpation — goitre, nodule",
"Galactorrhoea on breast examination",
"Pelvic examination — ovarian enlargement (tumour)",
"Blood pressure (adrenal tumour / Cushing)",
]
for item in exam_items:
story.append(Paragraph(f"• {item}", s['bullet']))
story.append(Spacer(1, 5))
story.append(Paragraph("C. Investigations", s['subsection']))
# First-line investigations
inv1_headers = [Paragraph("TEST", s['tbl_hdr']), Paragraph("PURPOSE / INTERPRETATION", s['tbl_hdr'])]
inv1_data = [
inv1_headers,
[ec("Free testosterone ★\n(most sensitive)", bold=True),
ec("Best screening marker. Elevated in PCOS, ovarian hyperandrogenism, adrenal disorders")],
[ec("Total testosterone"), ec("T >200 ng/dL → suspect androgen-secreting tumour → urgent imaging")],
[ec("DHEA-S"), ec("Screens for adrenal androgen source. Elevated in adrenal tumour and CAH. 2-3× upper limit → CT adrenal")],
[ec("LH : FSH ratio"), ec("Ratio >2-3 → consistent with PCOS")],
[ec("Prolactin"), ec("Elevated → hyperprolactinaemia; look for prolactinoma")],
[ec("TSH"), ec("Rules out hypothyroidism as cause of reduced SHBG")],
[ec("17-OHP (early morning)"), ec("Elevated in 21-hydroxylase deficiency NCAH; >10 ng/mL post-ACTH stimulation → diagnostic")],
[ec("Fasting glucose / OGTT"), ec("Insulin resistance assessment; impaired GTT → HAIR-AN or PCOS metabolic syndrome")],
[ec("Lipid profile"), ec("Dyslipidaemia common in PCOS / metabolic syndrome")],
]
note_txt = "<i>★ Lab evaluation most accurate when patient is off OCPs for 4–6 weeks and tests drawn before menses</i>"
inv1_colw = [col_w*0.27, col_w*0.73]
inv1_table = Table(inv1_data, colWidths=inv1_colw, repeatRows=1)
inv1_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), TBL_HDR_PURP),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor("#F3E5F5"), colors.white]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#BDBDBD")),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('BACKGROUND', (0,2), (-1,2), colors.HexColor("#EF9A9A")),
]))
story.append(Paragraph("First-Line Investigations:", s['subsubsection']))
story.append(inv1_table)
story.append(Paragraph(note_txt, s['caption']))
story.append(Spacer(1, 4))
# Second-line
story.append(Paragraph("Second-Line / Confirmatory Investigations:", s['subsubsection']))
inv2_data = [
[Paragraph("TEST", s['tbl_hdr']), Paragraph("INDICATION", s['tbl_hdr'])],
[ec("ACTH stimulation test"), ec("Confirm NCAH: 17-OHP >10 ng/mL post-ACTH → 21-hydroxylase deficiency")],
[ec("Dexamethasone suppression test (DST)"), ec("Distinguish adrenal vs ovarian vs Cushing origin. Suppressible → adrenal; non-suppressible → Cushing / neoplasm / PCOS")],
[ec("24-h urine cortisol / late-night salivary cortisol"), ec("Rule out Cushing syndrome")],
[ec("Pelvic ultrasound (USG)"), ec("PCOS (polycystic ovaries); ovarian tumour; Rotterdam criteria imaging")],
[ec("CT abdomen / adrenals"), ec("Adrenal mass if DHEA-S 2–3× upper limit; adrenal tumour suspected")],
[ec("MRI pituitary"), ec("Prolactinoma; Cushing's disease (pituitary source)"),],
[ec("β-hCG"), ec("Rule out pregnancy if amenorrhoeic")],
[ec("IGF-1 + GH"), ec("Suspected acromegaly")],
[ec("CYP21 genotyping"), ec("Confirm diagnosis of NCAH (21-hydroxylase deficiency)")],
]
inv2_table = Table(inv2_data, colWidths=inv1_colw, repeatRows=1)
inv2_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), PURPLE),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor("#EDE7F6"), colors.white]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#BDBDBD")),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('VALIGN', (0,0), (-1,-1), 'TOP'),
]))
story.append(inv2_table)
story.append(Spacer(1, 4))
# Red flag values box
rf_box = [
Paragraph("🔴 RED FLAG INVESTIGATION VALUES", ParagraphStyle('rfh', fontName='Helvetica-Bold', fontSize=10, textColor=RED_DARK)),
Paragraph("• Testosterone <b>>200 ng/dL</b> → Suspect adrenal or ovarian androgen-secreting tumour → urgent CT/MRI", s['bullet']),
Paragraph("• DHEA-S <b>2–3× upper limit</b> → Adrenal mass → CT adrenal", s['bullet']),
Paragraph("• 17-OHP <b>>10 ng/mL</b> post-ACTH stimulation → Non-classic CAH (21-OH deficiency)", s['bullet']),
]
story.append(coloured_box(rf_box, BOX_RED, RED_DARK))
story.append(Spacer(1, 5))
# Diagnostic algorithm description
story.append(Paragraph("D. Diagnostic Algorithm (Approach to Hirsutism)", s['subsection']))
algo_box = [
Paragraph("Step 1 — <b>Regular menstrual cycle?</b>", s['body']),
Paragraph("YES → <b>Normal T</b>: Idiopathic hirsutism | <b>Elevated T</b>: Idiopathic hyperandrogenism", s['bullet']),
Paragraph("NO → Is onset sudden ± virilisation?", s['bullet']),
Paragraph(" YES → Consider malignancy (imaging for adrenal/pelvic mass); T >200 ng/dL", s['bullet2']),
Paragraph(" NO → Exclude secondary causes:", s['bullet2']),
Paragraph(" PCOS · NCAH · Cushing syndrome · Hypothyroidism · Acromegaly ·", s['bullet2']),
Paragraph(" Hyperprolactinaemia · SAHA syndrome · HAIR-AN · Pregnancy · Medications", s['bullet2']),
Spacer(1, 3),
Paragraph("Step 2 — <b>Confirm androgen excess</b> (free testosterone; DHEA-S)", s['body']),
Paragraph("Step 3 — <b>Localise source</b> (dexamethasone suppression test; ACTH stimulation; imaging)", s['body']),
Paragraph("Step 4 — <b>Treatment based on aetiology</b> + symptomatic management", s['body']),
]
story.append(coloured_box(algo_box, BOX_YELLOW, AMBER))
story.append(Spacer(1, 8))
# ── MANAGEMENT ───────────────────────────────────────────────────────────
story.append(section_header("6. MANAGEMENT", s['section'], GREEN_DARK))
story.append(Spacer(1, 5))
story.append(Paragraph("A. General Principles", s['subsection']))
story.append(Paragraph(
"Management is directed at: (1) treating the <b>underlying cause</b>, and (2) managing individual "
"<b>cutaneous manifestations</b>. Involves pharmacological (hormonal, antiandrogen) and non-pharmacological approaches.",
s['body']))
story.append(Spacer(1, 4))
# Antiandrogen drugs master table
story.append(Paragraph("B. Antiandrogen and Hormonal Pharmacotherapy", s['subsection']))
drug_headers = [
Paragraph("DRUG", s['tbl_hdr']),
Paragraph("MECHANISM", s['tbl_hdr']),
Paragraph("DOSE / USES", s['tbl_hdr']),
Paragraph("KEY SIDE EFFECTS", s['tbl_hdr']),
Paragraph("CONTRA-\nINDICATION", s['tbl_hdr']),
]
drugs = [
["Spironolactone\n★ First-line",
"AR antagonist + 5α-RI + ↓ androgen synthesis",
"50–200 mg/day\nHirsutism, acne, AGA, seborrhoea",
"Hyperkalaemia, menstrual irregularity, breast tenderness, polyuria",
"Pregnancy; renal failure; hyperkalaemia"],
["Oral Contra-\nceptive Pills (OCP)",
"↑ SHBG, ↓ LH → ↓ ovarian androgen; antiandrogenic progestins",
"Drospirenone- or CPA-containing preferred\nHirsutism, acne, AGA in women",
"Thromboembolism, hypertension, nausea, mood changes",
"Thrombophilia; age >35 + smoking; migraine with aura"],
["Cyproterone\nAcetate (CPA)",
"AR antagonist + progestin",
"2 mg (in Diane-35) or 50–100 mg\nAcne, hirsutism, AGA",
"Hepatotoxicity (high dose), depression, decreased libido",
"Pregnancy; liver disease; meningioma"],
["Finasteride",
"5α-RI (Type 1+2)\nBlocks T → DHT",
"1 mg/day (AGA men)\n5 mg/day (hirsutism)\nPost-menopausal AGA (limited evidence)",
"↓ Libido, erectile dysfunction, depression, gynecomastia. Post-finasteride syndrome (persists after stopping)",
"PREGNANCY (feminises male fetus); women of childbearing age"],
["Dutasteride",
"Dual 5α-RI (Type 1 + Type 2)\nMore potent than finasteride",
"0.5 mg/day\nAGA in men; hirsutism",
"Same as finasteride",
"Pregnancy"],
["Flutamide\n⚠ AVOID",
"Pure AR antagonist",
"Rarely used\nHirsutism",
"★ FATAL HEPATOTOXICITY\nRisk to male fetus",
"Pregnancy; liver disease"],
["Bicalutamide",
"Non-steroidal pure AR antagonist",
"25 mg/day\nPCOS/idiopathic hirsutism",
"Hepatotoxicity at ≥50 mg/day",
"Pregnancy"],
["Metformin",
"Insulin sensitiser\nIndirectly ↓ androgen production",
"500–1500 mg/day\nPCOS with insulin resistance",
"GI side effects, lactic acidosis (rare)",
"Renal failure; contrast procedures"],
["Clascoterone\n(Winlevi 1%)",
"Topical AR antagonist",
"Topical cream BD\nAcne (FDA-approved)",
"Local skin irritation",
"—"],
["Eflornithine\n(Vaniqa 13.9%)",
"Topical ornithine decarboxylase inhibitor\n→ slows hair growth",
"Apply BD to face\nFacial hirsutism (adjunct; not permanent)",
"Local irritation, burning, stinging",
"—"],
["Glucocorticoids\n(prednisolone /\ndexamethasone)",
"↓ ACTH → ↓ adrenal androgen production",
"Prednisolone 5–7.5 mg/day or\nDexamethasone 0.25–0.5 mg at night\nCAH / adrenal hyperandrogenism",
"Cushing side effects (long-term), osteoporosis, immunosuppression",
"Active infections; use minimum effective dose"],
]
drug_col_w = [col_w*0.14, col_w*0.19, col_w*0.22, col_w*0.25, col_w*0.20]
drug_table_data = [drug_headers]
for d in drugs:
drug_table_data.append([Paragraph(txt, s['tbl_cell_bold'] if i == 0 else s['tbl_cell'])
for i, txt in enumerate(d)])
drug_table = Table(drug_table_data, colWidths=drug_col_w, repeatRows=1)
row_bg = [colors.HexColor("#E8F5E9"), colors.white]
drug_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), TBL_HDR_GREEN),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8),
('ROWBACKGROUNDS', (0,1), (-1,-1), row_bg),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#BDBDBD")),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 4),
('RIGHTPADDING', (0,0), (-1,-1), 4),
('VALIGN', (0,0), (-1,-1), 'TOP'),
# Highlight flutamide row (row 6) in red
('BACKGROUND', (0,6), (-1,6), colors.HexColor("#FFCDD2")),
# Highlight spironolactone (row 1) lightly
('BACKGROUND', (0,1), (-1,1), colors.HexColor("#C8E6C9")),
]))
story.append(drug_table)
story.append(Spacer(1, 8))
# Management of specific manifestations
story.append(Paragraph("C. Management of Specific Cutaneous Manifestations", s['subsection']))
story.append(Spacer(1, 3))
# Hirsutism management
story.append(Paragraph("1. Hirsutism", s['subsubsection']))
hirs_mgmt = [
Paragraph("<b>Physical / Cosmetic Methods (temporary):</b>", s['body']),
Paragraph("• Shaving, threading, waxing, depilatory creams (fast; not permanent)", s['bullet']),
Paragraph("<b>Eflornithine 13.9% cream:</b> Topical ornithine decarboxylase inhibitor; applied twice daily on face; slows growth (not permanent); improvement in 6–8 weeks", s['bullet']),
Spacer(1, 3),
Paragraph("<b>Laser Hair Removal:</b>", s['body']),
Paragraph("• Mechanism: Selective photothermolysis — targets melanin in pigmented hair follicles", s['bullet']),
Paragraph("• Devices: 755-nm Alexandrite | 800-nm Diode | 1064-nm Nd:YAG (preferred for dark skin) | Intense Pulsed Light (IPL)", s['bullet']),
Paragraph("• Requires 3–6 treatment cycles; not effective for light/white/grey hair", s['bullet']),
Paragraph("• Adverse effects: Post-inflammatory hyperpigmentation (PIH), folliculitis, HSV reactivation, paradoxical hypertrichosis (rare)", s['bullet']),
Spacer(1, 3),
Paragraph("<b>Electrolysis:</b>", s['body']),
Paragraph("• Only truly permanent hair removal method", s['bullet']),
Paragraph("• Types: Galvanic (chemical), thermolysis (heat), blend", s['bullet']),
Paragraph("• Disadvantages: Time-consuming, painful, risk of scarring and follicular hyperpigmentation; regrowth ~40%", s['bullet']),
Spacer(1, 3),
Paragraph("<b>Systemic Therapy:</b>", s['body']),
Paragraph("• Spironolactone 100–200 mg/day (first-line for idiopathic hirsutism / PCOS)", s['bullet']),
Paragraph("• OCP with drospirenone or CPA", s['bullet']),
Paragraph("• Finasteride / Dutasteride", s['bullet']),
Paragraph("• Metformin (PCOS with insulin resistance)", s['bullet']),
]
story.append(coloured_box(hirs_mgmt, BOX_GREEN, GREEN_DARK))
story.append(Spacer(1, 4))
story.append(Paragraph("2. Hormonal Acne", s['subsubsection']))
acne_mgmt = [
Paragraph("• OCP (drospirenone- or CPA-containing preferred)", s['bullet']),
Paragraph("• Spironolactone 50–150 mg/day", s['bullet']),
Paragraph("• Cyproterone acetate-containing OCP (Diane-35)", s['bullet']),
Paragraph("• Clascoterone 1% cream (topical AR antagonist — FDA approved)", s['bullet']),
Paragraph("• Plus standard topical therapy: Retinoids + Benzoyl peroxide ± Topical antibiotics", s['bullet']),
Paragraph("• Oral isotretinoin for severe nodulo-cystic disease", s['bullet']),
]
story.append(coloured_box(acne_mgmt, BOX_ORANGE, ORANGE))
story.append(Spacer(1, 4))
story.append(Paragraph("3. Androgenetic Alopecia (AGA)", s['subsubsection']))
aga_mgmt_data = [
[Paragraph("WOMEN", s['tbl_hdr']), Paragraph("MEN", s['tbl_hdr'])],
[ec("Topical minoxidil 2–5% (first-line)"), ec("Topical minoxidil 5% (first-line)")],
[ec("Spironolactone (systemic or topical)"), ec("Finasteride 1 mg/day")],
[ec("OCP with antiandrogenic progestin"), ec("Dutasteride 0.5 mg/day")],
[ec("Finasteride (post-menopausal; limited evidence)"), ec("Hair transplantation (FUE/FUT)")],
[ec("Low-level laser therapy (LLLT)"), ec("Low-level laser therapy (LLLT)")],
[ec("Platelet-rich plasma (PRP) injections"), ec("Platelet-rich plasma (PRP) injections")],
]
aga_mgmt_table = Table(aga_mgmt_data, colWidths=[col_w*0.5, col_w*0.5], repeatRows=1)
aga_mgmt_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), GREEN_DARK),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor("#E8F5E9"), colors.white]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#BDBDBD")),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('VALIGN', (0,0), (-1,-1), 'TOP'),
]))
story.append(aga_mgmt_table)
story.append(Spacer(1, 5))
# Disease-specific management
story.append(Paragraph("D. Management by Underlying Aetiology", s['subsection']))
disease_data = [
[Paragraph("CONDITION", s['tbl_hdr']), Paragraph("SPECIFIC MANAGEMENT", s['tbl_hdr'])],
[ec("PCOS", bold=True),
ec("1. Lifestyle modification + weight loss (5–10% wt loss restores cycles)\n"
"2. Metformin (insulin resistance, menstrual regulation)\n"
"3. OCP (cycle regulation, cutaneous hyperandrogenism)\n"
"4. Spironolactone (cutaneous features)\n"
"5. Clomiphene citrate (if fertility desired)\n"
"6. Bariatric surgery (severe obesity)")],
[ec("Non-Classic CAH\n(21-OH deficiency)", bold=True),
ec("1. Low-dose glucocorticoids: Dexamethasone 0.25–0.5 mg/night or prednisolone 5–7.5 mg/day (suppress ACTH)\n"
"2. OCP + antiandrogen therapy as alternative or adjunct\n"
"3. Antenatal dexamethasone (homozygous female fetuses → prevent virilisation)\n"
"4. CYP21 genotyping to confirm; genetic counselling")],
[ec("Adrenal / Ovarian\nTumour", bold=True),
ec("Surgical resection (curative).\nSertoli-Leydig, granulosa-theca, hilus cell tumours: low metastatic potential; surgery adequate")],
[ec("Cushing Syndrome", bold=True),
ec("Treat primary cause:\n• Pituitary adenoma → Transsphenoidal surgery\n• Adrenal adenoma → Adrenalectomy\n• Ectopic ACTH → Resect primary + ketoconazole/metyrapone")],
[ec("Hyperprolactinaemia", bold=True),
ec("Dopamine agonists: Cabergoline (first-line) or Bromocriptine\nIf prolactinoma: cabergoline often shrinks tumour; surgery if resistant")],
[ec("Hypothyroidism", bold=True),
ec("Thyroxine replacement → SHBG normalises → free androgen levels fall")],
[ec("Acromegaly", bold=True),
ec("Transsphenoidal surgery + Somatostatin analogues (octreotide, lanreotide) + Pegvisomant (GH antagonist)")],
[ec("Drug-induced", bold=True),
ec("Withdraw causative agent if possible")],
[ec("Idiopathic Hirsutism", bold=True),
ec("Spironolactone (first-line) or OCP. Laser hair removal / electrolysis. Eflornithine cream.")],
]
disease_colw = [col_w*0.21, col_w*0.79]
disease_table = Table(disease_data, colWidths=disease_colw, repeatRows=1)
disease_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), TBL_HDR_NAVY),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor("#E8EAF6"), colors.white]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#BDBDBD")),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('BACKGROUND', (0,1), (-1,1), colors.HexColor("#DCEDC8")),
('BACKGROUND', (0,2), (-1,2), colors.HexColor("#E8EAF6")),
('BACKGROUND', (0,3), (-1,3), colors.HexColor("#FFEBEE")),
]))
story.append(disease_table)
story.append(Spacer(1, 8))
# ── KEY EXAM POINTS ───────────────────────────────────────────────────────
story.append(section_header("7. HIGH-YIELD EXAM POINTS", s['section'], AMBER))
story.append(Spacer(1, 5))
exam_points = [
("<b>SAHA syndrome</b> = Seborrhoea + Acne + Hirsutism + Androgenetic Alopecia; 4 subtypes (idiopathic, ovarian, adrenal, hyperprolactinaemic); 17.7% of PCOS patients"),
("<b>HAIR-AN syndrome</b> = Hyperandrogenism + Insulin Resistance + Acanthosis Nigricans"),
("<b>mFG score</b>: ≤8 normal | 8–14 mild hirsutism | ≥15 moderate-severe hirsutism"),
("<b>DHT</b> is the most potent intracellular androgen; produced by 5α-reductase from testosterone"),
("<b>SHBG reduction</b> (obesity, insulin resistance, progestins) → ↑ free androgens → cutaneous features"),
("<b>Free testosterone</b> is the most sensitive biochemical marker of hyperandrogenism"),
("<b>Testosterone >200 ng/dL</b> → suspect androgen-secreting tumour → urgent CT/MRI"),
("<b>17-OHP >10 ng/mL</b> post-ACTH stimulation → NCAH (21-hydroxylase deficiency, CYP21A2 mutation)"),
("<b>Idiopathic hirsutism</b> = normal androgens + regular cycles + increased local 5α-reductase activity"),
("<b>Abrupt rapid onset of hirsutism</b> = red flag for malignancy"),
("<b>Finasteride</b> is CONTRAINDICATED in women of childbearing age (feminisation of male fetus)"),
("<b>Flutamide</b> — avoid due to fatal hepatotoxicity"),
("<b>Bicalutamide</b> — hepatotoxicity at ≥50 mg/day; safe at 25 mg/day"),
("<b>Laser hair removal</b>: works via selective photothermolysis; Nd:YAG 1064 nm preferred for dark skin; NOT effective for light/white/grey hair"),
("<b>Eflornithine</b> — topical ornithine decarboxylase inhibitor; slows facial hair growth; not permanent"),
("<b>Electrolysis</b> — only truly PERMANENT hair removal; regrowth rate ~40%"),
("<b>PCOS treatment</b>: weight loss first-line (obese); metformin; OCP; spironolactone; clomiphene if fertility desired"),
("<b>NCAH treatment</b>: low-dose glucocorticoids (dexamethasone 0.25–0.5 mg/night) to suppress ACTH"),
("<b>Spironolactone</b> — first-line antiandrogen in dermatology; monitor potassium; contraindicated in pregnancy"),
("<b>Acanthosis nigricans</b> in hyperandrogenism = marker of insulin resistance, NOT a direct androgen effect"),
]
exam_box_rows = [Paragraph("★ KEY POINTS FOR EXAM", ParagraphStyle('kph', fontName='Helvetica-Bold', fontSize=11, textColor=AMBER))]
for i, pt in enumerate(exam_points, 1):
exam_box_rows.append(Paragraph(f"<b>{i}.</b> {pt}", s['exam_point']))
story.append(coloured_box(exam_box_rows, BOX_YELLOW, AMBER))
story.append(Spacer(1, 8))
# ── QUICK REFERENCE TABLE ─────────────────────────────────────────────────
story.append(section_header("8. QUICK REFERENCE: ANTIANDROGENS IN DERMATOLOGY", s['section'], DARK_NAVY))
story.append(Spacer(1, 5))
qr_headers = [
Paragraph("DRUG", s['tbl_hdr']),
Paragraph("MECHANISM", s['tbl_hdr']),
Paragraph("MAIN USES", s['tbl_hdr']),
Paragraph("KEY CAUTION", s['tbl_hdr']),
]
qr_drugs = [
["Spironolactone", "AR antagonist + 5α-RI + ↓ synthesis", "Hirsutism, acne, AGA ♀", "Hyperkalaemia; CONTRAINDICATED in pregnancy"],
["Cyproterone acetate", "AR antagonist + progestin", "Hirsutism, acne, AGA ♀", "Hepatotoxicity (high dose)"],
["Finasteride", "5α-RI (Type 1+2)", "AGA ♂; hirsutism", "TERATOGENIC — contraindicated in pregnancy"],
["Dutasteride", "5α-RI dual (Type 1+2)", "AGA ♂; hirsutism", "Same as finasteride"],
["Flutamide ⚠", "AR antagonist", "Hirsutism (rarely)", "FATAL HEPATOTOXICITY — rarely used"],
["Bicalutamide", "Non-steroidal AR antagonist", "Hirsutism (PCOS/idiopathic)", "Hepatotoxicity ≥50 mg/day"],
["OCP", "↑ SHBG; ↓ LH; antiandrogenic progestins", "Hirsutism, acne, AGA ♀", "Thromboembolism; smoking + age >35"],
["Metformin", "Insulin sensitiser (indirect)", "PCOS", "Renal failure; GI side effects"],
["Clascoterone 1%", "Topical AR antagonist", "Acne (FDA approved)", "Local irritation"],
["Eflornithine 13.9%", "Topical ODC inhibitor", "Facial hirsutism (slows growth)", "Local irritation; not permanent"],
]
qr_data = [qr_headers]
for d in qr_drugs:
qr_data.append([Paragraph(d[0], s['tbl_cell_bold'])] + [Paragraph(x, s['tbl_cell']) for x in d[1:]])
qr_colw = [col_w*0.17, col_w*0.22, col_w*0.22, col_w*0.39]
qr_table = Table(qr_data, colWidths=qr_colw, repeatRows=1)
qr_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_NAVY),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor("#EEF2FF"), colors.white]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#BDBDBD")),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('BACKGROUND', (0,5), (-1,5), colors.HexColor("#FFCDD2")), # flutamide
]))
story.append(qr_table)
story.append(Spacer(1, 6))
# Footer note
story.append(HRFlowable(width=col_w, thickness=1, color=ROYAL_BLUE))
story.append(Spacer(1, 3))
story.append(Paragraph(
"<b>References:</b> Fitzpatrick's Dermatology 9th Ed (Vol. 1–2) | Andrews' Diseases of the Skin, Clinical Dermatology 13th Ed | "
"Dermatology 2-Volume Set 5th Ed | Harrison's Principles of Internal Medicine 22nd Ed (2025)",
ParagraphStyle('ref', fontName='Helvetica-Oblique', fontSize=8, textColor=colors.HexColor("#616161"),
alignment=TA_CENTER, leading=11)))
# Build
doc.build(story, onFirstPage=add_page_decorations, onLaterPages=add_page_decorations)
print(f"PDF generated: {output_path}")
if __name__ == "__main__":
build_pdf("/tmp/workspace/cutaneous-hyperandrogenism/Cutaneous_Hyperandrogenism_MD_Derm_Notes.pdf")
cd /tmp/workspace/cutaneous-hyperandrogenism && python generate_pdf.py 2>&1
ls -lh /tmp/workspace/cutaneous-hyperandrogenism/
Cutaneous Hyperandrogenism MD Derm Notes
PDF Document · PDF
| Section | Content |
|---|---|
| Title Page | Dark navy banner with full title and source citations |
| 1. Definition | SAHA syndrome and HAIR-AN syndrome in coloured highlight boxes |
| 2. Etiology | Full colour-coded table — Ovarian / Adrenal / Pituitary / Idiopathic / Drug-induced / Pregnancy causes |
| 3. Pathogenesis | Key hormones table, 5α-reductase and SHBG mechanism boxes, mechanisms of each cutaneous manifestation |
| 4. Clinical Features | Hirsutism (mFG scoring table), hormonal acne, seborrhoea, AGA, virilisation red-flag box, SAHA, HAIR-AN, PCOS features |
| 5. Approach | History, examination, first-line + second-line investigations, red-flag lab values, diagnostic algorithm flow |
| 6. Management | Full antiandrogen pharmacotherapy table (11 drugs), hirsutism (laser/electrolysis/eflornithine/systemic), acne, AGA, disease-specific management (PCOS, NCAH, tumour, Cushing, prolactinoma) |
| 7. High-Yield Exam Points | 20 numbered exam-ready key facts in an amber highlighted box |
| 8. Quick Reference Table | All antiandrogens at a glance — mechanism, uses, key cautions |
| Header + Footer | Every page has a navy header and blue footer with page number |