~/gynaec-notes/create_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib import colors
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
OUTPUT = "/home/daytona/workspace/gynaec-notes/Gynaecology_Special_Topics_Exam_Notes.pdf"
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
rightMargin=1.8*cm,
leftMargin=1.8*cm,
topMargin=2*cm,
bottomMargin=2*cm,
title="Gynaecology Special Topics - Exam Notes",
author="Orris AI"
)
styles = getSampleStyleSheet()
# Custom styles
title_style = ParagraphStyle(
'CustomTitle',
parent=styles['Title'],
fontSize=22,
textColor=colors.HexColor('#1a237e'),
spaceAfter=6,
alignment=TA_CENTER,
fontName='Helvetica-Bold'
)
subtitle_style = ParagraphStyle(
'Subtitle',
parent=styles['Normal'],
fontSize=11,
textColor=colors.HexColor('#424242'),
spaceAfter=16,
alignment=TA_CENTER,
fontName='Helvetica'
)
h1_style = ParagraphStyle(
'H1',
parent=styles['Heading1'],
fontSize=13,
textColor=colors.white,
backColor=colors.HexColor('#1a237e'),
borderPadding=(5, 8, 5, 8),
spaceBefore=14,
spaceAfter=6,
fontName='Helvetica-Bold',
leading=18
)
h2_style = ParagraphStyle(
'H2',
parent=styles['Heading2'],
fontSize=11,
textColor=colors.white,
backColor=colors.HexColor('#283593'),
borderPadding=(4, 6, 4, 6),
spaceBefore=10,
spaceAfter=4,
fontName='Helvetica-Bold',
leading=16
)
h3_style = ParagraphStyle(
'H3',
parent=styles['Heading3'],
fontSize=10,
textColor=colors.HexColor('#b71c1c'),
spaceBefore=8,
spaceAfter=3,
fontName='Helvetica-Bold'
)
body_style = ParagraphStyle(
'Body',
parent=styles['Normal'],
fontSize=9.5,
textColor=colors.HexColor('#212121'),
spaceAfter=3,
spaceBefore=1,
leading=14,
fontName='Helvetica',
leftIndent=0
)
bullet_style = ParagraphStyle(
'Bullet',
parent=styles['Normal'],
fontSize=9.5,
textColor=colors.HexColor('#212121'),
spaceAfter=2,
spaceBefore=1,
leading=13,
fontName='Helvetica',
leftIndent=14,
bulletIndent=4
)
sub_bullet_style = ParagraphStyle(
'SubBullet',
parent=styles['Normal'],
fontSize=9,
textColor=colors.HexColor('#424242'),
spaceAfter=2,
spaceBefore=0,
leading=12,
fontName='Helvetica',
leftIndent=28,
bulletIndent=18
)
highlight_style = ParagraphStyle(
'Highlight',
parent=styles['Normal'],
fontSize=9.5,
textColor=colors.HexColor('#b71c1c'),
spaceAfter=3,
spaceBefore=2,
leading=13,
fontName='Helvetica-Bold',
leftIndent=0
)
note_style = ParagraphStyle(
'Note',
parent=styles['Normal'],
fontSize=9,
textColor=colors.HexColor('#1b5e20'),
backColor=colors.HexColor('#e8f5e9'),
borderPadding=(4, 6, 4, 6),
spaceAfter=6,
spaceBefore=4,
leading=13,
fontName='Helvetica-Oblique',
leftIndent=4,
rightIndent=4
)
# Table style helper
def make_table_style(header_color=colors.HexColor('#283593')):
return TableStyle([
('BACKGROUND', (0, 0), (-1, 0), header_color),
('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, 0), 9),
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
('FONTSIZE', (0, 1), (-1, -1), 8.5),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.HexColor('#f5f5f5'), 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), 6),
('RIGHTPADDING', (0, 0), (-1, -1), 6),
])
def b(text): return f"<b>{text}</b>"
def red(text): return f"<font color='#b71c1c'><b>{text}</b></font>"
def blu(text): return f"<font color='#1a237e'><b>{text}</b></font>"
story = []
# ── TITLE PAGE ──────────────────────────────────────────────────────────────
story.append(Spacer(1, 1*cm))
story.append(Paragraph("GYNAECOLOGY", title_style))
story.append(Paragraph("Special Topics – Exam Oriented Notes", subtitle_style))
story.append(Paragraph("Chapter 34 | Dutta's Textbook of Gynaecology", subtitle_style))
story.append(HRFlowable(width="100%", thickness=2, color=colors.HexColor('#1a237e'), spaceAfter=8))
story.append(Spacer(1, 0.3*cm))
# TOC hint
toc_data = [
["#", "Topic"],
["1", "Abnormal Vaginal Discharge & Leukorrhea"],
["2", "Pruritus Vulvae"],
["3", "Pelvic Pain & Low Backache"],
["4", "Common Causes of Vaginitis (Table)"],
["5", "Breast in Gynaecology - Anatomy"],
["6", "Mastalgia"],
["7", "Fibroadenoma"],
["8", "Fibrocystic Breast Disease"],
["9", "Breast Carcinoma - Risk Factors & Screening"],
["10", "Nipple Discharge"],
["11", "Psychosexual Issues & Female Sexuality"],
["12", "Vaginismus"],
["13", "Dyspareunia"],
["14", "Intimate Partner Violence (IPV)"],
["15", "Abdominopelvic Lump"],
["16", "Adnexal Mass"],
["17", "Quick Revision Mnemonics"],
]
toc_table = Table(toc_data, colWidths=[1.2*cm, 14*cm])
toc_table.setStyle(make_table_style(colors.HexColor('#1a237e')))
story.append(toc_table)
story.append(Spacer(1, 0.5*cm))
# ═══════════════════════════════════════════════════════════════
# SECTION 1: ABNORMAL VAGINAL DISCHARGE
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("1. ABNORMAL VAGINAL DISCHARGE & LEUKORRHEA", h1_style))
story.append(Paragraph(b("Normal vaginal fluid:"), body_style))
for pt in [
"Watery, white, non-odorous, pH ~4.0",
"Contains squamous epithelial cells and Doderlein (Lactobacilli) bacilli",
"Few gram-negative bacteria and anaerobes present",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(red("Leukorrhea – Definition:"), h3_style))
story.append(Paragraph("Strictly defined as an excessive normal vaginal discharge.", body_style))
story.append(Paragraph(b("Criteria to call it leukorrhea:"), body_style))
for pt in [
"Excess secretion causing persistent vulvar moistness or staining undergarments (brownish yellow on drying) or need to wear a vulvar pad",
"Nonpurulent, nonoffensive",
"Nonirritant and NEVER causes pruritus",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Classification of Abnormal Vaginal Discharge:"), h3_style))
avd_data = [
["Type", "Features"],
["Noninfective", "Nonpurulent, nonoffensive, nonirritant → Leukorrhea"],
["Infective", "Purulent, offensive, irritant (Specific or Nonspecific)"],
["Neoplastic", "Often blood-stained, offensive"],
["Foreign body", "Offensive, copious, purulent, often blood-stained"],
]
avd_table = Table(avd_data, colWidths=[4*cm, 11*cm])
avd_table.setStyle(make_table_style())
story.append(avd_table)
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(b("Causes of Physiologic (Excess) Leukorrhea:"), h3_style))
for pt in [
b("Puberty:") + " Endogenous estrogen → endocervical epithelium encroaches onto ectocervix (congenital ectopy → increased secretion)",
b("Menstrual cycle:") + " Peak at ovulation (cervical glands) + premenstrual (hypertrophied endometrial glands)",
b("Pregnancy:") + " Hyperestrinsim → increased vaginal transudate + cervical gland secretion",
b("Sexual excitement:") + " Abundant secretion from Bartholin's glands",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Life Table of Abnormal Vaginal Discharge ") + red("(HIGH YIELD)"), h3_style))
life_data = [
["Period of Life", "Associated Symptoms", "Probable Diagnosis"],
["Early neonatal", "Nil", "Leukorrhea"],
["Pre-menarchal", "Nil / Offensive / Vulvar itching", "Ill health, Foreign body, Threadworm"],
["Puberty", "Nil", "Leukorrhea"],
["Reproductive (non-pregnant)", "Nil (related to menstrual cycle)", "Leukorrhea"],
["Pill users", "Nil / Pruritus", "Leukorrhea / Moniliasis"],
["During antibiotics", "Pruritus", "Moniliasis"],
["Diabetes", "Pruritus", "Moniliasis"],
["Pregnancy", "Nil / Pruritus", "Vaginitis (Moniliasis)"],
["Postmenopausal", "Nil / Offensive / Pruritus/Diabetic", "Senile vaginitis, Pyometra, Neoplasm"],
]
life_table = Table(life_data, colWidths=[4.5*cm, 5*cm, 5.5*cm])
life_table.setStyle(make_table_style())
story.append(life_table)
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(b("Treatment of Leukorrhea:"), h3_style))
for pt in [
"Improve general health and local hygiene",
"Cervical lesions → electrocautery, cryosurgery, or trachelorrhaphy",
"Pelvic lesions producing vaginal leukorrhea → treat the pathology",
"Pill users may need to stop the pill if symptom is very much annoying",
"Treatment for specific infection",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
# ═══════════════════════════════════════════════════════════════
# SECTION 2: PRURITUS VULVAE
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("2. PRURITUS VULVAE", h1_style))
story.append(Paragraph(
b("Definition:") + " Sense of itching confined to the vulva. Should NOT be confused with pain. "
+ red("~10% of gynecology clinic patients") + " complain of vulvar itching.",
body_style
))
story.append(Paragraph(b("Most common cause:") + " Vaginal discharge (Trichomonas vaginalis or Candida albicans)", highlight_style))
story.append(Paragraph(b("Causes:"), h3_style))
causes_pv = [
["Category", "Details"],
["Infective", "Fungal (Candida), Viral (Herpes, genital warts), Parasitic (Threadworm, scabies), STIs (Gonorrhea, Trichomoniasis)"],
["Local skin lesions", "Psoriasis, seborrheic dermatitis, intertrigo"],
["Allergy / Contact dermatitis", "Nylon underwear, soaps, detergents, bubble bath, chemical contraceptives"],
["Non-neoplastic epithelial disorders", "Squamous hyperplasia, Lichen sclerosus"],
["Neoplastic", "VIN, Paget's disease, Invasive carcinoma of vulva"],
["Systemic diseases", "Diabetes mellitus (glycosuria → Candida), Thyroid disorders, Dermatological causes, Deficiency states (iron, B12, folate, Vit A)"],
["Psychosomatic", "Diagnosis of exclusion – mental anxiety or sexual frustration"],
]
causes_pv_table = Table(causes_pv, colWidths=[4.5*cm, 10.5*cm])
causes_pv_table.setStyle(make_table_style())
story.append(causes_pv_table)
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(b("Investigations:"), h3_style))
for pt in [
"Microscopic examination of vaginal discharge/vulvar scraping → detect Candida or Trichomonas",
"Urine for sugar, protein, pus cells",
"Blood: CBC, postprandial glucose, thyroid profile, liver function, renal function tests",
"Biopsy (>1 year duration with vulvar epithelial disorders) to note skin changes and exclude malignancy",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Treatment:"), h3_style))
for pt in [
"Appropriate local hygiene; loose-fitting cotton undergarments",
"To prevent vicious cycle of 'itch-scratch': Local clobetasol propionate 0.05% or antibiotics ointment",
"If skin is atrophic → estrogen or testosterone cream",
"Treat specific etiological factor",
"Surgery: If biopsy confirms VIN or invasion",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
# ═══════════════════════════════════════════════════════════════
# SECTION 3: PELVIC PAIN & LOW BACKACHE
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("3. PELVIC PAIN & LOW BACKACHE", h1_style))
story.append(Paragraph(b("Acute Pelvic Pain:"), h3_style))
story.append(Paragraph("Short duration; symptoms proportionate to extent of tissue damage.", body_style))
story.append(Paragraph(b("Chronic Pelvic Pain:"), h3_style))
story.append(Paragraph("Insidious onset; pain NOT proportionate to structural tissue damage.", body_style))
story.append(Paragraph(red("Low Backache - Key Facts (EXAM FAVORITE):"), h3_style))
for pt in [
"Posterior peritoneum is poorly innervated → pain is dull and diffuse",
"Backache of pelvic origin NEVER reaches beyond the 4th lumbar vertebra",
"Pain pointed by one finger is NOT of gynecologic origin",
red("Vaginal prolapse does NOT cause backache"),
red("Mobile retroverted uterus does NOT produce backache"),
b("Cornett sign:") + " Localized pain over anterior abdominal wall (nerve entrapment/myofascial). Patient raises head/shoulder → anterior abdominal wall pain is RELIEVED (not gynecologic)",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Causes of Backache of Pelvic Origin:"), h3_style))
back_data = [
["Cause", "Key Points"],
["Uterine Prolapse", "Due to stretching of supporting ligaments. Atrophic ligaments → no pain. Subsides at rest, aggravates on standing"],
["Fixed Retroversion", "Backache only when fixed by inflammation or endometriosis"],
["Chronic PID", "Adhesions + tubo-ovarian mass → associated menstrual abnormalities + dyspareunia"],
["Endometriosis", "Involves pelvic peritoneum, uterosacral ligament or rectovaginal septum → backache + deep dyspareunia"],
["Malignant Neoplasm", "Benign tumors (ovarian, fibroid) ordinarily do NOT produce backache"],
]
back_table = Table(back_data, colWidths=[4*cm, 11*cm])
back_table.setStyle(make_table_style())
story.append(back_table)
story.append(Spacer(1, 0.3*cm))
# ═══════════════════════════════════════════════════════════════
# SECTION 4: CAUSES OF VAGINITIS TABLE
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("4. COMMON CAUSES OF VAGINITIS & ABNORMAL VAGINAL DISCHARGE", h1_style))
story.append(Paragraph(red("HIGH YIELD TABLE – Table 34.2"), highlight_style))
vag_data = [
["Type", "Cause", "Nature of Discharge"],
["Infective\n(Trichomonas)", "Trichomonas vaginalis", "Frothy, yellow, offensive"],
["Infective\n(Monilial)", "Candida albicans", "Curdy white in flakes, pruritic"],
["Bacterial Vaginosis", "BV (Gardnerella)", "Gray-white, fishy odor, nonpruritic"],
["Cervicitis", "Cervical infection", "Mucoid discharge"],
["Atrophic", "Postmenopausal", "Discharge not prominent; irritation prominent"],
["Foreign body", "Forgotten pessary, tampon", "Offensive, copious, purulent, blood-stained"],
["Chemical", "Douches, latex, deodorants", "Soreness > discharge; contact dermatitis"],
["Excretions", "Urine/feces contamination", "Offensive with pruritus"],
["Neoplasms", "Fibroid polyp, malignancy", "Serosanguinous, often offensive"],
]
vag_table = Table(vag_data, colWidths=[3.5*cm, 5*cm, 6.5*cm])
vag_table.setStyle(make_table_style())
story.append(vag_table)
# ═══════════════════════════════════════════════════════════════
# SECTION 5: BREAST IN GYNAECOLOGY
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("5. BREAST IN GYNAECOLOGY – ANATOMY", h1_style))
story.append(Paragraph(b("Key Anatomical Facts:"), h3_style))
for pt in [
"Extends from 2nd to 6th rib in midclavicular line",
"Lies in subcutaneous tissue over pectoralis major and external oblique",
"Contains 15-20 lobes, glandular tissue, duct system, fibrofatty tissues",
b("Axillary tail (of Spence):") + " Prolongation into axillary fossa, sometimes deep to deep fascia",
b("Areola:") + " ~2.5 cm diameter, contains few involuntary muscles and sebaceous (Montgomery's) glands",
b("Nipple:") + " 15-20 lactiferous ducts, muscular projection covered by pigmented skin",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Blood Supply:"), h3_style))
for pt in ["Lateral thoracic branches of axillary artery", "Internal mammary arteries", "Intercostal arteries"]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Lymphatics") + red(" (85% → Axillary nodes):"), h3_style))
lymph_data = [
["Convexity", "Drainage"],
["Lateral convexity", "Anterior axillary nodes"],
["Upper convexity", "Supraclavicular group"],
["Medial convexity", "Mediastinal glands (cross connection between breasts, internal mammary, supraclavicular)"],
["Inferior convexity", "Mediastinal glands, abdominal nodes"],
["Sentinel node", "First lymph node draining the tumor-bearing area"],
]
lymph_table = Table(lymph_data, colWidths=[4.5*cm, 10.5*cm])
lymph_table.setStyle(make_table_style())
story.append(lymph_table)
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(b("Nerve supply:") + " From 4th, 5th, and 6th intercostal nerves.", body_style))
story.append(Paragraph(b("Anatomical Defects:"), h3_style))
for pt in [
b("Small breasts (hypoplasia):") + " Non/underproduction of ovarian estrogens or developmental defect",
b("Huge enlargement:") + " During pregnancy; may be pendulous in parous women due to stretching of fibrous septa",
b("Asymmetry:") + " Common in initial development, usually rectifies within couple of years of menarche. Left slightly larger than right",
b("Accessory breasts (polymastia) / nipple (polythelia):") + " Along milk line (axilla to groin). Most common site is a 'tail' into the axilla",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
# ═══════════════════════════════════════════════════════════════
# SECTION 6: MASTALGIA
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("6. MASTALGIA (Breast Pain)", h1_style))
story.append(Paragraph(
"Common problem; may be cyclic or noncyclic. Noncyclic mastalgia is focal and not related to menstrual cycle.",
body_style
))
story.append(Paragraph(b("Types:"), h3_style))
mast_data = [
["Type", "Features"],
["Cyclic (premenstrual)", "Bilateral, diffuse, relieved with menstruation. Generally requires no specific evaluation"],
["Noncyclic", "Focal, not related to cycle. Thorough evaluation needed to exclude malignancy"],
["Extramammary", "Pain originating outside breast tissue"],
]
mast_table = Table(mast_data, colWidths=[4*cm, 11*cm])
mast_table.setStyle(make_table_style())
story.append(mast_table)
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(b("Diagnosis:") + " Careful history + examination + mammography (women ≥35 years). ~5% of breast cancer patients present with breast pain.", body_style))
story.append(Paragraph(b("Management:"), h3_style))
for pt in [
"Reassure + exclude malignancy",
b("First-line:") + " Acetaminophen/NSAIDs, Bromocriptine, Vitamin E 400 mg daily",
b("Refractory:") + " Cyclic combined estrogen-progestogen preparations / Danazol 200 mg BD / Bromocriptine 2.5–5 mg at bedtime / Tamoxifen 10 mg during luteal phase / Danazol 100–200 mg daily / GnRH analogs",
"Surgery – as indicated",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
# ═══════════════════════════════════════════════════════════════
# SECTION 7: FIBROADENOMA
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("7. FIBROADENOMA", h1_style))
story.append(Paragraph(
red("Most common benign tumor of breast") + " occurring among young women aged " + red("20–35 years."),
body_style
))
for pt in [
"Accidentally discovered by self-palpation (usually asymptomatic)",
"On palpation: uniform, firm, mobile, painless, well-defined mass",
"Commonly bilateral",
b("Giant fibroadenoma:") + " >5 cm",
"Histologically: epithelial and stromal component",
"Reviewed " + b("6 monthly") + "; risk of malignancy < 0.2%",
"FNAB: indicated if mass enlarges or is inconclusive. Ultrasound guided cryoablation is a treatment option",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
# ═══════════════════════════════════════════════════════════════
# SECTION 8: FIBROCYSTIC BREAST DISEASE
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("8. FIBROCYSTIC BREAST DISEASE", h1_style))
story.append(Paragraph(
red("Most common benign breast lesion;") + " generally observed between 20–50 years.",
body_style
))
story.append(Paragraph(b("Etiology:"), h3_style))
story.append(Paragraph("Altered estrogen-progesterone ratio or relative decrease in progesterone. Stress factor and sensitivity to prolactin may be implicated.", body_style))
story.append(Paragraph(b("Histology:"), h3_style))
story.append(Paragraph("Adenosis, fibrosis, ductal epithelial proliferation, papillomatosis.", body_style))
story.append(Paragraph(b("Types and Risk:"), h3_style))
fbd_data = [
["Type", "Features", "Malignancy Risk"],
["Proliferative without atypia", "Terminal ducts and acini of lobules", "Normal (R-R)"],
["Atypical ductal hyperplasia (ADH)", "Nuclear atypia, ductal", "4.5× relative risk"],
["Atypical lobular hyperplasia (ALH)", "Nuclear atypia, lobular", "4.5× relative risk"],
]
fbd_table = Table(fbd_data, colWidths=[5*cm, 6*cm, 4*cm])
fbd_table.setStyle(make_table_style())
story.append(fbd_table)
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(b("Clinical Features:"), h3_style))
for pt in [
"Usually premenopausal; pain throughout cycle, aggravated premenstrually",
"Palpation: nodular lumps, bilateral, prominent in premenstrual phase",
"Physicians too anxious to negate malignancy",
b("Triple assessment:") + " Careful palpation + mammography + USG + aspiration biopsy to exclude malignancy",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Treatment:"), h3_style))
for pt in [
"Reassurance and re-examination at intervals",
"Well-fitting brassiere day and night",
"Acetaminophen/NSAIDs",
"Reduce methylxanthines (coffee, tea, chocolate, soda) and tobacco",
"Vitamin E 400 mg daily",
b("Refractory:") + " Cyclic combined OCP / Danazol 200 mg BD / Bromocriptine 2.5–5 mg / Surgery",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
# ═══════════════════════════════════════════════════════════════
# SECTION 9: BREAST CARCINOMA
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("9. BREAST CARCINOMA – RISK FACTORS, SCREENING & DIAGNOSIS", h1_style))
story.append(Paragraph(
b("Most common (30%) of all cancers") + " and 2nd most common cause of cancer deaths in women. "
+ "1 in 8 obstetric-gynecologic patients is likely to develop breast carcinoma during her adult life in USA/Western countries.",
body_style
))
story.append(Paragraph(b("High Risk Factors") + red(" (Table 34.9 – HIGH YIELD):"), h3_style))
rf_data = [
["Risk Factor", "Details"],
["Early menarche / Late menopause", "Prolonged estrogen exposure"],
["Nulliparity / Late first birth (>35 yrs)", "Prolonged unopposed estrogen"],
["Never breastfed", "Lack of lactation protection"],
["Atypical lobular hyperplasia", "4.5× increased risk"],
["High dose breast/chest irradiation", "Direct carcinogen"],
["Combined OCP / HRT", "Increased age with prolonged use"],
["First-degree relative with breast CA", "Genetic predisposition"],
["BRCA1 (chromosome 17q)", "Hereditary; younger women; often bilateral; ~5% of all breast cancers"],
["BRCA2 (chromosome 13q)", "Hereditary breast cancer gene"],
["CA endometrium, ovary, colon", "Shared cancer predisposition"],
["Carcinoma in the other breast", "Bilateral tendency"],
]
rf_table = Table(rf_data, colWidths=[6*cm, 9*cm])
rf_table.setStyle(make_table_style())
story.append(rf_table)
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(b("Screening Guidelines (ACOG 2000):"), h3_style))
screen_data = [
["Age Group", "BSE", "CBE", "Mammography (MGY)"],
["20–39", "Monthly", "Yearly", "—"],
["40–49", "Monthly", "Yearly", "Every 1–2 years"],
["≥50", "Monthly", "Yearly", "Yearly"],
]
screen_table = Table(screen_data, colWidths=[4*cm, 3*cm, 3*cm, 5*cm])
screen_table.setStyle(make_table_style())
story.append(screen_table)
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(b("Clinical Features of Early Malignancy") + red(" (Table 34.10):"), h3_style))
for pt in [
"Nontender lump in breast (mostly upper outer quadrant)",
"Nonmilky nipple discharge – especially " + red("bloody"),
"Retraction of nipple (previously everted)",
"Indrawing of overlying skin",
"Localized edema of the skin",
"Persistent erosion or crusting of the nipple (refractory to medication)",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Breast Imaging:"), h3_style))
img_data = [
["Modality", "Key Points"],
["Mammography (MGY)", "Most effective screening method. False negative rate 10–15%. Two views (mediolateral + craniocaudal). Radiation risks negligible. Suspicious features: asymmetric densities, spiculated microcalcifications, architectural distortion"],
["Digital Mammography (DM)", "More accurate in women <50 yrs. Combines several images into 3D (tomosynthesis) to reduce false negative rate"],
["Ultrasonography", "Useful to differentiate cystic from solid. Cannot detect microcalcifications. Helps biopsy from deep nonpalpable lesion"],
["MRI", "Used for MRI guided surgery; low specificity (37–97%); use combined with mammography + CBE"],
["PET scan", "Improved tumor detection rate; reduced sensitivity for masses <1 cm"],
]
img_table = Table(img_data, colWidths=[4.5*cm, 10.5*cm])
img_table.setStyle(make_table_style())
story.append(img_table)
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(b("Breast Biopsy:"), h3_style))
bx_data = [
["Type", "Details"],
["FNAC (Fine Needle Aspiration Cytology)", "22G needle. Simple, cheap, no morbidity. False negative up to 20%. CANNOT differentiate invasive from non-invasive carcinoma"],
["Core Needle Biopsy (CNB)", "Histologic diagnosis. Highly accurate (98%) and specific (100%). Uses tactile/ultrasound/stereotactic guidance"],
["Open Biopsy – Excisional", "Lesion completely removed under local anesthesia. Done when FNAC inconclusive"],
["Open Biopsy – Incisional", "Only part of mass excised. Confirmation of diagnosis only"],
]
bx_table = Table(bx_data, colWidths=[5*cm, 10*cm])
bx_table.setStyle(make_table_style())
story.append(bx_table)
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(b("Triple Test:"), highlight_style))
story.append(Paragraph("CBE + Mammography + Needle biopsy", body_style))
for pt in [
"All three benign → risk of cancer < 1%",
"All three suggestive of cancer → risk 99%",
red("Lump MUST be excised if ANY ONE of the three assessments suggests malignancy"),
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Breast Self-Examination (BSE):"), h3_style))
story.append(Paragraph(
"Should be made a habit, " + red("certainly by age 20.") + " Performed monthly after menses (breasts less tender and engorged). "
"Both sitting position and lying supine. Axillary and supraclavicular areas to be palpated.",
body_style
))
# ═══════════════════════════════════════════════════════════════
# SECTION 10: NIPPLE DISCHARGE
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("10. NIPPLE DISCHARGE", h1_style))
story.append(Paragraph(
"Spontaneous, persistent, and unrelated to lactation is considered " + b("ABNORMAL.") + " Due to benign conditions or breast cancer (20%).",
body_style
))
story.append(Paragraph(b("Evaluate for:") + " Nature of discharge (milky/serous/bloody), unilateral or bilateral, single or multiple ducts, association of any mass, drug intake, premenopausal/postmenopausal.", body_style))
story.append(Paragraph(b("Nipple Discharge Color Chart") + red(" (Table 34.11 – HIGH YIELD):"), h3_style))
nd_data = [
["Color", "Probable Diagnosis"],
["Milky", "Physiologic (lactation), Pregnancy, OCP, Inappropriate (galactorrhea)"],
["Bloody / Sanguineous", "Intraductal papilloma, Intraductal cancer, Malignancy, Duct ectasia, Fibrocystic disease"],
["Clear watery", "Ductal cancer"],
["Green / Yellow", "Ductal ectasia"],
["Purulent", "Infective"],
["Serous or sticky", "Fibrocystic disease"],
]
nd_table = Table(nd_data, colWidths=[4*cm, 11*cm])
nd_table.setStyle(make_table_style())
story.append(nd_table)
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(b("Diagnosis:"), h3_style))
for pt in [
"Careful history-taking, clinical examination, mammography",
b("Occult blood testing") + " and microscopic examination of discharge",
b("Glass slide smear:") + " Fixed immediately for cytologic assessment",
b("Ductoscopy (microendoscopic):") + " Visualize individual discharging duct + biopsy",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Treatment:") + " Subareolar duct excision (microdochectomy).", body_style))
# ═══════════════════════════════════════════════════════════════
# SECTION 11: PSYCHOSEXUAL ISSUES
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("11. PSYCHOSEXUAL ISSUES & FEMALE SEXUALITY", h1_style))
story.append(Paragraph(b("Sexual Function Disorders:"), h3_style))
sfd_data = [
["Disorder", "Notes"],
["Hypoactive sexual desire disorder", "Loss of libido; may be due to depression or traumatic event. Drugs (antipsychotics, lithium, antihypertensives, beta blockers, OCP, phenytoin) can cause disorders of desire"],
["Sexual aversion disorder", "Active aversion/avoidance of sexual contact"],
["Female sexual arousal disorder", "Failure to attain or maintain adequate lubrication-swelling response"],
["Female orgasmic disorder", "Inability to achieve orgasm despite arousal"],
["Sexual pain disorder", "Pain during or after sexual activity"],
["Dyspareunia", "Painful coitus – most common sexual dysfunction"],
["Vaginismus", "Psychogenically mediated involuntary muscle spasm preventing intercourse"],
]
sfd_table = Table(sfd_data, colWidths=[5*cm, 10*cm])
sfd_table.setStyle(make_table_style())
story.append(sfd_table)
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(b("Treatment of Hypoactive Sexual Disorder:"), h3_style))
for pt in [
"Psychosexual therapy to deal with underlying psychologic problems",
"Psychiatric consultation if depressive symptoms present",
"Change contraceptive pill if it is the cause",
"Postmenopausal loss of libido → androgen hormone replacement therapy (HRT)",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
# ═══════════════════════════════════════════════════════════════
# SECTION 12: VAGINISMUS
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("12. VAGINISMUS", h1_style))
story.append(Paragraph(b("Definition:"), h3_style))
story.append(Paragraph(
"Psychogenically mediated " + red("involuntary spasm of vaginal muscles") +
" including the levator ani and/or thigh adductor muscles, resulting in inability to perform penetrative sexual intercourse.",
body_style
))
story.append(Paragraph(b("Etiology:"), h3_style))
eti_data = [
["Primary", "Secondary"],
["Nothing has ever entered the woman's vagina", "Usually appears after childbirth or any other event"],
["Vagina is normal anatomically and physiologically", "Some local painful lesions present"],
["Cause mostly psychosexual in origin", "Lesions include vulvitis, lacerations of hymen, tender scar on perineum or narrow vaginal introitus"],
["Often presence of subconscious fear of intercourse (sexual phobias)", "Entity is usually transient and is relieved when cause is removed"],
]
eti_table = Table(eti_data, colWidths=[7.5*cm, 7.5*cm])
eti_table.setStyle(make_table_style())
story.append(eti_table)
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(b("Clinical Presentation:"), h3_style))
for pt in [
"Avoids vaginal examination and smear",
"Painful sexual intercourse or inability to have coitus",
"May present with infertility",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Diagnosis:"), h3_style))
for pt in [
"Secondary vaginismus is easier to diagnose",
"Primary: examination under anesthesia may be required",
"If " + red("two fingers") + " can be easily introduced through the vaginal introitus → caliber of vagina is proved normal",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Treatment:"), h3_style))
story.append(Paragraph(b("Primary:"), body_style))
for pt in [
b("Psychodynamic therapy:") + " Address main causes of fear; educate and gain confidence of both husband and wife",
b("Behavioral therapy:") + " Dilatation of vaginal introitus digitally followed by introduction of gradually increasing size of dilators (using both hands). Plastic vaginal trainers (pseudopenises) with graduated sizes",
b("Vaginal dilators (pseudopenises):") + " Daily introduction for 1–2 weeks, kept inside for 10–15 minutes before attempting coital act",
b("Surgery:") + " Classic vaginismus needs no surgery. If hymen is found tight → hymenectomy or Fenton's operation (to enlarge introitus; see Ch 35, p. 489)",
]:
story.append(Paragraph(f" • {pt}", sub_bullet_style))
story.append(Paragraph(b("Secondary:"), body_style))
for pt in [
"Treat local lesion medically or surgically",
"Tender scar on perineum or vagina → excise",
]:
story.append(Paragraph(f" • {pt}", sub_bullet_style))
# ═══════════════════════════════════════════════════════════════
# SECTION 13: DYSPAREUNIA
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("13. DYSPAREUNIA", h1_style))
story.append(Paragraph(b("Definition:"), h3_style))
story.append(Paragraph(
"Coital act is difficult or painful. Apareunia = inability to practice coitus. "
+ red("Most common sexual dysfunction.") +
" The two are most often interchangeable.",
body_style
))
story.append(Paragraph(b("Causes by Site:"), h3_style))
dys_data = [
["Type", "Common Causes"],
["Superficial (at entrance)", "Narrow introitus, Tough hymen, Bartholin's gland cyst, Tender perineal scar, Vulvar infection, Urethral pathology, Vulvar vestibulitis syndrome"],
["Vaginal (barrel of vagina)", "Vaginismus, Vaginal septum, Tender scar, Vaginal stenosis (following gynecologic operation/delivery), Secondary vaginal atresia, Tumor, Vaginal atrophy (menopause)"],
["Deep (penis deep into vagina)", "Endometriosis (especially on rectovaginal septum), Chronic cervicitis, Chronic PID, Retroverted fixed uterus, Prolapsed ovary in pouch of Douglas"],
]
dys_table = Table(dys_data, colWidths=[4*cm, 11*cm])
dys_table.setStyle(make_table_style())
story.append(dys_table)
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(b("Treatment:"), h3_style))
for pt in [
"Depends upon cause; sex education of both partners relieves the symptom",
"Tender scar on perineum or vagina → excise",
"Treatment of vaginismus as mentioned",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
# ═══════════════════════════════════════════════════════════════
# SECTION 14: IPV
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("14. INTIMATE PARTNER VIOLENCE (IPV)", h1_style))
story.append(Paragraph(
"Injury inflicted by one intimate partner to the other. Main objective is to cause pain or control the partner's behavior.",
body_style
))
for pt in [
"Compared to older women, " + red("younger women are at greater risk"),
"Violence may include battering, sexual assault, incest, physical trauma, or elder abuse",
red("7–20% of pregnant women") + " may be victims of IPV",
b("IPV screening is a component of antenatal care"),
"Forms of abuse: Physical, emotional, financial, and sexual",
"Neglect is the prevalent form, often perpetrated by family members",
"Patient's disclosure should be validated by the physician",
"Document physical findings of violence; know respective state law",
"Direct patients to support and community resources",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
# ═══════════════════════════════════════════════════════════════
# SECTION 15: ABDOMINOPELVIC LUMP
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("15. ABDOMINOPELVIC LUMP", h1_style))
story.append(Paragraph(
"Pelvic and lower abdominal masses need differentiation by origin (ovary, cervix, uterus) or from other organs (bowel, retroperitoneal tissues). "
+ red("May occur at any age and are of different consistency (solid, cystic)."),
body_style
))
story.append(Paragraph(b("Causes by Age Group") + red(" (Table 34.12 – HIGH YIELD):"), h3_style))
age_data = [
["Age Group", "Causes"],
["Toddlers (<5 yrs)", "Ovarian tumor, Mucocilpos, Full bladder"],
["5 yrs to puberty", "Ovarian tumor, Full bladder, Hematocolpos"],
["Childbearing period", "Pregnancy, Ovarian tumor, Fibroid, Adenomyosis, Chocolate cyst, TO mass, Pelvic hematocele, Pelvic abscess, Encysted peritonitis, Pseudocysis"],
["Postmenopausal", "Ovarian tumor, Pyometra, Sarcoma uteri"],
]
age_table = Table(age_data, colWidths=[4.5*cm, 10.5*cm])
age_table.setStyle(make_table_style())
story.append(age_table)
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(b("Full Bladder Features:"), h3_style))
for pt in [
"Strictly suprapubic; may reach umbilicus",
"Cystic or tense cystic; margins ill-defined",
"Tendency to urge on pressure",
red("Disappears after catheterization"),
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Differential Diagnosis of Common Masses:"), h3_style))
diff_data = [
["Feature", "Fibroid", "Ovarian Tumor", "Adenomyosis", "Encysted Peritonitis", "Pseudocyesis"],
["Growth", "Slow (takes years)", "Slow (months)", "-", "-", "Phantom pregnancy"],
["Menstrual Hx", "Menorrhagia", "Not affected", "Menorrhagia + dysmenorrhea", "Amenorrhea may be present", "Amenorrhea"],
["Feel", "Firm, may be cystic in degeneration", "Cystic/tense cystic/solid", "Soft, tender", "Cystic", "No signs of pregnancy"],
["Surface", "Nodular", "-", "-", "-", "-"],
["Margins", "Well-defined lower pole may not be reached", "Well-defined lower pole may not be reached", "Well-defined", "Ill-defined", "-"],
["On IE", "Uterine in origin; cervix feels firm", "Swelling separated from uterus", "Swelling is uterine; cervix firm + tender", "Uterus separated from cystic mass", "Uterus normal size on EUA"],
["Special", "Pregnancy features absent", "Ascites may be present", "Associated endometriosis may be present", "Mantoux positive; X-ray chest may show lesions", "Sonography: empty uterus, no fetal echo"],
]
diff_table = Table(diff_data, colWidths=[2.5*cm, 2.6*cm, 2.6*cm, 2.6*cm, 2.6*cm, 2.5*cm])
diff_table.setStyle(make_table_style())
story.append(diff_table)
# ═══════════════════════════════════════════════════════════════
# SECTION 16: ADNEXAL MASS
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("16. ADNEXAL MASS", h1_style))
story.append(Paragraph(
"Any mass occupying the region of uterine appendages (adnexa). Major concern is the " + red("ovarian neoplasm (malignancy)."),
body_style
))
story.append(Paragraph(b("Common Adnexal Masses:"), h3_style))
adnex_data = [
["Ovarian", "Uterine", "Gastrointestinal"],
["Ovarian neoplasm", "Myoma", "Diverticulitis"],
["Ovarian cyst", "—", "Appendicular mass (right)"],
["Endometrioma", "—", "—"],
["Tubo-ovarian mass", "—", "—"],
]
adnex_table = Table(adnex_data, colWidths=[5*cm, 5*cm, 5*cm])
adnex_table.setStyle(make_table_style())
story.append(adnex_table)
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(b("Key point:") + " Diagnose by clinical evaluation. Do further investigations as often they have confusing presentations.", note_style))
# ═══════════════════════════════════════════════════════════════
# SECTION 17: QUICK REVISION MNEMONICS
# ═══════════════════════════════════════════════════════════════
story.append(Paragraph("17. QUICK REVISION MNEMONICS & KEY FACTS", h1_style))
mnemo_data = [
["Topic", "Key Fact / Mnemonic"],
["Leukorrhea NEVER", "Purulent | Offensive | Pruritic (P-O-P = absent in true leukorrhea)"],
["Vaginal prolapse", "Does NOT cause backache"],
["Mobile retroverted uterus", "Does NOT produce backache"],
["Fibroadenoma age", "20–35 years (most common benign breast tumor)"],
["Fibrocystic disease age", "20–50 years (most common benign breast lesion)"],
["ADH / ALH risk", "4.5× relative risk of malignancy"],
["BRCA1", "Chromosome 17q — hereditary, younger women, often bilateral"],
["BRCA2", "Chromosome 13q — hereditary breast cancer gene"],
["Mammography false -ve rate", "10–15%"],
["Triple test: all benign", "Risk of cancer < 1%"],
["Triple test: all malignant", "Risk of cancer = 99%"],
["FNAC false -ve rate", "Up to 20%; CANNOT differentiate invasive from non-invasive"],
["CNB accuracy", "98% accurate, 100% specific (for histologic diagnosis)"],
["Vaginismus treatment (P-B-V)", "Psychotherapy → Behavioral therapy → Vaginal dilators (→ Surgery if hymen tight)"],
["Dyspareunia", "Most common sexual dysfunction"],
["BSE start age", "Certainly by age 20"],
["IPV in pregnancy", "7–20% of pregnant women may be victims"],
["Full bladder sign", "Disappears after catheterization"],
["Pelvic backache limit", "Never reaches beyond 4th lumbar vertebra"],
["Cornett sign", "Anterior abdominal wall pain RELIEVED by head raising = not gynecologic"],
["Pseudocyesis diagnosis", "EUA: uterus normal size + Sonography: empty uterus, no fetal echo"],
]
mnemo_table = Table(mnemo_data, colWidths=[5.5*cm, 9.5*cm])
mnemo_table.setStyle(make_table_style(colors.HexColor('#b71c1c')))
story.append(mnemo_table)
story.append(Spacer(1, 0.5*cm))
story.append(HRFlowable(width="100%", thickness=1.5, color=colors.HexColor('#1a237e'), spaceAfter=6))
story.append(Paragraph(
"<i>Source: Dutta's Textbook of Gynaecology – Chapter 34: Special Topics | Compiled by Orris AI for exam preparation</i>",
ParagraphStyle('Footer', parent=styles['Normal'], fontSize=8, textColor=colors.HexColor('#757575'), alignment=TA_CENTER)
))
# Build PDF
doc.build(story)
print(f"PDF created successfully: {OUTPUT}")